|
|
@@ -0,0 +1,458 @@
|
|
|
+<script setup lang="tsx">
|
|
|
+import { computed, ref, useTemplateRef } from 'vue';
|
|
|
+import { NButton, NInputNumber, NSelect } from 'naive-ui';
|
|
|
+import dayjs from 'dayjs';
|
|
|
+// import { fetchGetAllStoreList } from '@/service/api/goods/desk-category';
|
|
|
+// import {
|
|
|
+// fetchGetAllChannelList
|
|
|
+// fetchImportGoods,
|
|
|
+// fetchSetUpChannels
|
|
|
+// } from '@/service/api/goods/store-goods';
|
|
|
+import { fetchGetDictDataList } from '@/service/api/system-manage';
|
|
|
+import {
|
|
|
+ fetchGetAllChannelList,
|
|
|
+ fetchImportGoods,
|
|
|
+ fetchProductList,
|
|
|
+ fetchSetUpChannels
|
|
|
+} from '@/service/api/goods-center/store-goods';
|
|
|
+import { areAllItemsAllFieldsFilled } from '@/utils/zt';
|
|
|
+import { commonExport } from '@/utils/common';
|
|
|
+import { useTable } from '@/components/zt/Table/hooks/useTable';
|
|
|
+import SvgIcon from '@/components/custom/svg-icon.vue';
|
|
|
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
|
|
|
+type Price = { channelId: number | undefined; channelProdPrice: number; id: number; channelName: string };
|
|
|
+const importTemplateRef = useTemplateRef('importTemplateRef');
|
|
|
+
|
|
|
+const options = ref<Api.goods.Channel[]>([]);
|
|
|
+// const TypeName = ['企业用户', 'B端用户', 'C端用户'];
|
|
|
+const statusList = ['商家下架', '上架', '违规下架', '平台审核'];
|
|
|
+const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
|
|
|
+ {
|
|
|
+ type: 'selection',
|
|
|
+ align: 'center',
|
|
|
+ width: 48,
|
|
|
+ fixed: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'supId',
|
|
|
+ title: '商品ID',
|
|
|
+ align: 'center',
|
|
|
+ width: 120
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'shopName',
|
|
|
+ title: '商品图片',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ render: (row: any) => {
|
|
|
+ return <n-image src={row.pic} width={60} height={60}></n-image>;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+ key: 'prodName',
|
|
|
+ title: '商品名称',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'channelCode',
|
|
|
+ title: '业务类型',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'shopName',
|
|
|
+ title: '商户',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'shopName',
|
|
|
+ title: '价格',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'shopSkuStocks',
|
|
|
+ title: '库存',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'status',
|
|
|
+ title: '状态',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ render: (row: any) => {
|
|
|
+ return (
|
|
|
+ <div class="flex items-center justify-center">
|
|
|
+ <n-badge color={row.status == 1 ? 'green' : 'red'} value={row.status} dot />
|
|
|
+ <span class="ml-2">{statusList[row.status]}</span>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'updateTime',
|
|
|
+ title: '更新时间',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ }
|
|
|
+];
|
|
|
+const PriceColumns: NaiveUI.TableColumn<Price>[] = [
|
|
|
+ {
|
|
|
+ title: '销售渠道',
|
|
|
+ key: 'channelId',
|
|
|
+ align: 'center',
|
|
|
+ width: 250,
|
|
|
+ render: row => {
|
|
|
+ return (
|
|
|
+ <NSelect
|
|
|
+ options={options.value}
|
|
|
+ labelField="channelName"
|
|
|
+ valueField="id"
|
|
|
+ value={row.channelId}
|
|
|
+ clearable
|
|
|
+ onUpdate:value={value => {
|
|
|
+ options.value.map(it => {
|
|
|
+ if (it.id == row.channelId) {
|
|
|
+ it.disabled = false;
|
|
|
+ }
|
|
|
+ return it;
|
|
|
+ });
|
|
|
+ row.channelId = value ? Number(value) : undefined;
|
|
|
+ }}
|
|
|
+ onUpdate:show={value => {
|
|
|
+ options.value.map(it => {
|
|
|
+ if (it.id == row.channelId) {
|
|
|
+ if (value) {
|
|
|
+ it.disabled = false;
|
|
|
+ }
|
|
|
+ if (!value) {
|
|
|
+ it.disabled = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return it;
|
|
|
+ });
|
|
|
+ }}
|
|
|
+ ></NSelect>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '售价(元)',
|
|
|
+ key: 'channelProdPrice',
|
|
|
+ align: 'center',
|
|
|
+ width: 250,
|
|
|
+ render: row => {
|
|
|
+ return (
|
|
|
+ <NInputNumber
|
|
|
+ value={row.channelProdPrice}
|
|
|
+ precision={2}
|
|
|
+ onUpdate:value={value => {
|
|
|
+ row.channelProdPrice = Number(value);
|
|
|
+ }}
|
|
|
+ min={0}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: () => {
|
|
|
+ return (
|
|
|
+ <div onClick={() => handleAddPrice()} class={'w-full flex items-center justify-center'}>
|
|
|
+ <SvgIcon
|
|
|
+ icon={'proicons:add-square'}
|
|
|
+ class={'cursor-pointer text-24px'}
|
|
|
+ style={'color:var(--n-color)'}
|
|
|
+ ></SvgIcon>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ key: 'action',
|
|
|
+ width: 80,
|
|
|
+ align: 'center',
|
|
|
+ render: row => {
|
|
|
+ return (
|
|
|
+ <div onClick={() => handleDelPrice(row.id)} class={'w-full flex items-center justify-center'}>
|
|
|
+ <SvgIcon
|
|
|
+ icon={'proicons:subtract-square'}
|
|
|
+ class={'cursor-pointer text-24px'}
|
|
|
+ style={'color:#f5222d'}
|
|
|
+ ></SvgIcon>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+];
|
|
|
+const PriceData = ref<Price[]>([]);
|
|
|
+const selectData = ref<Api.goods.ShopSku>();
|
|
|
+const [registerTable, { getTableCheckedRowKeys, refresh, getTableData, getSeachForm, setTableLoading }] = useTable({
|
|
|
+ searchFormConfig: {
|
|
|
+ schemas: [
|
|
|
+ // {
|
|
|
+ // label: '门店名称',
|
|
|
+ // component: 'ApiSelect',
|
|
|
+ // field: 'shopId',
|
|
|
+ // componentProps: {
|
|
|
+ // api: fetchGetAllStoreList,
|
|
|
+ // labelFeild: 'shopName',
|
|
|
+ // valueFeild: 'shopId'
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ label: '关键词',
|
|
|
+ component: 'NInput',
|
|
|
+ field: 'keywords'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '业务类型',
|
|
|
+ field: 'channelCode',
|
|
|
+ component: 'ApiSelect',
|
|
|
+ componentProps: {
|
|
|
+ api: fetchGetDictDataList,
|
|
|
+ labelFeild: 'name',
|
|
|
+ valueFeild: 'value',
|
|
|
+ resultFeild: 'data.list',
|
|
|
+ params: {
|
|
|
+ typeCode: 'sys_business_type'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '状态',
|
|
|
+ field: 'status',
|
|
|
+ component: 'NSelect',
|
|
|
+ componentProps: {
|
|
|
+ options: [
|
|
|
+ {
|
|
|
+ label: '上架',
|
|
|
+ value: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '商家下架',
|
|
|
+ value: 0
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '违规下架',
|
|
|
+ value: 2
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '平台审核',
|
|
|
+ value: 3
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '分类',
|
|
|
+ component: 'NCascader',
|
|
|
+ field: 'skuName',
|
|
|
+ componentProps: {}
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '更新时间',
|
|
|
+ component: 'NDatePicker',
|
|
|
+ field: 'createTime',
|
|
|
+ componentProps: {
|
|
|
+ type: 'datetimerange',
|
|
|
+ defaultTime: ['00:00:00', '23:59:59']
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '价格范围',
|
|
|
+ component: 'NInput',
|
|
|
+ field: 'price',
|
|
|
+ componentProps: {
|
|
|
+ separator: '-',
|
|
|
+ pair: true,
|
|
|
+ placeholder: ['最低价', '最高价'],
|
|
|
+ allowInput: (value: string) => !value || /^\d+$/.test(value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ inline: false,
|
|
|
+ size: 'small',
|
|
|
+ labelPlacement: 'left',
|
|
|
+ isFull: false
|
|
|
+ },
|
|
|
+ tableConfig: {
|
|
|
+ keyField: 'id',
|
|
|
+ title: '商品列表',
|
|
|
+ showAddButton: false,
|
|
|
+ scrollX: 1800,
|
|
|
+ fieldMapToTime: [
|
|
|
+ ['price', ['minPrice', 'maxPrice']],
|
|
|
+ ['createTime', ['startTime', 'endTime']]
|
|
|
+ ]
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const [
|
|
|
+ registerModalPrice,
|
|
|
+ { openModal: openPriceModal, setSubLoading: setSubModalLoding, closeModal: closePriceModal }
|
|
|
+] = useModal({
|
|
|
+ title: '设置渠道及价格',
|
|
|
+ width: 800,
|
|
|
+ height: 300
|
|
|
+});
|
|
|
+
|
|
|
+const isDisabledExport = computed(() => {
|
|
|
+ return !getTableCheckedRowKeys().length;
|
|
|
+});
|
|
|
+const tableData = computed(() => {
|
|
|
+ return getTableData();
|
|
|
+});
|
|
|
+
|
|
|
+async function handleSubmitImport(file: File) {
|
|
|
+ const { error } = await fetchImportGoods({ file });
|
|
|
+ if (!error) {
|
|
|
+ importTemplateRef.value?.closeModal();
|
|
|
+ }
|
|
|
+ importTemplateRef.value?.setSubLoading(false);
|
|
|
+}
|
|
|
+function openImportModal() {
|
|
|
+ importTemplateRef.value?.openModal();
|
|
|
+}
|
|
|
+function handleModalPrice(row: Api.goods.ShopSku) {
|
|
|
+ selectData.value = row;
|
|
|
+
|
|
|
+ if (row.channelVOS) {
|
|
|
+ PriceData.value = row.channelVOS?.map((it: Api.government.ChannelVO) => {
|
|
|
+ options.value.map(its => {
|
|
|
+ if (its.id == it.channelId) {
|
|
|
+ its.disabled = true;
|
|
|
+ }
|
|
|
+ return its;
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ channelName: it.channelName,
|
|
|
+ channelId: Number(it.channelId),
|
|
|
+ channelProdPrice: Number(it.price),
|
|
|
+ id: Number(it.id)
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ openPriceModal();
|
|
|
+}
|
|
|
+function handleAddPrice() {
|
|
|
+ if (PriceData.value.length == 3) {
|
|
|
+ window.$message?.error('最多只能添加3条数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ PriceData.value.push({
|
|
|
+ channelName: '',
|
|
|
+ channelId: undefined,
|
|
|
+ channelProdPrice: 1,
|
|
|
+ id: dayjs().valueOf()
|
|
|
+ });
|
|
|
+}
|
|
|
+function handleDelPrice(id: number) {
|
|
|
+ PriceData.value = PriceData.value.filter(item => item.id != id);
|
|
|
+}
|
|
|
+
|
|
|
+async function handleSubmitPrice() {
|
|
|
+ if (!PriceData.value.length) {
|
|
|
+ window.$message?.error('最少填写一条数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!areAllItemsAllFieldsFilled(PriceData.value)) {
|
|
|
+ window.$message?.error('请填写完整数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ setSubModalLoding(true);
|
|
|
+ const form = {
|
|
|
+ shopId: selectData.value?.shopId,
|
|
|
+ skuId: selectData.value?.skuId,
|
|
|
+ // purchasePrice: selectData.value?.channelProdList?.length ? selectData.value.channelProdList[0].purchasePrice : null,
|
|
|
+ // deliveryPrice: selectData.value?.channelProdList?.length ? selectData.value.channelProdList[0].deliveryPrice : null,
|
|
|
+ purchasePrice: selectData.value?.channelVOS?.length ? selectData.value.channelVOS[0].purchasePrice : null,
|
|
|
+ deliveryPrice: selectData.value?.channelVOS?.length ? selectData.value.channelVOS[0].deliveryPrice : null,
|
|
|
+ setChannelPriceFormList: PriceData.value.map(item => {
|
|
|
+ return {
|
|
|
+ channelName: item.channelName,
|
|
|
+ channelId: item.channelId,
|
|
|
+ channelProdPrice: item.channelProdPrice
|
|
|
+ };
|
|
|
+ })
|
|
|
+ };
|
|
|
+ const { error } = await fetchSetUpChannels(form);
|
|
|
+ if (!error) {
|
|
|
+ closePriceModal();
|
|
|
+ refresh();
|
|
|
+ PriceData.value = [];
|
|
|
+ options.value.map(it => (it.disabled = false));
|
|
|
+ } else {
|
|
|
+ setSubModalLoding(false);
|
|
|
+ }
|
|
|
+ console.log(PriceData.value, 'asdsad');
|
|
|
+}
|
|
|
+async function getData() {
|
|
|
+ const { data, error } = await fetchGetAllChannelList();
|
|
|
+ if (!error) {
|
|
|
+ options.value = data;
|
|
|
+ }
|
|
|
+}
|
|
|
+getData();
|
|
|
+async function handleExport() {
|
|
|
+ setTableLoading(true);
|
|
|
+ try {
|
|
|
+ await commonExport('/shop/shopProd/export', getSeachForm(), '商品列表.xlsx');
|
|
|
+ } finally {
|
|
|
+ setTableLoading(false);
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <LayoutTable>
|
|
|
+ <ZTable :columns="columns" :api="fetchProductList" @register="registerTable">
|
|
|
+ <template #op="{ row }">
|
|
|
+ <NButton size="small" ghost type="primary" @click="handleModalPrice(row)">设置渠道及价格</NButton>
|
|
|
+ </template>
|
|
|
+ <template #prefix="{ loading }">
|
|
|
+ <NSpace>
|
|
|
+ <NButton size="small" @click="openImportModal">导入商品销售渠道及价格</NButton>
|
|
|
+ <NButton size="small" :disabled="tableData.length == 0" :loading="loading" @click="handleExport">
|
|
|
+ 导出全部
|
|
|
+ </NButton>
|
|
|
+ <NButton size="small" :disabled="isDisabledExport">导出选中数据</NButton>
|
|
|
+ <NButton size="small">修改记录</NButton>
|
|
|
+ </NSpace>
|
|
|
+ </template>
|
|
|
+ </ZTable>
|
|
|
+ <ZImportTemplate
|
|
|
+ ref="importTemplateRef"
|
|
|
+ url="/smqjh-pms/api/v1/channelProd/template/download"
|
|
|
+ template-text="商品渠道及价格导入模版.xlsx"
|
|
|
+ modal-text="导入商品销售渠道及价格"
|
|
|
+ @submit="handleSubmitImport"
|
|
|
+ ></ZImportTemplate>
|
|
|
+ <BasicModal @register="registerModalPrice" @ok="handleSubmitPrice">
|
|
|
+ <NDataTable :columns="PriceColumns" :data="PriceData" :row-key="row => row.id" />
|
|
|
+ </BasicModal>
|
|
|
+ </LayoutTable>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped></style>
|