|
@@ -7,7 +7,9 @@
|
|
|
|
|
|
|
|
<div class="action-bar">
|
|
<div class="action-bar">
|
|
|
<el-button @click="handleDownloadTemplate">标准导入文件下载</el-button>
|
|
<el-button @click="handleDownloadTemplate">标准导入文件下载</el-button>
|
|
|
- <el-button type="danger" @click="handleImportProducts">导入订单商品</el-button>
|
|
|
|
|
|
|
+ <el-button type="danger" @click="handleImportProducts" :loading="importing">导入订单商品</el-button>
|
|
|
|
|
+ <!-- 隐藏的文件输入框 -->
|
|
|
|
|
+ <input ref="fileInputRef" type="file" accept=".xlsx,.xls" style="display: none" @change="handleFileChange" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="section-title">商品信息</div>
|
|
<div class="section-title">商品信息</div>
|
|
@@ -31,16 +33,12 @@
|
|
|
<div class="product-price">¥{{ item.price }}</div>
|
|
<div class="product-price">¥{{ item.price }}</div>
|
|
|
<div class="product-quantity">{{ item.quantity }}</div>
|
|
<div class="product-quantity">{{ item.quantity }}</div>
|
|
|
<div class="product-subtotal">¥{{ item.subtotal }}</div>
|
|
<div class="product-subtotal">¥{{ item.subtotal }}</div>
|
|
|
- <div class="product-action">
|
|
|
|
|
- <span v-if="item.offShelf" class="off-shelf">已下架</span>
|
|
|
|
|
- </div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="summary-info">
|
|
<div class="summary-info">
|
|
|
共导入<em>{{ totalProducts }}</em
|
|
共导入<em>{{ totalProducts }}</em
|
|
|
- >件商品,不可下单商品<em>{{ unavailableProducts }}</em
|
|
|
|
|
- >件
|
|
|
|
|
|
|
+ >件商品
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="address-section">
|
|
<div class="address-section">
|
|
@@ -96,8 +94,7 @@
|
|
|
>件商品
|
|
>件商品
|
|
|
</div>
|
|
</div>
|
|
|
<div class="total-amount">
|
|
<div class="total-amount">
|
|
|
- 合计: <span class="amount">¥{{ totalAmount }}</span
|
|
|
|
|
- ><span class="freight">(含运费{{ freight }}元)</span>
|
|
|
|
|
|
|
+ 合计: <span class="amount">¥{{ totalAmount }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
<el-button type="danger" size="large" @click="handleSubmitOrder">去下单</el-button>
|
|
<el-button type="danger" size="large" @click="handleSubmitOrder">去下单</el-button>
|
|
|
</div>
|
|
</div>
|
|
@@ -105,81 +102,52 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed } from 'vue';
|
|
|
|
|
|
|
+import { ref, computed, onMounted } from 'vue';
|
|
|
import { useRouter } from 'vue-router';
|
|
import { useRouter } from 'vue-router';
|
|
|
import { Picture, Location, Setting, ArrowDown } from '@element-plus/icons-vue';
|
|
import { Picture, Location, Setting, ArrowDown } from '@element-plus/icons-vue';
|
|
|
import { ElMessage } from 'element-plus';
|
|
import { ElMessage } from 'element-plus';
|
|
|
|
|
+import * as XLSX from 'xlsx';
|
|
|
|
|
+import { getPcProductPage } from '@/api/search/index';
|
|
|
|
|
+import { getAddressList } from '@/api/pc/enterprise/index';
|
|
|
|
|
+import { pcOrdersubmit } from '@/api/goods/index';
|
|
|
|
|
+import templateFile from './商品导入模版.xlsx?url';
|
|
|
|
|
|
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
|
const selectedAddress = ref(0);
|
|
const selectedAddress = ref(0);
|
|
|
const showAllAddress = ref(false);
|
|
const showAllAddress = ref(false);
|
|
|
const deliveryDate = ref('');
|
|
const deliveryDate = ref('');
|
|
|
const orderRemark = ref('');
|
|
const orderRemark = ref('');
|
|
|
|
|
+const importing = ref(false);
|
|
|
|
|
+const fileInputRef = ref<HTMLInputElement | null>(null);
|
|
|
|
|
+
|
|
|
|
|
+const productList = ref<any[]>([]);
|
|
|
|
|
+
|
|
|
|
|
+const addressList = ref<any[]>([]);
|
|
|
|
|
|
|
|
-const productList = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- image: '',
|
|
|
|
|
- name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)',
|
|
|
|
|
- spec1: '规格02',
|
|
|
|
|
- spec2: '规格01',
|
|
|
|
|
- price: '181',
|
|
|
|
|
- quantity: 1,
|
|
|
|
|
- subtotal: '181',
|
|
|
|
|
- offShelf: false
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- image: '',
|
|
|
|
|
- name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)',
|
|
|
|
|
- spec1: '规格02',
|
|
|
|
|
- spec2: '规格01',
|
|
|
|
|
- price: '181',
|
|
|
|
|
- quantity: 1,
|
|
|
|
|
- subtotal: '181',
|
|
|
|
|
- offShelf: true
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 3,
|
|
|
|
|
- image: '',
|
|
|
|
|
- name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)',
|
|
|
|
|
- spec1: '规格02',
|
|
|
|
|
- spec2: '规格01',
|
|
|
|
|
- price: '181',
|
|
|
|
|
- quantity: 1,
|
|
|
|
|
- subtotal: '181',
|
|
|
|
|
- offShelf: false
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 4,
|
|
|
|
|
- image: '',
|
|
|
|
|
- name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)',
|
|
|
|
|
- spec1: '规格02',
|
|
|
|
|
- spec2: '规格01',
|
|
|
|
|
- price: '181',
|
|
|
|
|
- quantity: 1,
|
|
|
|
|
- subtotal: '181',
|
|
|
|
|
- offShelf: false
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 5,
|
|
|
|
|
- image: '',
|
|
|
|
|
- name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)',
|
|
|
|
|
- spec1: '规格02',
|
|
|
|
|
- spec2: '规格01',
|
|
|
|
|
- price: '181',
|
|
|
|
|
- quantity: 1,
|
|
|
|
|
- subtotal: '181',
|
|
|
|
|
- offShelf: true
|
|
|
|
|
|
|
+const loadAddressList = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res: any = await getAddressList();
|
|
|
|
|
+ const list: any[] = res?.rows || res?.data || [];
|
|
|
|
|
+ addressList.value = list.map((item: any) => ({
|
|
|
|
|
+ ...item,
|
|
|
|
|
+ province: item.province || '',
|
|
|
|
|
+ city: item.city || '',
|
|
|
|
|
+ district: item.district || '',
|
|
|
|
|
+ detail: item.detailAddress || '',
|
|
|
|
|
+ company: item.consignee || '',
|
|
|
|
|
+ phone: item.phone || ''
|
|
|
|
|
+ }));
|
|
|
|
|
+ if (addressList.value.length > 0) {
|
|
|
|
|
+ selectedAddress.value = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ ElMessage.error('收货地址加载失败');
|
|
|
}
|
|
}
|
|
|
-]);
|
|
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
-const addressList = ref([
|
|
|
|
|
- { province: '广东省', city: '广州市', district: '萝岗区科学城11号', detail: '', company: '中国南方电网有限公司', phone: '18062697722' },
|
|
|
|
|
- { province: '广东省', city: '广州市', district: '萝岗区科学城11号', detail: '', company: '中国南方电网有限公司', phone: '18062697722' },
|
|
|
|
|
- { province: '广东省', city: '广州市', district: '萝岗区科学城11号', detail: '', company: '中国南方电网有限公司', phone: '18062697722' },
|
|
|
|
|
- { province: '广东省', city: '广州市', district: '萝岗区科学城11号', detail: '', company: '中国南方电网有限公司', phone: '18062697722' }
|
|
|
|
|
-]);
|
|
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ loadAddressList();
|
|
|
|
|
+});
|
|
|
|
|
|
|
|
const totalProducts = computed(() => productList.value.length);
|
|
const totalProducts = computed(() => productList.value.length);
|
|
|
const unavailableProducts = computed(() => productList.value.filter((p) => p.offShelf).length);
|
|
const unavailableProducts = computed(() => productList.value.filter((p) => p.offShelf).length);
|
|
@@ -187,29 +155,183 @@ const availableProducts = computed(() => productList.value.filter((p) => !p.offS
|
|
|
const totalAmount = computed(() =>
|
|
const totalAmount = computed(() =>
|
|
|
productList.value
|
|
productList.value
|
|
|
.filter((p) => !p.offShelf)
|
|
.filter((p) => !p.offShelf)
|
|
|
- .reduce((sum, p) => sum + parseFloat(p.subtotal), 0)
|
|
|
|
|
|
|
+ .reduce((sum, p) => sum + parseFloat(p.subtotal || '0'), 0)
|
|
|
.toFixed(2)
|
|
.toFixed(2)
|
|
|
);
|
|
);
|
|
|
const freight = ref('12.00');
|
|
const freight = ref('12.00');
|
|
|
|
|
|
|
|
|
|
+/** 下载标准导入模板 */
|
|
|
const handleDownloadTemplate = () => {
|
|
const handleDownloadTemplate = () => {
|
|
|
|
|
+ const link = document.createElement('a');
|
|
|
|
|
+ link.href = templateFile;
|
|
|
|
|
+ link.download = '商品导入模版.xlsx';
|
|
|
|
|
+ document.body.appendChild(link);
|
|
|
|
|
+ link.click();
|
|
|
|
|
+ document.body.removeChild(link);
|
|
|
ElMessage.success('开始下载模板文件');
|
|
ElMessage.success('开始下载模板文件');
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+/** 点击按钮触发文件选择 */
|
|
|
const handleImportProducts = () => {
|
|
const handleImportProducts = () => {
|
|
|
- ElMessage.info('请选择要导入的文件');
|
|
|
|
|
|
|
+ fileInputRef.value?.click();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+/** 解析 Excel 文件,提取商品编号和数量,调用接口查询商品信息 */
|
|
|
|
|
+const handleFileChange = async (event: Event) => {
|
|
|
|
|
+ const file = (event.target as HTMLInputElement).files?.[0];
|
|
|
|
|
+ if (!file) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 重置 input,允许再次选择同一文件
|
|
|
|
|
+ (event.target as HTMLInputElement).value = '';
|
|
|
|
|
+
|
|
|
|
|
+ importing.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const arrayBuffer = await file.arrayBuffer();
|
|
|
|
|
+ const workbook = XLSX.read(arrayBuffer, { type: 'array' });
|
|
|
|
|
+ const sheetName = workbook.SheetNames[0];
|
|
|
|
|
+ const worksheet = workbook.Sheets[sheetName];
|
|
|
|
|
+ // 从第1行(索引0)读取数据,转为对象数组(第一行为表头)
|
|
|
|
|
+ const rows: any[] = XLSX.utils.sheet_to_json(worksheet, { defval: '' });
|
|
|
|
|
+
|
|
|
|
|
+ if (rows.length === 0) {
|
|
|
|
|
+ ElMessage.warning('Excel 文件中没有数据');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 找到商品编号和商品数量对应的列名(兼容中英文列名)
|
|
|
|
|
+ const firstRow = rows[0];
|
|
|
|
|
+ const keys = Object.keys(firstRow);
|
|
|
|
|
+ const productNoKey = keys.find((k) => k.includes('商品编号') || k.toLowerCase().includes('productno') || k.toLowerCase() === 'no');
|
|
|
|
|
+ const quantityKey = keys.find(
|
|
|
|
|
+ (k) => k.includes('商品数量') || k.includes('数量') || k.toLowerCase().includes('quantity') || k.toLowerCase() === 'qty'
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (!productNoKey) {
|
|
|
|
|
+ ElMessage.error('未找到"商品编号"列,请检查模板格式');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!quantityKey) {
|
|
|
|
|
+ ElMessage.error('未找到"商品数量"列,请检查模板格式');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 提取有效行(商品编号不为空)
|
|
|
|
|
+ const validRows = rows.filter((row) => row[productNoKey] !== '' && row[productNoKey] != null);
|
|
|
|
|
+ if (validRows.length === 0) {
|
|
|
|
|
+ ElMessage.warning('Excel 中没有有效的商品编号');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 逐个查询商品信息
|
|
|
|
|
+ const results: any[] = [];
|
|
|
|
|
+ for (const row of validRows) {
|
|
|
|
|
+ const productNo = String(row[productNoKey]).trim();
|
|
|
|
|
+ const quantity = Number(row[quantityKey]) || 1;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getPcProductPage({ productNo, pageNum: 1, pageSize: 1 });
|
|
|
|
|
+ if (res.code === 200 && res.rows && res.rows.length > 0) {
|
|
|
|
|
+ const product = res.rows[0];
|
|
|
|
|
+ const price = String(product.price || product.memberPrice || product.minSellingPrice || '0');
|
|
|
|
|
+ const subtotal = (parseFloat(price) * quantity).toFixed(2);
|
|
|
|
|
+ results.push({
|
|
|
|
|
+ id: product.id,
|
|
|
|
|
+ image: product.productImage || '',
|
|
|
|
|
+ name: product.itemName || productNo,
|
|
|
|
|
+ spec1: product.productNo || productNo,
|
|
|
|
|
+ spec2: product.unitName || '',
|
|
|
|
|
+ price,
|
|
|
|
|
+ quantity,
|
|
|
|
|
+ subtotal,
|
|
|
|
|
+ offShelf: product.productStatus === '0'
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 接口未找到商品,提示下架但不加入列表
|
|
|
|
|
+ ElMessage.warning(`商品 ${productNo} 已下架或不存在,未加入当前订单`);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ ElMessage.warning(`商品 ${productNo} 查询失败,可能已下架,未加入当前订单`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ productList.value = results;
|
|
|
|
|
+ ElMessage.success(`成功导入 ${results.length} 件商品`);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ ElMessage.error('文件解析失败,请检查文件格式');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ importing.value = false;
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
const handleConfirmAddress = () => {
|
|
const handleConfirmAddress = () => {
|
|
|
ElMessage.success('已确认收货地址');
|
|
ElMessage.success('已确认收货地址');
|
|
|
};
|
|
};
|
|
|
const handleManageAddress = () => {
|
|
const handleManageAddress = () => {
|
|
|
router.push('/easybuv');
|
|
router.push('/easybuv');
|
|
|
};
|
|
};
|
|
|
-const handleSubmitOrder = () => {
|
|
|
|
|
|
|
+const handleSubmitOrder = async () => {
|
|
|
if (availableProducts.value === 0) {
|
|
if (availableProducts.value === 0) {
|
|
|
ElMessage.warning('没有可下单的商品');
|
|
ElMessage.warning('没有可下单的商品');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- ElMessage.success('订单提交成功');
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (selectedAddress.value === null || selectedAddress.value === undefined) {
|
|
|
|
|
+ ElMessage.warning('请选择收货地址');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const address = addressList.value[selectedAddress.value];
|
|
|
|
|
+ if (!address || !address.id) {
|
|
|
|
|
+ ElMessage.warning('收货地址信息不完整');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 构建商品信息
|
|
|
|
|
+ const productInfo = productList.value
|
|
|
|
|
+ .filter((p) => !p.offShelf)
|
|
|
|
|
+ .map((p) => ({
|
|
|
|
|
+ productId: p.id,
|
|
|
|
|
+ productNum: p.quantity
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ if (productInfo.length === 0) {
|
|
|
|
|
+ ElMessage.warning('没有可下单的商品');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 格式化配送时间
|
|
|
|
|
+ let deliveryDateStr = '';
|
|
|
|
|
+ if (deliveryDate.value) {
|
|
|
|
|
+ const date = new Date(deliveryDate.value);
|
|
|
|
|
+ deliveryDateStr = date.toISOString().split('T')[0]; // yyyy-MM-dd
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const submitData = {
|
|
|
|
|
+ shippingAddressId: address.id,
|
|
|
|
|
+ deliveryDate: deliveryDateStr,
|
|
|
|
|
+ payType: '', // 支付方式,根据业务需求填写
|
|
|
|
|
+ expenseType: '', // 费用类型,根据业务需求填写
|
|
|
|
|
+ purchaseReason: '', // 采购事由,根据业务需求填写
|
|
|
|
|
+ remark: orderRemark.value,
|
|
|
|
|
+ shippingFee: parseFloat(freight.value) || 0,
|
|
|
|
|
+ placeOrderType: 2, // 2为直接下单
|
|
|
|
|
+ productShoppingCartId: [], // 批量下单不走购物车
|
|
|
|
|
+ productInfo: productInfo
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res: any = await pcOrdersubmit(submitData);
|
|
|
|
|
+ if (res.code === 200) {
|
|
|
|
|
+ ElMessage.success('订单提交成功');
|
|
|
|
|
+ // 清空商品列表
|
|
|
|
|
+ productList.value = [];
|
|
|
|
|
+ // 可以跳转到订单列表页
|
|
|
|
|
+ // router.push('/order/list');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ElMessage.error(res.msg || '订单提交失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ ElMessage.error(error?.message || '订单提交失败,请稍后重试');
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|