feat: 电子药箱小程序 - 4Tab药品管理

- 电子药箱: 药品库存管理 + 手动录入 + 今日用药打卡
- 药品百科: 搜索 + 分类筛选 + 20种药品静态数据
- 惠教中心: 4条药品使用指南课程
- 我的: 家庭成员信息管理
- 自定义TabBar + navigation-bar组件
- SVG药品分类插图
This commit is contained in:
陈誉午
2026-07-29 11:20:52 +08:00
commit 994b267f5c
683 changed files with 43849 additions and 0 deletions
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"component":true,"usingComponents":{"mp-icon":"weui-miniprogram/icon/icon","product-message":"../../product-message/product-message"}}
@@ -0,0 +1,449 @@
<template name="rich-text-segments">
<block wx:for="{{segments}}" wx:for-item="segment" wx:key="index">
<text wx:if="{{segment.type === 'text'}}">{{segment.text}}</text>
<image wx:else class="{{emojiClass}} {{segment.isDynamic ? dynamicEmojiClass : ''}}" src="{{segment.src}}" mode="aspectFit"></image>
</block>
</template>
<template name="system-message">
<view id="{{item.Common.MsgId}}" class="chat-list-item-content" style="background-color: {{backgroundColor}}">
<view class="chat-list-user-info chat-list-user-info-other" style="background-color: {{ tagBackgroundColor }};">
{{ item.Extra.User.Nickname || '系统提示' }}
</view>
<view class="chat-list-msg-content" style="color: {{ textColor }}">
<template
is="rich-text-segments"
data="{{segments: item.Extra._richTextSegments_, emojiClass: 'chat-comment-inline-emoji', dynamicEmojiClass: 'chat-comment-inline-emoji-dynamic'}}"
/>
</view>
</view>
</template>
<template name="viewer-level-badge">
<view
wx:if="{{isViewerLevelEnable && item.Extra._levelMeta_}}"
class="chat-list-level-badge {{inlineFlow ? 'chat-list-level-badge-inline-flow' : ''}}"
style="background-color: {{item.Extra._levelMeta_.Color}}"
>
<image
wx:if="{{item.Extra._levelMeta_.LevelIcon}}"
class="chat-list-level-icon"
src="{{item.Extra._levelMeta_.LevelIcon}}"
mode="aspectFit"
></image>
<text class="chat-list-level-name">{{item.Extra._levelMeta_.LevelName}}</text>
</view>
</template>
<template name="comment-image-with-status">
<view class="chat-comment-image-wrapper" style="{{item.Extra._imageStyle_ || ''}}">
<image
class="chat-comment-image"
src="{{item.Extra._previewSrc_}}"
mode="aspectFit"
data-msg-id="{{item.Common.MsgId}}"
data-src="{{item.Extra._previewSrc_}}"
bindload="handleCommentImageLoad"
bindtap="previewCommentImage"
></image>
<view wx:if="{{showStatus && (item.Extra._uploadStatus_ === 'uploading' || item.Extra._uploadStatus_ === 'failed')}}" class="chat-comment-status">
{{item.Extra._uploadStatus_ === 'uploading' ? '上传中...' : '上传失败'}}
</view>
</view>
</template>
<template name="rich-comment-content">
<view class="chat-comment-rich-text chat-comment-inline-flow {{extraClass}}" style="color: {{textColor}}">
<template
is="viewer-level-badge"
data="{{item, isViewerLevelEnable: showViewerBadge ? isViewerLevelEnable : false, inlineFlow: true}}"
/>
<view
wx:if="{{pillText}}"
class="chat-list-user-info chat-list-user-info-other chat-presenter-tag-inline"
style="background-color: {{nicknameBackgroundColor}}"
>
{{ pillText }}
</view>
<text wx:if="{{pillText}}" class="chat-comment-name-inline"> </text>
<block wx:elif="{{hideNickname}}">
</block>
<text
wx:elif="{{usePillNickname && item.Extra.User.Nickname}}"
class="chat-comment-name-inline chat-comment-name-pill"
style="background-color: {{nicknameBackgroundColor}}"
>
{{ item.Extra.User.Nickname }}
</text>
<text wx:if="{{!pillText && !hideNickname && usePillNickname && item.Extra.User.Nickname}}" class="chat-comment-name-inline"> </text>
<block wx:elif="{{!pillText && !hideNickname && item.Extra.User.Nickname}}">
<text class="chat-comment-name-inline">{{ item.Extra.User.Nickname }}</text>
<text class="chat-comment-name-inline"></text>
</block>
<template
is="rich-text-segments"
data="{{segments: item.Extra._richTextSegments_, emojiClass: 'chat-comment-inline-emoji', dynamicEmojiClass: 'chat-comment-inline-emoji-dynamic'}}"
/>
</view>
</template>
<template name="photo-comment-content">
<view class="{{containerClass}}">
<template
is="viewer-level-badge"
data="{{item, isViewerLevelEnable: showViewerBadge ? isViewerLevelEnable : false, inlineFlow: false}}"
/>
<view class="chat-comment-body">
<view
wx:if="{{pillText}}"
class="chat-list-user-info chat-list-user-info-other"
style="background-color: {{nicknameBackgroundColor}}"
>
{{ pillText }}
</view>
<block wx:elif="{{hideNickname}}">
</block>
<view
wx:elif="{{usePillNickname && item.Extra.User.Nickname}}"
class="chat-list-user-info chat-list-user-info-other"
style="background-color: {{nicknameBackgroundColor}}"
>
{{ item.Extra.User.Nickname }}
</view>
<view wx:elif="{{!hideNickname && item.Extra.User.Nickname}}" class="chat-list-user-info" style="color: {{textColor}}">
{{ item.Extra.User.Nickname + '' }}
</view>
<template is="comment-image-with-status" data="{{item, showStatus}}" />
</view>
</view>
</template>
<template name="big-emoji-comment-content">
<view class="{{containerClass}}">
<template
is="viewer-level-badge"
data="{{item, isViewerLevelEnable: showViewerBadge ? isViewerLevelEnable : false, inlineFlow: false}}"
/>
<view class="chat-comment-body">
<view
wx:if="{{pillText}}"
class="chat-list-user-info chat-list-user-info-other"
style="background-color: {{nicknameBackgroundColor}}"
>
{{ pillText }}
</view>
<block wx:elif="{{hideNickname}}">
</block>
<view
wx:elif="{{usePillNickname && item.Extra.User.Nickname}}"
class="chat-list-user-info chat-list-user-info-other"
style="background-color: {{nicknameBackgroundColor}}"
>
{{ item.Extra.User.Nickname }}
</view>
<view wx:elif="{{!hideNickname && item.Extra.User.Nickname}}" class="chat-list-user-info" style="color: {{textColor}}">
{{ item.Extra.User.Nickname + '' }}
</view>
<image class="chat-comment-big-emoji" src="{{item.Extra._emojiImage_}}" mode="aspectFit"></image>
</view>
</view>
</template>
<template name="avatar-comment-content">
<view id="{{item.Common.MsgId}}" class="chat-avatar-comment">
<view class="chat-avatar-box">
<image
class="chat-avatar-image"
src="{{item.Extra._avatarUrl_ && !item.Extra._avatarLoadFailed_ ? item.Extra._avatarUrl_ : item.Extra._avatarDefaultUrl_}}"
mode="aspectFill"
data-msg-id="{{item.Common.MsgId}}"
data-src="{{item.Extra._avatarUrl_}}"
binderror="handleAvatarError"
></image>
</view>
<view class="chat-avatar-main">
<view class="chat-avatar-header">
<view
wx:if="{{pillText}}"
class="chat-list-user-info chat-list-user-info-other"
style="background-color: {{nicknameBackgroundColor}}"
>
{{ pillText }}
</view>
<template
wx:else
is="viewer-level-badge"
data="{{item, isViewerLevelEnable: showViewerBadge ? isViewerLevelEnable : false, inlineFlow: false}}"
/>
<text wx:if="{{!hideNickname && item.Extra.User.Nickname}}" class="chat-avatar-name" style="color: {{textColor}}">{{ item.Extra.User.Nickname }}</text>
</view>
<template
wx:if="{{item.Extra._renderType_ === 'photo'}}"
is="comment-image-with-status"
data="{{item, showStatus: true}}"
/>
<view wx:else class="chat-avatar-bubble" style="background-color: {{backgroundColor}}">
<image
wx:if="{{item.Extra._renderType_ === 'big-emoji'}}"
class="chat-comment-big-emoji"
src="{{item.Extra._emojiImage_}}"
mode="aspectFit"
></image>
<view wx:else class="chat-comment-rich-text" style="color: {{textColor}}">
<template
is="rich-text-segments"
data="{{segments: item.Extra._richTextSegments_, emojiClass: 'chat-comment-inline-emoji', dynamicEmojiClass: 'chat-comment-inline-emoji-dynamic'}}"
/>
</view>
</view>
</view>
</view>
</template>
<template name="auto-reply-card-content">
<view
class="chat-list-auto-reply-card {{avatarMode ? 'chat-list-auto-reply-card-avatar' : 'chat-list-item-content'}}"
style="background-color: {{backgroundColor}}"
bindtap="onAutoReplyTap"
data-item="{{item}}"
>
<view
wx:if="{{item.Extra._autoReplyDisplay_.hasAnchorPreview}}"
class="chat-list-auto-reply-preview {{avatarMode ? 'chat-list-auto-reply-preview-avatar' : ''}}"
style="color: {{ textColor }}"
>
<text class="chat-list-auto-reply-preview-text">{{ item.Extra._autoReplyDisplay_.anchorPreviewText }}</text>
</view>
<view
wx:if="{{item.Extra._autoReplyDisplay_.hasAnchorPreview}}"
class="chat-list-auto-reply-divider {{avatarMode ? 'chat-list-auto-reply-divider-avatar' : ''}}"
></view>
<view class="chat-list-auto-reply-footer {{avatarMode ? 'chat-list-auto-reply-footer-avatar' : ''}}">
<view wx:if="{{!avatarMode}}" class="chat-list-auto-reply-icon" aria-hidden="true">
<image
wx:if="{{item.Extra._autoReplyDisplay_.robotAvatar}}"
class="chat-list-auto-reply-icon-img"
src="{{item.Extra._autoReplyDisplay_.robotAvatar}}"
mode="aspectFill"
></image>
<view wx:else class="chat-list-auto-reply-icon-fallback"></view>
</view>
<text class="chat-list-auto-reply-status {{avatarMode ? 'chat-list-auto-reply-status-avatar' : ''}}">{{ avatarMode ? item.Extra._autoReplyDisplay_.avatarStatusText : item.Extra._autoReplyDisplay_.statusText }}</text>
<text class="chat-list-auto-reply-action {{avatarMode ? 'chat-list-auto-reply-action-avatar' : ''}}">查看 ></text>
</view>
</view>
</template>
<template name="avatar-auto-reply-content">
<view id="{{item.Common.MsgId}}" class="chat-avatar-comment chat-avatar-auto-reply">
<view class="chat-avatar-box">
<image
class="chat-avatar-image"
src="{{item.Extra._avatarUrl_ && !item.Extra._avatarLoadFailed_ ? item.Extra._avatarUrl_ : item.Extra._avatarDefaultUrl_}}"
mode="aspectFill"
data-msg-id="{{item.Common.MsgId}}"
data-src="{{item.Extra._avatarUrl_}}"
binderror="handleAvatarError"
></image>
</view>
<view class="chat-avatar-main chat-avatar-auto-reply-main">
<view class="chat-avatar-header">
<text class="chat-avatar-name chat-avatar-auto-reply-name" style="color: {{textColor}}">{{ item.Extra._autoReplyDisplay_.robotNameDisplay }}</text>
</view>
<template
is="auto-reply-card-content"
data="{{item, backgroundColor, textColor, avatarMode: true}}"
/>
</view>
</view>
</template>
<view class="chat-list-wrapper">
<view wx:if="{{topComment.length}}" class="chat-top-comment">
<view class="chat-top-comment-content">
<view class="chat-top-comment-main">
<view wx:if="{{topComment[0].Extra.User.Nickname}}" class="chat-top-comment-header">
<view class="chat-top-comment-accent"></view>
<text class="chat-top-comment-name">{{ topComment[0].Extra.User.Nickname }}</text>
</view>
<block wx:if="{{topComment[0].Extra._renderType_ === 'photo'}}">
<view class="chat-top-comment-media">
<template is="comment-image-with-status" data="{{item: topComment[0], showStatus: false}}" />
</view>
</block>
<block wx:elif="{{topComment[0].Extra._renderType_ === 'big-emoji'}}">
<view class="chat-top-comment-media">
<image class="chat-comment-big-emoji" src="{{topComment[0].Extra._emojiImage_}}" mode="aspectFit"></image>
</view>
</block>
<view wx:else class="chat-top-comment-rich-text-expandable" bindtap="handleTopCommentTap">
<view class="chat-top-comment-rich-text">
<template
is="rich-text-segments"
data="{{segments: topComment[0].Extra._richTextSegments_, emojiClass: 'chat-comment-inline-emoji', dynamicEmojiClass: 'chat-comment-inline-emoji-dynamic'}}"
/>
</view>
</view>
</view>
</view>
</view>
<scroll-view id="chat-scrollview" reverse="{{true}}" enable-flex="{{true}}" class="chat-scroll-view" scroll-y bindscrolltoupper="getHistoryList" scroll-into-view="{{scrollIntoViewId}}" bindscroll="bindscroll">
<view id="chat-list-list" class="chat-list-list" style="font-size: {{_chatFontSizeStyle}}">
<view style="color: {{ FontColor }}" class="chat-list-history" wx:if="{{ !hasHistory && filterListCount > 25 }}">
<view class="chat-list-history-content">没有更多了</view>
</view>
<view
id="view{{item.Common.MsgId}}"
class="chat-list-item"
hidden="{{item.Extra._systemNoticeInfo_ && !systemNoticeVisible}}"
wx:for="{{ chatList }}"
wx:for-index="index"
wx:for-item="item"
wx:key="id"
>
<!-- 欢迎语 -->
<template
wx:if="{{item.Extra._welcomeInfo_}}"
is="system-message"
data="{{item, backgroundColor: MobileChatBackgroundColor, tagBackgroundColor: PresenterTagBackgroundColor, textColor: FontColor}}"
/>
<template
wx:elif="{{item.Extra._operationInfo_ && item.Extra._operationInfo_.IsSendCommentEnable !== undefined}}"
is="system-message"
data="{{item, backgroundColor: MobileChatBackgroundColor, tagBackgroundColor: PresenterTagBackgroundColor, textColor: FontColor}}"
/>
<!-- 智能助理:头像模式对齐 Web ChatItemAvatar,非头像模式对齐 Web MobileAutoReplyMessage inline -->
<template
wx:elif="{{item.Extra._autoReplyInfo_.IsAutoReply && showAvatar}}"
is="avatar-auto-reply-content"
data="{{item, backgroundColor: MobileChatBackgroundColor, textColor: FontColor}}"
/>
<template
wx:elif="{{item.Extra._autoReplyInfo_.IsAutoReply}}"
is="auto-reply-card-content"
data="{{item, backgroundColor: MobileChatBackgroundColor, textColor: FontColor, avatarMode: false}}"
/>
<template
wx:elif="{{item.Extra.IsPresenter && showAvatar}}"
is="avatar-comment-content"
data="{{item, backgroundColor: MobileChatBackgroundColor, textColor: PresenterChatColor, showViewerBadge: false, isViewerLevelEnable: false, pillText: item.Extra._presenterNickname_, nicknameBackgroundColor: PresenterTagBackgroundColor, hideNickname: true}}"
/>
<!-- 主持人 -->
<view id="{{item.Common.MsgId}}" wx:elif="{{item.Extra.IsPresenter}}" class="chat-list-item-content" style="background-color: {{MobileChatBackgroundColor}}">
<template
wx:if="{{item.Extra._renderType_ === 'photo'}}"
is="photo-comment-content"
data="{{item, containerClass: '', usePillNickname: false, pillText: item.Extra._presenterNickname_, nicknameBackgroundColor: PresenterTagBackgroundColor, textColor: PresenterChatColor, showViewerBadge: false, isViewerLevelEnable: false, showStatus: true}}"
/>
<template
wx:elif="{{item.Extra._renderType_ === 'big-emoji'}}"
is="big-emoji-comment-content"
data="{{item, containerClass: '', usePillNickname: false, pillText: item.Extra._presenterNickname_, nicknameBackgroundColor: PresenterTagBackgroundColor, textColor: PresenterChatColor, showViewerBadge: false, isViewerLevelEnable: false}}"
/>
<template
wx:else
is="rich-comment-content"
data="{{item, textColor: PresenterChatColor, usePillNickname: false, pillText: item.Extra._presenterNickname_, nicknameBackgroundColor: PresenterTagBackgroundColor, showViewerBadge: false, isViewerLevelEnable: false, extraClass: ''}}"
/>
</view>
<!-- 观众 -->
<template
wx:elif="{{showAvatar}}"
is="avatar-comment-content"
data="{{item, backgroundColor: MobileChatBackgroundColor, textColor: FontColor, showViewerBadge: true, isViewerLevelEnable}}"
/>
<view id="{{item.Common.MsgId}}" wx:else class="chat-list-item-content" style="background-color: {{MobileChatBackgroundColor}}">
<template
wx:if="{{item.Extra._renderType_ === 'photo'}}"
is="photo-comment-content"
data="{{item, containerClass: 'chat-comment-main', usePillNickname: false, nicknameBackgroundColor: '', textColor: FontColor, showViewerBadge: true, isViewerLevelEnable, showStatus: true}}"
/>
<template
wx:elif="{{item.Extra._renderType_ === 'big-emoji'}}"
is="big-emoji-comment-content"
data="{{item, containerClass: 'chat-comment-main', usePillNickname: false, nicknameBackgroundColor: '', textColor: FontColor, showViewerBadge: true, isViewerLevelEnable}}"
/>
<template
wx:else
is="rich-comment-content"
data="{{item, textColor: FontColor, usePillNickname: false, nicknameBackgroundColor: '', showViewerBadge: true, isViewerLevelEnable, extraClass: ''}}"
/>
</view>
</view>
</view>
</scroll-view>
<product-message sdkInstance="{{sdkInstance}}" />
</view>
<!-- 智能助理底部横幅:白底双行 + 固定色「查看」,对齐设计稿 AutoReplyNotice -->
<view
wx:if="{{autoReplyNoticeVisible && autoReplyNoticeItem}}"
class="chat-auto-reply-notice"
catchtap="catchNone"
>
<view class="chat-auto-reply-notice-icon" aria-hidden="true">
<image
wx:if="{{autoReplyNoticeItem.Extra._autoReplyDisplay_.robotAvatar}}"
class="chat-auto-reply-notice-icon-img"
src="{{autoReplyNoticeItem.Extra._autoReplyDisplay_.robotAvatar}}"
mode="aspectFill"
></image>
<view wx:else class="chat-auto-reply-notice-icon-fallback"></view>
</view>
<view class="chat-auto-reply-notice-content">
<text class="chat-auto-reply-notice-title">{{ autoReplyNoticeItem.Extra._autoReplyDisplay_.noticeTitle }}</text>
<text class="chat-auto-reply-notice-desc">{{ autoReplyNoticeItem.Extra._autoReplyDisplay_.noticeDesc }}</text>
</view>
<view class="chat-auto-reply-notice-actions">
<text class="chat-auto-reply-notice-action" catchtap="onAutoReplyNoticeView">查看</text>
<text class="chat-auto-reply-notice-close" catchtap="closeAutoReplyNotice">×</text>
</view>
</view>
<view class="chat-more-notice" wx:if="{{newCount && isShowMoreNote}}" bind:tap="newCountClick">
<image src="../../../assets/arrowDoubleDown.svg" class="chat-more-notice-icon" />
{{newCount}} 条消息
</view>
<view wx:if="{{autoReplyPanelVisible && autoReplyPanelItem}}" class="chat-auto-reply-panel-mask" bindtap="closeAutoReplyPanel"></view>
<view wx:if="{{autoReplyPanelVisible && autoReplyPanelItem}}" class="chat-auto-reply-panel-sheet" catchtap="catchNone">
<view class="chat-auto-reply-panel">
<view class="chat-auto-reply-panel-header">
<view class="chat-auto-reply-panel-title">
{{ autoReplyPanelItem.Extra._autoReplyDisplay_.panelHeaderTitle }}
</view>
<view class="chat-auto-reply-panel-close" bindtap="closeAutoReplyPanel">
<mp-icon icon="close" size="{{16}}" color="#1d2129"></mp-icon>
</view>
</view>
<view class="chat-auto-reply-panel-card">
<view class="chat-auto-reply-panel-question">
{{ autoReplyPanelItem.Extra._autoReplyDisplay_.panelQuestion }}
</view>
<view class="chat-auto-reply-panel-answer">
<template
is="rich-text-segments"
data="{{segments: autoReplyPanelItem.Extra._richTextSegments_, emojiClass: 'chat-comment-inline-emoji', dynamicEmojiClass: 'chat-comment-inline-emoji-dynamic'}}"
/>
</view>
</view>
<view class="chat-auto-reply-panel-confirm" bindtap="closeAutoReplyPanel">我知道了</view>
</view>
</view>
<view wx:if="{{topCommentPopupVisible && topComment.length}}" class="chat-top-comment-drawer-mask" bindtap="closeTopCommentPopup"></view>
<view wx:if="{{topCommentPopupVisible && topComment.length}}" class="chat-top-comment-drawer-sheet" catchtap="catchNone">
<view class="chat-top-comment-drawer">
<view class="chat-top-comment-drawer-header">
<view class="chat-top-comment-drawer-title">置顶内容</view>
<view class="chat-top-comment-drawer-close" bindtap="closeTopCommentPopup">
<mp-icon icon="close" size="{{16}}" color="#1d2129"></mp-icon>
</view>
</view>
<view class="chat-top-comment-drawer-body">
<view class="chat-top-comment-drawer-name">{{topComment[0].Extra.User.Nickname}}</view>
<view class="chat-top-comment-drawer-content">
<template
is="rich-text-segments"
data="{{segments: topComment[0].Extra._richTextSegments_, emojiClass: 'chat-top-comment-drawer-emoji', dynamicEmojiClass: 'chat-top-comment-drawer-emoji-dynamic'}}"
/>
</view>
</view>
</view>
</view>
File diff suppressed because one or more lines are too long