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":{"thumb-button":"../../thumb/thumb-button/index"}}
@@ -0,0 +1,116 @@
<!-- 横屏大字版下 chat tab 容器高度可能不足,输入态面板必须挂到 root-portal,避免被 tab 内容区裁剪。 -->
<root-portal wx:if="{{entering}}">
<view class="chat-edit-drawer-mask" bindtap="handleOutsideTap"></view>
<view class="comment-wrapper comment-wrapper-portal {{isLandscape ? 'comment-wrapper-landscape' : 'comment-wrapper-portrait'}}">
<view class="chat-edit-drawer chat-edit-drawer-open" catchtap="catchNone">
<view class="chat-edit-row">
<view class="chat-edit-shell chat-edit-shell-open chat-edit-shell-active">
<view class="chat-edit-main">
<view wx:if="{{IsSendCommentEnable}}" class="chat-edit-input-wrap">
<textarea
value="{{chatValue}}"
class="chat-edit-input"
id="chat-edit-input"
placeholder-class="chat-edit-input-placeholder-active"
placeholder="{{IsSendCommentEnable === 2 ? '主持人暂未开启互动聊天' : CommentConfig.InputBoxPrompt}}"
maxlength="200"
bindinput="chatInputChange"
bindtap="handleInputTap"
bindfocus="handleInputFocus"
bindblur="bindblur"
bindconfirm="bindconfirm"
bindkeyboardheightchange="handleKeyboardHeightChange"
adjust-position="{{false}}"
confirm-type="send"
focus="{{focusInput}}"
auto-height="{{false}}"
fixed="{{false}}"
cursor-spacing="0"
show-confirm-bar="{{false}}"
disable-default-padding="{{true}}"
disabled="{{IsSendCommentEnable === 2 || InvalidToken}}"
></textarea>
</view>
</view>
<view class="chat-edit-footer">
<view class="chat-edit-counter">{{chatLength}}/200</view>
<view class="chat-edit-footer-actions">
<block wx:if="{{showEmojiEntry}}">
<view wx:if="{{showEmojiPanel}}" class="chat-edit-action-btn" catchtap="handleKeyboardTrigger">
<image class="chat-edit-action-icon" src="{{keyboardTriggerIconDark}}" mode="aspectFit"></image>
</view>
<view wx:else class="chat-edit-action-btn" catchtap="handleEmojiTrigger">
<image class="chat-edit-action-icon" src="{{emojiTriggerIconDark}}" mode="aspectFit"></image>
</view>
</block>
<view wx:if="{{showImageEntry}}" class="chat-edit-action-btn" catchtap="handleChooseImage">
<image class="chat-edit-action-icon" src="{{imageTriggerIconDark}}" mode="aspectFit"></image>
</view>
<view wx:if="{{showCloseEntry}}" class="chat-edit-close-btn" catchtap="handleCloseTap">关闭</view>
<view class="chat-edit-send-btn {{chatLength > 0 ? 'chat-edit-send-btn-active' : 'chat-edit-send-btn-disabled'}}" catchtap="handleSendTap">发送</view>
</view>
</view>
</view>
</view>
<scroll-view wx:if="{{showEmojiPanel}}" class="emoji-panel emoji-panel-active" scroll-y show-scrollbar="{{false}}" style="height: {{ panelHeight + 'px'}};">
<view
wx:for="{{emojiPanelSections}}"
wx:key="key"
wx:for-item="section"
class="emoji-section"
>
<view class="emoji-section-title">{{section.title}}</view>
<view class="emoji-grid">
<view
wx:for="{{section.emojis}}"
wx:key="_emojiIndex_"
wx:for-item="emoji"
class="emoji-item {{section.isDynamic ? 'emoji-item-dynamic' : ''}} {{emoji.EmojiType === 2 ? 'emoji-item-big' : ''}}"
data-set-index="{{emoji._setIndex_}}"
data-emoji-index="{{emoji._emojiIndex_}}"
bindtap="selectEmoji"
>
<image
wx:if="{{emoji.EmojiImage && emoji.EmojiStatus !== 2}}"
src="{{emoji.EmojiImage}}"
mode="aspectFit"
class="emoji-image {{section.isDynamic ? 'emoji-image-dynamic' : ''}} {{emoji.EmojiType === 2 ? 'emoji-image-big' : ''}}"
></image>
</view>
</view>
</view>
</scroll-view>
<view wx:else class="keyboard-spacer" style="height: {{ panelHeight + 'px'}};"></view>
</view>
</view>
</root-portal>
<view wx:else class="comment-wrapper {{isLandscape ? 'comment-wrapper-landscape' : 'comment-wrapper-portrait'}}">
<view class="chat-edit-drawer" catchtap="catchNone">
<view class="chat-edit-row">
<view class="chat-edit-shell">
<view class="chat-edit-main">
<view wx:if="{{IsSendCommentEnable}}" class="chat-edit-input-wrap">
<view
class="chat-edit-input-preview {{chatValue ? '' : 'chat-edit-input-preview-placeholder'}}"
catchtap="handleInputActivatorTap"
>
{{chatValue || (IsSendCommentEnable === 2 ? '主持人暂未开启互动聊天' : CommentConfig.InputBoxPrompt)}}
</view>
</view>
<view class="chat-edit-actions">
<block wx:if="{{showEmojiEntry}}">
<view class="chat-edit-icon" catchtap="handleEmojiTrigger">
<image class="chat-edit-icon-image" src="{{emojiTriggerIcon}}" mode="aspectFit"></image>
</view>
</block>
</view>
</view>
</view>
<view class="chat-edit-toolbar">
<slot></slot>
</view>
</view>
</view>
</view>
@@ -0,0 +1 @@
.comment-wrapper{margin:0 28rpx 10rpx 28rpx;position:relative;z-index:1103}.chat-edit-drawer-mask{position:fixed;inset:0;background:rgba(0,0,0,.24);z-index:1100}.comment-wrapper-landscape,.comment-wrapper-portrait{width:auto}.chat-edit-drawer{width:100%}.chat-edit-drawer-open{position:fixed;left:0;right:0;bottom:0;z-index:1103;background:#fff;padding:18rpx 16rpx 0;box-sizing:border-box;padding-bottom:constant(safe-area-inset-bottom);padding-bottom:env(safe-area-inset-bottom)}.chat-edit-row{display:flex;align-items:flex-end;margin-top:12rpx}.comment-wrapper-large{margin-bottom:16rpx}.chat-edit-shell{flex:1;border-radius:46rpx;background:rgba(0,0,0,.14);padding:0 16rpx 0 28rpx;box-sizing:border-box;overflow:hidden}.chat-edit-shell-open{border-radius:20rpx;background:#f3f4f6;padding:18rpx 18rpx 16rpx}.chat-edit-shell-active{min-height:224rpx;box-shadow:0 -4rpx 18rpx rgba(29,33,41,.06)}.chat-edit-main{display:flex;align-items:center;min-height:72rpx;min-width:0;width:100%;overflow:hidden}.chat-edit-input-wrap{flex:1;min-width:0;position:relative;overflow:hidden}.chat-edit-input{width:100%;height:calc(72rpx + .5 * (1rem - 16px));min-height:calc(72rpx + .5 * (1rem - 16px));min-width:0;color:#fff;font-size:calc(28rpx + .5 * (1rem - 16px));line-height:calc(40rpx + .5 * (1rem - 16px));padding:16rpx 0;box-sizing:border-box;background:0 0;overflow:hidden}.chat-edit-input-preview{display:flex;align-items:center;flex:1;height:calc(72rpx + .5 * (1rem - 16px));min-height:calc(72rpx + .5 * (1rem - 16px));min-width:0;color:#fff;font-size:calc(28rpx + .5 * (1rem - 16px));line-height:calc(40rpx + .5 * (1rem - 16px));padding:16rpx 0;box-sizing:border-box;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.chat-edit-input-preview-placeholder{color:rgba(255,255,255,.72)}.chat-edit-shell-active .chat-edit-main{align-items:flex-start;min-height:112rpx}.chat-edit-shell-active .chat-edit-input{height:calc(112rpx + .5 * (1rem - 16px));min-height:calc(112rpx + .5 * (1rem - 16px));color:#1d2129;font-size:calc(32rpx + .5 * (1rem - 16px));line-height:calc(46rpx + .5 * (1rem - 16px));padding:14rpx 0 0}.chat-edit-input-large{font-size:calc(34rpx + .5 * (1rem - 16px))}.chat-edit-input-placeholder{color:rgba(255,255,255,.72)}.chat-edit-input-placeholder-active{color:#d0d4dc;font-size:calc(32rpx + .5 * (1rem - 16px));line-height:calc(46rpx + .5 * (1rem - 16px))}.chat-edit-actions{display:flex;align-items:center;margin-left:12rpx}.chat-edit-icon{width:56rpx;height:56rpx;display:flex;align-items:center;justify-content:center;margin-left:12rpx}.chat-edit-icon-image{width:calc(40rpx + .5 * (1rem - 16px));height:calc(40rpx + .5 * (1rem - 16px))}.chat-edit-toolbar{display:flex;align-items:flex-end;flex:0 0 auto;margin-left:12rpx}.chat-edit-footer{display:flex;align-items:flex-end;justify-content:space-between;margin-top:14rpx;min-height:60rpx;padding-top:10rpx}.chat-edit-counter{color:#4e5969;font-size:calc(20rpx + .5 * (1rem - 16px));line-height:calc(28rpx + .5 * (1rem - 16px));padding-bottom:10rpx}.chat-edit-footer-actions{display:flex;align-items:flex-end}.chat-edit-action-btn{width:60rpx;height:60rpx;border-radius:30rpx;background:#fff;display:flex;align-items:center;justify-content:center;margin-left:12rpx}.chat-edit-action-icon{width:calc(36rpx + .5 * (1rem - 16px));height:calc(36rpx + .5 * (1rem - 16px))}.chat-edit-close-btn{min-width:112rpx;height:calc(64rpx + .5 * (1rem - 16px));padding:0 28rpx;border-radius:40rpx;margin-left:12rpx;background:rgba(254,44,85,.08);color:#fe2c55;font-size:calc(28rpx + .5 * (1rem - 16px));font-weight:500;display:flex;align-items:center;justify-content:center;box-sizing:border-box}.chat-edit-send-btn{min-width:112rpx;height:calc(64rpx + .5 * (1rem - 16px));padding:0 28rpx;border-radius:40rpx;margin-left:12rpx;background:#f7a3b3;color:#fff;font-size:calc(28rpx + .5 * (1rem - 16px));font-weight:500;display:flex;align-items:center;justify-content:center;box-sizing:border-box}.chat-edit-send-btn-active{background:#ff3158;opacity:1}.chat-edit-send-btn-disabled{opacity:.55}.emoji-panel{margin-top:16rpx;border-radius:0;background:#fff;padding:8rpx 0 12rpx 0;box-sizing:border-box;position:relative;z-index:1102;overflow:hidden;flex:0 0 auto}.emoji-panel-active{margin-top:0}.keyboard-spacer{flex:0 0 auto;width:100%}.emoji-section{padding:8rpx 14rpx 18rpx 14rpx}.emoji-section-title{color:#1d2129;font-size:calc(22rpx + .5 * (1rem - 16px));line-height:calc(32rpx + .5 * (1rem - 16px));margin-bottom:12rpx;font-weight:600}.emoji-grid{display:flex;flex-wrap:wrap;margin:0 -2rpx}.emoji-item{width:12.5%;height:76rpx;display:flex;align-items:center;justify-content:center;margin-bottom:8rpx}.emoji-item-big{width:20%;height:88rpx}.emoji-item-dynamic{width:16.66%;height:92rpx}.emoji-image{width:56rpx;height:56rpx}.emoji-image-big{width:76rpx;height:76rpx}.emoji-image-dynamic{width:76rpx;height:76rpx}
@@ -0,0 +1 @@
"use strict";var CommentType={MuteAll:0,Welcome:2,Mock:3},UploadStatus={Uploading:"uploading",Failed:"failed",Success:"success"};module.exports={CommentType:CommentType,UploadStatus:UploadStatus};
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
"use strict";function _typeof(e){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_typeof(e)}function ownKeys(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,i)}return o}function _objectSpread(e){for(var t=1;t<arguments.length;t++){var o=null!=arguments[t]?arguments[t]:{};t%2?ownKeys(Object(o),!0).forEach(function(t){_defineProperty(e,t,o[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):ownKeys(Object(o)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))})}return e}function _defineProperty(e,t,o){return(t=_toPropertyKey(t))in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function _toPropertyKey(e){var t=_toPrimitive(e,"string");return"symbol"===_typeof(t)?t:String(t)}function _toPrimitive(e,t){if("object"!==_typeof(e)||null===e)return e;var o=e[Symbol.toPrimitive];if(void 0!==o){var i=o.call(e,t||"default");if("object"!==_typeof(i))return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}var _require=require("../../../utils/comment"),EMOJI_TYPE=_require.EMOJI_TYPE,getDisplayName=function(e){return String(e||"").split("|")[0]},buildEmojiPanelSections=function(e){var t=(e||[]).map(function(e,t){return{emojiSet:e,setIndex:t}}),o=t.find(function(e){var t;return 1===(null===(t=e.emojiSet)||void 0===t?void 0:t.IsSystemEmojiSet)}),i=t.filter(function(e){var t;return 1!==(null===(t=e.emojiSet)||void 0===t?void 0:t.IsSystemEmojiSet)}),r=[];if(null!=o&&o.emojiSet){var n=o.emojiSet,u=o.setIndex,s=[],m=[];(n.Emojis||[]).forEach(function(e,t){if(e&&2!==e.EmojiStatus){var o=_objectSpread(_objectSpread({},e),{},{_setIndex_:u,_emojiIndex_:t});String(e.EmojiImage||"").toLowerCase().includes(".gif")?s.push(o):m.push(o)}}),s.length>0&&r.push({key:"system-dynamic-".concat(n.EmojiSetId||0),title:"动态",type:"system-dynamic",isDynamic:!0,emojis:s}),m.length>0&&r.push({key:"system-all-".concat(n.EmojiSetId||0),title:"全部",type:"system-default",emojis:m})}return i.forEach(function(e){var t=e.emojiSet,o=e.setIndex,i=(t.Emojis||[]).map(function(e,t){return _objectSpread(_objectSpread({},e),{},{_setIndex_:o,_emojiIndex_:t})}).filter(function(e){return 2!==(null==e?void 0:e.EmojiStatus)});i.length&&r.push({key:"custom-".concat(t.EmojiSetId||o),title:getDisplayName(t.EmojiSetName),type:t.EmojiSetType===EMOJI_TYPE.BIG?"custom-big":"custom-small",emojis:i})}),r},getImageMimeType=function(e){var t=String(e||"").toLowerCase();return t.endsWith(".png")?"image/png":t.endsWith(".jpg")?"image/jpg":t.endsWith(".jpeg")?"image/jpeg":""},isChooseImageCancelled=function(e){return String((null==e?void 0:e.errMsg)||(null==e?void 0:e.message)||"").toLowerCase().includes("cancel")};module.exports={getDisplayName:getDisplayName,buildEmojiPanelSections:buildEmojiPanelSections,getImageMimeType:getImageMimeType,isChooseImageCancelled:isChooseImageCancelled};
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
@@ -0,0 +1 @@
"use strict";Component({options:{multipleSlots:!0},properties:{sdkInstance:{type:Object,value:null},isLandscape:{type:Boolean,value:!1},externalEditorCloseSignal:{type:Number,value:0}},lifetimes:{detached:function(){"function"==typeof this.unsub&&this.unsub(),this.unsub=null}},observers:{sdkInstance:function(t){t&&(this.store=t.store)}},methods:{commentCheck:function(){this.triggerEvent("commentCheck")},openChatInput:function(){var t,e;null===(t=this.selectComponent("#chatEdit"))||void 0===t||null===(e=t.handleInputActivatorTap)||void 0===e||e.call(t)},previewImage:function(t){this.triggerEvent("previewimage",t.detail)},handleEditorStateChange:function(t){this.triggerEvent("editorstatechange",t.detail)}}});
@@ -0,0 +1 @@
{"component":true,"usingComponents":{"chat-list":"./chat-list/chat-list","chat-edit":"./chat-edit/chat-edit","notification-message":"../notification-message/notification-message"}}
@@ -0,0 +1,23 @@
<view class="volc-chat-wrap {{isLandscape ? 'volc-chat-wrap-landscape' : ''}}">
<slot name="top-area"></slot>
<notification-message sdkInstance="{{sdkInstance}}" isLandscape="{{isLandscape}}" bind:commentCheck="commentCheck" />
<chat-list
class="volc-chat-list"
id="chatList"
sdkInstance="{{sdkInstance}}"
isLandscape="{{isLandscape}}"
chat-list="{{ chatList }}"
bind:previewimage="previewImage"
/>
<chat-edit
id="chatEdit"
class="volc-chat-edit"
sdkInstance="{{sdkInstance}}"
isLandscape="{{isLandscape}}"
externalEditorCloseSignal="{{externalEditorCloseSignal}}"
bind:commentCheck="commentCheck"
bind:editorstatechange="handleEditorStateChange"
>
<slot></slot>
</chat-edit>
</view>
@@ -0,0 +1 @@
.volc-chat-wrap{display:flex;flex-direction:column;position:relative;width:100%;height:100%;font-family:PingFang SC,Helvetica Neue,Helvetica,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Arial,sans-serif}.volc-chat-wrap-landscape{height:calc(100% - env(safe-area-inset-bottom))}.volc-chat-list{height:0;flex:1;position:relative}.volc-chat-edit{width:100%;height:55px;z-index:102}