Browse Source

feat(营销活动): 完善降价分享活动功能

- 活动列表页新增状态筛选与展示
- 活动编辑弹窗重构为 SimpleForm 组件,优化表单交互
- 数据列表页重构搜索组件,调整查询参数格式
- 助力详情弹窗改为直接调用接口展示数据
- 书单管理页启用批量删除与单条删除功能
- 统一调整各页面 API 接口路径与参数格式
- 修复 CommonTable 组件中 toggleRowSelection 的缩进问题
ylong 3 tuần trước cách đây
mục cha
commit
9f17dcc9dd

+ 3 - 3
src/components/CommonPage/CommonTable.vue

@@ -208,9 +208,9 @@
         // Also try to toggle if table instance is available and supports it, 
         // ensuring visual sync if v-model isn't enough for pre-load
         if (tableRef.value?.toggleRowSelection) {
-             rows.forEach(row => {
-                 tableRef.value.toggleRowSelection(row, true);
-             });
+            rows.forEach(row => {
+                tableRef.value.toggleRowSelection(row, true);
+            });
         }
     }
 

+ 109 - 106
src/views/marketing/shareDiscount/activity/components/ActivityEdit.vue

@@ -1,132 +1,135 @@
+<!-- 新增/编辑分享降价活动弹窗 -->
 <template>
-    <ele-modal
-        :model-value="visible"
-        :title="title"
-        width="600px"
-        @update:modelValue="updateVisible"
-        @confirm="handleConfirm"
-        @cancel="handleCancel"
-    >
-        <el-form
-            ref="formRef"
-            :model="form"
-            :rules="rules"
-            label-width="100px"
-        >
-            <el-form-item label="活动名称" prop="activityName">
-                <el-input
-                    v-model="form.activityName"
-                    placeholder="请输入活动名称"
-                />
-            </el-form-item>
-            <el-form-item label="活动时间" prop="timeRange">
-                <el-date-picker
-                    v-model="form.timeRange"
-                    type="datetimerange"
-                    range-separator="至"
-                    start-placeholder="开始日期"
-                    end-placeholder="结束日期"
-                    value-format="YYYY-MM-DD HH:mm:ss"
-                />
-            </el-form-item>
-            <el-form-item label="限制首单" prop="limitFirst">
-                <el-switch v-model="form.limitFirst" />
-            </el-form-item>
-        </el-form>
+    <ele-modal form :width="560" v-model="visible" :title="title">
+        <SimpleForm :items="formItems" labelWidth="100px" ref="formRef" :initKeys="formData" />
+        <template #footer>
+            <el-button @click="handleCancel">关闭</el-button>
+            <el-button type="primary" @click="handleSubmit">确定</el-button>
+        </template>
     </ele-modal>
 </template>
 
 <script setup>
-    import { ref, reactive, computed } from 'vue';
-    import { EleMessage } from 'ele-admin-plus/es';
+    import { reactive, ref, computed, nextTick } from 'vue';
+    import SimpleForm from '@/components/CommonPage/SimpleForm.vue';
     import request from '@/utils/request';
+    import { EleMessage } from 'ele-admin-plus/es';
 
     const emit = defineEmits(['success']);
 
+    /** 弹窗是否打开 */
     const visible = ref(false);
-    const formRef = ref(null);
-    const isEdit = ref(false);
-    const editId = ref(null);
 
