This commit is contained in:
aaron 2025-03-16 00:10:38 +08:00
parent 3731b0efff
commit bb4d562346
2 changed files with 286 additions and 168 deletions

View File

@ -48,7 +48,8 @@
@ok="handleSubmit" @ok="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
:confirmLoading="submitting" :confirmLoading="submitting"
width="720px" width="800px"
class="coupon-activity-modal"
> >
<a-form <a-form
ref="formRef" ref="formRef"
@ -57,6 +58,7 @@
layout="vertical" layout="vertical"
class="coupon-activity-form" class="coupon-activity-form"
> >
<a-card :bordered="false" class="form-card">
<a-form-item <a-form-item
label="活动名称" label="活动名称"
name="name" name="name"
@ -69,20 +71,7 @@
/> />
</a-form-item> </a-form-item>
<a-form-item <a-row :gutter="24">
label="活动说明"
name="description"
>
<a-textarea
v-model:value="formState.description"
placeholder="请输入活动说明"
:rows="2"
:maxLength="200"
:auto-size="{ minRows: 2, maxRows: 3 }"
/>
</a-form-item>
<a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item <a-form-item
label="活动开始时间" label="活动开始时间"
@ -115,7 +104,7 @@
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="16"> <a-row :gutter="24">
<a-col :span="12"> <a-col :span="12">
<a-form-item <a-form-item
label="每日开始时间" label="每日开始时间"
@ -146,7 +135,7 @@
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="16"> <a-row :gutter="24">
<a-col :span="12"> <a-col :span="12">
<a-form-item <a-form-item
label="总限领数量" label="总限领数量"
@ -163,15 +152,15 @@
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item <a-form-item
label="每日限领数量" label="用户可领次数限制"
name="daily_limit" name="user_limit"
:rules="[{ required: true, message: '请输入每日限领数量' }]" :rules="[{ required: true, message: '请输入用户可领次数限制' }]"
> >
<a-input-number <a-input-number
v-model:value="formState.daily_limit" v-model:value="formState.user_limit"
:min="1" :min="1"
style="width: 100%" style="width: 100%"
placeholder="请输入每日限领数量" placeholder="请输入用户可领次数限制"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@ -182,26 +171,42 @@
name="coupon_config" name="coupon_config"
:rules="[{ required: true, message: '请至少选择一个优惠券' }]" :rules="[{ required: true, message: '请至少选择一个优惠券' }]"
> >
<div class="coupon-table-container">
<a-table <a-table
:columns="couponColumns" :columns="couponColumns"
:data-source="couponTemplates" :data-source="couponTemplates"
:pagination="false" :pagination="false"
size="small" size="small"
:scroll="{ y: 180 }" :scroll="{ y: 240 }"
:rowClassName="(record) => isSelectedCoupon(record.id) ? 'selected-coupon-row' : ''"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'selected'">
<a-checkbox
:checked="isSelectedCoupon(record.id)"
@change="(e) => handleCouponSelect(record.id, e.target.checked)"
/>
</template>
<template v-if="column.key === 'type'">
<a-tag :color="record.coupon_type === 'CASH' ? 'green' : 'blue'">
{{ record.coupon_type === 'CASH' ? '现金券' : '商品券' }}
</a-tag>
</template>
<template v-if="column.key === 'amount'"> <template v-if="column.key === 'amount'">
{{ record.amount }} <span v-if="record.coupon_type === 'CASH'">{{ record.amount }}</span>
<span v-else>-</span>
</template> </template>
<template v-if="column.key === 'quantity'"> <template v-if="column.key === 'quantity'">
<a-input-number <a-input-number
v-model:value="formState.coupon_config[record.id]" v-model:value="formState.coupon_config[record.id]"
:min="0" :min="0"
size="small" size="small"
:disabled="!isSelectedCoupon(record.id)"
/> />
</template> </template>
</template> </template>
</a-table> </a-table>
</div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
@ -209,7 +214,9 @@
name="is_active" name="is_active"
> >
<a-switch v-model:checked="formState.is_active" /> <a-switch v-model:checked="formState.is_active" />
<span class="status-label">{{ formState.is_active ? '启用' : '禁用' }}</span>
</a-form-item> </a-form-item>
</a-card>
</a-form> </a-form>
</a-modal> </a-modal>
</div> </div>
@ -227,7 +234,10 @@ import {
Input, Input,
InputNumber, InputNumber,
Switch, Switch,
Table Table,
Checkbox,
Card,
Divider
} from 'ant-design-vue' } from 'ant-design-vue'
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
@ -244,7 +254,10 @@ export default defineComponent({
ATextarea: Input.TextArea, ATextarea: Input.TextArea,
AInputNumber: InputNumber, AInputNumber: InputNumber,
ASwitch: Switch, ASwitch: Switch,
ATable: Table ATable: Table,
ACheckbox: Checkbox,
ACard: Card,
ADivider: Divider
}, },
setup() { setup() {
@ -291,9 +304,9 @@ export default defineComponent({
width: 120 width: 120
}, },
{ {
title: '每日限领', title: '用户可领次数',
dataIndex: 'daily_limit', dataIndex: 'user_limit',
key: 'daily_limit', key: 'user_limit',
width: 100, width: 100,
align: 'center' align: 'center'
}, },
@ -383,17 +396,30 @@ export default defineComponent({
daily_start_time: null, daily_start_time: null,
daily_end_time: null, daily_end_time: null,
total_limit: undefined, total_limit: undefined,
daily_limit: 1, user_limit: 1,
coupon_config: {}, coupon_config: {},
selected_coupons: [],
is_active: true is_active: true
}) })
const couponColumns = [ const couponColumns = [
{
title: '选择',
key: 'selected',
width: 60,
align: 'center'
},
{ {
title: '模板名称', title: '模板名称',
dataIndex: 'name', dataIndex: 'name',
key: 'name' key: 'name'
}, },
{
title: '券类型',
key: 'type',
width: 100,
align: 'center'
},
{ {
title: '优惠金额', title: '优惠金额',
key: 'amount', key: 'amount',
@ -420,6 +446,28 @@ export default defineComponent({
} }
} }
//
const isSelectedCoupon = (couponId) => {
return formState.value.selected_coupons.includes(couponId);
}
//
const handleCouponSelect = (couponId, checked) => {
if (checked) {
//
if (!isSelectedCoupon(couponId)) {
formState.value.selected_coupons.push(couponId);
// 1
if (!formState.value.coupon_config[couponId]) {
formState.value.coupon_config[couponId] = 1;
}
}
} else {
//
formState.value.selected_coupons = formState.value.selected_coupons.filter(id => id !== couponId);
}
}
// //
const showAddModal = async () => { const showAddModal = async () => {
isEdit.value = false isEdit.value = false
@ -432,8 +480,9 @@ export default defineComponent({
daily_start_time: null, daily_start_time: null,
daily_end_time: null, daily_end_time: null,
total_limit: undefined, total_limit: undefined,
daily_limit: 1, user_limit: 1,
coupon_config: {}, coupon_config: {},
selected_coupons: [],
is_active: true is_active: true
} }
await fetchCouponTemplates() await fetchCouponTemplates()
@ -451,9 +500,12 @@ export default defineComponent({
// 0 // 0
const couponConfig = {} const couponConfig = {}
const selectedCoupons = []
Object.entries(record.coupon_config || {}).forEach(([key, value]) => { Object.entries(record.coupon_config || {}).forEach(([key, value]) => {
if (value && value > 0) { if (value && value > 0) {
couponConfig[key] = value couponConfig[key] = value
selectedCoupons.push(parseInt(key))
} }
}) })
@ -466,8 +518,9 @@ export default defineComponent({
daily_start_time: dayjs(record.daily_start_time, 'HH:mm:ss'), daily_start_time: dayjs(record.daily_start_time, 'HH:mm:ss'),
daily_end_time: dayjs(record.daily_end_time, 'HH:mm:ss'), daily_end_time: dayjs(record.daily_end_time, 'HH:mm:ss'),
total_limit: record.total_limit, total_limit: record.total_limit,
daily_limit: record.daily_limit, user_limit: record.user_limit,
coupon_config: couponConfig, coupon_config: couponConfig,
selected_coupons: selectedCoupons,
is_active: record.is_active is_active: record.is_active
} }
@ -483,11 +536,12 @@ export default defineComponent({
formRef.value.validate().then(async () => { formRef.value.validate().then(async () => {
try { try {
submitting.value = true submitting.value = true
// //
const couponConfig = {} const couponConfig = {}
Object.entries(formState.value.coupon_config).forEach(([key, value]) => { formState.value.selected_coupons.forEach(couponId => {
if (value && value > 0) { const quantity = formState.value.coupon_config[couponId]
couponConfig[key] = value if (quantity && quantity > 0) {
couponConfig[couponId] = quantity
} }
}) })
@ -500,6 +554,9 @@ export default defineComponent({
daily_end_time: dayjs(formState.value.daily_end_time).format('HH:mm:ss') daily_end_time: dayjs(formState.value.daily_end_time).format('HH:mm:ss')
} }
//
delete params.selected_coupons
let res let res
if (isEdit.value) { if (isEdit.value) {
res = await request.put(`/api/coupon-activities/${currentId.value}`, params) res = await request.put(`/api/coupon-activities/${currentId.value}`, params)
@ -617,7 +674,9 @@ export default defineComponent({
disabledEndDate, disabledEndDate,
disabledDailyStartTime, disabledDailyStartTime,
disabledDailyEndTime, disabledDailyEndTime,
handleEdit handleEdit,
isSelectedCoupon,
handleCouponSelect
} }
} }
}) })
@ -645,7 +704,7 @@ export default defineComponent({
} }
.coupon-activity-form :deep(.ant-form-item) { .coupon-activity-form :deep(.ant-form-item) {
margin-bottom: 16px !important; margin-bottom: 24px !important;
} }
.coupon-activity-form :deep(.ant-form-item:last-child) { .coupon-activity-form :deep(.ant-form-item:last-child) {
@ -692,11 +751,66 @@ export default defineComponent({
} }
.coupon-activity-form :deep(.ant-row) { .coupon-activity-form :deep(.ant-row) {
margin-bottom: 0 !important; margin-bottom: 8px !important;
} }
/* 调整表格高度 */ /* 调整表格高度 */
.coupon-activity-form :deep(.ant-table-body) { .coupon-activity-form :deep(.ant-table-body) {
max-height: 200px !important; max-height: 200px !important;
} }
/* 选中的优惠券行样式 */
:deep(.selected-coupon-row) {
background-color: #e6f7ff;
}
:deep(.selected-coupon-row:hover) {
background-color: #cceeff !important;
}
/* 新增样式 - 优化模态框 */
.coupon-activity-modal {
:deep(.ant-modal-body) {
padding: 16px;
}
:deep(.ant-modal-footer) {
border-top: 1px solid #f0f0f0;
padding: 10px 24px;
}
}
.form-card {
box-shadow: none !important;
:deep(.ant-card-body) {
padding: 0;
}
}
.coupon-activity-form :deep(.ant-divider) {
margin: 16px 0;
color: #1890ff;
font-weight: 500;
}
.coupon-activity-form :deep(.ant-divider-inner-text) {
font-size: 14px;
}
.coupon-table-container {
border: 1px solid #f0f0f0;
border-radius: 4px;
padding: 0;
margin-top: 8px;
}
.status-label {
margin-left: 8px;
color: rgba(0, 0, 0, 0.65);
}
.coupon-activity-form :deep(.ant-card) {
background: transparent;
}
</style> </style>

View File

@ -109,7 +109,11 @@ export default defineComponent({
const handleSubmit = async (values) => { const handleSubmit = async (values) => {
try { try {
loading.value = true loading.value = true
const res = await request.post('/api/user/password-login', values) const params = {
...values,
role: formState.role
}
const res = await request.post('/api/user/password-login', params)
if (res.code === 200) { if (res.code === 200) {
localStorage.setItem('token', res.data.access_token) localStorage.setItem('token', res.data.access_token)