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
33f625f8
Commit
33f625f8
authored
Jul 27, 2025
by
spc
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feeding
parent
cb5235b5
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
726 additions
and
52 deletions
+726
-52
feedingIndex.vue
pages/feedingIndex/feedingIndex.vue
+720
-51
feedingRecord.vue
pages/feedingRecord/feedingRecord.vue
+6
-1
icon_luyining.gif
static/feedingIndex/v1/icon_luyining.gif
+0
-0
No files found.
pages/feedingIndex/feedingIndex.vue
View file @
33f625f8
...
@@ -22,13 +22,17 @@
...
@@ -22,13 +22,17 @@
</view>
</view>
<view
class=
"time-section"
>
<view
class=
"time-section"
>
<text
class=
"time-label"
>
喂养时间
</text>
<text
class=
"time-label"
>
喂养时间
</text>
<text
class=
"time-value"
>
2025-06-01 22:22
</text>
<uni-datetime-picker
ref=
"timePickerRef"
v-model=
"currentTime"
type=
"datetime"
:clear-icon=
"false"
:border=
"false"
@
change=
"onTimeChange"
:start=
"earliestDateString"
:end=
"latestDateString"
>
<view
class=
"time-display"
>
<text
class=
"time-value"
>
{{
formatCurrentTime
()
}}
</text>
<image
class=
"edit-icon"
src=
"/static/feedingIndex/v1/icon_modify.png"
/>
<image
class=
"edit-icon"
src=
"/static/feedingIndex/v1/icon_modify.png"
/>
</view>
</view>
</uni-datetime-picker>
</view>
</view>
</view>
<!-- 喂养记录 -->
<!-- 喂养记录 -->
<view
class=
"feeding-records"
>
<view
class=
"feeding-records"
@
click=
"goToFeedingRecord"
>
<text
class=
"records-link"
>
喂养记录
</text>
<text
class=
"records-link"
>
喂养记录
</text>
<image
class=
"arrow-right"
src=
"/static/feedingIndex/v1/icon_arrow_yellow.png"
></image>
<image
class=
"arrow-right"
src=
"/static/feedingIndex/v1/icon_arrow_yellow.png"
></image>
</view>
</view>
...
@@ -137,49 +141,54 @@
...
@@ -137,49 +141,54 @@
<!-- 辅食表单 -->
<!-- 辅食表单 -->
<view
v-if=
"selectedType === 'food'"
class=
"food-form"
>
<view
v-if=
"selectedType === 'food'"
class=
"food-form"
>
<!-- 已选择的辅食展示区域 -->
<view
class=
"selected-items"
>
<view
class=
"selected-items"
>
<text>
{{
feedingData
.
food
.
selectedItems
.
join
(
','
)
}}
</text>
<text>
{{
feedingData
.
food
.
selectedItems
.
join
(
','
)
||
'请选择辅食'
}}
</text>
</view>
</view>
<!-- 辅食分类标题和操作按钮 -->
<view
class=
"food-categories"
>
<view
class=
"food-categories"
>
<text
class=
"category-title"
>
辅食分类
</text>
<text
class=
"category-title"
>
辅食分类
</text>
<text
class=
"complete-btn"
>
完成
</text>
<view
class=
"category-actions"
>
</view>
<text
v-if=
"foodSelectionState.isEditMode"
class=
"complete-btn"
@
click=
"exitEditMode"
>
完成
</text>
<view
class=
"category-list"
>
<text
v-else
class=
"delete-btn"
@
click=
"enterEditMode"
>
删除
</text>
<view
class=
"category-item"
>
<text
class=
"category-name"
>
主食
</text>
<text
class=
"add-btn"
>
+
</text>
<view
class=
"selected-tags"
>
<text
class=
"tag"
>
米粉 ✕
</text>
<text
class=
"tag"
>
面条 ✕
</text>
</view>
</view>
</view>
</view>
<view
class=
"category-item"
>
<text
class=
"category-name"
>
蔬菜
</text>
<!-- 辅食分类列表 -->
<text
class=
"add-btn"
>
+
</text>
<view
class=
"category-list"
>
<view
class=
"selected-tags"
>
<view
v-for=
"(category, categoryName) in foodCategories"
:key=
"categoryName"
class=
"category-item"
>
<text
class=
"tag"
>
山药 ✕
</text>
<text
class=
"category-name"
>
{{
categoryName
}}
</text>
<text
class=
"tag"
>
萝卜 ✕
</text>
<text
class=
"add-btn"
:class=
"
{ disabled: foodSelectionState.isEditMode }" @click="showAddFoodPopup(categoryName)">+
</text>
<text
class=
"tag active"
>
土豆 ✕
</text>
<text
class=
"tag"
>
豆 ✕
</text>
<text
class=
"tag"
>
青瓜 ✕
</text>
<text
class=
"tag"
>
白菜 ✕
</text>
</view>
<text
class=
"expand-arrow"
>
↑
</text>
</view>
<view
class=
"category-item"
>
<text
class=
"category-name"
>
水果
</text>
<text
class=
"add-btn"
>
+
</text>
<view
class=
"selected-tags"
>
<text
class=
"tag"
>
香蕉 ✕
</text>
<text
class=
"tag active"
>
苹果 ✕
</text>
<text
class=
"tag"
>
牛油果 ✕
</text>
</view>
</view>
<view
class=
"category-item"
>
<text
class=
"category-name"
>
其他
</text>
<text
class=
"add-btn"
>
+
</text>
<view
class=
"selected-tags"
>
<view
class=
"selected-tags"
>
<text
class=
"tag"
>
鱼油 ✕
</text>
<!-- 默认辅食项 -->
<text
v-for=
"item in category.items"
:key=
"item"
class=
"tag"
:class=
"
{
active: feedingData.food.selectedItems.includes(item),
editing: foodSelectionState.isEditMode
}"
@click="toggleFoodSelection(item)"
>
{{
item
}}
<text
v-if=
"foodSelectionState.isEditMode"
class=
"delete-x"
@
click
.
stop=
"removeFoodItem(categoryName, item)"
>
✕
</text>
</text>
<!-- 自定义辅食项 -->
<text
v-for=
"item in category.customItems"
:key=
"`custom-$
{item}`"
class="tag"
:class="{
active: feedingData.food.selectedItems.includes(item),
editing: foodSelectionState.isEditMode
}"
@click="toggleFoodSelection(item)"
>
{{
item
}}
<text
v-if=
"foodSelectionState.isEditMode"
class=
"delete-x"
@
click
.
stop=
"removeFoodItem(categoryName, item)"
>
✕
</text>
</text>
</view>
</view>
</view>
</view>
</view>
</view>
...
@@ -199,15 +208,19 @@
...
@@ -199,15 +208,19 @@
</view>
</view>
<!-- 麦克风图标 -->
<!-- 麦克风图标 -->
<view
class=
"voice-microphone"
@
click=
"toggleRecording"
>
<view
class=
"voice-microphone"
@
touchstart=
"startRecording"
@
touchend=
"stopRecording"
@
touchcancel=
"stopRecording"
>
<image
class=
"microphone-icon"
<image
class=
"microphone-icon"
:src=
"
isRecording ? '/static/feedingIndex/v1/icon_luyining.png
' : '/static/feedingIndex/v1/icon_luyin.png'"
:src=
"
voiceRecognitionState.isRecording ? '/static/feedingIndex/v1/icon_luyining.gif
' : '/static/feedingIndex/v1/icon_luyin.png'"
mode=
"aspectFit"
/>
mode=
"aspectFit"
/>
</view>
</view>
<!--
点击
提示 -->
<!--
录音
提示 -->
<view
class=
"voice-click-tip"
>
<view
class=
"voice-click-tip"
>
<text
class=
"click-text"
>
点击识别语音
</text>
<text
class=
"click-text"
v-if=
"voiceRecognitionState.isRecording"
>
正在录音...
</text>
<text
class=
"click-text"
v-else
>
长按录音
</text>
</view>
</view>
</view>
</view>
...
@@ -264,13 +277,82 @@
...
@@ -264,13 +277,82 @@
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 添加辅食弹窗 -->
<uni-popup
ref=
"addFoodPopup"
type=
"center"
:mask-click=
"false"
>
<view
class=
"add-food-popup"
>
<view
class=
"popup-header"
>
<text
class=
"popup-title"
>
添加辅食
</text>
</view>
<view
class=
"popup-content"
>
<text
class=
"input-label"
>
请输入辅食名称(最多10个字)
</text>
<input
class=
"food-input"
v-model=
"foodSelectionState.newFoodItem"
placeholder=
"请输入辅食名称"
maxlength=
"10"
/>
</view>
<view
class=
"popup-buttons"
>
<text
class=
"cancel-btn"
@
click=
"cancelAddFood"
>
取消
</text>
<text
class=
"confirm-btn"
@
click=
"confirmAddFood"
>
添加
</text>
</view>
</view>
</uni-popup>
<!-- 语音识别结果页面 -->
<view
v-if=
"voiceRecognitionState.showResultPage"
class=
"voice-result-page"
>
<view
class=
"result-container"
>
<!-- 识别结果标题 -->
<view
class=
"result-header"
>
<text
class=
"result-title"
>
识别结果
</text>
</view>
<!-- 喂养信息展示 -->
<view
class=
"feeding-info"
>
<view
class=
"info-item"
>
<text
class=
"info-label"
>
喂养时间
</text>
<text
class=
"info-value"
>
{{
formatCurrentTime
()
}}
</text>
</view>
<view
class=
"info-item"
>
<text
class=
"info-label"
>
喂养方式
</text>
<text
class=
"info-value"
>
{{
getFeedingTypeLabel
()
}}
</text>
</view>
<view
class=
"info-item"
>
<text
class=
"info-label"
>
喂养详情
</text>
<input
class=
"detail-input"
v-model=
"voiceRecognitionState.recognizedText"
placeholder=
"请输入喂养详情"
maxlength=
"20"
/>
</view>
</view>
<!-- 操作按钮 -->
<view
class=
"result-actions"
>
<text
class=
"re-recognize-btn"
@
click=
"reRecognize"
>
不对,重新识别
</text>
<text
class=
"complete-record-btn"
@
click=
"completeVoiceRecord"
>
完成记录
</text>
</view>
</view>
</view>
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
import
{
ref
,
computed
}
from
'vue'
import
{
ref
,
computed
}
from
'vue'
// 弹窗引用
const
addFoodPopup
=
ref
(
null
)
const
swiperData
=
ref
([{
bannerImg
:
'/static/feedingIndex/v1/banner.png'
},
{
bannerImg
:
'/static/feedingIndex/v1/banner.png'
},
{
bannerImg
:
'/static/feedingIndex/v1/banner.png'
}]);
const
swiperData
=
ref
([{
bannerImg
:
'/static/feedingIndex/v1/banner.png'
},
{
bannerImg
:
'/static/feedingIndex/v1/banner.png'
},
{
bannerImg
:
'/static/feedingIndex/v1/banner.png'
}]);
const
indicatorStyle
=
`height: 40px; border: none;`
const
indicatorStyle
=
`height: 40px; border: none;`
// 当前时间,默认为当前时间
const
currentTime
=
ref
(
new
Date
().
getTime
())
// 日期范围限制
const
earliestDateString
=
ref
(
'2020-01-01'
)
const
latestDateString
=
ref
(
'2030-12-31'
)
const
bannerHandler
=
(
item
)
=>
{
const
bannerHandler
=
(
item
)
=>
{
console
.
log
(
item
);
console
.
log
(
item
);
}
}
...
@@ -294,6 +376,42 @@ const isRecording = ref(false)
...
@@ -294,6 +376,42 @@ const isRecording = ref(false)
const
isLeftTimerRunning
=
ref
(
false
)
const
isLeftTimerRunning
=
ref
(
false
)
const
isRightTimerRunning
=
ref
(
false
)
const
isRightTimerRunning
=
ref
(
false
)
// 辅食数据结构
const
foodCategories
=
ref
({
主食
:
{
items
:
[
'米粉'
,
'面条'
,
'馒头'
,
'包子'
,
'粥'
],
customItems
:
[]
},
蔬菜
:
{
items
:
[
'山药'
,
'萝卜'
,
'土豆'
,
'豆'
,
'青瓜'
,
'白菜'
,
'胡萝卜'
,
'南瓜'
],
customItems
:
[]
},
水果
:
{
items
:
[
'香蕉'
,
'苹果'
,
'牛油果'
,
'橙子'
,
'梨'
],
customItems
:
[]
},
其他
:
{
items
:
[
'鱼油'
,
'维生素'
],
customItems
:
[]
}
})
// 辅食选择状态
const
foodSelectionState
=
ref
({
isEditMode
:
false
,
// 是否处于编辑模式
showAddPopup
:
false
,
// 是否显示添加弹窗
currentCategory
:
''
,
// 当前添加的分类
newFoodItem
:
''
// 新添加的辅食项
})
// 语音识别状态
const
voiceRecognitionState
=
ref
({
isRecording
:
false
,
// 是否正在录音
showResultPage
:
false
,
// 是否显示识别结果页面
recognizedText
:
''
,
// 识别结果文本
recordingDuration
:
0
// 录音时长
})
// 为每种喂养方式设置独立的数据
// 为每种喂养方式设置独立的数据
const
feedingData
=
ref
({
const
feedingData
=
ref
({
breastfeeding
:
{
breastfeeding
:
{
...
@@ -424,6 +542,11 @@ function onPickerChange(e) {
...
@@ -424,6 +542,11 @@ function onPickerChange(e) {
function
setRecordMethod
(
method
)
{
function
setRecordMethod
(
method
)
{
recordMethods
.
value
[
selectedType
.
value
]
=
method
recordMethods
.
value
[
selectedType
.
value
]
=
method
// 如果切换到语音模式,检查录音权限
if
(
method
===
'voice'
)
{
checkVoicePermission
()
}
}
}
function
toggleRecording
()
{
function
toggleRecording
()
{
...
@@ -497,6 +620,27 @@ function goBack() {
...
@@ -497,6 +620,27 @@ function goBack() {
uni
.
navigateBack
()
uni
.
navigateBack
()
}
}
function
goToFeedingRecord
()
{
uni
.
navigateTo
({
url
:
'/pages/feedingRecord/feedingRecord'
})
}
// 时间选择器相关方法
function
onTimeChange
(
value
)
{
currentTime
.
value
=
value
}
function
formatCurrentTime
()
{
const
date
=
new
Date
(
currentTime
.
value
)
const
year
=
date
.
getFullYear
()
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)
const
day
=
String
(
date
.
getDate
()).
padStart
(
2
,
'0'
)
const
hours
=
String
(
date
.
getHours
()).
padStart
(
2
,
'0'
)
const
minutes
=
String
(
date
.
getMinutes
()).
padStart
(
2
,
'0'
)
return
`
${
year
}
-
${
month
}
-
${
day
}
${
hours
}
:
${
minutes
}
`
}
function
getTabImage
()
{
function
getTabImage
()
{
const
currentMethod
=
recordMethods
.
value
[
selectedType
.
value
]
const
currentMethod
=
recordMethods
.
value
[
selectedType
.
value
]
if
(
currentMethod
===
'manual'
)
{
if
(
currentMethod
===
'manual'
)
{
...
@@ -532,6 +676,305 @@ function formatTotalDuration() {
...
@@ -532,6 +676,305 @@ function formatTotalDuration() {
const
seconds
=
totalSeconds
%
60
const
seconds
=
totalSeconds
%
60
return
`
${
hours
.
toString
().
padStart
(
2
,
'0'
)}
:
${
minutes
.
toString
().
padStart
(
2
,
'0'
)}
:
${
seconds
.
toString
().
padStart
(
2
,
'0'
)}
`
return
`
${
hours
.
toString
().
padStart
(
2
,
'0'
)}
:
${
minutes
.
toString
().
padStart
(
2
,
'0'
)}
:
${
seconds
.
toString
().
padStart
(
2
,
'0'
)}
`
}
}
// 辅食选择相关方法
function
toggleFoodSelection
(
item
)
{
// 在编辑模式时不允许选择
if
(
foodSelectionState
.
value
.
isEditMode
)
{
return
}
const
selectedItems
=
feedingData
.
value
.
food
.
selectedItems
const
index
=
selectedItems
.
indexOf
(
item
)
if
(
index
>
-
1
)
{
// 如果已选中,则取消选中
selectedItems
.
splice
(
index
,
1
)
}
else
{
// 如果未选中,则添加选中
selectedItems
.
push
(
item
)
}
}
function
enterEditMode
()
{
foodSelectionState
.
value
.
isEditMode
=
true
}
function
exitEditMode
()
{
foodSelectionState
.
value
.
isEditMode
=
false
}
function
showAddFoodPopup
(
categoryName
)
{
// 在编辑模式下不允许添加
if
(
foodSelectionState
.
value
.
isEditMode
)
{
uni
.
showToast
({
title
:
'编辑模式下不能添加辅食'
,
icon
:
'none'
})
return
}
foodSelectionState
.
value
.
currentCategory
=
categoryName
foodSelectionState
.
value
.
newFoodItem
=
''
foodSelectionState
.
value
.
showAddPopup
=
true
// 打开弹窗
uni
.
showModal
({
title
:
'添加辅食'
,
content
:
'请输入辅食名称(最多10个字)'
,
editable
:
true
,
placeholderText
:
'请输入辅食名称'
,
success
:
(
res
)
=>
{
if
(
res
.
confirm
&&
res
.
content
)
{
addCustomFoodItem
(
categoryName
,
res
.
content
)
}
}
})
}
function
cancelAddFood
()
{
foodSelectionState
.
value
.
showAddPopup
=
false
foodSelectionState
.
value
.
newFoodItem
=
''
}
function
confirmAddFood
()
{
const
itemName
=
foodSelectionState
.
value
.
newFoodItem
.
trim
()
const
categoryName
=
foodSelectionState
.
value
.
currentCategory
if
(
!
itemName
)
{
uni
.
showToast
({
title
:
'请输入辅食名称'
,
icon
:
'none'
})
return
}
addCustomFoodItem
(
categoryName
,
itemName
)
cancelAddFood
()
}
function
addCustomFoodItem
(
categoryName
,
itemName
)
{
// 检查字数限制
if
(
itemName
.
length
>
10
)
{
uni
.
showToast
({
title
:
'辅食名称不能超过10个字'
,
icon
:
'none'
})
return
}
// 检查分类下的自定义辅食数量限制
const
category
=
foodCategories
.
value
[
categoryName
]
if
(
category
.
customItems
.
length
>=
20
)
{
uni
.
showToast
({
title
:
'该分类下最多可添加20个自定义辅食'
,
icon
:
'none'
})
return
}
// 检查是否已存在
if
(
category
.
items
.
includes
(
itemName
)
||
category
.
customItems
.
includes
(
itemName
))
{
uni
.
showToast
({
title
:
'该辅食已存在'
,
icon
:
'none'
})
return
}
// 添加自定义辅食
category
.
customItems
.
push
(
itemName
)
uni
.
showToast
({
title
:
'添加成功'
,
icon
:
'success'
})
}
function
removeFoodItem
(
categoryName
,
itemName
)
{
const
category
=
foodCategories
.
value
[
categoryName
]
// 从默认辅食中移除
const
defaultIndex
=
category
.
items
.
indexOf
(
itemName
)
if
(
defaultIndex
>
-
1
)
{
category
.
items
.
splice
(
defaultIndex
,
1
)
}
// 从自定义辅食中移除
const
customIndex
=
category
.
customItems
.
indexOf
(
itemName
)
if
(
customIndex
>
-
1
)
{
category
.
customItems
.
splice
(
customIndex
,
1
)
}
// 从已选择的辅食中移除
const
selectedIndex
=
feedingData
.
value
.
food
.
selectedItems
.
indexOf
(
itemName
)
if
(
selectedIndex
>
-
1
)
{
feedingData
.
value
.
food
.
selectedItems
.
splice
(
selectedIndex
,
1
)
}
uni
.
showToast
({
title
:
'删除成功'
,
icon
:
'success'
})
}
// 语音识别相关方法
function
checkVoicePermission
()
{
// 检查录音权限
uni
.
getSetting
({
success
:
(
res
)
=>
{
if
(
!
res
.
authSetting
[
'scope.record'
])
{
// 没有录音权限,请求授权
uni
.
authorize
({
scope
:
'scope.record'
,
success
:
()
=>
{
console
.
log
(
'录音权限授权成功'
)
},
fail
:
()
=>
{
// 用户拒绝授权
uni
.
showModal
({
title
:
'需要录音权限'
,
content
:
'为了使用语音识别功能,需要获取录音权限。请在设置中开启录音权限。'
,
confirmText
:
'去设置'
,
cancelText
:
'取消'
,
success
:
(
modalRes
)
=>
{
if
(
modalRes
.
confirm
)
{
// 打开设置页面
uni
.
openSetting
({
success
:
(
settingRes
)
=>
{
if
(
settingRes
.
authSetting
[
'scope.record'
])
{
console
.
log
(
'录音权限已开启'
)
}
}
})
}
}
})
}
})
}
else
{
console
.
log
(
'已有录音权限'
)
}
}
})
}
function
startRecording
()
{
// 再次检查录音权限
uni
.
getSetting
({
success
:
(
res
)
=>
{
if
(
!
res
.
authSetting
[
'scope.record'
])
{
uni
.
showToast
({
title
:
'请先授权录音权限'
,
icon
:
'none'
})
return
}
voiceRecognitionState
.
value
.
isRecording
=
true
voiceRecognitionState
.
value
.
recordingDuration
=
0
// 开始录音
uni
.
startRecord
({
success
:
(
res
)
=>
{
console
.
log
(
'开始录音成功'
,
res
)
},
fail
:
(
err
)
=>
{
console
.
log
(
'开始录音失败'
,
err
)
uni
.
showToast
({
title
:
'录音失败'
,
icon
:
'none'
})
}
})
}
})
}
function
stopRecording
()
{
if
(
!
voiceRecognitionState
.
value
.
isRecording
)
return
voiceRecognitionState
.
value
.
isRecording
=
false
// 停止录音并识别
uni
.
stopRecord
({
success
:
(
res
)
=>
{
console
.
log
(
'录音完成'
,
res
)
// 模拟语音识别结果
simulateVoiceRecognition
()
},
fail
:
(
err
)
=>
{
console
.
log
(
'停止录音失败'
,
err
)
uni
.
showToast
({
title
:
'录音失败'
,
icon
:
'none'
})
}
})
}
function
simulateVoiceRecognition
()
{
// 模拟语音识别结果
const
mockResults
=
{
breastfeeding
:
'早上8点,左边5分钟,右边6分钟'
,
bottle
:
'早上8点,喂奶量90毫升'
,
formula
:
'早上8点,喂奶量120毫升'
,
food
:
'早上8点,吃了米粉和苹果'
}
voiceRecognitionState
.
value
.
recognizedText
=
mockResults
[
selectedType
.
value
]
||
'早上8点,喂奶量90毫升'
voiceRecognitionState
.
value
.
showResultPage
=
true
}
function
getFeedingTypeLabel
()
{
const
typeMap
=
{
breastfeeding
:
'母乳亲喂'
,
bottle
:
'母乳瓶喂'
,
formula
:
'奶粉喂养'
,
food
:
'辅食'
}
return
typeMap
[
selectedType
.
value
]
||
'未知'
}
function
reRecognize
()
{
voiceRecognitionState
.
value
.
showResultPage
=
false
voiceRecognitionState
.
value
.
recognizedText
=
''
}
function
completeVoiceRecord
()
{
const
detailText
=
voiceRecognitionState
.
value
.
recognizedText
.
trim
()
if
(
!
detailText
)
{
uni
.
showToast
({
title
:
'还没有输入喂养信息哦~'
,
icon
:
'none'
})
return
}
// 生成喂养记录
const
record
=
{
time
:
formatCurrentTime
(),
type
:
getFeedingTypeLabel
(),
detail
:
detailText
}
console
.
log
(
'生成喂养记录:'
,
record
)
// 关闭结果页面
voiceRecognitionState
.
value
.
showResultPage
=
false
voiceRecognitionState
.
value
.
recognizedText
=
''
// 显示成功提示
uni
.
showToast
({
title
:
'记录成功'
,
icon
:
'success'
})
// 返回上一页
setTimeout
(()
=>
{
uni
.
navigateBack
()
},
1500
)
}
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
...
@@ -609,6 +1052,12 @@ function formatTotalDuration() {
...
@@ -609,6 +1052,12 @@ function formatTotalDuration() {
margin-right
:
15rpx
;
margin-right
:
15rpx
;
}
}
.time-display
{
display
:
flex
;
align-items
:
center
;
cursor
:
pointer
;
}
.time-value
{
.time-value
{
font-size
:
28rpx
;
font-size
:
28rpx
;
color
:
#1d1e25
;
color
:
#1d1e25
;
...
@@ -963,16 +1412,28 @@ function formatTotalDuration() {
...
@@ -963,16 +1412,28 @@ function formatTotalDuration() {
font-weight
:
bold
;
font-weight
:
bold
;
}
}
.category-actions
{
display
:
flex
;
gap
:
20rpx
;
.complete-btn
{
.complete-btn
{
font-size
:
26rpx
;
font-size
:
26rpx
;
color
:
#C89F6B
;
color
:
#C89F6B
;
cursor
:
pointer
;
}
.delete-btn
{
font-size
:
26rpx
;
color
:
#FF6B6B
;
cursor
:
pointer
;
}
}
}
}
}
.category-list
{
.category-list
{
.category-item
{
.category-item
{
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
flex-start
;
gap
:
20rpx
;
gap
:
20rpx
;
margin-bottom
:
20rpx
;
margin-bottom
:
20rpx
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
...
@@ -981,6 +1442,7 @@ function formatTotalDuration() {
...
@@ -981,6 +1442,7 @@ function formatTotalDuration() {
font-size
:
26rpx
;
font-size
:
26rpx
;
color
:
#333
;
color
:
#333
;
min-width
:
80rpx
;
min-width
:
80rpx
;
margin-top
:
8rpx
;
}
}
.add-btn
{
.add-btn
{
...
@@ -994,12 +1456,22 @@ function formatTotalDuration() {
...
@@ -994,12 +1456,22 @@ function formatTotalDuration() {
justify-content
:
center
;
justify-content
:
center
;
font-size
:
24rpx
;
font-size
:
24rpx
;
font-weight
:
bold
;
font-weight
:
bold
;
cursor
:
pointer
;
margin-top
:
8rpx
;
transition
:
all
0
.3s
ease
;
&
.disabled
{
background
:
#E0E0E0
;
color
:
#999
;
cursor
:
not
-
allowed
;
}
}
}
.selected-tags
{
.selected-tags
{
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
gap
:
10rpx
;
gap
:
10rpx
;
flex
:
1
;
.tag
{
.tag
{
background
:
#FFE4B5
;
background
:
#FFE4B5
;
...
@@ -1007,18 +1479,95 @@ function formatTotalDuration() {
...
@@ -1007,18 +1479,95 @@ function formatTotalDuration() {
padding
:
8rpx
16rpx
;
padding
:
8rpx
16rpx
;
border-radius
:
20rpx
;
border-radius
:
20rpx
;
font-size
:
22rpx
;
font-size
:
22rpx
;
cursor
:
pointer
;
position
:
relative
;
transition
:
all
0
.3s
ease
;
&
.active
{
&
.active
{
background
:
#C89F6B
;
background
:
#C89F6B
;
color
:
white
;
color
:
white
;
}
}
&
.editing
{
padding-right
:
30rpx
;
}
.delete-x
{
position
:
absolute
;
right
:
8rpx
;
top
:
50%
;
transform
:
translateY
(
-50%
);
font-size
:
18rpx
;
color
:
#FF6B6B
;
cursor
:
pointer
;
}
}
}
}
}
}
}
}
.expand-arrow
{
/* ===== 添加辅食弹窗 ===== */
font-size
:
24rpx
;
.add-food-popup
{
background
:
white
;
border-radius
:
20rpx
;
padding
:
40rpx
;
width
:
600rpx
;
.popup-header
{
text-align
:
center
;
margin-bottom
:
30rpx
;
.popup-title
{
font-size
:
32rpx
;
color
:
#333
;
font-weight
:
bold
;
}
}
.popup-content
{
margin-bottom
:
30rpx
;
.input-label
{
display
:
block
;
font-size
:
26rpx
;
color
:
#666
;
color
:
#666
;
margin-bottom
:
15rpx
;
}
}
.food-input
{
width
:
100%
;
padding
:
20rpx
;
border
:
2rpx
solid
#E0E0E0
;
border-radius
:
12rpx
;
font-size
:
28rpx
;
color
:
#333
;
box-sizing
:
border-box
;
}
}
.popup-buttons
{
display
:
flex
;
gap
:
20rpx
;
.cancel-btn
,
.confirm-btn
{
flex
:
1
;
text-align
:
center
;
padding
:
20rpx
;
border-radius
:
12rpx
;
font-size
:
28rpx
;
cursor
:
pointer
;
}
.cancel-btn
{
background
:
#F5F5F5
;
color
:
#666
;
}
.confirm-btn
{
background
:
#C89F6B
;
color
:
white
;
}
}
}
}
}
}
...
@@ -1156,6 +1705,11 @@ function formatTotalDuration() {
...
@@ -1156,6 +1705,11 @@ function formatTotalDuration() {
.microphone-icon
{
.microphone-icon
{
width
:
278rpx
;
width
:
278rpx
;
height
:
278rpx
;
height
:
278rpx
;
&
[
src
*=
"icon_luyining.gif"
]
{
width
:
351rpx
;
height
:
339rpx
;
}
}
}
}
}
...
@@ -1279,4 +1833,119 @@ function formatTotalDuration() {
...
@@ -1279,4 +1833,119 @@ function formatTotalDuration() {
}
}
}
}
}
}
// uni-datetime-picker样式覆盖(与feedingRecord页面保持一致)
::v-deep
.uni-datetime-picker--btn
{
background-color
:
#D4A574
!
important
;
}
::v-deep
.uni-calendar-item--checked
{
background-color
:
#D4A574
!
important
;
}
::v-deep
.uni-datetime-picker-btn-text
{
color
:
#D4A574
!
important
;
}
/* ===== 语音识别结果页面 ===== */
.voice-result-page
{
position
:
fixed
;
top
:
0
;
left
:
0
;
right
:
0
;
bottom
:
0
;
background
:
rgba
(
0
,
0
,
0
,
0
.5
);
z-index
:
1000
;
display
:
flex
;
align-items
:
flex-end
;
justify-content
:
center
;
.result-container
{
background
:
white
;
border-radius
:
20rpx
20rpx
0
0
;
padding
:
40rpx
;
width
:
100%
;
max-height
:
80vh
;
overflow-y
:
auto
;
.result-header
{
text-align
:
center
;
margin-bottom
:
40rpx
;
.result-title
{
font-size
:
36rpx
;
color
:
#333
;
font-weight
:
bold
;
}
}
.feeding-info
{
margin-bottom
:
40rpx
;
.info-item
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
30rpx
;
.info-label
{
font-size
:
28rpx
;
color
:
#666
;
width
:
120rpx
;
flex-shrink
:
0
;
}
.info-value
{
font-size
:
28rpx
;
color
:
#333
;
font-weight
:
500
;
}
.detail-input
{
flex
:
1
;
padding
:
15rpx
;
border
:
2rpx
solid
#E0E0E0
;
border-radius
:
12rpx
;
font-size
:
28rpx
;
color
:
#333
;
background
:
#F8F9FA
;
}
}
}
.result-actions
{
display
:
flex
;
gap
:
20rpx
;
.re-recognize-btn
,
.complete-record-btn
{
flex
:
1
;
text-align
:
center
;
padding
:
25rpx
;
border-radius
:
12rpx
;
font-size
:
28rpx
;
cursor
:
pointer
;
transition
:
all
0
.3s
ease
;
}
.re-recognize-btn
{
background
:
#F5F5F5
;
color
:
#666
;
border
:
2rpx
solid
#E0E0E0
;
&
:active
{
background
:
#E0E0E0
;
}
}
.complete-record-btn
{
background
:
#C89F6B
;
color
:
white
;
&
:active
{
background
:
#B27C1E
;
}
}
}
}
}
</
style
>
</
style
>
\ No newline at end of file
pages/feedingRecord/feedingRecord.vue
View file @
33f625f8
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
<!-- 头部导航 -->
<!-- 头部导航 -->
<view
class=
"header"
>
<view
class=
"header"
>
<view
class=
"nav-left"
>
<view
class=
"nav-left"
>
<image
:src=
"feedingRecordRes.icon_return"
class=
"back-btn"
/>
<image
:src=
"feedingRecordRes.icon_return"
class=
"back-btn"
@
click=
"goBack"
/>
<image
:src=
"feedingRecordRes.icon_star"
class=
"baby-icon-star"
/>
<image
:src=
"feedingRecordRes.icon_star"
class=
"baby-icon-star"
/>
<view
class=
"baby-info"
>
<view
class=
"baby-info"
>
<text
class=
"baby-name"
>
小糖豆3123
</text>
<text
class=
"baby-name"
>
小糖豆3123
</text>
...
@@ -586,6 +586,11 @@ function goToFeedingAnalysis() {
...
@@ -586,6 +586,11 @@ function goToFeedingAnalysis() {
})
})
}
}
// 返回上一页
function
goBack
()
{
uni
.
navigateBack
()
}
function
addFeedingRecord
()
{
function
addFeedingRecord
()
{
if
(
!
currentSelectedDate
.
value
)
{
if
(
!
currentSelectedDate
.
value
)
{
uni
.
showToast
({
uni
.
showToast
({
...
...
static/feedingIndex/v1/icon_luyining.gif
0 → 100644
View file @
33f625f8
261 KB
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