import dayjs from "dayjs"; import { message } from "@/utils/message"; import { addDialog } from "@/components/ReDialog"; import { getCouponList, getCouponById, createCoupon, updateCouponStatus, deleteCoupon, distributeCoupon } from "@/api/coupon"; import { distributeCoupons, getCouponDistributeRecords } from "@/api/marketing"; import { getUserList } from "@/api/user"; import ShopSelector from "../../components/ShopSelector.vue"; import ProductSelector from "../../components/ProductSelector.vue"; import DeviceSelector from "../../components/DeviceSelector.vue"; import type { PaginationProps } from "@pureadmin/table"; import { deviceDetection } from "@pureadmin/utils"; import { onMounted, reactive, ref, toRaw } from "vue"; import { ElForm, ElInput, ElFormItem, ElSelect, ElOption, ElDatePicker, ElInputNumber, ElTag, ElRadioGroup, ElRadioButton, ElAlert, ElTable, ElTableColumn } from "element-plus"; import { initPagination, handlePageSizeChange, handleCurrentPageChange, resetPagination, updatePaginationData } from "@/utils/paginationHelper"; interface SearchFormProps { name: string; type: number | undefined; status: number | undefined; receiveType: string | undefined; } interface CouponFormData { id?: number; name: string; activityId: string; receiveType: string; // Collect-主动领取,Release-系统发放 type: number | undefined; value: number | null; totalCount: number | null; shopIds: number[]; deviceIds: number[]; productIds: number[]; validPeriod: string[]; description: string; applyScope: number; deviceScope: number; productScope: number; } export function useCoupon() { const form = reactive({ name: "", type: undefined, status: undefined, receiveType: undefined }); const loading = ref(true); const dataList = ref([]); const pagination = reactive(initPagination()); type TagType = "success" | "primary" | "warning" | "info" | "danger"; const typeMap: Record = { 1: { text: "满减券", type: "primary" }, 2: { text: "折扣券", type: "success" }, 3: { text: "立减券", type: "warning" }, 4: { text: "商品券", type: "info" } }; const statusMap: Record = { 0: { text: "未启用", type: "info" }, 1: { text: "已启用", type: "success" }, 2: { text: "已过期", type: "danger" } }; const columns: TableColumnList = [ { label: "优惠券ID", prop: "id", width: 200 }, { label: "优惠券名称", prop: "couponName", minWidth: 150 }, { label: "关联活动", prop: "activityName", minWidth: 120, formatter: ({ activityName }) => activityName || "无关联活动" }, { label: "类型", prop: "couponType", minWidth: 100, cellRenderer: ({ row }) => { const item = typeMap[row.couponType] || { text: "未知", type: "info" }; return {item.text}; } }, { label: "发放类型", prop: "receiveType", minWidth: 80, cellRenderer: ({ row }) => { if (row.receiveType === 'Collect') { return 主动领取; } return 系统发放; } }, { label: "优惠值", prop: "discountValue", minWidth: 80, cellRenderer: ({ row }) => { if (row.couponType === 2) { return `${row.discountValue}折`; } return `¥${row.discountValue}`; } }, { label: "发放总量", prop: "totalCount", minWidth: 90 }, { label: "已领取", prop: "receivedCount", minWidth: 90 }, { label: "已使用", prop: "usedCount", minWidth: 90 }, { label: "有效期", minWidth: 200, cellRenderer: ({ row }) => { const start = row.startTime ? dayjs(row.startTime).format("YYYY-MM-DD") : ""; const end = row.endTime ? dayjs(row.endTime).format("YYYY-MM-DD") : ""; return `${start} ~ ${end}`; } }, { label: "状态", prop: "status", minWidth: 100, cellRenderer: ({ row }) => { const item = statusMap[row.status] || { text: "未知", type: "info" }; return {item.text}; } }, { label: "操作", fixed: "right", width: 200, slot: "operation" } ]; async function onSearch() { loading.value = true; try { const searchParams: any = { page: pagination.currentPage, pageSize: pagination.pageSize }; if (form.name) searchParams.name = form.name; if (form.type !== undefined) searchParams.type = form.type; if (form.status !== undefined) searchParams.status = form.status; if (form.receiveType) searchParams.receiveType = form.receiveType; const { data } = await getCouponList(searchParams); if (data) { dataList.value = data.list || []; updatePaginationData(pagination, data); } } finally { loading.value = false; } } function resetForm(formEl) { if (!formEl) return; formEl.resetFields(); resetPagination(pagination, onSearch); } async function handleDelete(row) { const { code } = await deleteCoupon(row.id); if (code === 200) { message("删除成功", { type: "success" }); onSearch(); } } async function handleToggleStatus(row) { const newStatus = row.status === 1 ? 0 : 1; const { code } = await updateCouponStatus(row.id, newStatus); if (code === 200) { message("状态更新成功", { type: "success" }); onSearch(); } } function handleDistribute(row) { const distributeForm = reactive({ targetType: "all", // all-全部用户, specific-指定用户 userIds: [], distributeCount: 1 // 发放数量 }); // 用户列表状态 const userList = ref([]); const userLoading = ref(false); const userSearchKeyword = ref(""); // 加载用户列表 const loadUserList = async (keyword = "") => { userLoading.value = true; try { const { data } = await getUserList({ page: 1, pageSize: 100, phone: keyword || undefined, nickname: keyword || undefined }); if (data) { userList.value = data.list || []; } } catch (error) { console.error("加载用户列表失败:", error); message("加载用户列表失败", { type: "error" }); } finally { userLoading.value = false; } }; // 用户搜索 const handleUserSearch = (query: string) => { userSearchKeyword.value = query; if (query) { loadUserList(query); } else { loadUserList(); } }; addDialog({ title: `发放优惠券 - ${row.couponName || '未知优惠券'}`, width: "700px", draggable: true, fullscreen: deviceDetection(), contentRenderer: () => ( { // 切换时清空用户选择 if (distributeForm.targetType === 'all') { distributeForm.userIds = []; } }} > {distributeForm.targetType === 'specific' && ( {loadUserList()} {userList.value.map((user: any) => ( ))}
已选择 {distributeForm.userIds.length} 个用户
)} {distributeForm.targetType === 'all' ? '将向平台所有用户发放该优惠券' : `将向已选择的 ${distributeForm.userIds.length} 个用户发放该优惠券`}
), beforeSure: async done => { // 验证 if (distributeForm.targetType === 'specific' && distributeForm.userIds.length === 0) { message("请选择至少一个用户", { type: "warning" }); return; } try { // 调用后端发放接口 const { code, message: errorMsg } = await distributeCoupon({ templateId: row.id, // 现在后端返回的是字符串,无需转换 targetType: distributeForm.targetType === 'all' ? 1 : 3, // 1-全部用户,3-指定用户 userIds: distributeForm.targetType === 'specific' ? distributeForm.userIds : undefined }); if (code === 200) { message("发放成功", { type: "success" }); done(); onSearch(); } else { message(errorMsg || "发放失败", { type: "error" }); } } catch (error) { const errorMsg = error?.response?.data?.message || "发放失败,请重试"; message(errorMsg, { type: "error" }); } } }); } function handleViewRecords(row) { addDialog({ title: "发放记录", width: "800px", draggable: true, fullscreen: deviceDetection(), contentRenderer: () => (
) }); } const formStyles = ` .coupon-form { padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } .form-block { margin-bottom: 20px; } .block-title { font-size: 14px; font-weight: 600; color: #374151; margin-bottom: 14px; padding-bottom: 8px; border-bottom: 1px solid #e5e7eb; } .form-grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px 24px; } .form-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px 24px; } .type-selector { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 18px; } .type-btn { padding: 12px 16px; border: 1px solid #e5e7eb; border-radius: 6px; background: #fff; cursor: pointer; transition: all 0.15s ease; text-align: center; } .type-btn:hover { border-color: #3b82f6; background: #f8fafc; } .type-btn.active { border-color: #3b82f6; background: #eff6ff; } .type-btn .name { font-size: 14px; font-weight: 600; color: #374151; margin-bottom: 4px; } .type-btn .hint { font-size: 12px; color: #9ca3af; } .type-btn.active .name { color: #3b82f6; } .scope-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } .scope-card { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 14px 16px; } .scope-card .card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .scope-card .card-label { font-size: 13px; font-weight: 500; color: #374151; } .scope-card .card-body { min-height: 32px; } .coupon-form .el-form-item { margin-bottom: 0; align-items: center; } .coupon-form .el-form-item__label { font-size: 13px; color: #606266; padding-right: 8px; } .coupon-form .el-input__wrapper, .coupon-form .el-textarea__inner { font-size: 13px; } .coupon-form .form-label-90 .el-form-item__label { width: 90px !important; } .coupon-form .form-label-100 .el-form-item__label { width: 100px !important; } .preview-box { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 12px 16px; background: #f9fafb; border-radius: 4px; margin-top: 12px; font-size: 14px; color: #374151; } .preview-box .value { font-weight: 600; } @media (max-width: 1200px) { .form-grid-3, .scope-grid { grid-template-columns: repeat(2, 1fr); } .type-selector { grid-template-columns: repeat(2, 1fr); } } @media (max-width: 768px) { .form-grid-2, .form-grid-3, .scope-grid { grid-template-columns: 1fr; } .type-selector { grid-template-columns: 1fr; } } `; function renderForm(formData: CouponFormData) { return (
基本信息
系统发放 主动领取
{formData.receiveType === 'Collect' ? '主动领取:将显示在用户端小程序领券中心,用户可自行领取' : '系统发放:创建后可在列表中点击发放,选择指定用户或全部用户'}
优惠券类型
{[1, 2, 3, 4].map(type => (
{ formData.type = type; }} >
{typeMap[type].text}
{type === 1 && "满额减免"} {type === 2 && "折扣优惠"} {type === 3 && "直接抵扣"} {type === 4 && "免费兑换"}
))}
{formData.type && (
)}
适用范围
门店范围 全部 指定
{formData.applyScope === 2 && }
设备范围 全部 指定
{formData.deviceScope === 2 && }
商品范围 全部 指定
{formData.productScope === 2 && }
使用说明
); } function validateForm(formData: CouponFormData): boolean { if (!formData.name) { message("请输入优惠券名称", { type: "warning" }); return false; } if (!formData.type) { message("请选择优惠券类型", { type: "warning" }); return false; } if (!formData.value || formData.value <= 0) { message("请输入有效的优惠值", { type: "warning" }); return false; } if (!formData.totalCount || formData.totalCount <= 0) { message("请输入发放总量", { type: "warning" }); return false; } return true; } function handleAdd() { const formData = reactive({ name: "", activityId: "", receiveType: "Release", // 默认为系统发放 type: undefined, value: null, totalCount: null, shopIds: [], deviceIds: [], productIds: [], validPeriod: [], description: "", applyScope: 1, deviceScope: 1, productScope: 1 }); addDialog({ title: "新增优惠券", width: "1100px", draggable: true, fullscreen: deviceDetection(), contentRenderer: () => renderForm(formData), beforeSure: async (done) => { if (!validateForm(formData)) return; try { // 将前端表单数据转换为后端 DTO 格式 const submitData = { couponName: formData.name, couponType: formData.type, receiveType: formData.receiveType, discountValue: formData.value, totalCount: formData.totalCount, validType: 1, // 固定时间 validStartTime: formData.validPeriod?.[0], validEndTime: formData.validPeriod?.[1], applyScope: formData.applyScope, productScope: formData.productScope, activityId: formData.activityId ? Number(formData.activityId) : null, shopIds: formData.shopIds, productIds: formData.productIds, couponDesc: formData.description, receiveLimit: 1 }; const { code, message: errorMsg } = await createCoupon(submitData); if (code === 200) { message("新增成功", { type: "success" }); done(); onSearch(); } else { message(errorMsg || "新增失败", { type: "error" }); } } catch (error: any) { // 展示后端返回的具体错误信息 const errorMsg = error?.response?.data?.message || error?.message || "新增失败,请重试"; message(errorMsg, { type: "error" }); } } }); } function handleSizeChange(val: number) { handlePageSizeChange(val, pagination, onSearch); } function handleCurrentChange(val: number) { handleCurrentPageChange(val, pagination, onSearch); } onMounted(() => { onSearch(); }); return { form, loading, columns, dataList, pagination, typeMap, statusMap, onSearch, resetForm, handleAdd, handleDelete, handleToggleStatus, handleDistribute, handleViewRecords, handleSizeChange, handleCurrentChange }; }