Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
190c6e38
authored
Sep 01, 2023
by
芋道源码
Committed by
Gitee
Sep 01, 2023
Browse files
Options
Browse Files
Download
Plain Diff
!225 优惠券
Merge pull request !225 from 疯狂的世界/owen_dev
parents
0743e494
27219e18
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
519 additions
and
89 deletions
+519
-89
src/api/mall/promotion/coupon/coupon.ts
+8
-0
src/api/mall/promotion/coupon/couponTemplate.ts
+9
-1
src/utils/constants.ts
+4
-0
src/views/mall/product/category/components/ProductCategorySelect.vue
+47
-0
src/views/mall/product/spu/components/SpuTableSelect.vue
+106
-22
src/views/mall/promotion/coupon/components/CouponSend.vue
+162
-0
src/views/mall/promotion/coupon/formatter.ts
+44
-0
src/views/mall/promotion/coupon/template/CouponTemplateForm.vue
+98
-28
src/views/mall/promotion/coupon/template/index.vue
+10
-37
src/views/member/user/index.vue
+31
-1
No files found.
src/api/mall/promotion/coupon/coupon.ts
View file @
190c6e38
...
...
@@ -16,3 +16,11 @@ export const getCouponPage = async (params: PageParam) => {
params
:
params
})
}
// 发送优惠券
export
const
sendCoupon
=
async
(
data
:
any
)
=>
{
return
request
.
post
({
url
:
'/promotion/coupon/send'
,
data
:
data
})
}
src/api/mall/promotion/coupon/couponTemplate.ts
View file @
190c6e38
...
...
@@ -9,7 +9,7 @@ export interface CouponTemplateVO {
takeType
:
number
usePrice
:
number
productScope
:
number
productSpuIds
:
string
productSpuIds
:
number
[]
validityType
:
number
validStartTime
:
Date
validEndTime
:
Date
...
...
@@ -73,6 +73,14 @@ export function getCouponTemplatePage(params: PageParam) {
})
}
// 获得可用于领取的优惠劵模板分页
export
function
getCanTakeCouponTemplatePage
(
params
:
PageParam
)
{
return
request
.
get
({
url
:
'/promotion/coupon-template/can-take-page'
,
params
:
params
})
}
// 导出优惠劵模板 Excel
export
function
exportCouponTemplateExcel
(
params
:
PageParam
)
{
return
request
.
get
({
...
...
src/utils/constants.ts
View file @
190c6e38
...
...
@@ -220,6 +220,10 @@ export const PromotionProductScopeEnum = {
SPU
:
{
scope
:
2
,
name
:
'指定商品参与'
},
CATEGORY
:
{
scope
:
3
,
name
:
'指定品类参与'
}
}
...
...
src/views/mall/product/category/components/ProductCategorySelect.vue
0 → 100644
View file @
190c6e38
<
template
>
<el-tree-select
v-model=
"selectCategoryId"
:data=
"categoryList"
:props=
"defaultProps"
:multiple=
"multiple"
:show-checkbox=
"multiple"
class=
"w-1/1"
node-key=
"id"
placeholder=
"请选择商品分类"
/>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
defaultProps
,
handleTree
}
from
'@/utils/tree'
import
*
as
ProductCategoryApi
from
'@/api/mall/product/category'
import
{
oneOf
}
from
'vue-types'
import
{
propTypes
}
from
'@/utils/propTypes'
/** 商品分类选择组件 */
defineOptions
({
name
:
'ProductCategorySelect'
})
const
props
=
defineProps
({
value
:
oneOf
([
propTypes
.
number
,
propTypes
.
array
.
def
([])]).
isRequired
,
// 选中的ID
multiple
:
propTypes
.
bool
.
def
(
false
)
// 是否多选
})
/** 选中的分类ID */
const
selectCategoryId
=
computed
({
get
:
()
=>
{
return
props
.
value
},
set
:
(
val
:
number
|
number
[])
=>
{
emit
(
'update:modelValue'
,
val
)
}
})
/** 分类选择 */
const
emit
=
defineEmits
([
'update:modelValue'
])
const
categoryList
=
ref
([])
// 分类树
onMounted
(
async
()
=>
{
// 获得分类树
const
data
=
await
ProductCategoryApi
.
getCategoryList
({})
categoryList
.
value
=
handleTree
(
data
,
'id'
,
'parentId'
)
})
</
script
>
src/views/mall/product/spu/components/SpuTableSelect.vue
View file @
190c6e38
<
template
>
<Dialog
v-model=
"dialogVisible"
:appendToBody=
"true"
title=
"选择商品"
width=
"70%"
>
<ContentWrap>
<el-row
:gutter=
"20"
class=
"mb-10px"
>
<el-col
:span=
"6"
>
<el-input
...
...
@@ -43,10 +44,26 @@
</el-button>
</el-col>
</el-row>
<el-table
ref=
"spuListRef"
v-loading=
"loading"
:data=
"list"
show-overflow-tooltip
>
<el-table-column
label=
"#"
width=
"55"
>
<el-table
v-loading=
"loading"
:data=
"list"
show-overflow-tooltip
>
<!-- 多选模式 -->
<el-table-column
key=
"2"
type=
"selection"
width=
"55"
v-if=
"multiple"
>
<template
#
header
>
<el-checkbox
:value=
"allChecked && checkedPageNos.indexOf(queryParams.pageNo) > -1"
@
change=
"handleCheckAll"
/>
</
template
>
<
template
#
default=
"{ row }"
>
<el-checkbox
:value=
"checkedSpuIds.indexOf(row.id) > -1"
@
change=
"(checked: boolean) => handleCheckOne(checked, row)"
/>
</
template
>
</el-table-column>
<!-- 单选模式 -->
<el-table-column
label=
"#"
width=
"55"
v-else
>
<
template
#
default=
"{ row }"
>
<el-radio
:label=
"row.id"
v-model=
"selectedSpuId"
@
change=
"hand
leSelected(row)"
<el-radio
:label=
"row.id"
v-model=
"selectedSpuId"
@
change=
"handleSing
leSelected(row)"
>
</el-radio
>
</
template
>
...
...
@@ -65,7 +82,7 @@
<el-table-column
label=
"商品名称"
min-width=
"200"
prop=
"name"
/>
<el-table-column
label=
"商品分类"
min-width=
"100"
prop=
"categoryId"
>
<
template
#
default=
"{ row }"
>
<span>
{{
categoryList
.
find
((
c
)
=>
c
.
id
===
row
.
categoryId
)?.
name
}}
</span>
<span>
{{
categoryList
?
.
find
((
c
)
=>
c
.
id
===
row
.
categoryId
)?.
name
}}
</span>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -76,6 +93,11 @@
:total=
"total"
@
pagination=
"getList"
/>
</ContentWrap>
<
template
#
footer
v-if=
"multiple"
>
<el-button
type=
"primary"
@
click=
"handleEmitChange"
>
确 定
</el-button>
<el-button
@
click=
"dialogVisible = false"
>
取 消
</el-button>
</
template
>
</Dialog>
</template>
...
...
@@ -85,12 +107,19 @@ import { defaultProps, handleTree } from '@/utils/tree'
import
*
as
ProductCategoryApi
from
'@/api/mall/product/category'
import
*
as
ProductSpuApi
from
'@/api/mall/product/spu'
import
{
propTypes
}
from
'@/utils/propTypes'
type
Spu
=
Required
<
ProductSpuApi
.
Spu
>
defineOptions
({
name
:
'SpuTableSelect'
})
const
message
=
useMessage
()
// 消息弹窗
const
props
=
defineProps
({
// 多选
multiple
:
propTypes
.
bool
.
def
(
false
)
})
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
<
any
[]
>
([])
// 列表的数据
const
list
=
ref
<
Spu
[]
>
([])
// 列表的数据
const
loading
=
ref
(
false
)
// 列表的加载中
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
queryParams
=
ref
({
...
...
@@ -101,26 +130,24 @@ const queryParams = ref({
categoryId
:
null
,
createTime
:
[]
})
// 查询参数
const
spuListRef
=
ref
<
InstanceType
<
typeof
ElTable
>>
()
const
selectedSpuId
=
ref
()
// 选中的商品 spuId
/** 选中时触发 */
const
handleSelected
=
(
row
:
ProductSpuApi
.
Spu
)
=>
{
emits
(
'change'
,
row
)
// 关闭弹窗
dialogVisible
.
value
=
false
selectedSpuId
.
value
=
undefined
}
// 确认选择时的触发事件
const
emits
=
defineEmits
<
{
(
e
:
'change'
,
spu
:
ProductSpuApi
.
Spu
):
void
}
>
()
/** 打开弹窗 */
const
open
=
()
=>
{
const
open
=
(
spus
?:
Spu
[])
=>
{
if
(
spus
&&
spus
.
length
>
0
)
{
// todo check-box不显示选中?
checkedSpus
.
value
=
[...
spus
]
checkedSpuIds
.
value
=
spus
.
map
((
spu
)
=>
spu
.
id
)
}
else
{
checkedSpus
.
value
=
[]
checkedSpuIds
.
value
=
[]
}
allChecked
.
value
=
false
checkedPageNos
.
value
=
[]
dialogVisible
.
value
=
true
resetQuery
()
}
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
...
...
@@ -138,6 +165,7 @@ const getList = async () => {
/** 搜索按钮操作 */
const
handleQuery
=
()
=>
{
queryParams
.
value
.
pageNo
=
1
getList
()
}
...
...
@@ -154,9 +182,65 @@ const resetQuery = () => {
getList
()
}
const
allChecked
=
ref
(
false
)
//是否全选
const
checkedPageNos
=
ref
<
number
[]
>
([])
//选中的页码
const
checkedSpuIds
=
ref
<
number
[]
>
([])
//选中的商品ID
const
checkedSpus
=
ref
<
Spu
[]
>
([])
//选中的商品
/** 单选中时触发 */
const
handleSingleSelected
=
(
row
:
Spu
)
=>
{
emits
(
'change'
,
row
)
// 关闭弹窗
dialogVisible
.
value
=
false
// 记住上次选择的ID
selectedSpuId
.
value
=
row
.
id
}
/** 多选完成 */
const
handleEmitChange
=
()
=>
{
// 关闭弹窗
dialogVisible
.
value
=
false
emits
(
'change'
,
[...
checkedSpus
.
value
])
}
/** 确认选择时的触发事件 */
const
emits
=
defineEmits
<
{
(
e
:
'change'
,
spu
:
Spu
|
Spu
[]
|
any
):
void
}
>
()
/** 全选 */
const
handleCheckAll
=
(
checked
:
boolean
)
=>
{
//todo 不触发?
console
.
log
(
'checkAll'
,
checked
)
allChecked
.
value
=
checked
const
index
=
checkedPageNos
.
value
.
indexOf
(
queryParams
.
value
.
pageNo
)
checkedPageNos
.
value
.
push
(
queryParams
.
value
.
pageNo
)
if
(
index
>
-
1
)
{
checkedPageNos
.
value
.
splice
(
index
,
1
)
}
list
.
value
.
forEach
((
item
)
=>
handleCheckOne
(
checked
,
item
))
}
/** 选中一行 */
const
handleCheckOne
=
(
checked
:
boolean
,
spu
:
Spu
)
=>
{
if
(
checked
)
{
const
index
=
checkedSpuIds
.
value
.
indexOf
(
spu
.
id
)
if
(
index
===
-
1
)
{
checkedSpuIds
.
value
.
push
(
spu
.
id
)
checkedSpus
.
value
.
push
(
spu
)
}
}
else
{
const
index
=
checkedSpuIds
.
value
.
indexOf
(
spu
.
id
)
if
(
index
>
-
1
)
{
checkedSpuIds
.
value
.
splice
(
index
,
1
)
checkedSpus
.
value
.
splice
(
index
,
1
)
}
}
}
const
categoryList
=
ref
()
// 分类列表
const
categoryTreeList
=
ref
()
// 分类树
/** 初始化 **/
onMounted
(
async
()
=>
{
await
getList
()
...
...
src/views/mall/promotion/coupon/components/CouponSend.vue
0 → 100644
View file @
190c6e38
<
template
>
<Dialog
v-model=
"dialogVisible"
:appendToBody=
"true"
title=
"发送优惠券"
width=
"70%"
>
<el-form
ref=
"queryFormRef"
:inline=
"true"
:model=
"queryParams"
class=
"-mb-15px"
label-width=
"82px"
>
<el-form-item
label=
"优惠券名称"
prop=
"name"
>
<el-input
v-model=
"queryParams.name"
class=
"!w-240px"
placeholder=
"请输入优惠劵名"
clearable
@
keyup=
"handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button
@
click=
"handleQuery"
>
<Icon
class=
"mr-5px"
icon=
"ep:search"
/>
搜索
</el-button>
<el-button
@
click=
"resetQuery"
>
<Icon
class=
"mr-5px"
icon=
"ep:refresh"
/>
重置
</el-button>
</el-form-item>
</el-form>
<el-table
v-loading=
"loading"
:data=
"list"
show-overflow-tooltip
>
<el-table-column
align=
"center"
label=
"优惠券名称"
prop=
"name"
min-width=
"60"
/>
<el-table-column
label=
"优惠金额 / 折扣"
align=
"center"
prop=
"discount"
:formatter=
"discountFormat"
min-width=
"60"
/>
<el-table-column
align=
"center"
label=
"最低消费"
prop=
"usePrice"
min-width=
"60"
:formatter=
"userPriceFormat"
/>
<el-table-column
align=
"center"
label=
"有效期限"
prop=
"validityType"
min-width=
"140"
:formatter=
"validityTypeFormat"
/>
<el-table-column
align=
"center"
label=
"剩余数量"
min-width=
"60"
:formatter=
"remainedCountFormat"
/>
<el-table-column
label=
"操作"
align=
"center"
min-width=
"60px"
fixed=
"right"
>
<template
#
default=
"scope"
>
<el-button
link
type=
"primary"
:disabled=
"sendLoading"
:loading=
"sendLoading"
@
click=
"handleSendCoupon(scope.row.id)"
v-hasPermi=
"['member:level:update']"
>
发送
</el-button>
</
template
>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit=
"queryParams.pageSize"
v-model:page=
"queryParams.pageNo"
:total=
"total"
@
pagination=
"getList"
/>
</Dialog>
</template>
<
script
lang=
"ts"
setup
>
import
*
as
CouponTemplateApi
from
'@/api/mall/promotion/coupon/couponTemplate'
import
*
as
CouponApi
from
'@/api/mall/promotion/coupon/coupon'
import
{
discountFormat
,
remainedCountFormat
,
userPriceFormat
,
validityTypeFormat
}
from
'@/views/mall/promotion/coupon/formatter'
defineOptions
({
name
:
'PromotionCouponSend'
})
const
message
=
useMessage
()
// 消息弹窗
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
<
any
[]
>
([])
// 列表的数据
const
loading
=
ref
(
false
)
// 列表的加载中
const
sendLoading
=
ref
(
false
)
// 发送按钮的加载中
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
queryParams
=
ref
({
pageNo
:
1
,
pageSize
:
10
,
name
:
null
})
// 查询参数
const
queryFormRef
=
ref
()
// 搜索的表单
// 领取人的编号列表
let
userIds
:
number
[]
=
[]
/** 打开弹窗 */
const
open
=
(
ids
:
number
[])
=>
{
userIds
=
ids
// 打开时重置查询,防止发送列表剩余数量未更新的问题
resetQuery
()
dialogVisible
.
value
=
true
}
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
/** 查询列表 */
const
getList
=
async
()
=>
{
loading
.
value
=
true
try
{
const
data
=
await
CouponTemplateApi
.
getCanTakeCouponTemplatePage
(
queryParams
.
value
)
list
.
value
=
data
.
list
total
.
value
=
data
.
total
}
finally
{
loading
.
value
=
false
}
}
/** 搜索按钮操作 */
const
handleQuery
=
()
=>
{
queryParams
.
pageNo
=
1
getList
()
}
/** 重置按钮操作 */
const
resetQuery
=
()
=>
{
queryFormRef
?.
value
?.
resetFields
()
handleQuery
()
}
/** 发送操作 **/
const
handleSendCoupon
=
async
(
templateId
:
number
)
=>
{
try
{
sendLoading
.
value
=
true
await
CouponApi
.
sendCoupon
({
templateId
,
userIds
})
message
.
success
(
'发送成功'
)
dialogVisible
.
value
=
false
}
finally
{
sendLoading
.
value
=
false
}
}
/** 初始化 **/
// onMounted(async () => {
// await getList()
// })
</
script
>
src/views/mall/promotion/coupon/formatter.ts
0 → 100644
View file @
190c6e38
import
{
CouponTemplateValidityTypeEnum
,
PromotionDiscountTypeEnum
}
from
'@/utils/constants'
import
{
formatDate
}
from
'@/utils/formatTime'
import
{
CouponTemplateVO
}
from
'@/api/mall/promotion/coupon/couponTemplate'
import
{
floatToFixed2
}
from
'@/utils'
// 格式化【优惠金额/折扣】
export
const
discountFormat
=
(
row
:
CouponTemplateVO
)
=>
{
if
(
row
.
discountType
===
PromotionDiscountTypeEnum
.
PRICE
.
type
)
{
return
`¥
${
floatToFixed2
(
row
.
discountPrice
)}
`
}
if
(
row
.
discountType
===
PromotionDiscountTypeEnum
.
PERCENT
.
type
)
{
return
`
${
row
.
discountPrice
}
%`
}
return
'未知【'
+
row
.
discountType
+
'】'
}
// 格式化【领取上限】
export
const
takeLimitCountFormat
=
(
row
:
CouponTemplateVO
)
=>
{
if
(
row
.
takeLimitCount
===
-
1
)
{
return
'无领取限制'
}
return
`
${
row
.
takeLimitCount
}
张/人`
}
// 格式化【有效期限】
export
const
validityTypeFormat
=
(
row
:
CouponTemplateVO
)
=>
{
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
+
'】'
}
// 格式化【剩余数量】
export
const
remainedCountFormat
=
(
row
:
CouponTemplateVO
)
=>
{
return
row
.
totalCount
-
row
.
takeCount
}
// 格式化【最低消费】
export
const
userPriceFormat
=
(
row
:
CouponTemplateVO
)
=>
{
return
`¥
${
floatToFixed2
(
row
.
usePrice
)}
`
}
src/views/mall/promotion/coupon/template/CouponTemplateForm.vue
View file @
190c6e38
...
...
@@ -29,7 +29,7 @@
<el-input-number
v-model=
"formData.discountPrice"
placeholder=
"请输入优惠金额,单位:元"
style=
"width: 400px
"
class=
"!w-400px mr-2
"
:precision=
"2"
:min=
"0"
/>
...
...
@@ -43,7 +43,7 @@
<el-input-number
v-model=
"formData.discountPercent"
placeholder=
"优惠券折扣不能小于 1 折,且不可大于 9.9 折"
style=
"width: 400px
"
class=
"!w-400px mr-2
"
:precision=
"1"
:min=
"1"
:max=
"9.9"
...
...
@@ -58,7 +58,7 @@
<el-input-number
v-model=
"formData.discountLimitPrice"
placeholder=
"请输入最多优惠"
style=
"width: 400px
"
class=
"!w-400px mr-2
"
:precision=
"2"
:min=
"0"
/>
...
...
@@ -68,7 +68,7 @@
<el-input-number
v-model=
"formData.usePrice"
placeholder=
"无门槛请设为 0"
style=
"width: 400px
"
class=
"!w-400px mr-2
"
:precision=
"2"
:min=
"0"
/>
...
...
@@ -84,7 +84,7 @@
<el-input-number
v-model=
"formData.totalCount"
placeholder=
"发放数量,没有之后不能领取或发放,-1 为不限制"
style=
"width: 400px
"
class=
"!w-400px mr-2
"
:precision=
"0"
:min=
"-1"
/>
...
...
@@ -94,7 +94,7 @@
<el-input-number
v-model=
"formData.takeLimitCount"
placeholder=
"设置为 -1 时,可无限领取"
style=
"width: 400px
"
class=
"!w-400px mr-2
"
:precision=
"0"
:min=
"-1"
/>
...
...
@@ -133,7 +133,7 @@
<el-input-number
v-model=
"formData.fixedStartTerm"
placeholder=
"0 为今天生效"
style=
"width: 165px
"
class=
"mx-2
"
:precision=
"0"
:min=
"0"
/>
...
...
@@ -141,7 +141,7 @@
<el-input-number
v-model=
"formData.fixedEndTerm"
placeholder=
"请输入结束天数"
style=
"width: 165px
"
class=
"mx-2
"
:precision=
"0"
:min=
"0"
/>
...
...
@@ -162,21 +162,21 @@
v-if=
"formData.productScope === PromotionProductScopeEnum.SPU.scope"
prop=
"productSpuIds"
>
<el-select
v-model=
"formData.productSpuIds"
placeholder=
"请选择活动商品"
clearable
multiple
filterable
style=
"width: 400px"
<div
class=
"flex items-center gap-1 flex-wrap"
>
<div
class=
"select-box spu-pic"
v-for=
"(spu, index) in productSpus"
:key=
"spu.id"
>
<el-image
:src=
"spu.picUrl"
/>
<Icon
icon=
"ep:circle-close-filled"
class=
"del-icon"
@
click=
"handleRemoveSpu(index)"
/>
</div>
<div
class=
"select-box"
@
click=
"openSpuTableSelect"
>
<Icon
icon=
"ep:plus"
/>
</div>
</div>
</el-form-item>
<el-form-item
v-if=
"formData.productScope === PromotionProductScopeEnum.CATEGORY.scope"
prop=
"productCategoryIds"
>
<el-option
v-for=
"item in productSpus"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
>
<span
style=
"float: left"
>
{{
item
.
name
}}
</span>
<span
style=
"float: right; font-size: 13px; color: #8492a6"
>
¥
{{
(
item
.
minPrice
/
100.0
).
toFixed
(
2
)
}}
</span>
</el-option>
</el-select>
<ProductCategorySelect
v-model=
"formData.productCategoryIds"
multiple
/>
</el-form-item>
</el-form>
<template
#
footer
>
...
...
@@ -184,6 +184,7 @@
<el-button
@
click=
"dialogVisible = false"
>
取 消
</el-button>
</
template
>
</Dialog>
<SpuTableSelect
ref=
"spuTableSelectRef"
multiple
@
change=
"handleSpuSelected"
/>
</template>
<
script
lang=
"ts"
setup
>
import
{
DICT_TYPE
,
getIntDictOptions
}
from
'@/utils/dict'
...
...
@@ -194,6 +195,8 @@ import {
PromotionDiscountTypeEnum
,
PromotionProductScopeEnum
}
from
'@/utils/constants'
import
SpuTableSelect
from
'@/views/mall/product/spu/components/SpuTableSelect.vue'
import
ProductCategorySelect
from
'@/views/mall/product/category/components/ProductCategorySelect.vue'
defineOptions
({
name
:
'CouponTemplateForm'
})
...
...
@@ -222,7 +225,8 @@ const formData = ref({
fixedStartTerm
:
undefined
,
fixedEndTerm
:
undefined
,
productScope
:
PromotionProductScopeEnum
.
ALL
.
scope
,
productSpuIds
:
[]
productSpuIds
:
[],
productCategoryIds
:
[]
})
const
formRules
=
reactive
({
name
:
[{
required
:
true
,
message
:
'优惠券名称不能为空'
,
trigger
:
'blur'
}],
...
...
@@ -239,10 +243,11 @@ const formRules = reactive({
fixedStartTerm
:
[{
required
:
true
,
message
:
'开始领取天数不能为空'
,
trigger
:
'blur'
}],
fixedEndTerm
:
[{
required
:
true
,
message
:
'开始领取天数不能为空'
,
trigger
:
'blur'
}],
productScope
:
[{
required
:
true
,
message
:
'商品范围不能为空'
,
trigger
:
'blur'
}],
productSpuIds
:
[{
required
:
true
,
message
:
'商品范围不能为空'
,
trigger
:
'blur'
}]
productSpuIds
:
[{
required
:
true
,
message
:
'商品范围不能为空'
,
trigger
:
'blur'
}],
productCategoryIds
:
[{
required
:
true
,
message
:
'分类范围不能为空'
,
trigger
:
'blur'
}]
})
const
formRef
=
ref
()
// 表单 Ref
const
productSpus
=
ref
([])
// 商品列表
const
productSpus
=
ref
<
ProductSpuApi
.
Spu
[]
>
([])
// 商品列表
/** 打开弹窗 */
const
open
=
async
(
type
:
string
,
id
?:
number
)
=>
{
...
...
@@ -265,12 +270,12 @@ const open = async (type: string, id?: number) => {
usePrice
:
data
.
usePrice
!==
undefined
?
data
.
usePrice
/
100.0
:
undefined
,
validTimes
:
[
data
.
validStartTime
,
data
.
validEndTime
]
}
// 获得商品范围
await
getProductScope
()
}
finally
{
formLoading
.
value
=
false
}
}
// 获得商品列表
productSpus
.
value
=
await
ProductSpuApi
.
getSpuSimpleList
()
}
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
...
...
@@ -306,6 +311,12 @@ const submitForm = async () => {
?
formData
.
value
.
validTimes
[
1
]
:
undefined
}
as
CouponTemplateApi
.
CouponTemplateVO
if
(
formData
.
value
.
productCategoryIds
?.
length
>
0
)
{
// 改个名字?加个字段?
data
.
productSpuIds
=
formData
.
value
.
productCategoryIds
}
if
(
formType
.
value
===
'create'
)
{
await
CouponTemplateApi
.
createCouponTemplate
(
data
)
message
.
success
(
t
(
'common.createSuccess'
))
...
...
@@ -341,8 +352,67 @@ const resetForm = () => {
fixedStartTerm
:
undefined
,
fixedEndTerm
:
undefined
,
productScope
:
PromotionProductScopeEnum
.
ALL
.
scope
,
productSpuIds
:
[]
productSpuIds
:
[],
productCategoryIds
:
[]
}
formRef
.
value
?.
resetFields
()
productSpus
.
value
=
[]
}
/** 获得商品范围 */
const
getProductScope
=
async
()
=>
{
switch
(
formData
.
value
.
productScope
)
{
case
PromotionProductScopeEnum
.
SPU
.
scope
:
// 获得商品列表
productSpus
.
value
=
await
ProductSpuApi
.
getSpuDetailList
(
formData
.
value
.
productSpuIds
)
break
case
PromotionProductScopeEnum
.
CATEGORY
.
scope
:
formData
.
value
.
productCategoryIds
=
formData
.
value
.
productSpuIds
formData
.
value
.
productSpuIds
=
[]
break
default
:
break
}
}
/** 活动商品 按钮 */
const
spuTableSelectRef
=
ref
()
const
openSpuTableSelect
=
()
=>
{
spuTableSelectRef
.
value
.
open
(
productSpus
.
value
)
}
/** 选择商品后触发 */
const
handleSpuSelected
=
(
spus
:
ProductSpuApi
.
Spu
[])
=>
{
productSpus
.
value
=
spus
formData
.
value
.
productSpuIds
=
spus
.
map
((
spu
)
=>
spu
.
id
)
as
[]
}
/** 选择商品后触发 */
const
handleRemoveSpu
=
(
index
:
number
)
=>
{
productSpus
.
value
.
splice
(
index
,
1
)
formData
.
value
.
productSpuIds
.
splice
(
index
,
1
)
}
</
script
>
<
style
scoped
lang=
"scss"
>
.select-box
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
border
:
1px
dashed
var
(
--el-border-color-darker
);
border-radius
:
8px
;
width
:
60px
;
height
:
60px
;
}
.spu-pic
{
position
:
relative
;
}
.del-icon
{
position
:
absolute
;
z-index
:
1
;
width
:
20px
!important
;
height
:
20px
!important
;
right
:
-10px
;
top
:
-10px
;
}
</
style
>
src/views/mall/promotion/coupon/template/index.vue
View file @
190c6e38
...
...
@@ -103,7 +103,7 @@
label=
"剩余数量"
align=
"center"
prop=
"totalCount"
:formatter=
"
(row) => row.totalCount - row.takeCoun
t"
:formatter=
"
remainedCountForma
t"
/>
<el-table-column
label=
"领取上限"
...
...
@@ -171,14 +171,16 @@
<
script
lang=
"ts"
setup
>
import
*
as
CouponTemplateApi
from
'@/api/mall/promotion/coupon/couponTemplate'
import
{
CommonStatusEnum
,
CouponTemplateValidityTypeEnum
,
PromotionDiscountTypeEnum
}
from
'@/utils/constants'
import
{
CommonStatusEnum
}
from
'@/utils/constants'
import
{
DICT_TYPE
,
getIntDictOptions
}
from
'@/utils/dict'
import
{
dateFormatter
,
formatDate
}
from
'@/utils/formatTime'
import
{
dateFormatter
}
from
'@/utils/formatTime'
import
CouponTemplateForm
from
'./CouponTemplateForm.vue'
import
{
discountFormat
,
remainedCountFormat
,
takeLimitCountFormat
,
validityTypeFormat
}
from
'@/views/mall/promotion/coupon/formatter'
defineOptions
({
name
:
'PromotionCouponTemplate'
})
...
...
@@ -193,6 +195,7 @@ const queryParams = reactive({
pageSize
:
10
,
name
:
null
,
status
:
null
,
discountType
:
null
,
type
:
null
,
createTime
:
[]
})
...
...
@@ -258,36 +261,6 @@ const handleDelete = async (id: number) => {
}
catch
{}
}
// 格式化【优惠金额/折扣】
const
discountFormat
=
(
row
:
any
)
=>
{
if
(
row
.
discountType
===
PromotionDiscountTypeEnum
.
PRICE
.
type
)
{
return
`¥
${(
row
.
discountPrice
/
100.0
).
toFixed
(
2
)}
`
}
if
(
row
.
discountType
===
PromotionDiscountTypeEnum
.
PERCENT
.
type
)
{
return
`¥
${(
row
.
discountPrice
/
100.0
).
toFixed
(
2
)}
`
}
return
'未知【'
+
row
.
discountType
+
'】'
}
// 格式化【领取上限】
const
takeLimitCountFormat
=
(
row
:
any
)
=>
{
if
(
row
.
takeLimitCount
===
-
1
)
{
return
'无领取限制'
}
return
`
${
row
.
takeLimitCount
}
张/人`
}
// 格式化【有效期限】
const
validityTypeFormat
=
(
row
:
any
)
=>
{
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
+
'】'
}
/** 初始化 **/
onMounted
(()
=>
{
getList
()
...
...
src/views/member/user/index.vue
View file @
190c6e38
...
...
@@ -60,13 +60,21 @@
<el-form-item>
<el-button
@
click=
"handleQuery"
><Icon
icon=
"ep:search"
class=
"mr-5px"
/>
搜索
</el-button>
<el-button
@
click=
"resetQuery"
><Icon
icon=
"ep:refresh"
class=
"mr-5px"
/>
重置
</el-button>
<el-button
@
click=
"openCoupon"
v-hasPermi=
"['promotion:coupon:send']"
>
发送优惠券
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table
v-loading=
"loading"
:data=
"list"
:stripe=
"true"
:show-overflow-tooltip=
"true"
>
<el-table
v-loading=
"loading"
:data=
"list"
:stripe=
"true"
:show-overflow-tooltip=
"true"
@
selection-change=
"handleSelectionChange"
>
<el-table-column
type=
"selection"
width=
"55"
/>
<el-table-column
label=
"用户编号"
align=
"center"
prop=
"id"
width=
"120px"
/>
<el-table-column
label=
"头像"
align=
"center"
prop=
"avatar"
width=
"80px"
>
<template
#
default=
"scope"
>
...
...
@@ -145,6 +153,8 @@
<UserForm
ref=
"formRef"
@
success=
"getList"
/>
<!-- 修改用户等级弹窗 -->
<UpdateLevelForm
ref=
"updateLevelFormRef"
@
success=
"getList"
/>
<!-- 发送优惠券弹窗 -->
<CouponSend
ref=
"couponSend"
/>
</template>
<
script
setup
lang=
"ts"
>
import
{
dateFormatter
}
from
'@/utils/formatTime'
...
...
@@ -154,9 +164,12 @@ import MemberTagSelect from '@/views/member/tag/components/MemberTagSelect.vue'
import
MemberLevelSelect
from
'@/views/member/level/components/MemberLevelSelect.vue'
import
MemberGroupSelect
from
'@/views/member/group/components/MemberGroupSelect.vue'
import
UpdateLevelForm
from
'@/views/member/user/UpdateLevelForm.vue'
import
CouponSend
from
'@/views/mall/promotion/coupon/components/CouponSend.vue'
defineOptions
({
name
:
'MemberUser'
})
const
message
=
useMessage
()
// 消息弹窗
const
loading
=
ref
(
true
)
// 列表的加载中
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
([])
// 列表的数据
...
...
@@ -173,6 +186,7 @@ const queryParams = reactive({
})
const
queryFormRef
=
ref
()
// 搜索的表单
const
updateLevelFormRef
=
ref
()
// 修改会员等级表单
const
selectedIds
=
ref
<
number
[]
>
([])
// 表格的选中 ID 数组
/** 查询列表 */
const
getList
=
async
()
=>
{
...
...
@@ -204,6 +218,22 @@ const openDetail = (id: number) => {
push
({
name
:
'MemberUserDetail'
,
params
:
{
id
}
})
}
/** 表格选中事件 */
const
handleSelectionChange
=
(
rows
:
UserApi
.
UserVO
[])
=>
{
selectedIds
.
value
=
rows
.
map
((
row
)
=>
row
.
id
)
}
/** 发送优惠券 */
const
couponSend
=
ref
()
const
openCoupon
=
()
=>
{
if
(
selectedIds
.
value
.
length
===
0
)
{
message
.
warning
(
'请选择要发送优惠券的用户'
)
return
}
couponSend
.
value
.
open
(
selectedIds
.
value
)
}
/** 初始化 **/
onMounted
(()
=>
{
getList
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment