feat: 电子药箱小程序 - 4Tab药品管理
- 电子药箱: 药品库存管理 + 手动录入 + 今日用药打卡 - 药品百科: 搜索 + 分类筛选 + 20种药品静态数据 - 惠教中心: 4条药品使用指南课程 - 我的: 家庭成员信息管理 - 自定义TabBar + navigation-bar组件 - SVG药品分类插图
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"popup": "/components/popup/popup"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/* components/banner/banner.wxss */
|
||||
.banner-wrap {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 7%;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
z-index: 9999;
|
||||
padding: 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.banner__avatar {
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.banner__card {
|
||||
border-radius: 25rpx;
|
||||
padding: 18rpx;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
margin-bottom: 20rpx;
|
||||
display: inline-block;
|
||||
}
|
||||
.banner__icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
.banner__avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
.banner-id {
|
||||
font-size: 21rpx;
|
||||
}
|
||||
.banner-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.banner__title {
|
||||
max-width: 250rpx;
|
||||
}
|
||||
.text-overflow {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.banner__count {
|
||||
color: #fff;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
.popup-text {
|
||||
color: #000;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.report-options {
|
||||
width: 100%;
|
||||
margin: 24rpx 0;
|
||||
}
|
||||
|
||||
.options-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.option-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.option-item radio {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
.other-input {
|
||||
margin: 24rpx 0;
|
||||
}
|
||||
|
||||
.other-input-field {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
padding: 0 20rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.upload-title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.image-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
position: relative;
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.uploaded-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.delete-image {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
right: -8rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.upload-btn {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border: 2rpx dashed #ddd;
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 48rpx;
|
||||
color: #999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.description-section {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.description-input {
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
padding: 16rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
text-align: right;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: #ff4d4f;
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
border-radius: 44rpx;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.submit-btn:active {
|
||||
background: #ff7875;
|
||||
}
|
||||
|
||||
.submit-btn[disabled] {
|
||||
background: #ccc;
|
||||
color: #999;
|
||||
opacity: 0.7;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
import { request } from "../../utils/request";
|
||||
|
||||
// components/banner/banner.ts
|
||||
interface BannerData {
|
||||
avatarUrl: string;
|
||||
nickName: string;
|
||||
userId: string;
|
||||
showPopup: boolean;
|
||||
reportOptions: Array<{
|
||||
id: number;
|
||||
text: string;
|
||||
checked: boolean;
|
||||
}>;
|
||||
showOtherInput: boolean;
|
||||
otherReason: string;
|
||||
uploadedImages: string[];
|
||||
description: string;
|
||||
state: number;
|
||||
isSubmitEnabled: boolean;
|
||||
}
|
||||
|
||||
interface BannerProperties {
|
||||
roomInfo: {
|
||||
iconUrl?: string;
|
||||
name?: string;
|
||||
mumId?: string;
|
||||
state: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface BannerComponent extends WechatMiniprogram.Component.TrivialInstance {
|
||||
data: BannerData;
|
||||
properties: BannerProperties;
|
||||
}
|
||||
|
||||
const state: Record<number, string> = {
|
||||
1: "直播中",
|
||||
2: "直播结束",
|
||||
};
|
||||
Component({
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
properties: {
|
||||
roomInfo: {
|
||||
type: Object,
|
||||
value: {
|
||||
iconUrl: "",
|
||||
nickname: "",
|
||||
mumId: "",
|
||||
state: null
|
||||
},
|
||||
observer: function (
|
||||
this: BannerComponent,
|
||||
newVal: BannerProperties["roomInfo"]
|
||||
) {
|
||||
this.updateRoomInfo(newVal);
|
||||
},
|
||||
},
|
||||
watchNum:{
|
||||
type:Number,
|
||||
data:0
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的初始数据
|
||||
*/
|
||||
data: {
|
||||
avatarUrl: "",
|
||||
nickName: "",
|
||||
userId: "",
|
||||
state: "",
|
||||
showPopup: false,
|
||||
reportOptions: [
|
||||
{ id: 1, text: "价格虚假", checked: false },
|
||||
{ id: 2, text: "品牌虚假", checked: false },
|
||||
{ id: 3, text: "色情低俗", checked: false },
|
||||
{ id: 4, text: "虚假活动", checked: false },
|
||||
{ id: 5, text: "不良行为", checked: false },
|
||||
{ id: 6, text: "商品信息虚假", checked: false },
|
||||
{ id: 7, text: "出售违禁品", checked: false },
|
||||
{ id: 8, text: "其他", checked: false },
|
||||
],
|
||||
showOtherInput: false,
|
||||
otherReason: "",
|
||||
uploadedImages: [],
|
||||
description: "",
|
||||
isSubmitEnabled: false,
|
||||
},
|
||||
lifetimes: {
|
||||
attached(this: BannerComponent) {
|
||||
this.updateRoomInfo(this.properties.roomInfo);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
updateRoomInfo(
|
||||
this: BannerComponent,
|
||||
roomInfo?: BannerProperties["roomInfo"]
|
||||
) {
|
||||
const info = roomInfo || this.properties.roomInfo;
|
||||
this.setData({
|
||||
avatarUrl: info?.iconUrl || "",
|
||||
nickName: info?.name || "",
|
||||
userId: info?.mumId || "",
|
||||
state: state[info?.state ?? 2],
|
||||
});
|
||||
},
|
||||
handOpen(this: WechatMiniprogram.Component.TrivialInstance) {
|
||||
this.setData({
|
||||
showPopup: true,
|
||||
isSubmitEnabled: false,
|
||||
});
|
||||
},
|
||||
closePopup(this: WechatMiniprogram.Component.TrivialInstance) {
|
||||
this.setData({
|
||||
showPopup: false,
|
||||
});
|
||||
},
|
||||
|
||||
onOptionChange(this: WechatMiniprogram.Component.TrivialInstance, e: any) {
|
||||
const { value } = e.detail;
|
||||
const reportOptions = this.data.reportOptions.map((item: any) => ({
|
||||
...item,
|
||||
checked: value.includes(item.id.toString()),
|
||||
}));
|
||||
|
||||
const showOtherInput =
|
||||
reportOptions.find((item: any) => item.id === 8)?.checked || false;
|
||||
|
||||
this.setData({
|
||||
reportOptions,
|
||||
showOtherInput,
|
||||
}, () => {
|
||||
this.updateSubmitEnabled();
|
||||
});
|
||||
},
|
||||
|
||||
onOtherInput(this: WechatMiniprogram.Component.TrivialInstance, e: any) {
|
||||
this.setData({
|
||||
otherReason: e.detail.value,
|
||||
}, () => {
|
||||
this.updateSubmitEnabled();
|
||||
});
|
||||
},
|
||||
deleteImage(
|
||||
this: WechatMiniprogram.Component.TrivialInstance,
|
||||
e: WechatMiniprogram.TouchEvent
|
||||
) {
|
||||
const { index } = e.currentTarget.dataset;
|
||||
wx.showModal({
|
||||
title: "提示",
|
||||
content: "确定要删除这张图片吗?",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const uploadedImages = [...this.data.uploadedImages];
|
||||
uploadedImages.splice(index, 1);
|
||||
this.setData({ uploadedImages });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
chooseImage(this: WechatMiniprogram.Component.TrivialInstance) {
|
||||
wx.chooseImage({
|
||||
count: 3 - this.data.uploadedImages.length,
|
||||
success: (res) => {
|
||||
this.setData({
|
||||
uploadedImages: [...this.data.uploadedImages, ...res.tempFilePaths],
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
previewImage(this: WechatMiniprogram.Component.TrivialInstance, e: any) {
|
||||
const { src } = e.currentTarget.dataset;
|
||||
wx.previewImage({
|
||||
current: src,
|
||||
urls: this.data.uploadedImages,
|
||||
});
|
||||
},
|
||||
|
||||
onDescriptionInput(
|
||||
this: WechatMiniprogram.Component.TrivialInstance,
|
||||
e: any
|
||||
) {
|
||||
this.setData({
|
||||
description: e.detail.value,
|
||||
}, () => {
|
||||
this.updateSubmitEnabled();
|
||||
});
|
||||
},
|
||||
|
||||
updateSubmitEnabled(this: BannerComponent) {
|
||||
const selectedOptions = this.data.reportOptions.filter(
|
||||
(item: any) => item.checked
|
||||
);
|
||||
const hasSelectedOptions = selectedOptions.length > 0;
|
||||
|
||||
const isOtherSelected = selectedOptions.some((item: any) => item.id === 8);
|
||||
const otherReasonValid = !isOtherSelected || (this.data.otherReason && this.data.otherReason.trim().length > 0);
|
||||
|
||||
const hasDescription = this.data.description && this.data.description.trim().length > 0;
|
||||
|
||||
this.setData({
|
||||
isSubmitEnabled: hasSelectedOptions && otherReasonValid && hasDescription,
|
||||
});
|
||||
},
|
||||
|
||||
async submitReport(this: BannerComponent) {
|
||||
if (!this.data.isSubmitEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedOptions = this.data.reportOptions.filter(
|
||||
(item: any) => item.checked
|
||||
);
|
||||
if (selectedOptions.length === 0) {
|
||||
wx.showToast({
|
||||
title: "请选择举报原因",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
selectedOptions.some((item: any) => item.id === 8) &&
|
||||
!this.data.otherReason
|
||||
) {
|
||||
wx.showToast({
|
||||
title: "请填写其他举报原因",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 提交举报信息
|
||||
const reportData = {
|
||||
reasons: selectedOptions,
|
||||
otherReason: this.data.otherReason,
|
||||
images: this.data.uploadedImages,
|
||||
description: this.data.description,
|
||||
userId: this.data.userId,
|
||||
roomId: wx.getStorageSync("joinedGroupId"),
|
||||
};
|
||||
console.log("举报信息:", reportData);
|
||||
|
||||
try {
|
||||
await request({
|
||||
options: {
|
||||
url: `/app/video-live/feedback`,
|
||||
data: reportData,
|
||||
},
|
||||
});
|
||||
|
||||
wx.showToast({
|
||||
title: "举报成功",
|
||||
icon: "success",
|
||||
});
|
||||
|
||||
// 提交成功后关闭弹窗
|
||||
this.setData({
|
||||
showPopup: false,
|
||||
reportOptions: this.data.reportOptions.map((item: any) => ({
|
||||
...item,
|
||||
checked: false,
|
||||
})),
|
||||
showOtherInput: false,
|
||||
otherReason: "",
|
||||
uploadedImages: [],
|
||||
description: "",
|
||||
isSubmitEnabled: false,
|
||||
});
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
<!--components/banner/banner.wxml-->
|
||||
<view class="banner-wrap">
|
||||
<view>
|
||||
<view class="banner__card banner-box">
|
||||
<image class="banner__avatar" src="{{avatarUrl}}" mode="aspectFill" />
|
||||
<view>
|
||||
<view class="banner__title text-overflow">{{nickName}}</view>
|
||||
<view class="banner-box banner__count">
|
||||
<image class="banner__icon" src="https://img.alicdn.com/imgextra/i1/O1CN01GC0HVZ1kWz8bvu0KX_!!6000000004692-2-tps-36-36.png" mode="widthFix" />
|
||||
<text>{{watchNum}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="banner__card">{{state}}</view>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<view class="banner__card banner-box" bind:tap="handOpen">
|
||||
<image class="banner__icon" src="/assets/img/p.svg" mode="widthFix"/>
|
||||
<text>投诉</text>
|
||||
</view>
|
||||
<view class="banner-id">用户ID:{{userId}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<popup visible="{{showPopup}}" bind:close="closePopup">
|
||||
<view slot="header" class="popup-header">
|
||||
<text>举报本场直播</text>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="popup-content">
|
||||
<radio-group class="report-options" bindchange="onOptionChange">
|
||||
<view class="options-row">
|
||||
<view class="option-item" wx:for="{{reportOptions}}" wx:for-item="item" wx:key="id">
|
||||
<radio value="{{item.id}}" checked="{{item.checked}}"/>
|
||||
<text class="popup-text">{{item.text}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</radio-group>
|
||||
|
||||
<view class="other-input" wx:if="{{showOtherInput}}">
|
||||
<input
|
||||
class="other-input-field popup-text"
|
||||
type="text"
|
||||
placeholder="请输入其他举报原因"
|
||||
value="{{otherReason}}"
|
||||
bindinput="onOtherInput"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="upload-section">
|
||||
<view class="upload-title popup-text">上传图片证据(最多3张)</view>
|
||||
<view class="image-list">
|
||||
<block wx:for="{{uploadedImages}}" wx:key="*this">
|
||||
<view class="image-wrapper">
|
||||
<image class="uploaded-image" src="{{item}}" mode="aspectFill" bindtap="previewImage" data-src="{{item}}"/>
|
||||
<view class="delete-image" catchtap="deleteImage" data-index="{{index}}">×</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="upload-btn" wx:if="{{uploadedImages.length < 3}}" bindtap="chooseImage">
|
||||
<view class="upload-icon">+</view>
|
||||
<text class="upload-text">上传图片</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="description-section">
|
||||
<textarea
|
||||
class="description-input popup-text"
|
||||
placeholder="请补充说明(选填)"
|
||||
value="{{description}}"
|
||||
bindinput="onDescriptionInput"
|
||||
maxlength="200"
|
||||
/>
|
||||
<text class="char-count popup-text">{{description.length}}/200</text>
|
||||
</view>
|
||||
|
||||
<view class="submit-section">
|
||||
<button class="submit-btn" disabled="{{!isSubmitEnabled}}" bindtap="submitReport">提交举报</button>
|
||||
</view>
|
||||
</view>
|
||||
</popup>
|
||||
Reference in New Issue
Block a user