diff --git a/apps/web-antd/src/views/mall/promotion/coupon/data.ts b/apps/web-antd/src/views/mall/promotion/coupon/data.ts
new file mode 100644
index 000000000..a8efb9f5e
--- /dev/null
+++ b/apps/web-antd/src/views/mall/promotion/coupon/data.ts
@@ -0,0 +1,129 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+
+import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
+
+import { discountFormat } from './formatter';
+
+/** 列表的搜索表单 */
+export function useGridFormSchema(): VbenFormSchema[] {
+ return [
+ {
+ fieldName: 'nickname',
+ label: '会员昵称',
+ component: 'Input',
+ componentProps: {
+ placeholder: '请输入会员昵称',
+ clearable: true,
+ },
+ },
+ {
+ fieldName: 'createTime',
+ label: '领取时间',
+ component: 'RangePicker',
+ componentProps: {
+ ...getRangePickerDefaultProps(),
+ clearable: true,
+ },
+ },
+ ];
+}
+
+/** 列表的字段 */
+export function useGridColumns(): VxeTableGridOptions['columns'] {
+ return [
+ {
+ field: 'nickname',
+ title: '会员昵称',
+ minWidth: 100,
+ },
+ {
+ field: 'name',
+ title: '优惠券名称',
+ minWidth: 140,
+ },
+ {
+ field: 'productScope',
+ title: '类型',
+ minWidth: 110,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE },
+ },
+ },
+ {
+ field: 'discountType',
+ title: '优惠',
+ minWidth: 110,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE },
+ },
+ },
+ {
+ field: 'discountPrice',
+ title: '优惠力度',
+ minWidth: 110,
+ formatter: ({ row }) => {
+ return discountFormat(row);
+ },
+ },
+ {
+ field: 'takeType',
+ title: '领取方式',
+ minWidth: 110,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE },
+ },
+ },
+ {
+ field: 'status',
+ title: '状态',
+ minWidth: 110,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.PROMOTION_COUPON_STATUS },
+ },
+ },
+ {
+ field: 'createTime',
+ title: '领取时间',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ {
+ field: 'useTime',
+ title: '使用时间',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ {
+ title: '操作',
+ width: 100,
+ fixed: 'right',
+ slots: { default: 'actions' },
+ },
+ ];
+}
+
+/** 获取状态选项卡配置 */
+export function getStatusTabs() {
+ const tabs = [
+ {
+ label: '全部',
+ value: 'all',
+ },
+ ];
+
+ // 添加字典状态选项
+ const statusOptions = getDictOptions(DICT_TYPE.PROMOTION_COUPON_STATUS);
+ for (const option of statusOptions) {
+ tabs.push({
+ label: option.label,
+ value: String(option.value),
+ });
+ }
+
+ return tabs;
+}
diff --git a/apps/web-antd/src/views/mall/promotion/coupon/formatter.ts b/apps/web-antd/src/views/mall/promotion/coupon/formatter.ts
new file mode 100644
index 000000000..9dfbac415
--- /dev/null
+++ b/apps/web-antd/src/views/mall/promotion/coupon/formatter.ts
@@ -0,0 +1,65 @@
+import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate';
+
+import { floatToFixed2, formatDate } from '@vben/utils';
+
+import {
+ CouponTemplateValidityTypeEnum,
+ PromotionDiscountTypeEnum,
+} from '#/utils';
+
+// 格式化【优惠金额/折扣】
+export function discountFormat(row: MallCouponTemplateApi.CouponTemplate) {
+ if (row.discountType === PromotionDiscountTypeEnum.PRICE.type) {
+ return `¥${floatToFixed2(row.discountPrice)}`;
+ }
+ if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
+ return `${row.discountPercent}%`;
+ }
+ return `未知【${row.discountType}】`;
+}
+
+// 格式化【领取上限】
+export function takeLimitCountFormat(
+ row: MallCouponTemplateApi.CouponTemplate,
+) {
+ if (row.takeLimitCount) {
+ if (row.takeLimitCount === -1) {
+ return '无领取限制';
+ }
+ return `${row.takeLimitCount} 张/人`;
+ } else {
+ return ' ';
+ }
+}
+
+// 格式化【有效期限】
+export function validityTypeFormat(row: MallCouponTemplateApi.CouponTemplate) {
+ if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
+ return `${formatDate(row.validStartTime)} 至 ${formatDate(row.validEndTime)}`;
+ }
+ if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
+ return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`;
+ }
+ return `未知【${row.validityType}】`;
+}
+
+// 格式化【totalCount】
+export function totalCountFormat(row: MallCouponTemplateApi.CouponTemplate) {
+ if (row.totalCount === -1) {
+ return '不限制';
+ }
+ return row.totalCount;
+}
+
+// 格式化【剩余数量】
+export function remainedCountFormat(row: MallCouponTemplateApi.CouponTemplate) {
+ if (row.totalCount === -1) {
+ return '不限制';
+ }
+ return row.totalCount - row.takeCount;
+}
+
+// 格式化【最低消费】
+export function usePriceFormat(row: MallCouponTemplateApi.CouponTemplate) {
+ return `¥${floatToFixed2(row.usePrice)}`;
+}
diff --git a/apps/web-antd/src/views/mall/promotion/coupon/index.vue b/apps/web-antd/src/views/mall/promotion/coupon/index.vue
index 90f761d43..27fbb2e38 100644
--- a/apps/web-antd/src/views/mall/promotion/coupon/index.vue
+++ b/apps/web-antd/src/views/mall/promotion/coupon/index.vue
@@ -1,32 +1,132 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-antd/src/views/mall/promotion/coupon/template/data.ts b/apps/web-antd/src/views/mall/promotion/coupon/template/data.ts
new file mode 100644
index 000000000..08ea5b772
--- /dev/null
+++ b/apps/web-antd/src/views/mall/promotion/coupon/template/data.ts
@@ -0,0 +1,252 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+
+// 格式化函数移到组件内部实现
+import { z } from '#/adapter/form';
+import {
+ CommonStatusEnum,
+ DICT_TYPE,
+ getDictOptions,
+ getRangePickerDefaultProps,
+} from '#/utils';
+
+import {
+ discountFormat,
+ remainedCountFormat,
+ takeLimitCountFormat,
+ totalCountFormat,
+ validityTypeFormat,
+} from '../formatter';
+
+/** 新增/修改的表单 */
+export function useFormSchema(): VbenFormSchema[] {
+ return [
+ {
+ fieldName: 'id',
+ component: 'Input',
+ dependencies: {
+ triggerFields: [''],
+ show: () => false,
+ },
+ },
+ {
+ fieldName: 'name',
+ label: '优惠券名称',
+ component: 'Input',
+ componentProps: {
+ placeholder: '请输入优惠券名称',
+ },
+ rules: 'required',
+ },
+ {
+ fieldName: 'description',
+ label: '优惠券描述',
+ component: 'Textarea',
+ },
+ // TODO
+ {
+ fieldName: 'productScope',
+ label: '优惠类型',
+ component: 'RadioGroup',
+ componentProps: {
+ options: getDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE, 'number'),
+ },
+ rules: 'required',
+ },
+ {
+ fieldName: 'takeType',
+ label: '领取方式',
+ component: 'Select',
+ componentProps: {
+ placeholder: '请选择领取方式',
+ options: getDictOptions(DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE, 'number'),
+ },
+ rules: 'required',
+ },
+ {
+ fieldName: 'validityType',
+ label: '有效期类型',
+ component: 'Select',
+ componentProps: {
+ placeholder: '请选择有效期类型',
+ options: getDictOptions(
+ DICT_TYPE.PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE,
+ 'number',
+ ),
+ },
+ rules: 'required',
+ },
+ {
+ fieldName: 'totalCount',
+ label: '发放数量',
+ component: 'InputNumber',
+ componentProps: {
+ min: 0,
+ placeholder: '请输入发放数量',
+ },
+ rules: 'required',
+ },
+ {
+ fieldName: 'takeLimitCount',
+ label: '领取上限',
+ component: 'InputNumber',
+ componentProps: {
+ min: 0,
+ placeholder: '请输入领取上限',
+ },
+ rules: 'required',
+ },
+ {
+ fieldName: 'status',
+ label: '优惠券状态',
+ component: 'RadioGroup',
+ componentProps: {
+ options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
+ buttonStyle: 'solid',
+ optionType: 'button',
+ },
+ rules: z.number().default(CommonStatusEnum.ENABLE),
+ },
+ ];
+}
+
+/** 列表的搜索表单 */
+export function useGridFormSchema(): VbenFormSchema[] {
+ return [
+ {
+ fieldName: 'name',
+ label: '优惠券名称',
+ component: 'Input',
+ componentProps: {
+ placeholder: '请输入优惠券名称',
+ clearable: true,
+ },
+ },
+ {
+ fieldName: 'discountType',
+ label: '优惠类型',
+ component: 'Select',
+ componentProps: {
+ placeholder: '请选择优惠类型',
+ clearable: true,
+ options: getDictOptions(DICT_TYPE.PROMOTION_DISCOUNT_TYPE, 'number'),
+ },
+ },
+ {
+ fieldName: 'status',
+ label: '优惠券状态',
+ component: 'Select',
+ componentProps: {
+ placeholder: '请选择优惠券状态',
+ clearable: true,
+ options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
+ },
+ },
+ {
+ fieldName: 'createTime',
+ label: '创建时间',
+ component: 'RangePicker',
+ componentProps: {
+ ...getRangePickerDefaultProps(),
+ clearable: true,
+ },
+ },
+ ];
+}
+
+/** 列表的字段 */
+export function useGridColumns(): VxeTableGridOptions['columns'] {
+ return [
+ { type: 'checkbox', width: 40 },
+ {
+ field: 'name',
+ title: '优惠券名称',
+ minWidth: 140,
+ },
+ {
+ field: 'productScope',
+ title: '类型',
+ minWidth: 130,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE },
+ },
+ },
+ {
+ field: 'discountType',
+ title: '优惠',
+ minWidth: 110,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE },
+ },
+ },
+ {
+ field: 'discountPrice',
+ title: '优惠力度',
+ minWidth: 110,
+ formatter: ({ row }) => {
+ return discountFormat(row);
+ },
+ },
+ {
+ field: 'takeType',
+ title: '领取方式',
+ minWidth: 100,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE },
+ },
+ },
+ {
+ field: 'validityType',
+ title: '使用时间',
+ minWidth: 180,
+ formatter: ({ row }) => {
+ return validityTypeFormat(row);
+ },
+ },
+ {
+ field: 'totalCount',
+ title: '发放数量',
+ minWidth: 100,
+ formatter: ({ row }) => {
+ return totalCountFormat(row);
+ },
+ },
+ {
+ field: 'remainedCount',
+ title: '剩余数量',
+ minWidth: 100,
+ formatter: ({ row }) => {
+ return remainedCountFormat(row);
+ },
+ },
+ {
+ field: 'takeLimitCount',
+ title: '领取上限',
+ minWidth: 100,
+ formatter: ({ row }) => {
+ return takeLimitCountFormat(row);
+ },
+ },
+ {
+ field: 'status',
+ title: '状态',
+ minWidth: 100,
+ slots: { default: 'status' },
+ },
+ {
+ field: 'createTime',
+ title: '创建时间',
+ width: 180,
+ formatter: 'formatDateTime',
+ },
+ {
+ title: '操作',
+ width: 120,
+ fixed: 'right',
+ slots: { default: 'actions' },
+ },
+ ];
+}
diff --git a/apps/web-antd/src/views/mall/promotion/coupon/template/index.vue b/apps/web-antd/src/views/mall/promotion/coupon/template/index.vue
index d5e866f89..985295643 100644
--- a/apps/web-antd/src/views/mall/promotion/coupon/template/index.vue
+++ b/apps/web-antd/src/views/mall/promotion/coupon/template/index.vue
@@ -1,32 +1,190 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-antd/src/views/mall/promotion/coupon/template/modules/form.vue b/apps/web-antd/src/views/mall/promotion/coupon/template/modules/form.vue
new file mode 100644
index 000000000..f64360980
--- /dev/null
+++ b/apps/web-antd/src/views/mall/promotion/coupon/template/modules/form.vue
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+