-    const form = reactive({
+    /** 表单标题 */
+    const title = ref('新增活动');
+
+    /** 表单引用 */
+    const formRef = ref();
+
+    /** 关闭弹窗 */
+    const handleCancel = () => {
+        visible.value = false;
+        nextTick(() => {
+            formRef.value?.resetForm();
+        });
+    };
+
+    /** 表单数据 */
+    const formData = reactive({
         activityName: '',
+        startTime: '',
+        endTime: '',
+        limitFirst: '0',
         timeRange: [],
-        limitFirst: false
+        activityId: ''
     });
 
-    const rules = {
-        activityName: [
-            { required: true, message: '请输入活动名称', trigger: 'blur' }
-        ],
-        timeRange: [
-            { required: true, message: '请选择活动时间', trigger: 'change' }
-        ]
-    };
-
-    const title = computed(() => (isEdit.value ? '编辑活动' : '新建活动'));
+    /** 表单项配置 */
+    const formItems = computed(() => {
+        return [
+            {
+                type: 'input',
+                label: '活动名称',
+                prop: 'activityName',
+                required: true
+            },
+            {
+                type: 'datetimerange',
+                label: '活动时间',
+                required: true,
+                prop: 'timeRange',
+                props: {
+                    format: 'YYYY-MM-DD HH:mm:ss',
+                    valueFormat: 'YYYY-MM-DD HH:mm:ss',
+                    rangeSeparator: '至'
+                }
+            },
+            {
+                type: 'radio',
+                label: '限制首单',
+                prop: 'limitFirst',
+                options: [
+                    { label: '是', value: '1' },
+                    { label: '否', value: '0' }
+                ],
+                required: true
+            }
+        ];
+    });
 
-    const handleOpen = (data) => {
+    /** 弹窗打开事件 */
+    function handleOpen(data = {}) {
         visible.value = true;
-        if (data) {
-            isEdit.value = true;
-            editId.value = data.id;
-            form.activityName = data.activityName;
-            form.timeRange = [data.startTime, data.endTime];
-            form.limitFirst = !!data.limitFirst;
-        } else {
-            isEdit.value = false;
-            editId.value = null;
-            form.activityName = '';
-            form.timeRange = [];
-            form.limitFirst = false;
-            // resetFields if needed
-            if (formRef.value) formRef.value.clearValidate();
+        title.value = data && data.id ? '编辑活动' : '新增活动';
+
+        // 重置表单数据
+        for (const key in formData) {
+            formData[key] = '';
         }
-    };
 
-    const updateVisible = (val) => {
-        visible.value = val;
-    };
+        // 设置默认值
+        formData.limitFirst = '0';
 
-    const handleCancel = () => {
-        visible.value = false;
-    };
+        // 如果是编辑模式,合并数据
+        if (data && data.id) {
+            Object.assign(formData, data);
+            formData.activityId = data.id;
+            // 接口返回的 limitFirst 是 string '0' or '1'
+            formData.limitFirst = String(data.limitFirst);
+            formData.timeRange = [data.startTime, data.endTime];
+        }
 
-    const handleConfirm = () => {
-        formRef.value.validate((valid) => {
-            if (valid) {
-                const params = {
-                    ...form,
-                    startTime: form.timeRange?.[0],
-                    endTime: form.timeRange?.[1]
-                };
-                if (isEdit.value) {
-                    params.id = editId.value;
-                }
-                
-                const url = isEdit.value 
-                    ? '/marketing/shareDiscount/activity/update' 
-                    : '/marketing/shareDiscount/activity/add';
-
-                request.post(url, params).then(res => {
-                    if (res.data.code === 200) {
-                        EleMessage.success('保存成功');
-                        visible.value = false;
-                        emit('success');
-                    } else {
-                        EleMessage.error(res.data.msg || '保存失败');
-                    }
-                }).catch(() => {
-                    // Mock success for now since API doesn't exist
-                    EleMessage.success('保存成功 (Mock)');
+        nextTick(() => {
+            formRef.value?.setData(formData);
+        });
+    }
+
+    /** 提交表单 */
+    function handleSubmit() {
+        formRef.value?.submitForm().then((data) => {
+            const url = formData.activityId
+                ? '/activity/activityReduceInfo/update'
+                : '/activity/activityReduceInfo/add';
+
+            let params = {
+                ...data,
+                activityId: formData.activityId,
+                startTime: data.timeRange[0],
+                endTime: data.timeRange[1]
+            };
+            delete params.timeRange;
+
+            request.post(url, params).then((res) => {
+                if (res.data.code === 200) {
+                    EleMessage.success('保存成功');
                     visible.value = false;
                     emit('success');
-                });
-            }
+                } else {
+                    EleMessage.error(res.data.msg || '保存失败');
+                }
+            });
         });
-    };
+    }
 
-    defineExpose({
-        handleOpen
-    });
+    defineExpose({ handleOpen });
 </script>

+ 13 - 3
src/views/marketing/shareDiscount/activity/components/PageSearch.vue

@@ -18,9 +18,18 @@
     const formItems = computed(() => {
         return [
             { type: 'input', label: '活动名称', prop: 'activityName' },
+            {
+                type: 'select',
+                label: '状态',
+                prop: 'status',
+                options: [
+                    { label: '开启', value: '1' },
+                    { label: '关闭', value: '0' }
+                ]
+            },
             {
                 type: 'daterange',
-                label: '选择时间',
+                label: '开始时间',
                 prop: 'timeRange',
                 style: {
                     minWidth: '300px'
@@ -41,6 +50,7 @@
 
     const initKeys = reactive({
         activityName: '',
+        status: '',
         timeRange: []
     });
 
@@ -49,8 +59,8 @@
     const search = (data) => {
         const params = { ...data };
         if (params.timeRange && params.timeRange.length === 2) {
-            params.startTime = params.timeRange[0];
-            params.endTime = params.timeRange[1];
+            params.startTimeStart = params.timeRange[0];
+            params.startTimeEnd = params.timeRange[1];
         }
         delete params.timeRange;
         emit('search', params);

+ 70 - 76
src/views/marketing/shareDiscount/activity/index.vue

@@ -4,11 +4,9 @@
 
         <common-table ref="tableRef" :columns="columns" :page-config="pageConfig" :bodyStyle="{ padding: 0 }">
             <template #toolbar>
-                <div class="flex items-center justify-end w-full">
-                    <el-button type="primary" plain :icon="Plus" @click="handleCreate">
-                        新增
-                    </el-button>
-                </div>
+                <el-button type="primary" plain :icon="Plus" @click="handleCreate">
+                    新增
+                </el-button>
             </template>
 
             <template #time="{ row }">
@@ -16,7 +14,15 @@
             </template>
 
             <template #limitFirst="{ row }">
-                {{ row.limitFirst ? '是' : '否' }}
+                <el-tag :type="String(row.limitFirst) === '1' ? 'success' : 'info'">
+                    {{ String(row.limitFirst) === '1' ? '限制' : '不限制' }}
+                </el-tag>
+            </template>
+
+            <template #status="{ row }">
+                <el-tag :type="String(row.status) === '1' ? 'success' : 'danger'">
+                    {{ String(row.status) === '1' ? '开启' : '关闭' }}
+                </el-tag>
             </template>
 
             <template #action="{ row }">
@@ -27,8 +33,9 @@
                     <el-button type="success" link @click="handleViewData(row)">
                         [查看数据]
                     </el-button>
-                    <el-button :type="row.status === 1 ? 'danger' : 'warning'" link @click="handleToggleStatus(row)">
-                        [{{ row.status === 1 ? '关闭活动' : '启动活动' }}]
+                    <el-button :type="String(row.status) === '1' ? 'danger' : 'success'" link
+                        @click="handleToggleStatus(row)">
+                        [{{ String(row.status) === '1' ? '关闭活动' : '启动活动' }}]
                     </el-button>
                 </div>
             </template>
@@ -39,74 +46,61 @@
 </template>
 
 <script setup>
-import { ref, reactive } from 'vue';
-import { EleMessage } from 'ele-admin-plus/es';
-import CommonTable from '@/components/CommonPage/CommonTable.vue';
-import request from '@/utils/request';
-import PageSearch from './components/PageSearch.vue';
-import ActivityEdit from './components/ActivityEdit.vue';
-import { Plus } from '@element-plus/icons-vue';
-
-defineOptions({ name: 'ShareDiscountActivity' });
-
-const tableRef = ref(null);
-const activityEditRef = ref(null);
-
-const pageConfig = reactive({
-    pageUrl: '/marketing/shareDiscount/activity/list',
-    fileName: '分享降价活动列表',
-    cacheKey: 'shareDiscountActivityTable',
-    rowKey: 'id'
-});
-
-const columns = ref([
-    { label: '活动名称', prop: 'activityName', minWidth: 150 },
-    { label: '活动时间', slot: 'time', minWidth: 300 },
-    { label: '是否限制首单', slot: 'limitFirst', width: 120 },
-    { label: '操作', slot: 'action', width: 250, fixed: 'right' }
-]);
-
-const reload = (where) => {
-    tableRef.value?.reload(where);
-};
-
-const handleCreate = () => {
-    activityEditRef.value?.handleOpen();
-};
-
-const handleEdit = (row) => {
-    request.get(`/marketing/shareDiscount/activity/info/${row.id}`).then((res) => {
-        if (res.data.code === 200) {
-            activityEditRef.value?.handleOpen(res.data.data);
-        } else {
-            EleMessage.error(res.data.msg || '获取详情失败');
-        }
-    }).catch(() => {
-        activityEditRef.value?.handleOpen(row);
+    import { ref, reactive } from 'vue';
+    import { EleMessage } from 'ele-admin-plus/es';
+    import CommonTable from '@/components/CommonPage/CommonTable.vue';
+    import request from '@/utils/request';
+    import PageSearch from './components/PageSearch.vue';
+    import ActivityEdit from './components/ActivityEdit.vue';
+    import { Plus } from '@element-plus/icons-vue';
+
+    defineOptions({ name: 'ShareDiscountActivity' });
+
+    const tableRef = ref(null);
+    const activityEditRef = ref(null);
+
+    const pageConfig = reactive({
+        pageUrl: '/activity/activityReduceInfo/pagelist',
+        fileName: '分享降价活动列表',
+        cacheKey: 'shareDiscountActivityTable',
+        rowKey: 'id'
     });
-};
-
-const handleViewData = (row) => {
-    EleMessage.info('查看数据功能待对接');
-};
-
-const handleToggleStatus = (row) => {
-    const newStatus = row.status === 1 ? 0 : 1;
-    const actionText = newStatus === 1 ? '启动' : '关闭';
-
-    request.post('/marketing/shareDiscount/activity/status', { id: row.id, status: newStatus })
-        .then(res => {
-            if (res.data.code === 200) {
-                EleMessage.success(`${actionText}成功`);
-                reload();
-            } else {
-                EleMessage.error(res.data.msg || `${actionText}失败`);
-            }
-        })
-        .catch(() => {
-            // Mock success
-            EleMessage.success(`${actionText}成功 (Mock)`);
-            row.status = newStatus;
+
+    const columns = ref([
+        { label: '活动名称', prop: 'activityName', minWidth: 150 },
+        { label: '活动时间', slot: 'time', minWidth: 300 },
+        { label: '是否限制首单', slot: 'limitFirst', minWidth: 120 },
+        { label: '状态', slot: 'status', minWidth: 100 },
+        { label: '创建时间', prop: 'createTime', minWidth: 180 },
+        { label: '操作', slot: 'action', minWidth: 200, fixed: 'right' }
+    ]);
+
+    const reload = (where) => {
+        tableRef.value?.reload(where);
+    };
+
+    const handleCreate = () => {
+        activityEditRef.value?.handleOpen();
+    };
+
+    const handleEdit = (row) => {
+        activityEditRef.value?.handleOpen(row);
+    };
+
+    const handleViewData = (row) => {
+        EleMessage.info('查看数据功能待对接');
+    };
+
+    const handleToggleStatus = (row) => {
+        // 0-关闭 1-开启
+        let message = row.status == 1 ? '确认关闭活动?' : '确认开启活动?';
+        let data = {
+            activityId: row.id,
+            status: row.status == 1 ? 0 : 1
+        };
+        pageRef.value?.messageBoxConfirm({
+            message,
+            fetch: () => request.post('/activity/activityReduceInfo/changeStatus', data)
         });
-};
+    };
 </script>

+ 77 - 50
src/views/marketing/shareDiscount/booklist/components/BooklistImport.vue

@@ -1,43 +1,50 @@
 <template>
-    <ele-modal
-        :model-value="visible"
-        title="导入图书"
-        @update:modelValue="updateVisible"
-        @confirm="handleConfirm"
-        @cancel="handleCancel"
-    >
-        <div class="p-4">
-            <el-upload
-                class="upload-demo"
-                drag
-                action="#"
-                :auto-upload="false"
-                :on-change="handleChange"
-                multiple
-            >
-                <el-icon class="el-icon--upload"><upload-filled /></el-icon>
-                <div class="el-upload__text">
-                    Drop file here or <em>click to upload</em>
-                </div>
-                <template #tip>
-                    <div class="el-upload__tip">
-                        只能上传 xlsx/xls 文件,且不超过 500kb
-                    </div>
-                </template>
+    <ele-modal :width="460" title="导入图书" :body-style="{ paddingTop: '8px' }" :model-value="visible"
+        @update:modelValue="updateVisible" @confirm="handleConfirm" @cancel="handleCancel">
+        <div class="flex mb-2">
+            <div class="text-sm">请先下载'降价营销书单导入模板':</div>
+            <el-button @click="
+                downloadOssLink(
+                    'https://shuhi.oss-cn-qingdao.aliyuncs.com/default/reduce_book_isbn_template.xlsx',
+                    '降价营销书单导入模板'
+                )
+                " link type="primary">下载降价营销书单导入模板</el-button>
+        </div>
+
+        <div class="flex flex-col mb-2">
+            <div class="text-sm text-yellow-500">使用说明:</div>
+            <div class="text-sm">1.支持通过导入批量添加图书</div>
+            <div class="text-sm">2.文件ISBN不存在或者错误,会自动过滤掉</div>
+            <div class="text-sm mb-2">3.导入文件第一行需与模版完全一致</div>
+        </div>
+        <div v-loading="loading" class="user-import-upload">
+            <el-upload v-model:file-list="fileList" ref="uploadRef" action="" accept=".xls,.xlsx"
+                :before-upload="doUpload" :auto-upload="false" :limit="1" :on-exceed="handleExceed">
+                <el-button type="primary" class="mr-2">选择文件</el-button>
+                <ele-text type="placeholder">只能上传 xls、xlsx 文件</ele-text>
             </el-upload>
         </div>
+
+        <template #footer>
+            <el-button @click="handleCancel">关闭</el-button>
+            <el-button type="primary" @click="handleConfirm" :loading="loading">导入</el-button>
+        </template>
     </ele-modal>
 </template>
 
 <script setup>
     import { ref, defineEmits } from 'vue';
+    import { genFileId } from 'element-plus';
     import { ElMessage } from 'element-plus';
+    import { EleMessage } from 'ele-admin-plus/es';
     import { UploadFilled } from '@element-plus/icons-vue';
+    import { downloadOssLink } from '@/utils/common';
     import request from '@/utils/request';
 
     const emit = defineEmits(['success']);
 
     const visible = ref(false);
+    const loading = ref(false);
     const fileList = ref([]);
     const uploadRef = ref(null);
 
@@ -50,36 +57,56 @@
         fileList.value = [];
     };
 
-    const handleChange = (file, fileListRef) => {
-        fileList.value = fileListRef;
-    };
+    function handleExceed(files) {
+        uploadRef.value?.clearFiles();
+        const file = files[0];
+        file.uid = genFileId();
+        uploadRef.value?.handleStart(file);
+    }
 
-    const handleConfirm = async () => {
-        if (fileList.value.length === 0) {
-            ElMessage.warning('请选择文件');
-            return;
+    const doUpload = (file) => {
+        if (
+            ![
+                'application/vnd.ms-excel',
+                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+            ].includes(file.type)
+        ) {
+            EleMessage.error('只能选择 excel 文件');
+            return false;
+        }
+        if (file.size / 1024 / 1024 > 10) {
+            EleMessage.error('大小不能超过 10MB');
+            return false;
         }
-        
+        return false;
+    };
+
+     //导入图书
+    async function importBooks(file) {
         const formData = new FormData();
-        formData.append('file', fileList.value[0].raw);
+        formData.append('file', file);
 
-        try {
-            const res = await request.post('/activity/reduce/book/import', formData, {
-                headers: {
-                    'Content-Type': 'multipart/form-data'
-                }
-            });
-            if (res.data.code === 0) {
-                ElMessage.success(res.data.msg || '导入成功');
-                emit('success');
-                updateVisible(false);
-            } else {
-                ElMessage.error(res.data.msg || '导入失败');
-            }
-        } catch (error) {
-            console.error(error);
-            ElMessage.error('导入失败');
+        const res = await request.post('/activity/reduce/book/import', formData);
+        if (res.data.code === 200) {
+            return res.data;
         }
+        return Promise.reject(new Error(res.data.msg));
+    }
+
+    /** 提交 */
+    const handleConfirm = () => {
+        loading.value = true;
+        let file = fileList.value[0];
+        importBooks(file.raw)
+            .then((res) => {
+                loading.value = false;
+                ElMessage.success(res.data || res.msg);
+                visible.value = false;
+                emit('success');
+            })
+            .catch((e) => {
+                loading.value = false;
+            });
     };
 
     const handleCancel = () => {

+ 30 - 25
src/views/marketing/shareDiscount/booklist/index.vue

@@ -1,13 +1,15 @@
 <template>
     <ele-page flex-table :bodyStyle="{ padding: 0 }">
-        <page-search @search="handleSearch" />
         <common-table ref="tableRef" :columns="columns" :page-config="pageConfig"
-            @selection-change="handleSelectionChange">
+            @selection-change="handleSelectionChange" :bodyStyle="{ padding: '0' }" flex-table="auto">
             <template #toolbar>
-                <el-button type="primary" :icon="Plus" @click="handleAdd">新建</el-button>
-                <!-- <el-button type="danger" :icon="Delete" @click="handleBatchDelete">批量删除</el-button> -->
-                <el-button type="success" :icon="Upload" @click="handleImport">导入</el-button>
-                <el-button type="warning" :icon="Download" @click="handleExport">导出</el-button>
+                <div class="flex items-center w-full">
+                    <page-search @search="handleSearch" style="margin-right: 30px" />
+                    <el-button type="primary" :icon="Plus" @click="handleAdd">新建</el-button>
+                    <el-button type="danger" :icon="Delete" @click="handleBatchDelete">批量删除</el-button>
+                    <el-button type="success" :icon="Upload" @click="handleImport">导入</el-button>
+                    <el-button type="warning" :icon="Download" @click="handleExport">导出</el-button>
+                </div>
             </template>
 
             <template #cover="{ row }">
@@ -18,9 +20,9 @@
                 <dict-data code="sell_status" v-model="row.sellStatus" type="tag"></dict-data>
             </template>
 
-            <!-- <template #action="{ row }">
+            <template #action="{ row }">
                 <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
-            </template> -->
+            </template>
         </common-table>
 
         <booklist-add ref="addRef" @success="refreshData" />
@@ -52,7 +54,7 @@
         exportUrl: '/activity/reduce/book/export',
         fileName: '降价营销书单',
         cacheKey: 'shareDiscountBookListTable',
-        rowKey: 'id',
+        rowKey: 'isbn',
         tool: true
     });
 
@@ -77,7 +79,7 @@
         { prop: 'recyclePrice', label: '回收价格', width: 100 },
         { prop: 'upsellPrice', label: '加价金额', width: 100 },
         { prop: 'createTime', label: '添加时间', width: 160 },
-        // { prop: 'action', label: '操作', slot: 'action', width: 100, fixed: 'right' }
+        { prop: 'action', label: '操作', slot: 'action', width: 100, fixed: 'right' }
     ];
 
     const handleSearch = (params) => {
@@ -104,19 +106,22 @@
         tableRef.value?.exportData();
     };
 
-    // const handleBatchDelete = () => {
-    //     if (selection.value.length === 0) {
-    //         ElMessage.warning('请选择要删除的记录');
-    //         return;
-    //     }
-    //     ElMessageBox.confirm('确认删除选中的记录吗?', '提示', {
-    //         type: 'warning'
-    //     }).then(async () => {
-    //         // No delete API available
-    //     });
-    // };
-
-    // const handleDelete = (row) => {
-    //     // No delete API available
-    // };
+    const handleBatchDelete = () => {
+        tableRef.value?.operatBatch({
+            method: 'post',
+            url: '/activity/reduce/book/delete',
+            title: `确认批量删除这 ${selection.value.length} 条记录吗?`,
+            data: { isbnList: selection.value.map(row => row.isbn) }
+        })
+    };
+
+    const handleDelete = (row) => {
+        pageRef.value?.messageBoxConfirm({
+            message: '确认删除此记录吗?',
+            fetch: () =>
+                proxy.$http.post('/activity/reduce/book/delete', {
+                    isbns: [row.isbn]
+                })
+        });
+    };
 </script>

+ 31 - 32
src/views/marketing/shareDiscount/datalist/components/PageSearch.vue

@@ -1,49 +1,48 @@
 <!-- 搜索表单 -->
 <template>
-    <ele-card :body-style="{ padding: '0', paddingTop: '12px' }">
-        <ProSearch
-            :items="formItems"
-            ref="searchRef"
-            @search="search"
-            :initKeys="initKeys"
-        />
-    </ele-card>
+    <ProSearch :items="formItems" @search="search" :initKeys="initKeys" />
 </template>
 
 <script setup>
-    import { reactive, ref, computed, defineEmits } from 'vue';
+    import { computed, reactive } from 'vue';
     import ProSearch from '@/components/CommonPage/ProSearch2.vue';
 
     const emit = defineEmits(['search']);
-    const formItems = computed(() => {
-        return [
-            {
-                type: 'daterange',
-                label: '开始时间',
-                prop: 'timeRange',
-                style: {
-                    minWidth: '300px'
-                },
-                props: {
-                    valueFormat: 'YYYY-MM-DD HH:mm:ss',
-                    format: 'YYYY-MM-DD HH:mm:ss',
-                    startPlaceholder: '开始日期',
-                    endPlaceholder: '结束日期'
-                }
+
+    const formItems = computed(() => [
+        {
+            type: 'input',
+            label: '用户ID',
+            prop: 'userId',
+            props: { placeholder: '请输入用户ID' }
+        },
+        {
+            type: 'input',
+            label: '订单ID',
+            prop: 'orderId',
+            props: { placeholder: '请输入订单ID' }
+        },
+        {
+            type: 'daterange',
+            label: '参与时间',
+            prop: 'timeRange',
+            colProps: {
+                span: 8
             },
-            { type: 'input', label: '用户ID', prop: 'userId' },
-            { type: 'input', label: '订单编号', prop: 'orderNo' }
-        ];
-    });
+            props: {
+                valueFormat: 'YYYY-MM-DD HH:mm:ss',
+                startPlaceholder: '开始时间',
+                endPlaceholder: '结束时间',
+            }
+        }
+    ]);
 
     const initKeys = reactive({
-        timeRange: [],
         userId: '',
-        orderNo: ''
+        orderId: '',
+        timeRange: []
     });
 
-    const searchRef = ref(null);
-    /** 搜索 */
     const search = (data) => {
         const params = { ...data };
         if (params.timeRange && params.timeRange.length === 2) {

+ 43 - 39
src/views/marketing/shareDiscount/datalist/components/ShareInfo.vue

@@ -1,50 +1,54 @@
 <template>
-    <ele-modal :model-value="visible" title="分享助力信息" width="900px" @update:modelValue="updateVisible"
-        :footer="null">
-        <common-table ref="tableRef" :columns="columns" :page-config="pageConfig" :bodyStyle="{ padding: 0 }" />
+    <ele-modal :model-value="visible" title="助力详情" width="900px" @update:modelValue="updateVisible" :footer="null">
+        <el-table :data="tableData" border stripe v-loading="loading">
+            <el-table-column prop="userId" label="用户ID" align="center" />
+            <el-table-column prop="nickName" label="用户昵称" align="center" />
+            <el-table-column prop="orderStatus" label="是否下单" align="center">
+                <template #default="{ row }">
+                    {{ row.orderStatus == 1 ? '是' : '否' }}
+                </template>
+            </el-table-column>
+            <el-table-column prop="totalNum" label="订单总本数" align="center" width="120" />
+            <el-table-column prop="totalMoney" label="订单总金额" align="center" width="120" />
+            <el-table-column prop="reduceMoney" label="降价金额" align="center" width="120" />
+        </el-table>
     </ele-modal>
 </template>
 
 <script setup>
-import { ref, reactive, nextTick } from 'vue';
-import CommonTable from '@/components/CommonPage/CommonTable.vue';
+    import { ref } from 'vue';
+    import { ElMessage } from 'element-plus';
+    import request from '@/utils/request';
 
-const visible = ref(false);
-const tableRef = ref(null);
-const currentId = ref(null);
+    const visible = ref(false);
+    const loading = ref(false);
+    const tableData = ref([]);
 
-const pageConfig = reactive({
-    pageUrl: '/marketing/shareDiscount/data/shareInfo',
-    fileName: '分享助力信息',
-    cacheKey: 'shareDiscountShareInfoTable',
-    rowKey: 'id',
-    tool: false
-});
+    const updateVisible = (val) => {
+        visible.value = val;
+    };
 
-const columns = ref([
-    { label: '用户UID', prop: 'userId', minWidth: 150 },
-    { label: '图书信息', prop: 'bookInfo', minWidth: 200 },
-    { label: '是否下单', prop: 'hasOrdered', minWidth: 100 },
-    { label: '订单总本数', prop: 'totalBooks', minWidth: 100 },
-    { label: '订单总金额', prop: 'totalAmount', minWidth: 100 },
-    { label: '降价金额', prop: 'discountAmount', minWidth: 100 }
-]);
-
-const updateVisible = (val) => {
-    visible.value = val;
-};
-
-const handleOpen = (data) => {
-    visible.value = true;
-    currentId.value = data?.id;
-    nextTick(() => {
-        if (tableRef.value) {
-            tableRef.value.reload({ shareId: data?.id });
+    const handleOpen = async (data) => {
+        visible.value = true;
+        loading.value = true;
+        tableData.value = [];
+        try {
+            // 使用 orderId 获取助力详情
+            const res = await request.get(`/activity/activityReduceInfo/orderHelpList/${data.orderId}`);
+            if (res.data.code === 200) {
+                tableData.value = res.data.data || [];
+            } else {
+                ElMessage.error(res.data.msg || '获取助力详情失败');
+            }
+        } catch (error) {
+            console.error(error);
+            ElMessage.error('获取助力详情失败');
+        } finally {
+            loading.value = false;
         }
-    });
-};
+    };
 
-defineExpose({
-    handleOpen
-});
+    defineExpose({
+        handleOpen
+    });
 </script>

+ 34 - 37
src/views/marketing/shareDiscount/datalist/index.vue

@@ -1,33 +1,28 @@
 <template>
     <ele-page flex-table :bodyStyle="{ padding: 0 }">
-        <page-search @search="reload" />
 
-        <common-table
-            ref="tableRef"
-            :columns="columns"
-            :page-config="pageConfig"
-            :bodyStyle="{ padding: 0 }"
-        >
-            <template #orderNo="{ row }">
-                <el-button type="primary" link @click="handleOrderClick(row)">
-                    {{ row.orderNo }}
-                </el-button>
+        <common-table ref="tableRef" :columns="columns" :page-config="pageConfig" :bodyStyle="{ padding: 0 }">
+            <template #toolbar>
+                <page-search @search="reload" />
             </template>
 
+            <template #orderTime="{ row }">
+                {{ row.orderTime }}
+            </template>
             <template #action="{ row }">
-                <el-button type="primary" link @click="handleShareInfo(row)">
-                    [分享信息]
+                <el-button link type="primary" @click="handleViewHelp(row)">
+                    查看助力
                 </el-button>
             </template>
         </common-table>
 
-        <share-info ref="shareInfoRef" />
+        <ShareInfo ref="shareInfoRef" />
     </ele-page>
 </template>
 
 <script setup>
     import { ref, reactive } from 'vue';
-    import { EleMessage } from 'ele-admin-plus/es';
+    import { ElMessage } from 'element-plus';
     import CommonTable from '@/components/CommonPage/CommonTable.vue';
     import PageSearch from './components/PageSearch.vue';
     import ShareInfo from './components/ShareInfo.vue';
@@ -38,36 +33,38 @@
     const shareInfoRef = ref(null);
 
     const pageConfig = reactive({
-        pageUrl: '/marketing/shareDiscount/data/list',
-        fileName: '降价营销数据列表',
-        cacheKey: 'shareDiscountDataTable',
-        rowKey: 'id'
+        pageUrl: '/activity/activityReduceInfo/reduceOrderList',
+        fileName: '降价订单列表',
+        cacheKey: 'reduceOrderListTable',
+        rowKey: 'orderId'
     });
 
     const columns = ref([
-        { label: '用户ID', prop: 'userId', minWidth: 120 },
-        { label: '订单编号', slot: 'orderNo', minWidth: 150 },
-        { label: '参与时间', prop: 'participateTime', minWidth: 180 },
-        { label: '订单总本数', prop: 'totalBooks', minWidth: 100 },
-        { label: '降价本数', prop: 'discountBooks', minWidth: 100 },
-        { label: '订单总金额', prop: 'totalAmount', minWidth: 120 },
-        { label: '实际降价金额', prop: 'actualDiscountAmount', minWidth: 120 },
-        { label: '操作', slot: 'action', width: 120, fixed: 'right' }
+        { label: '用户ID', prop: 'userId', minWidth: 120, align: 'center' },
+        { label: '用户昵称', prop: 'nickName', minWidth: 150, align: 'center' },
+        { label: '订单ID', prop: 'orderId', minWidth: 150, align: 'center' },
+        { label: '参与时间', prop: 'orderTime', minWidth: 160, align: 'center', slot: 'orderTime' },
+        { label: '订单总本数', prop: 'totalNum', minWidth: 120, align: 'center' },
+        { label: '降价次数', prop: 'reduceNum', minWidth: 100, align: 'center' },
+        { label: '订单总金额', prop: 'totalMoney', minWidth: 120, align: 'center' },
+        { label: '降价金额', prop: 'reduceMoney', minWidth: 120, align: 'center' },
+        { label: '操作', slot: 'action', width: 120, fixed: 'right', align: 'center' }
     ]);
 
     const reload = (where) => {
-        tableRef.value?.reload(where);
-    };
-
-    const handleSearch = (params) => {
-        reload(params);
-    };
-
-    const handleOrderClick = (row) => {
-        EleMessage.info(`View Order: ${row.orderNo}`);
+        // 构造 param.xxx 格式的查询参数
+        const params = {};
+        if (where) {
+            Object.keys(where).forEach(key => {
+                if (where[key] !== null && where[key] !== undefined && where[key] !== '') {
+                    params[`param.${key}`] = where[key];
+                }
+            });
+        }
+        tableRef.value?.reload({ page: 1, where: params });
     };
 
-    const handleShareInfo = (row) => {
+    const handleViewHelp = (row) => {
         shareInfoRef.value?.handleOpen(row);
     };
 </script>