|
@@ -0,0 +1,413 @@
|
|
|
|
|
+<script setup lang="tsx">
|
|
|
|
|
+import { nextTick, onMounted, ref } from 'vue';
|
|
|
|
|
+import { useRoute } from 'vue-router';
|
|
|
|
|
+import type { DataTableColumns, FormInst, FormRules } from 'naive-ui';
|
|
|
|
|
+import { NForm, NImage, NInput, useMessage } from 'naive-ui';
|
|
|
|
|
+import dayjs from 'dayjs';
|
|
|
|
|
+import { router } from '@/router';
|
|
|
|
|
+import { fetchActivityDetail, fetchAddActivity, fetchEditActivity } from '@/service/api/djk-manage/activity';
|
|
|
|
|
+import { fetchProductList } from '@/service/api/goods-center/health-goods';
|
|
|
|
|
+import { fetchGetStoreList } from '@/service/api/xsb-manage/store-info';
|
|
|
|
|
+import { useTable } from '@/components/zt/Table/hooks/useTable';
|
|
|
|
|
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
|
|
|
|
|
+import type { FormSchema } from '@/components/zt/Form/types/form';
|
|
|
|
|
+import ZUpload from '../../../components/zt/upload/z-upload.vue';
|
|
|
|
|
+const current = ref('A');
|
|
|
|
|
+const route = useRoute();
|
|
|
|
|
+const formRef = ref<FormInst | null>(null);
|
|
|
|
|
+const message = useMessage();
|
|
|
|
|
+const disabled = ref(false);
|
|
|
|
|
+const useTime = ref<[string, string]>([dayjs().format('YYYY-MM-DD HH:mm:ss'), dayjs().format('YYYY-MM-DD HH:mm:ss')]);
|
|
|
|
|
+const loading = ref(false);
|
|
|
|
|
+const options = [
|
|
|
|
|
+ { label: '新用户专享', value: 1 },
|
|
|
|
|
+ { label: '节日活动', value: 2 },
|
|
|
|
|
+ { label: '用户召回', value: 3 },
|
|
|
|
|
+ { label: '会员活动', value: 4 },
|
|
|
|
|
+ { label: '其他', value: 5 }
|
|
|
|
|
+];
|
|
|
|
|
+// const userOptions = [
|
|
|
|
|
+// { label: '全部用户', value: 1 },
|
|
|
|
|
+// { label: '仅新用户', value: 2 },
|
|
|
|
|
+// { label: '特定用户标签', value: 3 },
|
|
|
|
|
+// { label: '指定用户', value: 4 }
|
|
|
|
|
+// ];
|
|
|
|
|
+const welfareGoodList = ref<any>([]);
|
|
|
|
|
+const productAInfo = ref<any>();
|
|
|
|
|
+const productBInfo = ref<any>();
|
|
|
|
|
+const model = ref({
|
|
|
|
|
+ activityName: '',
|
|
|
|
|
+ activityImg: '',
|
|
|
|
|
+ activityType: null as number | null,
|
|
|
|
|
+ activityStartTime: '',
|
|
|
|
|
+ activityEndTime: '',
|
|
|
|
|
+ activityUser: 0,
|
|
|
|
|
+ claimLimit: 0 as number | null,
|
|
|
|
|
+ shelfStatus: 0,
|
|
|
|
|
+ claimNum: 0,
|
|
|
|
|
+ inventory: 0,
|
|
|
|
|
+ productA: 0,
|
|
|
|
|
+ productB: 0,
|
|
|
|
|
+ createTime: '',
|
|
|
|
|
+ updateTime: '',
|
|
|
|
|
+ deleted: 0,
|
|
|
|
|
+ activityStatus: 0,
|
|
|
|
|
+ welfareGoodList: [] as any[]
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const rules: FormRules = {
|
|
|
|
|
+ activityName: {
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ trigger: ['blur', 'input'],
|
|
|
|
|
+ message: '请输入'
|
|
|
|
|
+ },
|
|
|
|
|
+ activityImg: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ trigger: ['blur', 'change'],
|
|
|
|
|
+ message: '请输入'
|
|
|
|
|
+ },
|
|
|
|
|
+ activityUser: {
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ trigger: ['blur', 'change'],
|
|
|
|
|
+ message: '请输入'
|
|
|
|
|
+ },
|
|
|
|
|
+ activityType: {
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ trigger: ['blur', 'change'],
|
|
|
|
|
+ message: '请输入'
|
|
|
|
|
+ },
|
|
|
|
|
+ claimNum: {
|
|
|
|
|
+ min: 1,
|
|
|
|
|
+ max: 100,
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ trigger: ['blur', 'input'],
|
|
|
|
|
+ message: '请输入1-100的数字'
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ useStartTime: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ trigger: ['blur', 'change'],
|
|
|
|
|
+ message: '请输入'
|
|
|
|
|
+ },
|
|
|
|
|
+ productA: {
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ min: 1,
|
|
|
|
|
+ trigger: ['blur', 'change'],
|
|
|
|
|
+ message: '请输入'
|
|
|
|
|
+ },
|
|
|
|
|
+ productB: {
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ min: 1,
|
|
|
|
|
+ trigger: ['blur', 'change'],
|
|
|
|
|
+ message: '请输入'
|
|
|
|
|
+ },
|
|
|
|
|
+ inventory: {
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ min: 1,
|
|
|
|
|
+ trigger: ['blur', 'change'],
|
|
|
|
|
+ message: '请输入'
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+interface ProductItem {
|
|
|
|
|
+ goodsImg: string;
|
|
|
|
|
+ goodsName: string;
|
|
|
|
|
+ goodsCode: string;
|
|
|
|
|
+ name?: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const columns: DataTableColumns<ProductItem> = [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'selection',
|
|
|
|
|
+ multiple: false,
|
|
|
|
|
+ disabled(row: any) {
|
|
|
|
|
+ return row.name === 'Edward King 3';
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ key: 'goodsImg',
|
|
|
|
|
+ title: '商品封面',
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ width: 120,
|
|
|
|
|
+ render(row: any) {
|
|
|
|
|
+ return <NImage src={row.goodsImg} class={'h90px w90px'}></NImage>;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ key: 'goodsName',
|
|
|
|
|
+ title: '商品名称',
|
|
|
|
|
+ align: 'center'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ key: 'goodsCode',
|
|
|
|
|
+ title: '商品编码',
|
|
|
|
|
+ align: 'center'
|
|
|
|
|
+ }
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
|
|
+const searchSchemas: FormSchema[] = [
|
|
|
|
|
+ {
|
|
|
|
|
+ label: '门店名称',
|
|
|
|
|
+ component: 'ApiSelect',
|
|
|
|
|
+ field: 'shopId',
|
|
|
|
|
+ componentProps: {
|
|
|
|
|
+ api: fetchGetStoreList,
|
|
|
|
|
+ resultFeild: 'data.list',
|
|
|
|
|
+ labelFeild: 'shopName',
|
|
|
|
|
+ valueFeild: 'shopId'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ label: '商品名称',
|
|
|
|
|
+ component: 'NInput',
|
|
|
|
|
+ field: 'goodsName'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ label: '商品编码',
|
|
|
|
|
+ component: 'NInput',
|
|
|
|
|
+ field: 'goodsCode'
|
|
|
|
|
+ }
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
|
|
+const [registerModalTable, { refresh, getTableCheckedRowKeys, getTableData }] = useTable({
|
|
|
|
|
+ searchFormConfig: {
|
|
|
|
|
+ schemas: searchSchemas,
|
|
|
|
|
+ inline: false,
|
|
|
|
|
+ size: 'small',
|
|
|
|
|
+ labelPlacement: 'left',
|
|
|
|
|
+ isFull: false
|
|
|
|
|
+ },
|
|
|
|
|
+ tableConfig: {
|
|
|
|
|
+ keyField: 'id',
|
|
|
|
|
+ title: '商品列表',
|
|
|
|
|
+ showAddButton: false,
|
|
|
|
|
+ minHeight: 400,
|
|
|
|
|
+ defaultParamsNotReset: 'channelIdList'
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const [registerModal, { openModal, closeModal }] = useModal({
|
|
|
|
|
+ title: '商品选择',
|
|
|
|
|
+ height: 800,
|
|
|
|
|
+ width: 1200
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+function handleValidateButtonClick(e: MouseEvent) {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ formRef.value?.validate(async errors => {
|
|
|
|
|
+ if (!errors) {
|
|
|
|
|
+ loading.value = true;
|
|
|
|
|
+ model.value.activityStartTime = useTime.value[0];
|
|
|
|
|
+ model.value.activityEndTime = useTime.value[1];
|
|
|
|
|
+ if (model.value.claimLimit === 0) {
|
|
|
|
|
+ model.value.claimLimit = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ let res;
|
|
|
|
|
+ if (route.query.mode == 'edit') {
|
|
|
|
|
+ res = await fetchEditActivity(model.value);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ res = await fetchAddActivity(model.value);
|
|
|
|
|
+ }
|
|
|
|
|
+ loading.value = false;
|
|
|
|
|
+ console.log(res);
|
|
|
|
|
+
|
|
|
|
|
+ if (res.data === null) {
|
|
|
|
|
+ router.push({
|
|
|
|
|
+ path: '/djk-manage/activity'
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.log(errors);
|
|
|
|
|
+ message.error('验证失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function getDetail() {
|
|
|
|
|
+ // 获取详情接口
|
|
|
|
|
+ const res = await fetchActivityDetail(route.query.id);
|
|
|
|
|
+ if (res.data) {
|
|
|
|
|
+ model.value = res.data;
|
|
|
|
|
+ welfareGoodList.value = res.data.welfareGoodList;
|
|
|
|
|
+ productAInfo.value = welfareGoodList.value.find((item: any) => item.id === res.data.productA);
|
|
|
|
|
+ productBInfo.value = welfareGoodList.value.find((item: any) => item.id === res.data.productB);
|
|
|
|
|
+ useTime.value = [res.data.activityStartTime, res.data.activityEndTime];
|
|
|
|
|
+ console.log(useTime.value);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function handleAdd(type: string) {
|
|
|
|
|
+ current.value = type;
|
|
|
|
|
+ openModal();
|
|
|
|
|
+ await nextTick();
|
|
|
|
|
+ refresh();
|
|
|
|
|
+}
|
|
|
|
|
+function choose() {
|
|
|
|
|
+ const keys = getTableCheckedRowKeys();
|
|
|
|
|
+ const data = getTableData();
|
|
|
|
|
+ closeModal();
|
|
|
|
|
+ if (current.value === 'A') {
|
|
|
|
|
+ model.value.productA = keys[0] as unknown as number;
|
|
|
|
|
+ productAInfo.value = data.find((item: any) => item.id === keys[0]);
|
|
|
|
|
+ model.value.welfareGoodList.push(productAInfo.value);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ model.value.productB = keys[0] as unknown as number;
|
|
|
|
|
+ productBInfo.value = data.find((item: any) => item.id === keys[0]);
|
|
|
|
|
+ model.value.welfareGoodList.push(productBInfo.value);
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log(keys, '选择的商品', productBInfo.value, data);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleDelete(type: string) {
|
|
|
|
|
+ if (type === 'A') {
|
|
|
|
|
+ model.value.welfareGoodList = model.value.welfareGoodList.filter((item: any) => item.id !== model.value.productA);
|
|
|
|
|
+ model.value.productA = 0;
|
|
|
|
|
+ productAInfo.value = null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ model.value.welfareGoodList = model.value.welfareGoodList.filter((item: any) => item.id !== model.value.productB);
|
|
|
|
|
+ model.value.productB = 0;
|
|
|
|
|
+ productBInfo.value = null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ if (route.query.id) {
|
|
|
|
|
+ if (route.query.mode === 'detail') {
|
|
|
|
|
+ disabled.value = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ getDetail();
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log(route.query);
|
|
|
|
|
+});
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="edit-health-goods pl-20px pt-20px">
|
|
|
|
|
+ <NForm
|
|
|
|
|
+ ref="formRef"
|
|
|
|
|
+ :model="model"
|
|
|
|
|
+ :rules="rules"
|
|
|
|
|
+ label-placement="left"
|
|
|
|
|
+ label-width="auto"
|
|
|
|
|
+ require-mark-placement="right-hanging"
|
|
|
|
|
+ size="medium"
|
|
|
|
|
+ class="max-w-640px"
|
|
|
|
|
+ :disabled="disabled"
|
|
|
|
|
+ >
|
|
|
|
|
+ <NFormItem label="活动名称" path="activityName">
|
|
|
|
|
+ <NInput v-model:value="model.activityName" placeholder="" />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ <NFormItem label="活动封面" path="activityImg">
|
|
|
|
|
+ <ZUpload v-model:value="model.activityImg" :max="1"></ZUpload>
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ <NFormItem label="活动类型" path="activityType">
|
|
|
|
|
+ <NSelect v-model:value="model.activityType" placeholder="" :options="options" />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ <NFormItem label="可用时间">
|
|
|
|
|
+ <NDatePicker
|
|
|
|
|
+ v-model:formatted-value="useTime"
|
|
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
|
|
+ type="datetimerange"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ <!--
|
|
|
|
|
+ <NFormItem label="参与用户" path="activityType">
|
|
|
|
|
+ <n-select v-model:value="model.activityType" placeholder="" :options="userOptions" />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+-->
|
|
|
|
|
+ <NFormItem label="领取限制" :show-feedback="false">
|
|
|
|
|
+ <NGrid :cols="3" :x-gap="6">
|
|
|
|
|
+ <NFormItemGi>
|
|
|
|
|
+ <NRadioGroup v-model:value="model.claimLimit" name="radiogroup2">
|
|
|
|
|
+ <NRadio :value="0">不限制</NRadio>
|
|
|
|
|
+ <NRadio :value="1">限制</NRadio>
|
|
|
|
|
+ </NRadioGroup>
|
|
|
|
|
+ </NFormItemGi>
|
|
|
|
|
+ <NFormItemGi v-if="model.claimLimit === 1" path="claimNum">
|
|
|
|
|
+ <div class="flex items-center whitespace-nowrap">
|
|
|
|
|
+ 每个用户限领
|
|
|
|
|
+ <NInputNumber v-model:value="model.claimNum" />
|
|
|
|
|
+ 次
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </NFormItemGi>
|
|
|
|
|
+ </NGrid>
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+
|
|
|
|
|
+ <NFormItem label="商品A" path="productA">
|
|
|
|
|
+ <NButton v-if="!model.productA" icon-placement="left" secondary strong @click="handleAdd('A')">
|
|
|
|
|
+ <template #icon>
|
|
|
|
|
+ <icon-material-symbols:add-circle class="text-icon" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ 添加商品
|
|
|
|
|
+ </NButton>
|
|
|
|
|
+ <div v-else class="relative flex border-1px rounded-8px p-10px">
|
|
|
|
|
+ <NImage :src="productAInfo.goodsImg" class="h90px w90px"></NImage>
|
|
|
|
|
+ <div class="flex-1">
|
|
|
|
|
+ <div>{{ productAInfo.goodsName }}</div>
|
|
|
|
|
+ <div>商品编码:{{ productAInfo.goodsCode }}</div>
|
|
|
|
|
+ <div>商品价格:¥{{ productAInfo.price }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <NButton class="absolute bottom-10px right-10px" text @click="handleDelete('A')">删除</NButton>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ <NFormItem label="商品B" path="productB">
|
|
|
|
|
+ <NButton v-if="!model.productB" icon-placement="left" secondary strong @click="handleAdd('B')">
|
|
|
|
|
+ <template #icon>
|
|
|
|
|
+ <icon-material-symbols:add-circle class="text-icon" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ 添加商品
|
|
|
|
|
+ </NButton>
|
|
|
|
|
+ <div v-else class="relative flex border-1px rounded-8px p-10px">
|
|
|
|
|
+ <NImage :src="productBInfo.goodsImg" class="h90px w90px"></NImage>
|
|
|
|
|
+ <div class="flex-1">
|
|
|
|
|
+ <div>{{ productBInfo.goodsName }}</div>
|
|
|
|
|
+ <div>商品编码:{{ productBInfo.goodsCode }}</div>
|
|
|
|
|
+ <div>商品价格:¥{{ productBInfo.price }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <NButton class="absolute bottom-10px right-10px" text @click="handleDelete('B')">删除</NButton>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ <NFormItem label="库存" path="inventory">
|
|
|
|
|
+ <NInputNumber v-model:value="model.inventory" />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ <div class="flex justify-end">
|
|
|
|
|
+ <NButton round type="primary" :loading="loading" @click="handleValidateButtonClick">保存</NButton>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </NForm>
|
|
|
|
|
+
|
|
|
|
|
+ <!--
|
|
|
|
|
+ <BasicModal @register="registerModal">
|
|
|
|
|
+ <NDataTable :columns="columns" :data="data" size="small" remote class="sm:h-full" />
|
|
|
|
|
+ </BasicModal>
|
|
|
|
|
+-->
|
|
|
|
|
+
|
|
|
|
|
+ <BasicModal @register="registerModal" @ok="choose">
|
|
|
|
|
+ <LayoutTable>
|
|
|
|
|
+ <ZTable
|
|
|
|
|
+ :immediate="false"
|
|
|
|
|
+ :show-table-action="false"
|
|
|
|
|
+ :columns="columns"
|
|
|
|
|
+ :api="fetchProductList"
|
|
|
|
|
+ :default-params="{ productStatus: 1 }"
|
|
|
|
|
+ @register="registerModalTable"
|
|
|
|
|
+ ></ZTable>
|
|
|
|
|
+ </LayoutTable>
|
|
|
|
|
+ </BasicModal>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+::v-deep .n-form-item-blank {
|
|
|
|
|
+ z-index: 6 !important;
|
|
|
|
|
+}
|
|
|
|
|
+.edit-health-goods {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|