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
Commit
7a6c6949
authored
Sep 25, 2024
by
YunaiV
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
https://gitee.com/yudaocode/yudao-ui-admin-vue3
into dev
parents
989e3081
0db57da9
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
422 additions
and
106 deletions
+422
-106
src/api/mall/promotion/seckill/seckillActivity.ts
+6
-0
src/components/DiyEditor/components/mobile/FloatingActionButton/index.vue
+1
-1
src/components/DiyEditor/components/mobile/PromotionSeckill/config.ts
+37
-5
src/components/DiyEditor/components/mobile/PromotionSeckill/index.vue
+141
-75
src/components/DiyEditor/components/mobile/PromotionSeckill/property.vue
+68
-16
src/components/FormCreate/src/config/useDictSelectRule.ts
+1
-1
src/router/index.ts
+1
-1
src/utils/routerHelper.ts
+2
-1
src/views/crm/contract/detail/index.vue
+1
-1
src/views/mall/promotion/kefu/components/KeFuMessageList.vue
+3
-3
src/views/mall/promotion/kefu/components/tools/emoji.ts
+5
-2
src/views/mall/promotion/seckill/components/SeckillShowcase.vue
+156
-0
src/views/mall/promotion/seckill/components/SeckillTableSelect.vue
+0
-0
No files found.
src/api/mall/promotion/seckill/seckillActivity.ts
View file @
7a6c6949
...
...
@@ -18,6 +18,7 @@ export interface SeckillActivityVO {
singleLimitCount
?:
number
stock
?:
number
totalStock
?:
number
seckillPrice
?:
number
products
?:
SeckillProductVO
[]
}
...
...
@@ -43,6 +44,11 @@ export const getSeckillActivityPage = async (params) => {
return
await
request
.
get
({
url
:
'/promotion/seckill-activity/page'
,
params
})
}
// 查询秒杀活动列表,基于活动编号数组
export
const
getSeckillActivityListByIds
=
(
ids
:
number
[])
=>
{
return
request
.
get
({
url
:
`/promotion/seckill-activity/list-by-ids?ids=
${
ids
}
`
})
}
// 查询秒杀活动详情
export
const
getSeckillActivity
=
async
(
id
:
number
)
=>
{
return
await
request
.
get
({
url
:
'/promotion/seckill-activity/get?id='
+
id
})
...
...
src/components/DiyEditor/components/mobile/FloatingActionButton/index.vue
View file @
7a6c6949
...
...
@@ -44,7 +44,7 @@ defineOptions({ name: 'FloatingActionButton' })
defineProps
<
{
property
:
FloatingActionButtonProperty
}
>
()
// 是否展开
const
expanded
=
ref
(
tru
e
)
const
expanded
=
ref
(
fals
e
)
// 处理展开/折叠
const
handleToggleFab
=
()
=>
{
expanded
.
value
=
!
expanded
.
value
...
...
src/components/DiyEditor/components/mobile/PromotionSeckill/config.ts
View file @
7a6c6949
...
...
@@ -3,13 +3,21 @@ import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
/** 秒杀属性 */
export
interface
PromotionSeckillProperty
{
// 布局类型:单列 | 三列
layoutType
:
'oneCol
'
|
'three
Col'
layoutType
:
'oneCol
BigImg'
|
'oneColSmallImg'
|
'two
Col'
// 商品字段
fields
:
{
// 商品名称
name
:
PromotionSeckillFieldProperty
// 商品简介
introduction
:
PromotionSeckillFieldProperty
// 商品价格
price
:
PromotionSeckillFieldProperty
// 市场价
marketPrice
:
PromotionSeckillFieldProperty
// 商品销量
salesCount
:
PromotionSeckillFieldProperty
// 商品库存
stock
:
PromotionSeckillFieldProperty
}
// 角标
badge
:
{
...
...
@@ -18,6 +26,19 @@ export interface PromotionSeckillProperty {
// 角标图片
imgUrl
:
string
}
// 按钮
btnBuy
:
{
// 类型:文字 | 图片
type
:
'text'
|
'img'
// 文字
text
:
string
// 文字按钮:背景渐变起始颜色
bgBeginColor
:
string
// 文字按钮:背景渐变结束颜色
bgEndColor
:
string
// 图片按钮:图片地址
imgUrl
:
string
}
// 上圆角
borderRadiusTop
:
number
// 下圆角
...
...
@@ -25,10 +46,11 @@ export interface PromotionSeckillProperty {
// 间距
space
:
number
// 秒杀活动编号
activityId
:
number
activityId
s
:
number
[]
// 组件样式
style
:
ComponentStyle
}
// 商品字段
export
interface
PromotionSeckillFieldProperty
{
// 是否显示
...
...
@@ -43,13 +65,23 @@ export const component = {
name
:
'秒杀'
,
icon
:
'mdi:calendar-time'
,
property
:
{
activityId
:
undefined
,
layoutType
:
'oneCol'
,
layoutType
:
'oneColBigImg'
,
fields
:
{
name
:
{
show
:
true
,
color
:
'#000'
},
price
:
{
show
:
true
,
color
:
'#ff3000'
}
introduction
:
{
show
:
true
,
color
:
'#999'
},
price
:
{
show
:
true
,
color
:
'#ff3000'
},
marketPrice
:
{
show
:
true
,
color
:
'#c4c4c4'
},
salesCount
:
{
show
:
true
,
color
:
'#c4c4c4'
},
stock
:
{
show
:
false
,
color
:
'#c4c4c4'
}
},
badge
:
{
show
:
false
,
imgUrl
:
''
},
btnBuy
:
{
type
:
'text'
,
text
:
'立即秒杀'
,
bgBeginColor
:
'#FF6000'
,
bgEndColor
:
'#FE832A'
,
imgUrl
:
''
},
borderRadiusTop
:
8
,
borderRadiusBottom
:
8
,
space
:
8
,
...
...
src/components/DiyEditor/components/mobile/PromotionSeckill/index.vue
View file @
7a6c6949
<
template
>
<el-scrollbar
ref=
"containerRef"
class=
"z-1 min-h-30px"
wrap-class=
"w-full"
>
<!-- 商品网格 -->
<div
:class=
"`box-content min-h-30px w-full flex flex-row flex-wrap`"
ref=
"containerRef"
>
<div
class=
"relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
:style=
"
{
gridGap: `${property.space}px`,
gridTemplateColumns,
width: scrollbarWidth
}"
class="grid overflow-x-auto"
>
<!-- 商品 -->
<div
v-for=
"(spu, index) in spuList"
:key=
"index"
:style=
"
{
...calculateSpace(index),
...calculateWidth(),
borderTopLeftRadius: `${property.borderRadiusTop}px`,
borderTopRightRadius: `${property.borderRadiusTop}px`,
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
borderBottomRightRadius: `${property.borderRadiusBottom}px`
}"
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
v-for="(spu, index) in spuList"
:key="index"
>
<!-- 角标 -->
<div
v-if=
"property.badge.show"
class=
"absolute left-0 top-0 z-1 items-center justify-center"
>
<el-image
fit=
"cover"
:src=
"property.badge.imgUrl"
class=
"h-26px w-38px"
/>
</div>
<!-- 商品封面图 -->
<div
v-if=
"property.badge.show"
class=
"absolute left-0 top-0 z-1 items-center justify-center"
:class=
"[
'h-140px',
{
'w-full': property.layoutType !== 'oneColSmallImg',
'w-140px': property.layoutType === 'oneColSmallImg'
}
]"
>
<el-image
:src=
"property.badge.imgUrl"
class=
"h-26px w-38px"
fit=
"cover
"
/>
<el-image
fit=
"cover"
class=
"h-full w-full"
:src=
"spu.picUrl
"
/>
</div>
<!-- 商品封面图 -->
<el-image
:src=
"spu.picUrl"
:style=
"
{ width: imageSize, height: imageSize }" fit="cover" />
<div
:class=
"[
'
flex flex-col gap-8px p-8px box-border',
'
flex flex-col gap-8px p-8px box-border',
{
'w-[calc(100%-64px)]': columns === 2
,
'w-full': columns === 3
'w-full': property.layoutType !== 'oneColSmallImg'
,
'w-[calc(100%-140px-16px)]': property.layoutType === 'oneColSmallImg'
}
]"
>
<!-- 商品名称 -->
<div
v-if=
"property.fields.name.show"
:class=
"[
'text-14px ',
{
truncate: property.layoutType !== 'oneColSmallImg',
'overflow-ellipsis line-clamp-2': property.layoutType === 'oneColSmallImg'
}
]"
:style="{ color: property.fields.name.color }"
class="truncate text-12px"
>
{{
spu
.
name
}}
</div>
<!-- 商品简介 -->
<div
v-if=
"property.fields.introduction.show"
class=
"truncate text-12px"
:style=
"
{ color: property.fields.introduction.color }"
>
{{
spu
.
introduction
}}
</div>
<div>
<!-- 商品
价格 -->
<!--
价格 -->
<span
v-if=
"property.fields.price.show"
class=
"text-16px"
:style=
"
{ color: property.fields.price.color }"
class="text-12px"
>
¥
{{
fenToYuan
(
spu
.
seckillPrice
||
spu
.
price
||
0
)
}}
¥
{{
fenToYuan
(
spu
.
price
||
Infinity
)
}}
</span>
<!-- 市场价 -->
<span
v-if=
"property.fields.marketPrice.show && spu.marketPrice"
class=
"ml-4px text-10px line-through"
:style=
"
{ color: property.fields.marketPrice.color }"
>¥
{{
fenToYuan
(
spu
.
marketPrice
)
}}
</span
>
</div>
<div
class=
"text-12px"
>
<!-- 销量 -->
<span
v-if=
"property.fields.salesCount.show"
:style=
"
{ color: property.fields.salesCount.color }"
>
已售
{{
(
spu
.
salesCount
||
0
)
+
(
spu
.
virtualSalesCount
||
0
)
}}
件
</span>
<!-- 库存 -->
<span
v-if=
"property.fields.stock.show"
:style=
"
{ color: property.fields.stock.color }">
库存
{{
spu
.
stock
||
0
}}
</span>
</div>
</div>
<!-- 购买按钮 -->
<div
class=
"absolute bottom-8px right-8px"
>
<!-- 文字按钮 -->
<span
v-if=
"property.btnBuy.type === 'text'"
class=
"rounded-full p-x-12px p-y-4px text-12px text-white"
:style=
"
{
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`
}"
>
{{
property
.
btnBuy
.
text
}}
</span>
<!-- 图片按钮 -->
<el-image
v-else
class=
"h-28px w-28px rounded-full"
fit=
"cover"
:src=
"property.btnBuy.imgUrl"
/>
</div>
</div>
</div>
</el-scrollbar>
</
template
>
<
script
lang=
"ts"
setup
>
<
script
setup
lang=
"ts"
>
import
{
PromotionSeckillProperty
}
from
'./config'
import
*
as
ProductSpuApi
from
'@/api/mall/product/spu'
import
{
Spu
}
from
'@/api/mall/product/spu'
import
*
as
SeckillActivityApi
from
'@/api/mall/promotion/seckill/seckillActivity'
import
{
SeckillProductVO
}
from
'@/api/mall/promotion/seckill/seckillActivity'
import
{
fenToYuan
}
from
'@/utils'
/** 秒杀 */
/** 秒杀
卡片
*/
defineOptions
({
name
:
'PromotionSeckill'
})
// 定义属性
const
props
=
defineProps
<
{
property
:
PromotionSeckillProperty
}
>
()
// 商品列表
const
spuList
=
ref
<
ProductSpuApi
.
Spu
[]
>
([])
const
spuIdList
=
ref
<
number
[]
>
([])
const
seckillActivityList
=
ref
<
SeckillActivityApi
.
SeckillActivityVO
[]
>
([])
watch
(
()
=>
props
.
property
.
activityId
,
()
=>
props
.
property
.
activityId
s
,
async
()
=>
{
if
(
!
props
.
property
.
activityId
)
return
const
activity
=
await
SeckillActivityApi
.
getSeckillActivity
(
props
.
property
.
activityId
)
if
(
!
activity
?.
spuId
)
return
spuList
.
value
=
[
await
ProductSpuApi
.
getSpu
(
activity
.
spuId
)]
spuList
.
value
=
[
await
ProductSpuApi
.
getSpu
(
activity
.
spuId
)]
// 循环活动信息,赋值秒杀最低价格
activity
.
products
.
forEach
((
product
:
SeckillProductVO
)
=>
{
spuList
.
value
.
forEach
((
spu
:
Spu
)
=>
{
spu
.
seckillPrice
=
Math
.
min
(
spu
.
seckillPrice
||
Infinity
,
product
.
seckillPrice
)
// 设置 SPU 的最低价格
})
try
{
// 新添加的秒杀组件,是没有活动ID的
const
activityIds
=
props
.
property
.
activityIds
// 检查活动ID的有效性
if
(
Array
.
isArray
(
activityIds
)
&&
activityIds
.
length
>
0
)
{
// 获取秒杀活动详情列表
seckillActivityList
.
value
=
await
SeckillActivityApi
.
getSeckillActivityListByIds
(
activityIds
)
// 获取秒杀活动的 SPU 详情列表
spuList
.
value
=
[]
spuIdList
.
value
=
seckillActivityList
.
value
.
map
((
activity
)
=>
activity
.
spuId
)
.
filter
((
spuId
):
spuId
is
number
=>
typeof
spuId
===
'number'
)
if
(
spuIdList
.
value
.
length
>
0
)
{
spuList
.
value
=
await
ProductSpuApi
.
getSpuDetailList
(
spuIdList
.
value
)
}
// 更新 SPU 的最低价格
seckillActivityList
.
value
.
forEach
((
activity
)
=>
{
// 匹配spuId
const
spu
=
spuList
.
value
.
find
((
spu
)
=>
spu
.
id
===
activity
.
spuId
)
if
(
spu
)
{
// 赋值活动价格,哪个最便宜就赋值哪个
spu
.
price
=
Math
.
min
(
activity
.
seckillPrice
||
Infinity
,
spu
.
price
||
Infinity
)
}
})
}
}
catch
(
error
)
{
console
.
error
(
'获取秒杀活动细节或 SPU 细节时出错:'
,
error
)
}
},
{
immediate
:
true
,
deep
:
true
}
)
// 手机宽度
const
phoneWidth
=
ref
(
375
)
/**
* 计算商品的间距
* @param index 商品索引
*/
const
calculateSpace
=
(
index
:
number
)
=>
{
// 商品的列数
const
columns
=
props
.
property
.
layoutType
===
'twoCol'
?
2
:
1
// 第一列没有左边距
const
marginLeft
=
index
%
columns
===
0
?
'0'
:
props
.
property
.
space
+
'px'
// 第一行没有上边距
const
marginTop
=
index
<
columns
?
'0'
:
props
.
property
.
space
+
'px'
return
{
marginLeft
,
marginTop
}
}
// 容器
const
containerRef
=
ref
()
// 商品的列数
const
columns
=
ref
(
2
)
// 滚动条宽度
const
scrollbarWidth
=
ref
(
'100%'
)
// 商品图大小
const
imageSize
=
ref
(
'0'
)
// 商品网络列数
const
gridTemplateColumns
=
ref
(
''
)
// 计算布局参数
watch
(
()
=>
[
props
.
property
,
phoneWidth
,
spuList
.
value
.
length
],
()
=>
{
// 计算列数
columns
.
value
=
props
.
property
.
layoutType
===
'oneCol'
?
1
:
3
// 每列的宽度为:(总宽度 - 间距 * (列数 - 1))/ 列数
const
productWidth
=
(
phoneWidth
.
value
-
props
.
property
.
space
*
(
columns
.
value
-
1
))
/
columns
.
value
// 商品图布局:2列时,左右布局 3列时,上下布局
imageSize
.
value
=
columns
.
value
===
2
?
'64px'
:
`
${
productWidth
}
px`
// 指定列数
gridTemplateColumns
.
value
=
`repeat(
${
columns
.
value
}
, auto)`
// 不滚动
scrollbarWidth
.
value
=
'100%'
},
{
immediate
:
true
,
deep
:
true
}
)
onMounted
(()
=>
{
// 提取手机宽度
phoneWidth
.
value
=
containerRef
.
value
?.
wrapRef
?.
offsetWidth
||
375
})
// 计算商品的宽度
const
calculateWidth
=
()
=>
{
let
width
=
'100%'
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
if
(
props
.
property
.
layoutType
===
'twoCol'
)
{
width
=
`
${(
containerRef
.
value
.
offsetWidth
-
props
.
property
.
space
)
/
2
}
px`
}
return
{
width
}
}
</
script
>
<
style
lang=
"scss"
scoped
></
style
>
<
style
scoped
lang=
"scss"
></
style
>
src/components/DiyEditor/components/mobile/PromotionSeckill/property.vue
View file @
7a6c6949
...
...
@@ -2,30 +2,31 @@
<ComponentContainerProperty
v-model=
"formData.style"
>
<el-form
label-width=
"80px"
:model=
"formData"
>
<el-card
header=
"秒杀活动"
class=
"property-group"
shadow=
"never"
>
<el-form-item
label=
"秒杀活动"
prop=
"activityId"
>
<el-select
v-model=
"formData.activityId"
>
<el-option
v-for=
"activity in activityList"
:key=
"activity.id"
:label=
"activity.name"
:value=
"activity.id"
/>
</el-select>
</el-form-item>
<SeckillShowcase
v-model=
"formData.activityIds"
/>
</el-card>
<el-card
header=
"商品样式"
class=
"property-group"
shadow=
"never"
>
<el-form-item
label=
"布局"
prop=
"type"
>
<el-radio-group
v-model=
"formData.layoutType"
>
<el-tooltip
class=
"item"
content=
"单列"
placement=
"bottom"
>
<el-radio-button
value=
"oneCol"
>
<el-tooltip
class=
"item"
content=
"单列
大图
"
placement=
"bottom"
>
<el-radio-button
value=
"oneCol
BigImg
"
>
<Icon
icon=
"fluent:text-column-one-24-filled"
/>
</el-radio-button>
</el-tooltip>
<el-tooltip
class=
"item"
content=
"三列"
placement=
"bottom"
>
<el-tooltip
class=
"item"
content=
"单列小图"
placement=
"bottom"
>
<el-radio-button
value=
"oneColSmallImg"
>
<Icon
icon=
"fluent:text-column-two-left-24-filled"
/>
</el-radio-button>
</el-tooltip>
<el-tooltip
class=
"item"
content=
"双列"
placement=
"bottom"
>
<el-radio-button
value=
"twoCol"
>
<Icon
icon=
"fluent:text-column-two-24-filled"
/>
</el-radio-button>
</el-tooltip>
<!--
<el-tooltip
class=
"item"
content=
"三列"
placement=
"bottom"
>
<el-radio-button
value=
"threeCol"
>
<Icon
icon=
"fluent:text-column-three-24-filled"
/>
</el-radio-button>
</el-tooltip>
</el-tooltip>
-->
</el-radio-group>
</el-form-item>
<el-form-item
label=
"商品名称"
prop=
"fields.name.show"
>
...
...
@@ -34,12 +35,36 @@
<el-checkbox
v-model=
"formData.fields.name.show"
/>
</div>
</el-form-item>
<el-form-item
label=
"商品简介"
prop=
"fields.introduction.show"
>
<div
class=
"flex gap-8px"
>
<ColorInput
v-model=
"formData.fields.introduction.color"
/>
<el-checkbox
v-model=
"formData.fields.introduction.show"
/>
</div>
</el-form-item>
<el-form-item
label=
"商品价格"
prop=
"fields.price.show"
>
<div
class=
"flex gap-8px"
>
<ColorInput
v-model=
"formData.fields.price.color"
/>
<el-checkbox
v-model=
"formData.fields.price.show"
/>
</div>
</el-form-item>
<el-form-item
label=
"市场价"
prop=
"fields.marketPrice.show"
>
<div
class=
"flex gap-8px"
>
<ColorInput
v-model=
"formData.fields.marketPrice.color"
/>
<el-checkbox
v-model=
"formData.fields.marketPrice.show"
/>
</div>
</el-form-item>
<el-form-item
label=
"商品销量"
prop=
"fields.salesCount.show"
>
<div
class=
"flex gap-8px"
>
<ColorInput
v-model=
"formData.fields.salesCount.color"
/>
<el-checkbox
v-model=
"formData.fields.salesCount.show"
/>
</div>
</el-form-item>
<el-form-item
label=
"商品库存"
prop=
"fields.stock.show"
>
<div
class=
"flex gap-8px"
>
<ColorInput
v-model=
"formData.fields.stock.color"
/>
<el-checkbox
v-model=
"formData.fields.stock.show"
/>
</div>
</el-form-item>
</el-card>
<el-card
header=
"角标"
class=
"property-group"
shadow=
"never"
>
<el-form-item
label=
"角标"
prop=
"badge.show"
>
...
...
@@ -47,9 +72,35 @@
</el-form-item>
<el-form-item
label=
"角标"
prop=
"badge.imgUrl"
v-if=
"formData.badge.show"
>
<UploadImg
v-model=
"formData.badge.imgUrl"
height=
"44px"
width=
"72px"
>
<template
#
tip
>
建议尺寸:36 * 22
</
template
>
<template
#
tip
>
建议尺寸:36 * 22
</
template
>
</UploadImg>
</el-form-item>
</el-card>
<el-card
header=
"按钮"
class=
"property-group"
shadow=
"never"
>
<el-form-item
label=
"按钮类型"
prop=
"btnBuy.type"
>
<el-radio-group
v-model=
"formData.btnBuy.type"
>
<el-radio-button
value=
"text"
>
文字
</el-radio-button>
<el-radio-button
value=
"img"
>
图片
</el-radio-button>
</el-radio-group>
</el-form-item>
<
template
v-if=
"formData.btnBuy.type === 'text'"
>
<el-form-item
label=
"按钮文字"
prop=
"btnBuy.text"
>
<el-input
v-model=
"formData.btnBuy.text"
/>
</el-form-item>
<el-form-item
label=
"左侧背景"
prop=
"btnBuy.bgBeginColor"
>
<ColorInput
v-model=
"formData.btnBuy.bgBeginColor"
/>
</el-form-item>
<el-form-item
label=
"右侧背景"
prop=
"btnBuy.bgEndColor"
>
<ColorInput
v-model=
"formData.btnBuy.bgEndColor"
/>
</el-form-item>
</
template
>
<
template
v-else
>
<el-form-item
label=
"图片"
prop=
"btnBuy.imgUrl"
>
<UploadImg
v-model=
"formData.btnBuy.imgUrl"
height=
"56px"
width=
"56px"
>
<template
#
tip
>
建议尺寸:56 * 56
</
template
>
</UploadImg>
</el-form-item>
</template>
</el-card>
<el-card
header=
"商品样式"
class=
"property-group"
shadow=
"never"
>
<el-form-item
label=
"上圆角"
prop=
"borderRadiusTop"
>
...
...
@@ -92,6 +143,7 @@ import { PromotionSeckillProperty } from './config'
import
{
usePropertyForm
}
from
'@/components/DiyEditor/util'
import
*
as
SeckillActivityApi
from
'@/api/mall/promotion/seckill/seckillActivity'
import
{
CommonStatusEnum
}
from
'@/utils/constants'
import
SeckillShowcase
from
'@/views/mall/promotion/seckill/components/SeckillShowcase.vue'
// 秒杀属性面板
defineOptions
({
name
:
'PromotionSeckillProperty'
})
...
...
@@ -100,7 +152,7 @@ const props = defineProps<{ modelValue: PromotionSeckillProperty }>()
const
emit
=
defineEmits
([
'update:modelValue'
])
const
{
formData
}
=
usePropertyForm
(
props
.
modelValue
,
emit
)
// 活动列表
const
activityList
=
ref
<
SeckillActivityApi
.
SeckillActivityVO
>
([])
const
activityList
=
ref
<
SeckillActivityApi
.
SeckillActivityVO
[]
>
([])
onMounted
(
async
()
=>
{
const
{
list
}
=
await
SeckillActivityApi
.
getSeckillActivityPage
({
status
:
CommonStatusEnum
.
ENABLE
...
...
src/components/FormCreate/src/config/useDictSelectRule.ts
View file @
7a6c6949
...
...
@@ -48,7 +48,7 @@ export const useDictSelectRule = () => {
},
{
type
:
'select'
,
field
:
'
dictV
alueType'
,
field
:
'
v
alueType'
,
title
:
'字典值类型'
,
value
:
'str'
,
options
:
[
...
...
src/router/index.ts
View file @
7a6c6949
...
...
@@ -5,7 +5,7 @@ import remainingRouter from './modules/remaining'
// 创建路由实例
const
router
=
createRouter
({
history
:
createWebHistory
(),
// createWebHashHistory URL带#,createWebHistory URL不带#
history
:
createWebHistory
(
import
.
meta
.
env
.
VITE_BASE_PATH
),
// createWebHashHistory URL带#,createWebHistory URL不带#
strict
:
true
,
routes
:
remainingRouter
as
RouteRecordRaw
[],
scrollBehavior
:
()
=>
({
left
:
0
,
top
:
0
})
...
...
src/utils/routerHelper.ts
View file @
7a6c6949
...
...
@@ -88,7 +88,8 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord
// 2. 生成 data(AppRouteRecordRaw)
// 路由地址转首字母大写驼峰,作为路由名称,适配keepAlive
let
data
:
AppRouteRecordRaw
=
{
path
:
route
.
path
.
indexOf
(
'?'
)
>
-
1
?
route
.
path
.
split
(
'?'
)[
0
]
:
route
.
path
,
path
:
route
.
path
.
indexOf
(
'?'
)
>
-
1
&&
!
isUrl
(
route
.
path
)
?
route
.
path
.
split
(
'?'
)[
0
]
:
route
.
path
,
// 注意,需要排除 http 这种 url,避免它带 ? 参数被截取掉
name
:
route
.
componentName
&&
route
.
componentName
.
length
>
0
?
route
.
componentName
...
...
src/views/crm/contract/detail/index.vue
View file @
7a6c6949
...
...
@@ -36,7 +36,7 @@
ref=
"permissionListRef"
:biz-id=
"contract.id!"
:biz-type=
"BizTypeEnum.CRM_CONTRACT"
:show-action=
"
!permissionListRef?.isPool || fals
e"
:show-action=
"
tru
e"
@
quit-team=
"close"
/>
</el-tab-pane>
...
...
src/views/mall/promotion/kefu/components/KeFuMessageList.vue
View file @
7a6c6949
...
...
@@ -86,7 +86,7 @@
<OrderItem
v-if=
"KeFuMessageContentTypeEnum.ORDER === item.contentType"
:message=
"item"
class=
"max-w-
7
0%"
class=
"max-w-
10
0%"
/>
</MessageItem>
</div>
...
...
@@ -423,9 +423,9 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
//
消息气泡
.kefu-message
{
color
:
#
A9A9A
9
;
color
:
#
a9a9a
9
;
border-radius
:
5px
;
box-shadow
:
3px
3px
5px
rgba
(
220
,
220
,
220
,
0.1
);
box-shadow
:
3px
3px
5px
rgba
(
220
,
220
,
220
,
0.1
);
padding
:
5px
10px
;
width
:
auto
;
max-width
:
50%
;
...
...
src/views/mall/promotion/kefu/components/tools/emoji.ts
View file @
7a6c6949
...
...
@@ -66,7 +66,7 @@ export const useEmoji = () => {
)
for
(
const
path
in
pathList
)
{
const
imageModule
:
any
=
await
pathList
[
path
]()
emojiPathList
.
value
.
push
(
imageModule
.
default
)
emojiPathList
.
value
.
push
(
{
path
:
path
,
src
:
imageModule
.
default
}
)
}
}
...
...
@@ -116,7 +116,10 @@ export const useEmoji = () => {
function
getEmojiFileByName
(
name
:
string
)
{
for
(
const
emoji
of
emojiList
)
{
if
(
emoji
.
name
===
name
)
{
return
emojiPathList
.
value
.
find
((
item
:
string
)
=>
item
.
indexOf
(
emoji
.
file
)
>
-
1
)
const
emojiPath
=
emojiPathList
.
value
.
find
(
(
item
:
{
path
:
string
;
src
:
string
})
=>
item
.
path
.
indexOf
(
emoji
.
file
)
>
-
1
)
return
emojiPath
?
emojiPath
.
src
:
undefined
}
}
return
false
...
...
src/views/mall/promotion/seckill/components/SeckillShowcase.vue
0 → 100644
View file @
7a6c6949
<
template
>
<div
class=
"flex flex-wrap items-center gap-8px"
>
<div
v-for=
"(seckillActivity, index) in Activitys"
:key=
"seckillActivity.id"
class=
"select-box spu-pic"
>
<el-tooltip
:content=
"seckillActivity.name"
>
<div
class=
"relative h-full w-full"
>
<el-image
:src=
"seckillActivity.picUrl"
class=
"h-full w-full"
/>
<Icon
v-show=
"!disabled"
class=
"del-icon"
icon=
"ep:circle-close-filled"
@
click=
"handleRemoveActivity(index)"
/>
</div>
</el-tooltip>
</div>
<el-tooltip
content=
"选择活动"
v-if=
"canAdd"
>
<div
class=
"select-box"
@
click=
"openSeckillActivityTableSelect"
>
<Icon
icon=
"ep:plus"
/>
</div>
</el-tooltip>
</div>
<!-- 拼团活动选择对话框(表格形式) -->
<SeckillTableSelect
ref=
"seckillActivityTableSelectRef"
:multiple=
"limit != 1"
@
change=
"handleActivitySelected"
/>
</
template
>
<
script
lang=
"ts"
setup
>
import
*
as
SeckillActivityApi
from
'@/api/mall/promotion/seckill/seckillActivity'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
oneOfType
}
from
'vue-types'
import
{
isArray
}
from
'@/utils/is'
import
SeckillTableSelect
from
'@/views/mall/promotion/seckill/components/SeckillTableSelect.vue'
// 活动橱窗,一般用于装修时使用
// 提供功能:展示活动列表、添加活动、删除活动
defineOptions
({
name
:
'SeckillShowcase'
})
const
props
=
defineProps
({
modelValue
:
oneOfType
<
number
|
Array
<
number
>>
([
Number
,
Array
]).
isRequired
,
// 限制数量:默认不限制
limit
:
propTypes
.
number
.
def
(
Number
.
MAX_VALUE
),
disabled
:
propTypes
.
bool
.
def
(
false
)
})
// 计算是否可以添加
const
canAdd
=
computed
(()
=>
{
// 情况一:禁用时不可以添加
if
(
props
.
disabled
)
return
false
// 情况二:未指定限制数量时,可以添加
if
(
!
props
.
limit
)
return
true
// 情况三:检查已添加数量是否小于限制数量
return
Activitys
.
value
.
length
<
props
.
limit
})
// 拼团活动列表
const
Activitys
=
ref
<
SeckillActivityApi
.
SeckillActivityVO
[]
>
([])
watch
(
()
=>
props
.
modelValue
,
async
()
=>
{
const
ids
=
isArray
(
props
.
modelValue
)
?
// 情况一:多选
props
.
modelValue
:
// 情况二:单选
props
.
modelValue
?
[
props
.
modelValue
]
:
[]
// 不需要返显
if
(
ids
.
length
===
0
)
{
Activitys
.
value
=
[]
return
}
// 只有活动发生变化之后,才会查询活动
if
(
Activitys
.
value
.
length
===
0
||
Activitys
.
value
.
some
((
seckillActivity
)
=>
!
ids
.
includes
(
seckillActivity
.
id
!
))
)
{
Activitys
.
value
=
await
SeckillActivityApi
.
getSeckillActivityListByIds
(
ids
)
}
},
{
immediate
:
true
}
)
/** 活动表格选择对话框 */
const
seckillActivityTableSelectRef
=
ref
()
// 打开对话框
const
openSeckillActivityTableSelect
=
()
=>
{
seckillActivityTableSelectRef
.
value
.
open
(
Activitys
.
value
)
}
/**
* 选择活动后触发
* @param activityVOs 选中的活动列表
*/
const
handleActivitySelected
=
(
activityVOs
:
SeckillActivityApi
.
SeckillActivityVO
|
SeckillActivityApi
.
SeckillActivityVO
[]
)
=>
{
Activitys
.
value
=
isArray
(
activityVOs
)
?
activityVOs
:
[
activityVOs
]
emitActivityChange
()
}
/**
* 删除活动
* @param index 活动索引
*/
const
handleRemoveActivity
=
(
index
:
number
)
=>
{
Activitys
.
value
.
splice
(
index
,
1
)
emitActivityChange
()
}
const
emit
=
defineEmits
([
'update:modelValue'
,
'change'
])
const
emitActivityChange
=
()
=>
{
if
(
props
.
limit
===
1
)
{
const
seckillActivity
=
Activitys
.
value
.
length
>
0
?
Activitys
.
value
[
0
]
:
null
emit
(
'update:modelValue'
,
seckillActivity
?.
id
||
0
)
emit
(
'change'
,
seckillActivity
)
}
else
{
emit
(
'update:modelValue'
,
Activitys
.
value
.
map
((
seckillActivity
)
=>
seckillActivity
.
id
)
)
emit
(
'change'
,
Activitys
.
value
)
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.select-box
{
display
:
flex
;
width
:
60px
;
height
:
60px
;
border
:
1px
dashed
var
(
--el-border-color-darker
);
border-radius
:
8px
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
}
.spu-pic
{
position
:
relative
;
}
.del-icon
{
position
:
absolute
;
top
:
-10px
;
right
:
-10px
;
z-index
:
1
;
width
:
20px
!important
;
height
:
20px
!important
;
}
</
style
>
src/views/mall/promotion/seckill/components/SeckillTableSelect.vue
0 → 100644
View file @
7a6c6949
This diff is collapsed.
Click to expand it.
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