Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
飞
飞鹤小程序
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
FH
飞鹤小程序
Commits
85291c3f
Commit
85291c3f
authored
Jun 07, 2025
by
tao.huang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 多选
parent
5b57105a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
164 additions
and
114 deletions
+164
-114
MultiSelectLayer.vue
components/MultiSelectLayer.vue
+47
-19
PickerCustom.vue
components/PickerCustom.vue
+5
-3
person.vue
pages/person/person.vue
+111
-91
My.vue
views/My.vue
+1
-1
No files found.
components/MultiSelectLayer.vue
View file @
85291c3f
<
template
>
<Layer
:modelValue=
"modelValue"
:
title=
"titl
e"
:showCancel=
"
tru
e"
:showConfirm=
"
tru
e"
:
customHeader=
"tru
e"
:showCancel=
"
fals
e"
:showConfirm=
"
fals
e"
@
update:modelValue=
"$emit('update:modelValue', $event)"
@
confirm=
"onConfirm"
@
cancel=
"onCancel"
>
<template>
<view
class=
"multi-select-title"
>
{{
title
}}
</view>
<view
class=
"multi-select-list"
>
<view
v-for=
"opt in options"
...
...
@@ -21,18 +22,22 @@
<image
v-if=
"selectedSet.has(opt)"
class=
"check-icon"
src=
"/static/check.png
"
:src=
"$baseUrl + 'person/icon_gou.png'
"
/>
</view>
</view>
<view
class=
"multi-select-btn"
@
click=
"onConfirm"
>
保存
</view>
</
template
>
</Layer>
</template>
<
script
setup
>
import
{
ref
,
watch
,
computed
}
from
"vue"
;
import
{
ref
,
watch
,
computed
,
getCurrentInstance
}
from
"vue"
;
import
Layer
from
"./Layer.vue"
;
const
{
proxy
}
=
getCurrentInstance
();
const
$baseUrl
=
proxy
.
$baseUrl
;
const
props
=
defineProps
({
modelValue
:
Boolean
,
title
:
String
,
...
...
@@ -40,7 +45,12 @@ const props = defineProps({
modelSelected
:
{
type
:
Array
,
default
:
()
=>
[]
},
max
:
Number
,
});
const
emit
=
defineEmits
([
"update:modelValue"
,
"update:selected"
,
"confirm"
,
"cancel"
]);
const
emit
=
defineEmits
([
"update:modelValue"
,
"update:selected"
,
"confirm"
,
"cancel"
,
]);
const
selected
=
ref
([...
props
.
modelSelected
]);
watch
(
...
...
@@ -72,32 +82,50 @@ function onCancel() {
</
script
>
<
style
lang=
"less"
scoped
>
.multi-select-btn {
width: 686rpx;
height: 94rpx;
background: #d3a358;
color: #fff;
border-radius: 46rpx;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
margin-top: 24rpx;
font-size: 32rpx;
}
.multi-select-title {
font-size: 32rpx;
color: #222;
font-weight: bold;
margin-bottom: 24rpx;
}
.multi-select-list {
display: flex;
flex-wrap: wrap;
gap:
24
rpx 20rpx;
gap:
15
rpx 20rpx;
}
.multi-select-tag {
display: flex;
align-items: center;
padding: 0
32
rpx;
padding: 0
24
rpx;
height: 56rpx;
background: #fff;
background: #fff
fff
;
border-radius: 28rpx;
color: #222;
font-size: 28rpx;
margin-bottom: 24rpx;
color: #1d1e25;
font-size: 24rpx;
position: relative;
border: 1rpx solid #eee;
.check-icon {
width: 28rpx;
height: 28rpx;
margin-left: 8rpx;
width: 19rpx;
height: 19rpx;
position: absolute;
right: 2rpx;
top: 3rpx;
display: block;
}
&.selected {
background: #e9d3b1;
color: #b88a3a;
border: 1rpx solid #b88a3a;
background: #efe7da;
}
}
</
style
>
\ No newline at end of file
components/PickerCustom.vue
View file @
85291c3f
...
...
@@ -21,7 +21,7 @@
<picker-view
class=
"picker-layer-view"
mask-style=
"background: rgb(246, 248, 250); z-index: 0;"
indicator-style=
"border-radius: 10px; height: 50px;background:#ffffff; z-index:0"
indicator-style=
"border-radius: 10px; height: 50px;
background:#ffffff; z-index:0"
:value=
"pickerValue"
@
change=
"onChange"
>
...
...
@@ -55,9 +55,9 @@
<
/picker-view-column
>
<
/template
>
<
template
v
-
else
>
<
picker
-
view
-
column
v
-
for
=
"(col, colIdx) in columns"
:
key
=
"colIdx"
>
<
picker
-
view
-
column
>
<
view
v
-
for
=
"(item, idx) in col"
v
-
for
=
"(item, idx) in col
umns[0]
"
:
key
=
"idx"
class
=
"picker-layer-item"
>
...
...
@@ -309,6 +309,7 @@ function onChange(e) {
overflow
:
hidden
;
display
:
flex
;
flex
-
direction
:
column
;
position
:
relative
;
}
.
picker
-
layer
-
header
{
display
:
flex
;
...
...
@@ -350,6 +351,7 @@ function onChange(e) {
.
picker
-
layer
-
view
{
flex
:
1
;
width
:
100
%
;
height
:
100
%
;
}
.
picker
-
layer
-
item
{
height
:
100
rpx
;
...
...
pages/person/person.vue
View file @
85291c3f
...
...
@@ -55,7 +55,7 @@
.filter(formItemFilter)
.slice(
0,
formData.currentBaby ==
'已出生'
&& pageStatus.formStatus == 2
formData.currentBaby ==
2
&& pageStatus.formStatus == 2
? 6
: formItems.length
)"
...
...
@@ -95,7 +95,9 @@
:onStatusChange=
"onDateStatusChange"
>
<view
class=
"form-input-box"
>
<view
class=
"form-input"
>
{{
getLabelByValue
(
item
,
formData
[
item
.
name
])
||
item
.
placeholder
}}
</view>
<view
class=
"form-input"
>
{{
getLabelByValue
(
item
,
formData
[
item
.
name
])
||
item
.
placeholder
}}
</view>
<image
class=
"form-input-icon"
:src=
"$baseUrl + 'person/icon_arrow_yellow_right.png'"
...
...
@@ -103,10 +105,17 @@
</view>
</picker-custom>
<!-- 多选弹窗类型 -->
<view
v-else-if=
"item.type === 'multi-picker'"
@
click=
"handleMultiPickerOpen(item)"
>
<view
v-else-if=
"item.type === 'multi-picker'"
@
click=
"handleMultiPickerOpen(item)"
>
<view
class=
"form-input-box"
>
<view
class=
"form-input"
>
{{
(
formData
[
item
.
name
]
&&
formData
[
item
.
name
].
split
(
','
).
join
(
'、'
))
||
item
.
placeholder
}}
{{
(
formData
[
item
.
name
]
&&
formData
[
item
.
name
].
split
(
","
).
join
(
"、"
))
||
item
.
placeholder
}}
</view>
<image
class=
"form-input-icon"
...
...
@@ -135,7 +144,7 @@
</block>
<view
v-if=
"
pageStatus.formStatus != 0
"
v-if=
"
formData.currentBaby == 2
"
class=
"form-bottom-btn"
@
click=
"handleFormBottomBtn"
>
...
...
@@ -166,8 +175,11 @@
<MultiSelectLayer
v-if=
"multiPickerStatus && currentMultiPickerName"
v-model=
"multiPickerStatus"
:title=
"formItems.find(i => i.name === currentMultiPickerName)?.label + '(多选)'"
:options=
"formItems.find(i => i.name === currentMultiPickerName)?.range"
:title=
"
formItems.find((i) => i.name === currentMultiPickerName)?.label +
'(多选)'
"
:options=
"formItems.find((i) => i.name === currentMultiPickerName)?.range"
:modelSelected=
"multiPickerSelected[currentMultiPickerName]"
@
confirm=
"handleMultiPickerConfirm"
@
cancel=
"handleMultiPickerCancel"
...
...
@@ -177,20 +189,19 @@
<
script
setup
>
import
{
ref
,
watch
,
onMounted
}
from
"vue"
;
import
{
onLoad
}
from
'@dcloudio/uni-app'
;
import
{
useUserStore
}
from
'../../stores/user.js'
;
import
{
onLoad
}
from
"@dcloudio/uni-app"
;
import
{
useUserStore
}
from
"../../stores/user.js"
;
import
PickerCustom
from
"../../components/PickerCustom.vue"
;
import
MultiSelectLayer
from
"../../components/MultiSelectLayer.vue"
;
const
userStore
=
useUserStore
();
const
pageType
=
ref
(
'add'
);
const
pageType
=
ref
(
"add"
);
const
pageStatus
=
ref
({
formStatus
:
0
,
// 0: 隐藏 1: 展开, 2: 收起
btnStatus
:
true
,
// 是否显示提交按钮
});
// 0备孕 1孕中 2出生
const
formData
=
ref
({
currentBaby
:
2
,
...
...
@@ -208,19 +219,19 @@ const formData = ref({
});
const
babyTypeMap
=
[
{
label
:
'一胎'
,
value
:
1
},
{
label
:
'二胎'
,
value
:
2
},
{
label
:
'三胎'
,
value
:
3
},
{
label
:
"一胎"
,
value
:
1
},
{
label
:
"二胎"
,
value
:
2
},
{
label
:
"三胎"
,
value
:
3
},
];
const
babyStageMap
=
[
{
label
:
'备孕'
,
value
:
0
},
{
label
:
'孕中'
,
value
:
1
},
{
label
:
'已出生'
,
value
:
2
},
{
label
:
"备孕"
,
value
:
0
},
{
label
:
"孕中"
,
value
:
1
},
{
label
:
"已出生"
,
value
:
2
},
];
const
babyGenderMap
=
[
{
label
:
'男'
,
value
:
'M'
},
{
label
:
'女'
,
value
:
'F'
},
{
label
:
'其他'
,
value
:
'O'
},
{
label
:
"男"
,
value
:
"M"
},
{
label
:
"女"
,
value
:
"F"
},
{
label
:
"其他"
,
value
:
"O"
},
];
const
FormMap
=
{
...
...
@@ -253,18 +264,36 @@ const formItemFilter = (item) => {
const
MAX_NICKNAME_LENGTH
=
10
;
watch
(
()
=>
formData
.
value
.
currentBaby
,
(
newVal
)
=>
{
pageStatus
.
value
.
formStatus
=
newVal
==
2
?
2
:
0
;
}
);
const
contentLikeOptions
=
[
'奶粉选择'
,
'奶粉成分'
,
'奶粉冲泡'
,
'宝宝奶量'
,
'生长发育'
,
'常见疾病'
,
'体检疫苗'
,
'饮食营养补充'
,
'辅食营养'
,
'亲子关系'
,
'早教游戏'
"奶粉选择"
,
"奶粉成分"
,
"奶粉冲泡"
,
"宝宝奶量"
,
"生长发育"
,
"常见疾病"
,
"体检疫苗"
,
"饮食营养补充"
,
"辅食营养"
,
"亲子关系"
,
"早教游戏"
,
];
const
productLikeOptions
=
[
'飞鹤星飞帆'
,
'星飞帆卓睿'
,
'星飞帆卓耀'
,
'星飞帆卓初'
,
'星飞帆臻稚卓蓓'
,
'星飞帆淳芮有机'
];
const
specialAttentionOptions
=
[
'过敏'
,
'长肉'
,
'肠道'
,
'消化'
,
'脑发育'
];
const
channelOptions
=
[
'电商(京东/天猫)'
,
'母婴店'
"飞鹤星飞帆"
,
"星飞帆卓睿"
,
"星飞帆卓耀"
,
"星飞帆卓初"
,
"星飞帆臻稚卓蓓"
,
"星飞帆淳芮有机"
,
];
const
specialAttentionOptions
=
[
"过敏"
,
"长肉"
,
"肠道"
,
"消化"
,
"脑发育"
];
const
channelOptions
=
[
"电商(京东/天猫)"
,
"母婴店"
];
const
formItems
=
[
{
...
...
@@ -273,7 +302,7 @@ const formItems = [
required
:
true
,
placeholder
:
"请选择当前状态"
,
type
:
"picker"
,
range
:
babyStageMap
.
map
(
i
=>
i
.
label
),
range
:
babyStageMap
.
map
(
(
i
)
=>
i
.
label
),
mode
:
"custom"
,
},
{
...
...
@@ -307,7 +336,7 @@ const formItems = [
required
:
true
,
placeholder
:
"未选择"
,
type
:
"picker"
,
range
:
babyTypeMap
.
map
(
i
=>
i
.
label
),
range
:
babyTypeMap
.
map
(
(
i
)
=>
i
.
label
),
mode
:
"custom"
,
disabled
:
false
,
},
...
...
@@ -317,7 +346,7 @@ const formItems = [
required
:
true
,
placeholder
:
"未选择"
,
type
:
"picker"
,
range
:
babyGenderMap
.
map
(
i
=>
i
.
label
),
range
:
babyGenderMap
.
map
(
(
i
)
=>
i
.
label
),
mode
:
"custom"
,
},
{
...
...
@@ -376,16 +405,16 @@ const formItems = [
const
getPickerIndex
=
(
item
)
=>
{
const
val
=
formData
.
value
[
item
.
name
];
if
(
item
.
name
===
'currentBaby'
)
{
const
idx
=
babyStageMap
.
findIndex
(
i
=>
i
.
value
===
val
);
if
(
item
.
name
===
"currentBaby"
)
{
const
idx
=
babyStageMap
.
findIndex
(
(
i
)
=>
i
.
value
===
val
);
return
idx
>
-
1
?
idx
:
0
;
}
if
(
item
.
name
===
'babyIndex'
)
{
const
idx
=
babyTypeMap
.
findIndex
(
i
=>
i
.
value
===
val
);
if
(
item
.
name
===
"babyIndex"
)
{
const
idx
=
babyTypeMap
.
findIndex
(
(
i
)
=>
i
.
value
===
val
);
return
idx
>
-
1
?
idx
:
0
;
}
if
(
item
.
name
===
'gender'
)
{
const
idx
=
babyGenderMap
.
findIndex
(
i
=>
i
.
value
===
val
);
if
(
item
.
name
===
"gender"
)
{
const
idx
=
babyGenderMap
.
findIndex
(
(
i
)
=>
i
.
value
===
val
);
return
idx
>
-
1
?
idx
:
0
;
}
if
(
item
.
mode
===
"date"
)
{
...
...
@@ -410,15 +439,15 @@ const getPickerIndex = (item) => {
};
const
onPickerChange
=
(
e
,
name
)
=>
{
const
item
=
formItems
.
find
((
i
)
=>
i
.
name
===
name
);
if
(
name
===
'currentBaby'
)
{
if
(
name
===
"currentBaby"
)
{
formData
.
value
[
name
]
=
babyStageMap
[
e
].
value
;
return
;
}
if
(
name
===
'babyIndex'
)
{
if
(
name
===
"babyIndex"
)
{
formData
.
value
[
name
]
=
babyTypeMap
[
e
].
value
;
return
;
}
if
(
name
===
'gender'
)
{
if
(
name
===
"gender"
)
{
formData
.
value
[
name
]
=
babyGenderMap
[
e
].
value
;
return
;
}
...
...
@@ -448,43 +477,30 @@ const onChooseAvatar = (e) => {
formData
.
value
.
avatarUrl
=
e
.
detail
.
avatarUrl
;
};
watch
(
()
=>
formData
.
value
.
currentBaby
,
(
newVal
)
=>
{
if
(
newVal
===
"已出生"
)
{
pageStatus
.
value
.
formStatus
=
2
;
}
else
{
pageStatus
.
value
.
formStatus
=
0
;
}
}
);
function
onDateStatusChange
(
status
)
{
if
(
status
===
'已出生'
||
status
===
'孕中'
)
{
formData
.
value
.
currentBaby
=
status
;
}
formData
.
value
.
currentBaby
=
status
;
}
// getLabelByValue 支持所有映射字段
const
getLabelByValue
=
(
item
,
value
)
=>
{
if
(
item
.
name
===
'currentBaby'
)
{
return
babyStageMap
.
find
(
i
=>
i
.
value
===
value
)?.
label
||
''
;
if
(
item
.
name
===
"currentBaby"
)
{
return
babyStageMap
.
find
(
(
i
)
=>
i
.
value
===
value
)?.
label
||
""
;
}
if
(
item
.
name
===
'babyIndex'
)
{
return
babyTypeMap
.
find
(
i
=>
i
.
value
===
value
)?.
label
||
''
;
if
(
item
.
name
===
"babyIndex"
)
{
return
babyTypeMap
.
find
(
(
i
)
=>
i
.
value
===
value
)?.
label
||
""
;
}
if
(
item
.
name
===
'gender'
)
{
return
babyGenderMap
.
find
(
i
=>
i
.
value
===
value
)?.
label
||
''
;
if
(
item
.
name
===
"gender"
)
{
return
babyGenderMap
.
find
(
(
i
)
=>
i
.
value
===
value
)?.
label
||
""
;
}
return
value
;
};
const
getValueByLabel
=
(
map
,
label
)
=>
{
const
found
=
map
.
find
(
i
=>
i
.
label
===
label
);
return
found
?
found
.
value
:
''
;
const
found
=
map
.
find
(
(
i
)
=>
i
.
label
===
label
);
return
found
?
found
.
value
:
""
;
};
const
multiPickerStatus
=
ref
(
false
);
// 控制弹窗显示
const
currentMultiPickerName
=
ref
(
''
);
// 当前弹窗对应的item.name
const
currentMultiPickerName
=
ref
(
""
);
// 当前弹窗对应的item.name
const
multiPickerSelected
=
ref
({});
// 存储每个multi-picker的选中项
function
handleMultiPickerOpen
(
item
)
{
...
...
@@ -492,54 +508,58 @@ function handleMultiPickerOpen(item) {
multiPickerStatus
.
value
=
true
;
// 初始化选中项
let
val
=
formData
.
value
[
item
.
name
];
if
(
typeof
val
===
'string'
)
{
val
=
val
?
val
.
split
(
','
)
:
[];
if
(
typeof
val
===
"string"
)
{
val
=
val
?
val
.
split
(
","
)
:
[];
}
multiPickerSelected
.
value
[
item
.
name
]
=
val
||
[];
}
function
handleMultiPickerConfirm
(
selected
)
{
if
(
!
currentMultiPickerName
.
value
)
return
;
formData
.
value
[
currentMultiPickerName
.
value
]
=
selected
.
join
(
','
);
formData
.
value
[
currentMultiPickerName
.
value
]
=
selected
.
join
(
","
);
multiPickerStatus
.
value
=
false
;
currentMultiPickerName
.
value
=
''
;
currentMultiPickerName
.
value
=
""
;
}
function
handleMultiPickerCancel
()
{
multiPickerStatus
.
value
=
false
;
currentMultiPickerName
.
value
=
''
;
currentMultiPickerName
.
value
=
""
;
}
onLoad
((
options
)
=>
{
pageType
.
value
=
options
.
type
||
'add'
;
if
(
pageType
.
value
===
'edit'
)
{
pageType
.
value
=
options
.
type
||
"add"
;
if
(
pageType
.
value
===
"edit"
)
{
const
baby
=
userStore
.
babyInfo
;
formData
.
value
.
currentBaby
=
baby
.
babyStage
??
2
;
formData
.
value
.
nickName
=
baby
.
babyName
||
''
;
formData
.
value
.
dateOfBirth
=
baby
.
content
?.
babyBirthday
||
''
;
formData
.
value
.
dateOfBirth2
=
baby
.
content
?.
dueDate
||
''
;
formData
.
value
.
nickName
=
baby
.
babyName
||
""
;
formData
.
value
.
dateOfBirth
=
baby
.
content
?.
babyBirthday
||
""
;
formData
.
value
.
dateOfBirth2
=
baby
.
content
?.
dueDate
||
""
;
formData
.
value
.
babyIndex
=
baby
.
content
?.
babyType
??
1
;
formData
.
value
.
gender
=
baby
.
content
?.
babyGender
??
'M'
;
formData
.
value
.
eatFunc
=
baby
.
content
?.
feedingType
||
''
;
formData
.
value
.
contentLike
=
baby
.
content
?.
contentPreference
||
''
;
formData
.
value
.
productLike
=
baby
.
content
?.
productPreference
||
''
;
formData
.
value
.
specialAttention
=
baby
.
content
?.
followInfo
||
''
;
formData
.
value
.
channel
=
baby
.
content
?.
purchaseChannel
||
''
;
formData
.
value
.
avatarUrl
=
baby
.
babyAvatar
||
''
;
formData
.
value
.
gender
=
baby
.
content
?.
babyGender
??
"M"
;
formData
.
value
.
eatFunc
=
baby
.
content
?.
feedingType
||
""
;
formData
.
value
.
contentLike
=
baby
.
content
?.
contentPreference
||
""
;
formData
.
value
.
productLike
=
baby
.
content
?.
productPreference
||
""
;
formData
.
value
.
specialAttention
=
baby
.
content
?.
followInfo
||
""
;
formData
.
value
.
channel
=
baby
.
content
?.
purchaseChannel
||
""
;
formData
.
value
.
avatarUrl
=
baby
.
babyAvatar
||
""
;
}
else
{
Object
.
assign
(
formData
.
value
,
{
currentBaby
:
2
,
nickName
:
''
,
dateOfBirth
:
''
,
dateOfBirth2
:
''
,
gender
:
''
,
babyIndex
:
''
,
eatFunc
:
''
,
contentLike
:
''
,
productLike
:
''
,
specialAttention
:
''
,
channel
:
''
,
avatarUrl
:
''
,
nickName
:
""
,
dateOfBirth
:
""
,
dateOfBirth2
:
""
,
gender
:
""
,
babyIndex
:
""
,
eatFunc
:
""
,
contentLike
:
""
,
productLike
:
""
,
specialAttention
:
""
,
channel
:
""
,
avatarUrl
:
""
,
});
}
if
(
formData
.
value
.
currentBaby
==
2
)
{
pageStatus
.
value
.
formStatus
=
2
;
}
});
</
script
>
...
...
views/My.vue
View file @
85291c3f
...
...
@@ -69,7 +69,7 @@
<BabySwitcher
v-if=
"showBabySwitcher"
:show=
"showBabySwitcher"
:babyList=
"babyInfo.allBabyBaseInfo"
:babyList=
"babyInfo.allBabyBaseInfo
|| []
"
:addIcon=
"$baseUrl + 'my/baby_add_btn.png'"
@
close=
"showBabySwitcher = false"
@
select=
"onSelectBaby"
...
...
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