feat(订单): 添加借还动态功能
- 新增订单API模块,包含借还动态查询接口和类型定义 - 在商品详情页添加借还动态标签页,展示相关记录 - 实现分页加载和图片预览功能 - 更新环境配置,注释掉不再使用的API地址 - 移除过期的文档链接 - 修复商品详情页高度问题,适配动态列表展示
This commit is contained in:
parent
db6f31228d
commit
cdc59df3db
|
|
@ -132,8 +132,6 @@ http.post<T>('/api/users', data)
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- **Standards**: `/doc/代码编写规范.md`
|
- **Standards**: `/doc/代码编写规范.md`
|
||||||
- **Cleanup**: `/doc/项目清理计划.md`
|
|
||||||
- **Migration**: `/doc/迁移指令.md`
|
|
||||||
- **wot-design-uni**: UI component library documentation located in `/doc/wot-design-uni/docs/`
|
- **wot-design-uni**: UI component library documentation located in `/doc/wot-design-uni/docs/`
|
||||||
- View `index.md` for main documentation
|
- View `index.md` for main documentation
|
||||||
- Browse `guide/` for usage guides
|
- Browse `guide/` for usage guides
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ VITE_APP_PROXY_ENABLE = true
|
||||||
VITE_APP_PROXY_PREFIX = '/api/'
|
VITE_APP_PROXY_PREFIX = '/api/'
|
||||||
|
|
||||||
# 第二个请求地址 (目前alova中可以使用)
|
# 第二个请求地址 (目前alova中可以使用)
|
||||||
VITE_API_SECONDARY_URL = 'https://wxshop.ab98.cn/shop-api/api/'
|
# VITE_API_SECONDARY_URL = 'https://wxshop.ab98.cn/shop-api/api/'
|
||||||
|
|
||||||
# 认证模式,'single' | 'double' ==> 单token | 双token
|
# 认证模式,'single' | 'double' ==> 单token | 双token
|
||||||
VITE_AUTH_MODE = 'single'
|
VITE_AUTH_MODE = 'single'
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,4 @@ VITE_SHOW_SOURCEMAP = false
|
||||||
#VITE_HUIBANG_BASEURL = 'https://www.ab98.cn'
|
#VITE_HUIBANG_BASEURL = 'https://www.ab98.cn'
|
||||||
|
|
||||||
# VITE_SERVER_BASEURL = 'http://localhost:8090/api/'
|
# VITE_SERVER_BASEURL = 'http://localhost:8090/api/'
|
||||||
VITE_SERVER_BASEURL = 'https://wxshop.ab98.cn/shop-api/api/'
|
# VITE_SERVER_BASEURL = 'https://wxshop.ab98.cn/shop-api/api/'
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,5 @@ VITE_DELETE_CONSOLE = false
|
||||||
VITE_SHOW_SOURCEMAP = false
|
VITE_SHOW_SOURCEMAP = false
|
||||||
|
|
||||||
# 后台请求地址
|
# 后台请求地址
|
||||||
VITE_SERVER_BASEURL = 'https://wxshop.ab98.cn/shop-api/api/'
|
# VITE_SERVER_BASEURL = 'https://wxshop.ab98.cn/shop-api/api/'
|
||||||
VITE_HUIBANG_BASEURL = 'https://www.ab98.cn'
|
VITE_HUIBANG_BASEURL = 'https://www.ab98.cn'
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,10 @@ export interface PageResult<T> {
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PageDTO<T> {
|
||||||
|
total: number;
|
||||||
|
rows: Array<T>;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* 分页查询基本参数
|
* 分页查询基本参数
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { http } from "@/http/http";
|
||||||
|
import { PageDTO } from "..";
|
||||||
|
|
||||||
|
/** 借还动态查询参数 */
|
||||||
|
export interface SearchBorrowReturnDynamicQuery {
|
||||||
|
/** 商品ID,精确筛选 */
|
||||||
|
goodsId?: number;
|
||||||
|
/** 格口ID,精确筛选 */
|
||||||
|
cellId?: number;
|
||||||
|
/** 状态筛选(仅对归还记录有效) */
|
||||||
|
status?: number;
|
||||||
|
/** 动态类型筛选 */
|
||||||
|
dynamicType?: number;
|
||||||
|
/** 页码(默认1) */
|
||||||
|
pageNum?: number;
|
||||||
|
/** 每页大小(默认10) */
|
||||||
|
pageSize?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 借还动态响应数据 */
|
||||||
|
export interface BorrowReturnDynamicDTO {
|
||||||
|
/** 订单商品ID */
|
||||||
|
orderGoodsId: number;
|
||||||
|
/** 动态类型(0-借出 1-归还) */
|
||||||
|
dynamicType: number;
|
||||||
|
/** 动态类型描述 */
|
||||||
|
dynamicTypeStr: string;
|
||||||
|
/** 订单ID */
|
||||||
|
orderId: number;
|
||||||
|
/** 订单创建时间/借出时间 */
|
||||||
|
orderTime: string;
|
||||||
|
/** 商品ID */
|
||||||
|
goodsId: number;
|
||||||
|
/** 商品名称 */
|
||||||
|
goodsName: string;
|
||||||
|
/** 商品单价 */
|
||||||
|
goodsPrice: number;
|
||||||
|
/** 数量 */
|
||||||
|
quantity: number;
|
||||||
|
/** 支付方式 */
|
||||||
|
paymentMethod: string;
|
||||||
|
/** 订单姓名 */
|
||||||
|
orderName: string;
|
||||||
|
/** 订单手机号 */
|
||||||
|
orderMobile: string;
|
||||||
|
/** 格口ID */
|
||||||
|
cellId: number;
|
||||||
|
/** 格口号 */
|
||||||
|
cellNo: number;
|
||||||
|
/** 柜机ID */
|
||||||
|
cabinetId: number;
|
||||||
|
/** 柜机名称 */
|
||||||
|
cabinetName: string;
|
||||||
|
/** 归还/审批时间(归还记录时有效) */
|
||||||
|
operateTime?: string;
|
||||||
|
/** 审批ID(归还记录时有效) */
|
||||||
|
approvalId?: number;
|
||||||
|
/** 审批状态(归还记录时有效) */
|
||||||
|
status: number;
|
||||||
|
/** 状态描述 */
|
||||||
|
statusStr: string;
|
||||||
|
/** 审批人(归还记录时有效) */
|
||||||
|
auditName?: string;
|
||||||
|
/** 审核说明(归还记录时有效) */
|
||||||
|
auditRemark?: string;
|
||||||
|
/** 归还图片(归还记录时有效) */
|
||||||
|
images?: string;
|
||||||
|
/** 审核图片(归还记录时有效) */
|
||||||
|
auditImages?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取借还动态分页列表 */
|
||||||
|
export async function getBorrowReturnDynamicApi(query: SearchBorrowReturnDynamicQuery) {
|
||||||
|
return await http.get<PageDTO<BorrowReturnDynamicDTO>>("order/borrow-return-dynamic", query);
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,9 @@ import { ref, computed, watch } from 'vue'
|
||||||
import type { Product } from '@/pinia/stores/product'
|
import type { Product } from '@/pinia/stores/product'
|
||||||
import { useCartStore } from '@/pinia/stores/cart'
|
import { useCartStore } from '@/pinia/stores/cart'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { toHttpsUrl } from '@/utils'
|
||||||
|
// import { onLoad, onReachBottom, onShow } from '@dcloudio/uni-app'
|
||||||
|
import { getBorrowReturnDynamicApi, type BorrowReturnDynamicDTO, type SearchBorrowReturnDynamicQuery } from '@/api/order'
|
||||||
|
|
||||||
// 定义组件接收的属性
|
// 定义组件接收的属性
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
@ -22,6 +25,30 @@ const { cartItems } = storeToRefs(cartStore)
|
||||||
const showAddCart = ref<boolean>(false)
|
const showAddCart = ref<boolean>(false)
|
||||||
const quantity = ref<number>(1)
|
const quantity = ref<number>(1)
|
||||||
|
|
||||||
|
// 标签页控制
|
||||||
|
const activeTab = ref<number>(0)
|
||||||
|
|
||||||
|
// 借还动态数据
|
||||||
|
const dynamicList = ref<BorrowReturnDynamicDTO[]>([]);
|
||||||
|
const total = ref<number>(-1);
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
|
// 分页参数
|
||||||
|
const pageNum = ref<number>(1)
|
||||||
|
const pageSize = ref<number>(20)
|
||||||
|
// loadmore 状态
|
||||||
|
const loadmoreState = ref<'loading' | 'finished' | 'error'>('loading')
|
||||||
|
|
||||||
|
// scroll-view 高度计算
|
||||||
|
const scrollHeight = computed(() => {
|
||||||
|
// 计算可用高度:屏幕高度 - 顶部操作栏 - 底部操作栏 - 标签栏
|
||||||
|
const windowHeight = uni.getSystemInfoSync().windowHeight
|
||||||
|
const headerHeight = 60 // 顶部操作栏高度
|
||||||
|
const actionBarHeight = 64 // 底部操作栏高度
|
||||||
|
const tabHeight = 44 // 标签栏高度
|
||||||
|
console.log('scrollHeight', windowHeight, headerHeight, actionBarHeight, tabHeight)
|
||||||
|
return `${windowHeight - headerHeight - actionBarHeight - tabHeight}px`
|
||||||
|
})
|
||||||
|
|
||||||
const maxQuantity = computed(() => {
|
const maxQuantity = computed(() => {
|
||||||
const existingItem = cartItems.value.find(item => item.product.id === props.product.id)
|
const existingItem = cartItems.value.find(item => item.product.id === props.product.id)
|
||||||
if (existingItem) {
|
if (existingItem) {
|
||||||
|
|
@ -74,11 +101,96 @@ function doShowAddCart(): boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取借还动态数据(带分页)
|
||||||
|
async function loadDynamicList(page: number = 1) {
|
||||||
|
console.log('loadDynamicList', page)
|
||||||
|
console.log('props.product', props.product)
|
||||||
|
if (!props.product?.id) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
loadmoreState.value = 'loading'
|
||||||
|
try {
|
||||||
|
const query: SearchBorrowReturnDynamicQuery = {
|
||||||
|
goodsId: props.product.id,
|
||||||
|
cellId: props.product.cellId,
|
||||||
|
pageNum: page,
|
||||||
|
pageSize: pageSize.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await getBorrowReturnDynamicApi(query)
|
||||||
|
console.log('response', response)
|
||||||
|
|
||||||
|
if (response?.data?.rows) {
|
||||||
|
if (page === 1) {
|
||||||
|
// 首次加载,替换数据
|
||||||
|
dynamicList.value = response.data.rows
|
||||||
|
} else {
|
||||||
|
// 追加数据
|
||||||
|
dynamicList.value = [...dynamicList.value, ...response.data.rows]
|
||||||
|
}
|
||||||
|
total.value = response.data.total
|
||||||
|
pageNum.value = page
|
||||||
|
|
||||||
|
// 判断是否已加载全部
|
||||||
|
console.log('dynamicList.value.length', dynamicList.value.length, 'total.value', total.value)
|
||||||
|
if (dynamicList.value.length >= total.value) {
|
||||||
|
loadmoreState.value = 'finished'
|
||||||
|
} else {
|
||||||
|
loadmoreState.value = 'loading'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取借还动态失败:', error)
|
||||||
|
loadmoreState.value = 'error'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多数据
|
||||||
|
function loadMoreData() {
|
||||||
|
console.log('loadMoreData', pageNum.value, loadmoreState.value, loading.value, dynamicList.value.length, total.value)
|
||||||
|
if (loadmoreState.value === 'finished' || loading.value) return
|
||||||
|
if (dynamicList.value.length >= total.value) {
|
||||||
|
loadmoreState.value = 'finished'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadDynamicList(pageNum.value + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll-view 滚动到底部事件处理
|
||||||
|
function handleScrollToLower() {
|
||||||
|
console.log('handleScrollToLower', pageNum.value, loadmoreState.value, loading.value, dynamicList.value.length, total.value)
|
||||||
|
loadMoreData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听标签页切换,加载借还动态
|
||||||
|
watch(activeTab, (newVal) => {
|
||||||
|
if (newVal === 1 && dynamicList.value.length === 0) {
|
||||||
|
pageNum.value = 1
|
||||||
|
loadDynamicList(1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const refreshDynamicList = () => {
|
||||||
|
dynamicList.value = [];
|
||||||
|
pageNum.value = 1;
|
||||||
|
loadDynamicList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
refreshDynamicList();
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
watch(() => props.product, () => {
|
watch(() => props.product, () => {
|
||||||
// 重置参数
|
// 重置参数
|
||||||
quantity.value = 1
|
activeTab.value = 0;
|
||||||
showAddCart.value = false
|
quantity.value = 1;
|
||||||
}, { deep: true })
|
showAddCart.value = false;
|
||||||
|
refreshDynamicList();
|
||||||
|
}, { deep: true, immediate: true })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -92,85 +204,152 @@ watch(() => props.product, () => {
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品内容区域(可滚动) -->
|
<!-- 商品内容区域(可滚动) -->
|
||||||
<scroll-view class="content-area" scroll-y>
|
<view class="content-area">
|
||||||
<!-- 商品主图 -->
|
<!-- 标签页内容 -->
|
||||||
<view class="product-image-wrapper">
|
<view class="tab-content">
|
||||||
<wd-img
|
<wd-tabs v-model="activeTab">
|
||||||
v-if="product"
|
<!-- 标签1:商品信息 -->
|
||||||
:src="product.image"
|
<wd-tab title="商品信息">
|
||||||
width="100%"
|
<!-- 商品主图 -->
|
||||||
height="275"
|
<view class="product-image-wrapper">
|
||||||
>
|
<wd-img v-if="product" :src="toHttpsUrl(product.image)" width="100%" height="275">
|
||||||
<view v-if="product.stock === 0" class="sold-out-overlay">
|
<view v-if="product.stock === 0" class="sold-out-overlay">
|
||||||
<text class="sold-out-text">已售罄</text>
|
<text class="sold-out-text">已售罄</text>
|
||||||
</view>
|
</view>
|
||||||
<template #error>
|
<template #error>
|
||||||
<view class="custom-error">
|
<view class="custom-error">
|
||||||
图片加载失败
|
图片加载失败
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</wd-img>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
<!-- 分隔线 -->
|
||||||
</wd-img>
|
<view class="divider"></view>
|
||||||
|
<!-- 商品信息 -->
|
||||||
|
<view class="product-info">
|
||||||
|
<view class="product-name">
|
||||||
|
{{ product.name }}
|
||||||
|
</view>
|
||||||
|
<view class="price-row">
|
||||||
|
<text class="product-price">¥{{ product.price.toFixed(2) }}</text>
|
||||||
|
<text v-if="product.stock > 0" class="stock-count">
|
||||||
|
剩余{{ product.stock }}件
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="description">
|
||||||
|
{{ product.description }}
|
||||||
|
</view>
|
||||||
|
</wd-tab>
|
||||||
|
|
||||||
|
<!-- 标签2:借还动态 -->
|
||||||
|
<wd-tab title="借还动态">
|
||||||
|
<!-- 动态列表 -->
|
||||||
|
<scroll-view class="dynamic-list-scroll" scroll-y :style="{ height: scrollHeight }"
|
||||||
|
@scrolltolower="handleScrollToLower" :lower-threshold="100">
|
||||||
|
<view class="dynamic-list">
|
||||||
|
<view v-for="item in dynamicList" :key="String(item.orderGoodsId) + item.dynamicType"
|
||||||
|
class="dynamic-item">
|
||||||
|
<view class="dynamic-header">
|
||||||
|
<view class="dynamic-type"
|
||||||
|
:class="{ borrow: item.dynamicType === 0, return: item.dynamicType === 1 }">
|
||||||
|
{{ item.dynamicTypeStr }}
|
||||||
|
</view>
|
||||||
|
<!-- 归还记录显示额外信息 -->
|
||||||
|
<view v-if="item.dynamicType === 1" class="info-row">
|
||||||
|
<text class="info-value" :class="item.status === 2 ? 'success' : 'warning'">{{ item.statusStr
|
||||||
|
}}</text>
|
||||||
|
</view>
|
||||||
|
<text class="dynamic-time">{{ item.orderTime }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="dynamic-body">
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">借用人:</text>
|
||||||
|
<text class="info-value">{{ item.orderName }}</text>
|
||||||
|
<text v-if="item.dynamicType === 1 && item.auditName" class="info-label audit-name">审批人:</text>
|
||||||
|
<text v-if="item.dynamicType === 1 && item.auditName" class="info-value">{{ item.auditName
|
||||||
|
}}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.dynamicType === 1 && item.auditRemark && item.auditRemark != '自动审批'"
|
||||||
|
class="info-row">
|
||||||
|
<text class="info-label">审核说明:</text>
|
||||||
|
<text class="info-value">{{ item.auditRemark }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 归还图片 -->
|
||||||
|
<view v-if="item.dynamicType === 1 && item.images" class="image-section">
|
||||||
|
<view class="info-label">归还图片:</view>
|
||||||
|
<view class="image-list">
|
||||||
|
<view v-for="(image, index) in item.images.split(',')" :key="index" class="image-item">
|
||||||
|
<wd-img :src="toHttpsUrl(image.trim())" width="80" height="80" mode="aspectFill"
|
||||||
|
:enable-preview="true">
|
||||||
|
<template #error>
|
||||||
|
<view class="image-error">图片加载失败</view>
|
||||||
|
</template>
|
||||||
|
</wd-img>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 审核图片 -->
|
||||||
|
<view v-if="item.dynamicType === 1 && item.auditImages" class="image-section">
|
||||||
|
<view class="info-label">审核图片:</view>
|
||||||
|
<view class="image-list">
|
||||||
|
<view v-for="(image, index) in item.auditImages.split(',')" :key="index" class="image-item">
|
||||||
|
<wd-img :src="toHttpsUrl(image.trim())" width="80" height="80" mode="aspectFill"
|
||||||
|
:enable-preview="true">
|
||||||
|
<template #error>
|
||||||
|
<view class="image-error">图片加载失败</view>
|
||||||
|
</template>
|
||||||
|
</wd-img>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多状态 -->
|
||||||
|
<view v-if="dynamicList.length > 0" class="loadmore-status">
|
||||||
|
<view v-if="loadmoreState === 'loading'" class="loading-text">
|
||||||
|
<wd-loading size="16px" color="#969799"></wd-loading>
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="loadmoreState === 'finished'" class="finished-text">
|
||||||
|
<text>没有更多数据了</text>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="loadmoreState === 'error'" class="error-text" @click="loadMoreData">
|
||||||
|
<text>加载失败,点击重试</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</wd-tab>
|
||||||
|
</wd-tabs>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 商品信息 -->
|
<!-- 购买数量选择 -->
|
||||||
<view class="product-info">
|
|
||||||
<view class="product-name">
|
|
||||||
{{ product.name }}
|
|
||||||
</view>
|
|
||||||
<view class="price-row">
|
|
||||||
<text class="product-price">¥{{ product.price.toFixed(2) }}</text>
|
|
||||||
<text v-if="product.stock > 0" class="stock-count">
|
|
||||||
剩余{{ product.stock }}件
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 分隔线 -->
|
|
||||||
<view class="divider"></view>
|
|
||||||
|
|
||||||
<!-- 商品详细描述 -->
|
|
||||||
<view v-if="!showAddCart" class="description">
|
|
||||||
{{ product.description }}
|
|
||||||
</view>
|
|
||||||
<view v-if="showAddCart" class="action-row">
|
<view v-if="showAddCart" class="action-row">
|
||||||
<view class="product-name">
|
<view class="product-name">
|
||||||
购买数量
|
购买数量
|
||||||
</view>
|
</view>
|
||||||
<view class="cart-actions">
|
<view class="cart-actions">
|
||||||
<view
|
<view class="cart-btn-minus" :class="{ disabled: quantity === 1 }" @click.stop="handleRemoveFromCart()">
|
||||||
class="cart-btn-minus"
|
|
||||||
:class="{ disabled: quantity === 1 }"
|
|
||||||
@click.stop="handleRemoveFromCart()"
|
|
||||||
>
|
|
||||||
<wd-icon name="decrease" size="12px" :color="quantity === 1 ? '#fff' : '#F56C6C'"></wd-icon>
|
<wd-icon name="decrease" size="12px" :color="quantity === 1 ? '#fff' : '#F56C6C'"></wd-icon>
|
||||||
</view>
|
</view>
|
||||||
<text class="cart-count">{{ quantity }}</text>
|
<text class="cart-count">{{ quantity }}</text>
|
||||||
<view
|
<view class="cart-btn-plus" :class="{ disabled: maxQuantity }" @click.stop="handleAddToCart()">
|
||||||
class="cart-btn-plus"
|
|
||||||
:class="{ disabled: maxQuantity }"
|
|
||||||
@click.stop="handleAddToCart()"
|
|
||||||
>
|
|
||||||
<wd-icon name="add" size="12px" color="#fff"></wd-icon>
|
<wd-icon name="add" size="12px" color="#fff"></wd-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部操作按钮栏 -->
|
<!-- 底部操作按钮栏 -->
|
||||||
<view class="action-bar">
|
<view v-if="activeTab === 0" class="action-bar">
|
||||||
<view
|
<view v-if="!showAddCart" class="add-cart-btn" :class="{ disabled: product?.stock === 0 }"
|
||||||
v-if="!showAddCart"
|
@click="doShowAddCart()">
|
||||||
class="add-cart-btn"
|
|
||||||
:class="{ disabled: product?.stock === 0 }"
|
|
||||||
@click="doShowAddCart()"
|
|
||||||
>
|
|
||||||
{{ product?.stock === 0 ? '已售罄' : '加入购物车' }}
|
{{ product?.stock === 0 ? '已售罄' : '加入购物车' }}
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view v-if="showAddCart" class="add-cart-btn confirm" @click="confirmAddCart()">
|
||||||
v-if="showAddCart"
|
|
||||||
class="add-cart-btn confirm"
|
|
||||||
@click="confirmAddCart()"
|
|
||||||
>
|
|
||||||
确认
|
确认
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -181,7 +360,7 @@ watch(() => props.product, () => {
|
||||||
.detail-container {
|
.detail-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 80vh;
|
height: 100%;
|
||||||
background: #f7f8fa;
|
background: #f7f8fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -369,4 +548,176 @@ watch(() => props.product, () => {
|
||||||
background-color: #07c160;
|
background-color: #07c160;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 标签页内容样式
|
||||||
|
.tab-content {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-content {
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state,
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #969799;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll-view 样式
|
||||||
|
.dynamic-list-scroll {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-list {
|
||||||
|
min-height: 600rpx;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多状态样式
|
||||||
|
.loadmore-status {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
color: #969799;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finished-text {
|
||||||
|
color: #969799;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
color: #F56C6C;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-item {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-type {
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.borrow {
|
||||||
|
background: #EBF5FF;
|
||||||
|
color: #1989FA;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.return {
|
||||||
|
background: #F0F9EB;
|
||||||
|
color: #52C41A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
color: #646566;
|
||||||
|
margin-right: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
color: #323233;
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: #52C41A;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
color: #FF976A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-name {
|
||||||
|
margin-left: 50rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
color: #646566;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-item {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-error {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #f7f8fa;
|
||||||
|
color: #969799;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -5,6 +5,7 @@ import { useCartStore } from '@/pinia/stores/cart'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import Detail from './detail.vue'
|
import Detail from './detail.vue'
|
||||||
import Cart from './cart.vue'
|
import Cart from './cart.vue'
|
||||||
|
import { toHttpsUrl } from '@/utils'
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
style: {
|
style: {
|
||||||
|
|
@ -30,10 +31,10 @@ const activeCategory = ref<number>(0)
|
||||||
// 商品详情弹窗控制
|
// 商品详情弹窗控制
|
||||||
const showDetailPopup = ref<boolean>(false)
|
const showDetailPopup = ref<boolean>(false)
|
||||||
// 当前查看的商品ID
|
// 当前查看的商品ID
|
||||||
const currentProductId = ref<number>()
|
const currentCellId = ref<number>()
|
||||||
// 当前商品详情计算属性
|
// 当前商品详情计算属性
|
||||||
const currentProduct = computed(() =>
|
const currentProduct = computed(() =>
|
||||||
categories.value.find(p => p.id === currentProductId.value)
|
categories.value.find(p => p.cellId === currentCellId.value)
|
||||||
)
|
)
|
||||||
// 购物车弹窗控制
|
// 购物车弹窗控制
|
||||||
const showCartPopup = ref<boolean>(false)
|
const showCartPopup = ref<boolean>(false)
|
||||||
|
|
@ -50,10 +51,10 @@ function handleCategoryClick(index: number) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开商品详情弹窗
|
* 打开商品详情弹窗
|
||||||
* @param productId - 要显示详情的商品ID
|
* @param cellId - 要显示详情的格口ID
|
||||||
*/
|
*/
|
||||||
function showProductDetail(productId: number) {
|
function showProductDetail(cellId: number) {
|
||||||
currentProductId.value = productId
|
currentCellId.value = cellId
|
||||||
showDetailPopup.value = true
|
showDetailPopup.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,9 +137,9 @@ function handleCheckout() {
|
||||||
<view class="category-section">
|
<view class="category-section">
|
||||||
<view v-for="product in currentProducts" :key="product.cellId" class="product-item">
|
<view v-for="product in currentProducts" :key="product.cellId" class="product-item">
|
||||||
<view class="product-content">
|
<view class="product-content">
|
||||||
<view class="product-image-wrapper" @click.stop="showProductDetail(product.id)">
|
<view class="product-image-wrapper" @click.stop="showProductDetail(product.cellId)">
|
||||||
<wd-img
|
<wd-img
|
||||||
:src="product.image"
|
:src="toHttpsUrl(product.image)"
|
||||||
width="80"
|
width="80"
|
||||||
height="80"
|
height="80"
|
||||||
border-radius="4"
|
border-radius="4"
|
||||||
|
|
@ -153,10 +154,10 @@ function handleCheckout() {
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="product-info">
|
<view class="product-info">
|
||||||
<view class="product-name" @click.stop="showProductDetail(product.id)">
|
<view class="product-name" @click.stop="showProductDetail(product.cellId)">
|
||||||
{{ product.name }}
|
{{ product.name }}
|
||||||
</view>
|
</view>
|
||||||
<view class="product-price" @click.stop="showProductDetail(product.id)">
|
<view class="product-price" @click.stop="showProductDetail(product.cellId)">
|
||||||
¥{{ product.price.toFixed(2) }}
|
¥{{ product.price.toFixed(2) }}
|
||||||
</view>
|
</view>
|
||||||
<view class="action-row">
|
<view class="action-row">
|
||||||
|
|
@ -442,6 +443,6 @@ function handleCheckout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-container {
|
.detail-container {
|
||||||
height: 80vh;
|
height: 92vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Loading…
Reference in New Issue