初始化上传医疗项目到 medical-mall

This commit is contained in:
2026-04-10 09:03:21 +08:00
parent ca8794ea3a
commit ce124e7119
421 changed files with 15139 additions and 2363 deletions

View File

@@ -1,4 +1,4 @@
<!-- 商家端 - 库存管理页面 -->
<!-- 机构端 - 器械库存管理页面 -->
<template>
<view class="inventory-page">
<!-- #ifdef MP-WEIXIN -->
@@ -9,14 +9,21 @@
</view>
</view>
<!-- #endif -->
<view class="filter-tabs">
<view class="filter-tab" :class="{ active: currentFilter === 'all' }" @click="switchFilter('all')">全部</view>
<view class="filter-tab" :class="{ active: currentFilter === 'low' }" @click="switchFilter('low')">器械预警</view>
<view class="filter-tab" :class="{ active: currentFilter === 'out' }" @click="switchFilter('out')">已售罄</view>
</view>
<view class="stats-bar">
<view class="stat-item">
<text class="stat-value">{{ stats.totalProducts }}</text>
<text class="stat-label">商品总数</text>
<text class="stat-label">在售服务项目数</text>
</view>
<view class="stat-item warning">
<text class="stat-value">{{ stats.lowStock }}</text>
<text class="stat-label">库存预警</text>
<text class="stat-label">器械预警</text>
</view>
<view class="stat-item">
<text class="stat-value">{{ stats.outOfStock }}</text>
@@ -24,15 +31,11 @@
</view>
</view>
<view class="filter-tabs">
<view class="filter-tab" :class="{ active: currentFilter === 'all' }" @click="switchFilter('all')">全部</view>
<view class="filter-tab" :class="{ active: currentFilter === 'low' }" @click="switchFilter('low')">库存预警</view>
<view class="filter-tab" :class="{ active: currentFilter === 'out' }" @click="switchFilter('out')">已售罄</view>
</view>
<scroll-view class="inventory-list" scroll-y :refresher-enabled="true" :refresher-triggered="refreshing" @refresherrefresh="onRefresh" @scrolltolower="loadMore">
<view v-if="loading && products.length === 0" class="loading-container"><text class="loading-text">加载中...</text></view>
<view v-else-if="products.length === 0" class="empty-container"><text class="empty-icon">📊</text><text class="empty-text">暂无商品</text></view>
<view v-else-if="products.length === 0" class="empty-container"><text class="empty-icon">📊</text><text class="empty-text">暂无服务项目</text></view>
<view v-else>
<view v-for="product in products" :key="product.id" class="product-card">
<image :src="product.main_image_url || '/static/images/default-product.png'" class="product-image" mode="aspectFill"/>
@@ -43,7 +46,7 @@
<text class="stock-value" :class="getStockClass(product.total_stock)">{{ product.total_stock }}</text>
</view>
<view class="warning-info" v-if="product.total_stock <= (product.warning_stock || 10)">
<text class="warning-text">库存不足</text>
<text class="warning-text">库存/名额不足</text>
</view>
</view>
<view class="product-actions">
@@ -58,7 +61,7 @@
<view class="modal-header"><text class="modal-title">调整库存</text><text class="modal-close" @click="closeStockModal">×</text></view>
<view class="modal-body">
<view class="form-item">
<text class="label">商品</text>
<text class="label">服务项目</text>
<text class="value">{{ currentProduct?.name }}</text>
</view>
<view class="form-item">
@@ -166,7 +169,7 @@
const response = await query.execute()
if (response.error != null) {
console.error('加载商品失败:', response.error)
console.error('加载服务项目失败:', response.error)
return
}
@@ -314,24 +317,33 @@
<style>
.inventory-page { background-color: #f5f5f5; min-height: 100vh; }
.stats-bar { display: flex; background-color: #fff; padding: 30rpx 20rpx; margin-bottom: 20rpx; }
.stats-bar { display: flex;
flex-direction: row;
background-color: #fff; padding: 30rpx 20rpx; margin-bottom: 20rpx;
}
.stat-item { flex: 1; text-align: center; }
.stat-value { font-size: 40rpx; font-weight: bold; color: #333; display: block; }
.stat-item.warning .stat-value { color: #FF9800; }
.stat-label { font-size: 24rpx; color: #999; }
.filter-tabs { display: flex; background-color: #fff; padding: 0 20rpx; margin-bottom: 20rpx; }
.filter-tabs {
display: flex;
flex-direction: row;
background-color: #fff;
padding: 0 20rpx;
margin-bottom: 20rpx;
}
.filter-tab { flex: 1; text-align: center; padding: 24rpx 0; font-size: 26rpx; color: #666; position: relative; }
.filter-tab.active { color: #007AFF; font-weight: bold; }
.filter-tab.active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 40rpx; height: 4rpx; background-color: #007AFF; border-radius: 2rpx; }
.filter-tab.active { color: rgb(66, 121, 240); font-weight: bold; }
.filter-tab.active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 40rpx; height: 4rpx; background-color: rgb(66, 121, 240); border-radius: 2rpx; }
.inventory-list { padding: 0 20rpx; height: calc(100vh - 280rpx); }
.loading-container, .empty-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 100rpx 0; }
.empty-icon { font-size: 100rpx; margin-bottom: 20rpx; }
.empty-text, .loading-text { font-size: 28rpx; color: #999; }
.product-card { display: flex; align-items: center; background-color: #fff; border-radius: 16rpx; padding: 20rpx; margin-bottom: 16rpx; }
.product-card { display: flex; flex-direction: row; align-items: center; background-color: #fff; border-radius: 16rpx; padding: 20rpx; margin-bottom: 16rpx; }
.product-image { width: 120rpx; height: 120rpx; border-radius: 8rpx; margin-right: 20rpx; background-color: #f5f5f5; }
.product-info { flex: 1; }
.product-name { font-size: 28rpx; color: #333; font-weight: 500; display: block; margin-bottom: 10rpx; }
.stock-info { display: flex; align-items: center; }
.stock-info { display: flex; flex-direction: row; align-items: center; }
.stock-label { font-size: 24rpx; color: #999; margin-right: 10rpx; }
.stock-value { font-size: 28rpx; font-weight: bold; }
.stock-value.normal { color: #4CAF50; }
@@ -344,15 +356,15 @@
.modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000; }
.modal-content { width: 80%; background-color: #fff; border-radius: 16rpx; }
.modal-body { padding: 30rpx; }
.adjust-type { display: flex; justify-content: space-between; margin-bottom: 30rpx; }
.adjust-type { display: flex; flex-direction: row; justify-content: space-between; margin-bottom: 30rpx; }
.type-btn { flex: 1; height: 64rpx; line-height: 64rpx; text-align: center; font-size: 24rpx; background-color: #f5f5f5; color: #666; margin: 0 10rpx; border-radius: 32rpx; border: 1rpx solid #eee; }
.type-btn.active { background-color: #E3F2FD; color: #007AFF; border-color: #007AFF; }
.type-btn.active { background-color: #E3F2FD; color: rgb(66, 121, 240); border-color: rgb(66, 121, 240); }
.form-item { margin-bottom: 20rpx; }
.form-item .label { font-size: 26rpx; color: #999; display: block; margin-bottom: 10rpx; }
.form-item .value { font-size: 28rpx; color: #333; }
.input { height: 72rpx; border: 1rpx solid #e5e5e5; border-radius: 8rpx; padding: 0 20rpx; font-size: 28rpx; }
.modal-footer { display: flex; border-top: 1rpx solid #f5f5f5; }
.modal-footer { display: flex; flex-direction: row; border-top: 1rpx solid #f5f5f5; }
.modal-btn { flex: 1; height: 88rpx; line-height: 88rpx; text-align: center; font-size: 28rpx; }
.modal-btn.cancel { color: #666; border-right: 1rpx solid #f5f5f5; }
.modal-btn.confirm { color: #007AFF; font-weight: bold; }
.modal-btn.confirm { color: rgb(66, 121, 240); font-weight: bold; }
</style>