商品列表状态保存

This commit is contained in:
2026-10-30 09:34:43 +08:00
parent 44e422febb
commit f0375cdb50
4 changed files with 84 additions and 218 deletions
+18 -89
View File
@@ -4,11 +4,6 @@
padding: 20rpx 24rpx 60rpx;
box-sizing: border-box;
// 多选模式为底部导出栏留出空间
&--select {
padding-bottom: 160rpx;
}
// 底部待支付汇总栏留出空间
&--pay {
padding-bottom: 180rpx;
@@ -55,7 +50,7 @@
}
}
// ===== 状态筛选 + 导出入口 =====
// ===== 状态筛选 =====
.bill-toolbar {
display: flex;
align-items: center;
@@ -67,22 +62,6 @@
white-space: nowrap;
margin-bottom: 0;
}
&__export {
flex-shrink: 0;
margin-left: 16rpx;
padding: 12rpx 28rpx;
border-radius: 999rpx;
background: #fff;
border: 1rpx solid #ee0a24;
color: #ee0a24;
font-size: 26rpx;
&.active {
background: #ee0a24;
color: #fff;
}
}
}
.status-scroll {
@@ -132,11 +111,6 @@
padding: 24rpx;
margin-bottom: 16rpx;
&--select {
display: flex;
align-items: center;
}
&__content {
flex: 1;
min-width: 0;
@@ -192,7 +166,24 @@
font-size: 32rpx;
color: #323233;
font-weight: 600;
}
// 右侧栏:金额 + 导出按钮
&__side {
margin-left: 20rpx;
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: flex-end;
}
&__export {
margin-top: 12rpx;
padding: 6rpx 24rpx;
border-radius: 999rpx;
border: 1rpx solid #ee0a24;
color: #ee0a24;
font-size: 22rpx;
}
&__footer {
@@ -213,68 +204,6 @@
}
}
// ===== 勾选圆圈 =====
.bill-check {
width: 36rpx;
height: 36rpx;
border-radius: 50%;
border: 2rpx solid #c8c9cc;
margin-right: 20rpx;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
&.on {
background: #ee0a24;
border-color: #ee0a24;
}
}
// ===== 导出操作栏 =====
.export-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
display: flex;
align-items: center;
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
background: #fff;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.06);
&__count {
flex: 1;
min-width: 0;
font-size: 26rpx;
color: #646566;
}
&__all {
padding: 12rpx 24rpx;
margin-right: 16rpx;
border: 1rpx solid #dcdee0;
border-radius: 999rpx;
font-size: 26rpx;
color: #323233;
}
&__btn {
padding: 14rpx 40rpx;
border-radius: 999rpx;
background: #ee0a24;
color: #fff;
font-size: 28rpx;
font-weight: 500;
&.disabled {
opacity: 0.5;
}
}
}
// ===== 分类选择弹层 =====
.category-popup {
padding: 32rpx 32rpx 24rpx;
+31 -94
View File
@@ -11,9 +11,6 @@ import './index.less'
const PAGE_SIZE = 10
/** 单次导出上限(后端限制 1~100 张) */
const EXPORT_MAX = 100
/** 状态筛选(接口 status 为原始支付状态:0 未支付含审核中 / 1 已支付) */
const STATUS_FILTERS: Array<{ value: 0 | 1 | undefined; label: string }> = [
{ value: undefined, label: '全部' },
@@ -30,7 +27,7 @@ interface CategoryOption {
/**
* 账单列表页
* 采购单完成后由后台按门店生成(只读);底部汇总栏为门店口径待支付汇总(含审核中,不受筛选影响)
* 支持多选账单合并导出 Excel(可按一级分类过滤商品明细)
* 每个账单可单独导出 Excel(可按一级分类过滤商品明细),不支持合并导出
*/
export default function BillListPage() {
const token = useAuthStore(s => s.token)
@@ -43,9 +40,8 @@ export default function BillListPage() {
const [finished, setFinished] = useState(false)
const loadingRef = useRef(false)
/** 导出:多选模式 + 已选账单 + 分类弹层 */
const [selectMode, setSelectMode] = useState(false)
const [selectedIds, setSelectedIds] = useState<number[]>([])
/** 导出:待导出账单 + 分类弹层 */
const [exportBill, setExportBill] = useState<Bill | null>(null)
const [showCategory, setShowCategory] = useState(false)
const [categories, setCategories] = useState<CategoryOption[]>([])
@@ -107,49 +103,9 @@ export default function BillListPage() {
Taro.navigateTo({ url: '/pages/payment/index' })
}, [])
/** 进入/退出多选导出模式 */
const toggleSelectMode = useCallback(() => {
setSelectMode(prev => !prev)
setSelectedIds([])
}, [])
/** 账单点击:多选模式切换勾选,否则进详情 */
const handleItemTap = useCallback(
(bill: Bill) => {
if (!selectMode) {
goDetail(bill.id)
return
}
setSelectedIds(prev => {
if (prev.includes(bill.id)) return prev.filter(i => i !== bill.id)
if (prev.length >= EXPORT_MAX) {
Taro.showToast({ title: `最多导出 ${EXPORT_MAX} 张`, icon: 'none' })
return prev
}
return [...prev, bill.id]
})
},
[selectMode, goDetail],
)
/** 全选当前已加载账单(受导出上限约束) */
const handleSelectAll = useCallback(() => {
setSelectedIds(prev => {
if (prev.length === bills.length) return []
if (bills.length > EXPORT_MAX) {
Taro.showToast({ title: `最多导出 ${EXPORT_MAX} 张,已选前 ${EXPORT_MAX} 张`, icon: 'none' })
return bills.slice(0, EXPORT_MAX).map(b => b.id)
}
return bills.map(b => b.id)
})
}, [bills])
/** 打开分类选择弹层(首次加载分类树根节点) */
const handleExportTap = useCallback(async () => {
if (selectedIds.length === 0) {
Taro.showToast({ title: '请先勾选要导出的账单', icon: 'none' })
return
}
const handleExportTap = useCallback(
async (bill: Bill) => {
if (categories.length === 0) {
try {
const res = await getCategoriesApi()
@@ -161,18 +117,21 @@ export default function BillListPage() {
return // 错误已由 request 层 toast
}
}
setExportBill(bill)
setShowCategory(true)
}, [selectedIds, categories])
},
[categories],
)
/** 按所选分类导出合并 Excel */
/** 按所选分类导出单个账单 Excel */
const handleExport = useCallback(
async (categoryId: number) => {
if (!exportBill) return
const isH5 = process.env.TARO_ENV === 'h5'
Taro.showLoading({ title: '导出中...', mask: true })
try {
await downloadExportFile(getBillExportUrl(selectedIds, categoryId), '门店账单.xlsx')
await downloadExportFile(getBillExportUrl(exportBill.id, categoryId), `账单-${exportBill.bill_no}.xlsx`)
setShowCategory(false)
toggleSelectMode()
if (isH5) {
Taro.showToast({ title: '导出成功', icon: 'success' })
}
@@ -182,15 +141,15 @@ export default function BillListPage() {
Taro.hideLoading()
}
},
[selectedIds, toggleSelectMode],
[exportBill],
)
/** 底部待支付汇总栏是否可见(多选导出时让位给导出栏) */
const showPayBar = loggedIn && !selectMode && !!summary && summary.unpaid_count > 0
/** 底部待支付汇总栏是否可见 */
const showPayBar = loggedIn && !!summary && summary.unpaid_count > 0
return (
<View className={`bill-page ${selectMode ? 'bill-page--select' : ''} ${showPayBar ? 'bill-page--pay' : ''}`}>
{/* ========== 状态筛选 + 导出入口 ========== */}
<View className={`bill-page ${showPayBar ? 'bill-page--pay' : ''}`}>
{/* ========== 状态筛选 ========== */}
<View className='bill-toolbar'>
<ScrollView scrollX className='status-scroll'>
{STATUS_FILTERS.map(item => (
@@ -203,11 +162,6 @@ export default function BillListPage() {
</View>
))}
</ScrollView>
{loggedIn && bills.length > 0 && (
<View className={`bill-toolbar__export ${selectMode ? 'active' : ''}`} onClick={toggleSelectMode}>
<Text>{selectMode ? '取消' : '导出'}</Text>
</View>
)}
</View>
{/* ========== 账单列表 ========== */}
@@ -222,19 +176,8 @@ export default function BillListPage() {
<Empty description='暂无账单' className='bill-empty' />
)
) : (
bills.map(bill => {
const checked = selectMode && selectedIds.includes(bill.id)
return (
<View
key={bill.id}
className={`bill-item ${selectMode ? 'bill-item--select' : ''}`}
onClick={() => handleItemTap(bill)}
>
{selectMode && (
<View className={`bill-check ${checked ? 'on' : ''}`}>
{checked && <Icon name='success' size={14} color='#fff' />}
</View>
)}
bills.map(bill => (
<View key={bill.id} className='bill-item' onClick={() => goDetail(bill.id)}>
<View className='bill-item__content'>
<View className='bill-item__header'>
<Text className='bill-item__no'>{bill.bill_no}</Text>
@@ -249,7 +192,18 @@ export default function BillListPage() {
<Text className='bill-item__purchase'>采购单 {bill.purchase.purchase_no}</Text>
)}
</View>
<View className='bill-item__side'>
<Text className='bill-item__amount'>¥{bill.total_amount}</Text>
<View
className='bill-item__export'
onClick={e => {
e.stopPropagation()
handleExportTap(bill)
}}
>
导出
</View>
</View>
</View>
<View className='bill-item__footer'>
<Text className='bill-item__settle'>应结算 {bill.settlement_date}</Text>
@@ -259,8 +213,7 @@ export default function BillListPage() {
</View>
</View>
</View>
)
})
))
)}
{loggedIn && finished && bills.length > 0 && (
@@ -278,22 +231,6 @@ export default function BillListPage() {
</View>
)}
{/* ========== 导出操作栏 ========== */}
{selectMode && (
<View className='export-bar'>
<Text className='export-bar__count'>已选 {selectedIds.length} 张</Text>
<View className='export-bar__all' onClick={handleSelectAll}>
{selectedIds.length === bills.length && bills.length > 0 ? '取消全选' : '全选'}
</View>
<View
className={`export-bar__btn ${selectedIds.length === 0 ? 'disabled' : ''}`}
onClick={handleExportTap}
>
导出 Excel
</View>
</View>
)}
{/* ========== 分类选择弹层 ========== */}
<Popup
show={showCategory}
+3 -3
View File
@@ -123,9 +123,9 @@ export function getBillDetailApi(id: number) {
}
/**
* 账单合并导出文件地址:GET /mini/bill/export
* 单个账单导出文件地址:GET /mini/bill/{id}/export
* 返回 xlsx 文件流(非 JSON,需带 token 下载);categoryId 仅过滤商品明细,费用仍全额汇总
*/
export function getBillExportUrl(ids: number[], categoryId = 0): string {
return `${BASE_URL}/mini/bill/export?ids=${ids.join(',')}&category_id=${categoryId}`
export function getBillExportUrl(id: number, categoryId = 0): string {
return `${BASE_URL}/mini/bill/${id}/export?category_id=${categoryId}`
}
+2 -2
View File
@@ -10,8 +10,8 @@ const LOGIN_PATH = '/pages/login/index'
/** 默认请求超时(ms) */
const DEFAULT_TIMEOUT = 15000
/** 接口根地址(uploadFile 等原生请求同样使用) */
// export const BASE_URL = "http://localhost:8000"
export const BASE_URL = "https://purchase.henanklkj.com/index.php"
export const BASE_URL = "http://localhost:8000"
// export const BASE_URL = "https://purchase.henanklkj.com/index.php"
/**
* HTTP 状态码 → 错误提示映射