|
|
@@ -0,0 +1,516 @@
|
|
|
+<script setup lang="tsx">
|
|
|
+import { computed, ref, useTemplateRef } from 'vue';
|
|
|
+import { NButton, NInputNumber, NSelect } from 'naive-ui';
|
|
|
+// import dayjs from 'dayjs';
|
|
|
+// import { fetchGetStoreList } from '@/service/api/xsb-manage/store-info';
|
|
|
+// import {
|
|
|
+// fetchGetAllChannelList
|
|
|
+// fetchImportGoods,
|
|
|
+// fetchSetUpChannels
|
|
|
+// } from '@/service/api/goods/store-goods';
|
|
|
+// import { fetchGetDictDataList } from '@/service/api/system-manage';
|
|
|
+import {
|
|
|
+ // fetchGetAllChannelList,
|
|
|
+ fetchChangeStatus,
|
|
|
+ fetchGetChannelList,
|
|
|
+ fetchImportGoods,
|
|
|
+ fetchProductList,
|
|
|
+ fetchSetUpChannels
|
|
|
+} from '@/service/api/goods-center/virtual-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: 'productNumber',
|
|
|
+ title: '商品ID',
|
|
|
+ align: 'left',
|
|
|
+ width: 200
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'productName',
|
|
|
+ title: '商品名称',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'productType',
|
|
|
+ title: '商品类型',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'businessType',
|
|
|
+ title: '业务类型',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // {
|
|
|
+ // key: 'shopName',
|
|
|
+ // title: '商户',
|
|
|
+ // align: 'center',
|
|
|
+ // width: 120,
|
|
|
+ // ellipsis: {
|
|
|
+ // tooltip: true
|
|
|
+ // },
|
|
|
+ // render: (row: any) => {
|
|
|
+ // return '-';
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ key: 'faceValue',
|
|
|
+ title: '面值(元)',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'purchasePrice',
|
|
|
+ title: '采购价',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'pmsVideoChannelPrices',
|
|
|
+ title: '价格',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ render: (row: any) => {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ {row.pmsVideoChannelPrices.map((it: Api.government.ChannelVO) => {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ {it.channelName}:¥{it.price}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'inventory',
|
|
|
+ title: '库存',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'productStatus',
|
|
|
+ title: '状态',
|
|
|
+ align: 'center',
|
|
|
+ width: 120,
|
|
|
+ render: (row: any) => {
|
|
|
+ return (
|
|
|
+ <div class="flex items-center justify-center">
|
|
|
+ <n-badge color={row.productStatus == 0 ? 'green' : 'red'} value={row.productStatus} dot />
|
|
|
+ <span class="ml-2">{statusList[row.productStatus]}</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;
|
|
|
+ row.channelName = options.value.find(it => it.id == row.channelId)?.channelName;
|
|
|
+ }}
|
|
|
+ onUpdate:show={value => {
|
|
|
+ console.log('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, index) => {
|
|
|
+ return (
|
|
|
+ <div onClick={() => handleDelPrice(index)} 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, { refresh, getTableData, getSeachForm, setTableLoading }] = useTable({
|
|
|
+ searchFormConfig: {
|
|
|
+ schemas: [
|
|
|
+ // {
|
|
|
+ // label: '门店名称',
|
|
|
+ // component: 'ApiSelect',
|
|
|
+ // field: 'shopId',
|
|
|
+ // componentProps: {
|
|
|
+ // api: fetchGetStoreList,
|
|
|
+ // resultFeild: 'data.list',
|
|
|
+ // 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: 'productStatus',
|
|
|
+ component: 'NSelect',
|
|
|
+ componentProps: {
|
|
|
+ options: [
|
|
|
+ {
|
|
|
+ label: '下架',
|
|
|
+ value: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '上架',
|
|
|
+ value: 0
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // {
|
|
|
+ // 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: 'skuId',
|
|
|
+ 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.pmsVideoChannelPrices) {
|
|
|
+ PriceData.value = row.pmsVideoChannelPrices?.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: 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()
|
|
|
+ });
|
|
|
+ console.log(PriceData.value);
|
|
|
+}
|
|
|
+function handleDelPrice(index: number) {
|
|
|
+ // PriceData.value = PriceData.value.filter(item => item.id != id);
|
|
|
+ PriceData.value.splice(index, 1);
|
|
|
+ console.log('删除', index, PriceData.value);
|
|
|
+}
|
|
|
+
|
|
|
+async function handleSubmitPrice() {
|
|
|
+ console.log(PriceData.value);
|
|
|
+
|
|
|
+ if (!PriceData.value.length) {
|
|
|
+ window.$message?.error('最少填写一条数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!areAllItemsAllFieldsFilled(PriceData.value)) {
|
|
|
+ window.$message?.error('请填写完整数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ setSubModalLoding(true);
|
|
|
+ console.log(PriceData.value);
|
|
|
+
|
|
|
+ const form = {
|
|
|
+ // shopId: selectData.value?.shopId,
|
|
|
+ videoProductId: selectData.value?.id,
|
|
|
+ // 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?.pmsVideoChannelPrices?.length
|
|
|
+ ? selectData.value.pmsVideoChannelPrices[0].purchasePrice
|
|
|
+ : null,
|
|
|
+ deliveryPrice: selectData.value?.pmsVideoChannelPrices?.length
|
|
|
+ ? selectData.value.pmsVideoChannelPrices[0].deliveryPrice
|
|
|
+ : null,
|
|
|
+ videoChannelPriceForms: PriceData.value.map(item => {
|
|
|
+ return {
|
|
|
+ // channelName: item.channelName,
|
|
|
+ id: item?.id,
|
|
|
+ channelId: item.channelId,
|
|
|
+ price: item.channelProdPrice
|
|
|
+ };
|
|
|
+ })
|
|
|
+ };
|
|
|
+ const { error } = await fetchSetUpChannels(form);
|
|
|
+ if (!error) {
|
|
|
+ closePriceModal();
|
|
|
+ refresh();
|
|
|
+ PriceData.value = [];
|
|
|
+ options.value.map(it => (it.disabled = false));
|
|
|
+ } else {
|
|
|
+ setSubModalLoding(false);
|
|
|
+ }
|
|
|
+}
|
|
|
+async function getData() {
|
|
|
+ const { data, error } = await fetchGetChannelList();
|
|
|
+ if (!error) {
|
|
|
+ options.value = data;
|
|
|
+ }
|
|
|
+}
|
|
|
+getData();
|
|
|
+async function handleExport() {
|
|
|
+ setTableLoading(true);
|
|
|
+ try {
|
|
|
+ await commonExport('/smqjh-pms/v1/videoProduct/export', getSeachForm(), '虚拟商品列表.xlsx');
|
|
|
+ } finally {
|
|
|
+ setTableLoading(false);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleStatus(row: any) {
|
|
|
+ window.$dialog?.info({
|
|
|
+ title: '提示',
|
|
|
+ content: `你确定要该操作吗?`,
|
|
|
+ positiveText: '确定',
|
|
|
+ negativeText: '取消',
|
|
|
+ onPositiveClick: async () => {
|
|
|
+ const { error } = await fetchChangeStatus({ id: row.id });
|
|
|
+ if (!error) {
|
|
|
+ refresh();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <LayoutTable>
|
|
|
+ <ZTable :columns="columns" :api="fetchProductList" @register="registerTable">
|
|
|
+ <template #op="{ row }">
|
|
|
+ <NSpace align="center">
|
|
|
+ <NButton size="small" ghost type="primary" @click="handleModalPrice(row)">设置渠道及价格</NButton>
|
|
|
+ <NButton size="small" ghost type="primary" @click="handleStatus(row)">
|
|
|
+ {{ row.productStatus == 1 ? '上架' : '下架' }}
|
|
|
+ </NButton>
|
|
|
+ </NSpace>
|
|
|
+ </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/v1/videoProduct/exportTemplate"
|
|
|
+ template-text="虚拟商品渠道及价格导入模版.xlsx"
|
|
|
+ modal-text="导入虚拟商品销售渠道及价格"
|
|
|
+ @submit="handleSubmitImport"
|
|
|
+ ></ZImportTemplate>
|
|
|
+ <BasicModal @register="registerModalPrice" @ok="handleSubmitPrice">
|
|
|
+ <NDataTable :columns="PriceColumns" :data="PriceData" :row-key="row => row.channelId" />
|
|
|
+ </BasicModal>
|
|
|
+ </LayoutTable>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped></style>
|