daShangDao_miniProgram/pkgUser/dispatch-management.vue
2026-06-15 16:37:57 +08:00

1544 lines
35 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<view class="content">
<view class="form-card">
<!-- 第一行订单编号和ISBN扫码 -->
<view class="form-row">
<view class="form-item-half">
<text class="label">订单编号</text>
<view class="order-sn-wrapper">
<text class="order-sn-value">{{ formData.orderSn }}</text>
<button class="refresh-btn-mini" @click="generateOrderSn" size="mini">刷新</button>
</view>
</view>
<view class="form-item-half">
<text class="label">ISBN扫码</text>
<view class="scan-input-wrapper">
<input
class="input"
type="text"
v-model="formData.isbn"
placeholder="请点击扫码或输入ISBN"
placeholder-style="color: #999;"
/>
<button class="scan-btn" @click="startScanning" size="mini">扫码</button>
</view>
</view>
</view>
<!-- 第二行:支付金额和收货地址 -->
<view class="form-row">
<view class="form-item-half">
<text class="label">支付金额</text>
<input
class="input"
type="digit"
v-model="formData.payAmount"
placeholder="请输入支付金额"
placeholder-style="color: #999;"
/>
</view>
<view class="form-item-half">
<text class="label">收货地址</text>
<view class="address-input-wrapper" @click="openAddressModal">
<input
class="input"
type="text"
:value="displayAddress"
placeholder="请选择省/市/区/街道"
placeholder-style="color: #999;"
readonly
/>
<text class="address-arrow"></text>
</view>
</view>
</view>
<!-- 第三行:选择书籍按钮 -->
<view class="form-item">
<button class="select-books-btn" @click="showBookSelector">
<text class="btn-icon">📚</text>
<text>选择书籍 (共{{ allPagination.total }}本)</text>
</button>
</view>
<!-- 选中的书籍信息 -->
<view v-if="selectedBooks.length > 0" class="selected-books-card">
<view class="selected-books-header">
<text class="selected-books-title">已选书籍 ({{ selectedBooks.length }})</text>
<view class="header-actions">
<text class="change-book-btn" @click="showBookSelector">添加/修改</text>
<text class="clear-books-btn" @click="clearSelectedBooks">清空已选</text>
</view>
</view>
<scroll-view class="selected-books-list" scroll-y :style="{ 'max-height': selectedBooks.length > 2 ? '600rpx' : 'auto' }">
<view class="selected-book-item" v-for="(book, index) in selectedBooks" :key="book.id">
<view class="book-item-left">
<image
:src="book.bookPic || '/static/placeholder-book.png'"
class="book-thumb"
mode="aspectFill"
></image>
<view class="book-info-compact">
<text class="book-name-compact">{{ book.goodsName }}</text>
<text class="book-isbn">ISBN: {{ book.isbn }}</text>
<text class="book-price-compact">¥{{ (book.price / 100).toFixed(2) }}</text>
</view>
</view>
<view class="book-item-right">
<view class="inventory-control-mini" :class="{ 'disabled-control': book.inventory === 0 }">
<button class="mini-btn" @click.stop="decreaseBookStock(index)" :class="{ 'disabled': book.inventory === 0 || book.updateStock <= 0 }"></button>
<input class="mini-input" type="number" v-model.number="book.updateStock" :disabled="book.inventory === 0" @click.stop />
<button class="mini-btn" @click.stop="increaseBookStock(index)" :class="{ 'disabled': book.inventory === 0 || book.updateStock >= book.inventory }">+</button>
</view>
<view class="stock-info-row">
<text class="original-stock">原库存: {{ book.inventory }}</text>
</view>
<view class="stock-info-row">
<text class="new-stock" :class="{ 'stock-changed': book.updateStock > 0 }">
修改后: {{ book.inventory - book.updateStock }}
</text>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
<!-- 固定在底部的提交按钮 -->
<view class="submit-btn-wrapper">
<button class="submit-btn" @click="handleSubmit">提交</button>
</view>
</view>
<!-- 书籍选择弹窗 -->
<view v-if="showBookModal" class="book-modal-mask" @click="closeBookModal">
<view class="book-modal" @click.stop>
<view class="book-modal-header">
<text class="book-modal-title">选择书籍 (共{{ pagination.total }}本)</text>
<text class="close-modal-btn" @click="closeBookModal">✕</text>
</view>
<scroll-view class="book-modal-content" scroll-y>
<view class="book-list">
<view
class="book-item"
v-for="book in bookList"
:key="book.id"
:class="{ 'selected-book': selectedBookIds.includes(book.id) }"
@click="selectBook(book)"
>
<view class="book-image-wrapper">
<image
:src="book.bookPic || '/static/placeholder-book.png'"
class="book-image"
mode="aspectFill"
></image>
<view class="stock-badge" :class="{ 'in-stock': book.inventory > 0 }">
{{ book.inventory > 0 ? '库存:' + book.inventory : '无库存' }}
</view>
</view>
<view class="book-info">
<view class="book-name">{{ book.goodsName }}</view>
<view class="book-detail-item">
<text class="detail-label">ISBN:</text>
<text class="detail-value">{{ book.isbn }}</text>
</view>
<view class="book-detail-item">
<text class="detail-label">品相:</text>
<text class="detail-value condition-tag">{{ book.conditionCode }}</text>
</view>
<view class="book-detail-item">
<text class="detail-label">价格:</text>
<text class="detail-value price-text">¥{{ (book.price / 100).toFixed(2) }}</text>
</view>
<view class="book-detail-item">
<text class="detail-label">仓库:</text>
<text class="detail-value">{{ book.warehouseName }}</text>
</view>
<view class="book-detail-item">
<text class="detail-label">位置:</text>
<text class="detail-value">{{ book.templateName }}</text>
</view>
<view class="book-detail-item">
<text class="detail-label">货号:</text>
<text class="detail-value art-no">{{ book.artNo }}</text>
</view>
</view>
<view class="book-action">
<view class="select-indicator" v-if="selectedBookIds.includes(book.id)">
<text>✓</text>
</view>
</view>
</view>
</view>
<!-- 加载更多按钮 -->
<view v-if="pagination.pageNum < pagination.totalPages" class="load-more-container">
<button class="load-more-btn" @click="loadNextPage">
加载更多 ({{ pagination.pageNum }}/{{ pagination.totalPages }})
</button>
</view>
<!-- 没有更多数据提示 -->
<view v-if="pagination.pageNum >= pagination.totalPages && bookList.length > 0" class="no-more-tip">
<text class="no-more-text">已加载全部 {{ pagination.total }} 本书籍</text>
</view>
</scroll-view>
<view class="book-modal-footer">
<button class="modal-btn cancel-btn" @click="closeBookModal">取消</button>
<button class="modal-btn confirm-btn" @click="confirmBookSelection">确定</button>
</view>
</view>
</view>
<!-- 地址输入弹窗 -->
<view v-if="showAddressModal" class="address-modal-mask" @click="closeAddressModal">
<view class="address-modal" @click.stop>
<view class="address-modal-header">
<text class="address-modal-title">填写收货地址</text>
<text class="close-modal-btn" @click="closeAddressModal">✕</text>
</view>
<view class="address-modal-content">
<view class="address-form-item">
<text class="modal-label">省份</text>
<input class="modal-input" type="text" v-model="formData.province" placeholder="请输入省份" />
</view>
<view class="address-form-item">
<text class="modal-label">城市</text>
<input class="modal-input" type="text" v-model="formData.city" placeholder="请输入城市" />
</view>
<view class="address-form-item">
<text class="modal-label">区县</text>
<input class="modal-input" type="text" v-model="formData.country" placeholder="请输入区县" />
</view>
<view class="address-form-item">
<text class="modal-label">街道</text>
<input class="modal-input" type="text" v-model="formData.town" placeholder="请输入街道" />
</view>
</view>
<view class="address-modal-footer">
<button class="modal-btn cancel-btn" @click="closeAddressModal">取消</button>
<button class="modal-btn confirm-btn" @click="confirmAddress">确定</button>
</view>
</view>
</view>
<!-- 空状态提示 -->
<view v-if="hasSearched && bookList.length === 0 && !showBookModal && !showAddressModal" class="empty-state">
<text class="empty-icon">📚</text>
<text class="empty-text">暂无相关书籍</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
formData: {
orderSn: '',
isbn: '',
payAmount: '',
province: '',
city: '',
country: '',
town: ''
},
allBookList: [], // 所有书籍列表
allPagination: { // 所有书籍的分页信息
pageSize: 10,
pageNum: 1,
total: 0,
totalPages: 0
},
bookList: [], // 当前显示的书籍列表(用于弹窗显示)
selectedBookIds: [], // 选中的书籍ID数组(多选)
selectedBooks: [], // 选中的书籍完整信息数组
showBookModal: false, // 控制书籍选择弹窗显示
showAddressModal: false, // 控制地址输入弹窗显示
hasSearched: false, // 是否已搜索过
updateInventory: 0, // 修改后的库存数量
isbnFilter: false, // 是否正在进行ISBN筛选
pagination: {
pageSize: 10,
pageNum: 1,
total: 0,
totalPages: 0
} // 分页信息
}
},
computed: {
// 显示完整地址
displayAddress() {
const { province, city, country, town } = this.formData
const parts = [province, city, country, town].filter(Boolean)
return parts.length > 0 ? parts.join(' ') : ''
}
},
onLoad() {
this.generateOrderSn()
// 页面加载时获取所有商品
this.fetchAllBooks()
},
methods: {
// 生成订单编号
generateOrderSn() {
const timestamp = Date.now()
const random = Math.floor(Math.random() * 1000)
this.formData.orderSn = `${timestamp}${random}`.substring(0, 19)
},
// 获取所有商品列表
async fetchAllBooks(pageNum = 1) {
try {
if (pageNum === 1) {
uni.showLoading({
title: '加载中...'
})
}
// 从本地获取 userId
const userId = uni.getStorageSync('userId') || ''
// 调用接口获取所有商品
const res = await uni.request({
url: 'https://api.buzhiyushu.cn/zhishu/shopGoods/list',
method: 'GET',
data: {
pageSize: this.allPagination.pageSize,
pageNum: pageNum,
isQueryAllGoods: 1,
userId: userId
}
})
if (pageNum === 1) {
uni.hideLoading()
}
console.log('获取所有商品接口响应:', res)
// 判断是否成功
if (res[1].data && res[1].data.code === 200) {
const rows = res[1].data.rows || []
const total = res[1].data.total || 0
// 如果是第一页,替换列表;否则追加
if (pageNum === 1) {
this.bookList = rows
this.allBookList = rows
} else {
this.bookList = [...this.bookList, ...rows]
this.allBookList = [...this.allBookList, ...rows]
}
this.hasSearched = true
this.allPagination.total = total
this.allPagination.totalPages = Math.ceil(total / this.allPagination.pageSize)
this.allPagination.pageNum = pageNum
// 同步更新 pagination如果不是ISBN筛选模式
if (!this.isbnFilter) {
this.pagination = {
total: total,
totalPages: Math.ceil(total / this.allPagination.pageSize),
pageNum: pageNum,
pageSize: this.allPagination.pageSize
}
}
} else {
uni.showToast({
title: res[1].data.msg || '获取商品列表失败',
icon: 'none',
duration: 2000
})
}
} catch (error) {
uni.hideLoading()
console.error('获取所有商品失败:', error)
this.hasSearched = true
uni.showToast({
title: '网络请求失败',
icon: 'none',
duration: 2000
})
}
},
// 扫码功能
startScanning() {
uni.scanCode({
onlyFromCamera: true,
scanType: ['bar'],
success: (res) => {
console.log('扫码结果:', res.result)
this.formData.isbn = res.result
// 扫码成功后调用接口
this.fetchBookInfoByIsbn(res.result)
},
fail: (err) => {
console.error('扫码失败:', err)
uni.showToast({
title: '扫码失败',
icon: 'none',
duration: 2000
})
}
})
},
// 根据ISBN调用接口获取书籍信息
async fetchBookInfoByIsbn(isbn, pageNum = 1) {
try {
uni.showLoading({
title: '加载中...'
})
// 从本地获取 userId
const userId = uni.getStorageSync('userId') || ''
// 调用实际的接口
const res = await uni.request({
url: 'https://api.buzhiyushu.cn/zhishu/shopGoods/list',
method: 'GET',
data: {
pageSize: this.pagination.pageSize,
pageNum: pageNum,
isQueryAllGoods: 1,
isbn: isbn,
userId: userId
}
})
uni.hideLoading()
console.log('接口响应:', res)
// 判断是否成功
if (res[1].data && res[1].data.code === 200) {
const rows = res[1].data.rows || []
const total = res[1].data.total || 0
// 如果是第一页,替换列表;否则追加
if (pageNum === 1) {
this.bookList = rows
this.isbnFilter = true // 标记正在进行ISBN筛选
} else {
this.bookList = [...this.bookList, ...rows]
}
this.hasSearched = true
this.pagination.total = total
this.pagination.totalPages = Math.ceil(total / this.pagination.pageSize)
this.pagination.pageNum = pageNum
if (this.bookList.length > 0 && pageNum === 1) {
// 自动打开书籍选择弹窗
this.showBookModal = true
} else if (rows.length === 0 && pageNum === 1) {
uni.showToast({
title: '暂无相关书籍',
icon: 'none',
duration: 2000
})
}
} else {
uni.showToast({
title: res[1].data.msg || '查询失败',
icon: 'none',
duration: 2000
})
}
} catch (error) {
uni.hideLoading()
console.error('获取书籍信息失败:', error)
this.hasSearched = true
uni.showToast({
title: '网络请求失败',
icon: 'none',
duration: 2000
})
}
},
// 加载下一页
async loadNextPage() {
// 根据是否是ISBN筛选模式来决定加载哪个数据源
if (this.isbnFilter) {
// ISBN筛选模式加载ISBN筛选的下一页
if (this.pagination.pageNum < this.pagination.totalPages) {
await this.fetchBookInfoByIsbn(this.formData.isbn, this.pagination.pageNum + 1)
} else {
uni.showToast({
title: '没有更多数据了',
icon: 'none'
})
}
} else {
// 所有书籍模式:加载所有书籍的下一页
if (this.allPagination.pageNum < this.allPagination.totalPages) {
await this.fetchAllBooks(this.allPagination.pageNum + 1)
} else {
uni.showToast({
title: '没有更多数据了',
icon: 'none'
})
}
}
},
// 显示书籍选择器
showBookSelector() {
if (this.allBookList.length === 0) {
uni.showToast({
title: '暂无商品列表',
icon: 'none'
})
return
}
// 显示所有书籍清除ISBN筛选状态
this.bookList = this.allBookList
// 使用所有书籍的分页信息
this.pagination = { ...this.allPagination }
this.isbnFilter = false
// 清除ISBN输入表示显示全部书籍
this.formData.isbn = ''
this.showBookModal = true
},
// 关闭书籍选择弹窗
closeBookModal() {
this.showBookModal = false
// 关闭弹窗后,如果不是扫码筛选模式,恢复显示所有书籍
// 如果是扫码筛选模式,保持筛选结果,以便用户可以继续操作
if (!this.isbnFilter) {
this.bookList = this.allBookList
this.formData.isbn = ''
}
// 如果没有选中书籍,重置选中状态
if (this.selectedBooks.length === 0) {
this.selectedBookIds = []
}
},
// 选择书籍(多选)
selectBook(book) {
const index = this.selectedBookIds.indexOf(book.id)
if (index > -1) {
// 已选中,取消选择
this.selectedBookIds.splice(index, 1)
} else {
// 未选中,添加选择
this.selectedBookIds.push(book.id)
}
console.log('选中的书籍ID:', this.selectedBookIds)
},
// 打开地址弹窗
openAddressModal() {
this.showAddressModal = true
},
// 关闭地址弹窗
closeAddressModal() {
this.showAddressModal = false
},
// 确认地址
confirmAddress() {
this.showAddressModal = false
},
// 确认书籍选择
confirmBookSelection() {
if (this.selectedBookIds.length === 0) {
uni.showToast({
title: '请至少选择一本书籍',
icon: 'none'
})
return
}
// 合并所有可能的书籍来源allBookList + 当前bookList中独特的书籍
let allBooks = [...this.allBookList]
// 如果当前bookList中有allBookList中没有的书籍比如ISBN筛选的结果也要合并进去
this.bookList.forEach(book => {
if (!allBooks.find(ab => ab.id === book.id)) {
allBooks.push(book)
}
})
// 从合并后的所有书籍中查找选中的书籍
let newSelectedBooks = allBooks.filter(book => this.selectedBookIds.includes(book.id))
// 保留现有的 updateStock 值,避免重新选择时丢失之前的修改
// 初始值为 0表示要减去的库存数量
newSelectedBooks = newSelectedBooks.map(book => {
const existing = this.selectedBooks.find(sb => sb.id === book.id)
return {
...book,
updateStock: existing ? existing.updateStock : 0
}
})
this.selectedBooks = newSelectedBooks
this.showBookModal = false
uni.showToast({
title: `已选择 ${this.selectedBooks.length} 本书籍`,
icon: 'success',
duration: 1500
})
},
// 增加书籍库存(实际是增加要减去的数量)
increaseBookStock(index) {
const book = this.selectedBooks[index]
// 只有当原库存大于0时才允许增加要减去的数量
// 最大值不能超过原库存
if (book.inventory > 0 && book.updateStock < book.inventory) {
book.updateStock++
}
},
// 减少书籍库存(实际是减少要减去的数量)
decreaseBookStock(index) {
const book = this.selectedBooks[index]
// 只有当原库存大于0且当前要减去的数量大于0时才允许减少
// 最小值为 0
if (book.inventory > 0 && book.updateStock > 0) {
book.updateStock--
}
},
// 清空已选书籍
clearSelectedBooks() {
uni.showModal({
title: '确认清空',
content: '确定要清空所有已选书籍吗?',
success: (res) => {
if (res.confirm) {
this.selectedBooks = []
this.selectedBookIds = []
uni.showToast({
title: '已清空所有书籍',
icon: 'success',
duration: 1500
})
}
}
})
},
// 提交表单
async handleSubmit() {
if (this.selectedBooks.length === 0) {
uni.showToast({
title: '请至少选择一本书籍',
icon: 'none'
})
return
}
if (!this.formData.payAmount) {
uni.showToast({
title: '请输入支付金额',
icon: 'none'
})
return
}
if (!this.formData.province) {
uni.showToast({
title: '请输入省份',
icon: 'none'
})
return
}
if (!this.formData.city) {
uni.showToast({
title: '请输入城市',
icon: 'none'
})
return
}
if (!this.formData.country) {
uni.showToast({
title: '请输入区县',
icon: 'none'
})
return
}
if (!this.formData.town) {
uni.showToast({
title: '请输入街道',
icon: 'none'
})
return
}
// 从本地获取 userId
const userId = uni.getStorageSync('userId') || ''
// 构建提交数据
const submitData = {
orderSn: this.formData.orderSn,
payAmount: parseFloat(this.formData.payAmount),
province: this.formData.province,
city: this.formData.city,
country: this.formData.country,
town: this.formData.town,
createdBy: userId,
zhishuShopGoodsList: this.selectedBooks.map(book => ({
id: book.id,
goodsName: book.goodsName,
price: book.price,
inventory: book.inventory,
updateInventory: book.updateStock,
artNo: book.artNo,
bookPic: book.bookPic
}))
}
console.log('提交的数据:', submitData)
try {
uni.showLoading({
title: '提交中...'
})
// 调用接口
const res = await uni.request({
url: 'http://192.168.101.127:8080/zhishu/erpGoodsOrder/addErpGoodsOrder',
method: 'POST',
data: submitData,
header: {
'Content-Type': 'application/json'
}
})
uni.hideLoading()
console.log('接口响应:', res)
if (res[1].data && res[1].data.code === 200) {
uni.showToast({
title: '提交成功',
icon: 'success',
duration: 2000
})
// 清空表单和已选书籍(保留收货地址)
setTimeout(() => {
this.generateOrderSn()
this.formData.isbn = ''
this.formData.payAmount = ''
// 不清空收货地址字段
// this.formData.province = ''
// this.formData.city = ''
// this.formData.country = ''
// this.formData.town = ''
this.selectedBooks = []
this.selectedBookIds = []
}, 2000)
} else {
uni.showToast({
title: res[1].data.msg || '提交失败',
icon: 'none',
duration: 2000
})
}
} catch (error) {
uni.hideLoading()
console.error('提交失败:', error)
uni.showToast({
title: '网络请求失败',
icon: 'none',
duration: 2000
})
}
}
}
}
</script>
<style scoped>
.container {
min-height: 100vh;
background-color: #f5f5f5;
}
.content {
padding: 30rpx;
padding-bottom: 150rpx;
}
.form-card {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 0;
height: calc(100vh - 240rpx);
overflow-y: auto;
}
.form-item {
margin-bottom: 30rpx;
}
/* 表单行布局 */
.form-row {
display: flex;
gap: 20rpx;
margin-bottom: 30rpx;
}
.form-item-half {
flex: 1;
min-width: 0;
}
.form-item-half .label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
font-weight: 500;
}
/* 订单编号小号按钮 */
.refresh-btn-mini {
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
background-color: #3c9cff;
color: #fff;
border: none;
padding: 0 20rpx;
}
.order-sn-wrapper {
display: flex;
align-items: center;
gap: 15rpx;
}
.order-sn-value {
flex: 1;
height: 70rpx;
line-height: 70rpx;
border: 1rpx solid #e5e5e5;
border-radius: 10rpx;
padding: 0 15rpx;
font-size: 22rpx;
color: #666;
background-color: #f5f5f5;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.scan-input-wrapper {
display: flex;
align-items: center;
gap: 15rpx;
}
.scan-input-wrapper .input {
flex: 1;
height: 70rpx;
}
.scan-btn {
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
background-color: #3c9cff;
color: #fff;
border: none;
padding: 0 20rpx;
white-space: nowrap;
}
.label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
font-weight: 500;
}
.input {
width: 100%;
height: 80rpx;
border: 1rpx solid #e5e5e5;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 28rpx;
box-sizing: border-box;
}
/* 固定在底部的提交按钮 */
.submit-btn-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx 30rpx;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
z-index: 100;
}
.submit-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
background-color: #3c9cff;
color: #fff;
font-size: 32rpx;
border: none;
border-radius: 10rpx;
}
.submit-btn:active {
opacity: 0.8;
}
/* 选择书籍按钮 */
.select-books-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
background-color: #fff;
border: 2rpx dashed #3c9cff;
color: #3c9cff;
font-size: 30rpx;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
margin-top: 10rpx;
}
.select-books-btn .btn-icon {
font-size: 36rpx;
}
.select-books-btn:active {
background-color: #f0f7ff;
}
/* 已选书籍卡片(简化版) */
.selected-books-card {
background-color: #fff;
border-radius: 16rpx;
padding: 25rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.selected-books-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 15rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.selected-books-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.header-actions {
display: flex;
gap: 15rpx;
align-items: center;
}
.change-book-btn {
font-size: 24rpx;
color: #3c9cff;
padding: 8rpx 16rpx;
background-color: #f0f7ff;
border-radius: 20rpx;
}
.clear-books-btn {
font-size: 24rpx;
color: #ff5722;
padding: 8rpx 16rpx;
background-color: #fff3e0;
border-radius: 20rpx;
}
.selected-books-list {
max-height: 400rpx;
}
.selected-book-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15rpx;
background-color: #f9f9f9;
border-radius: 10rpx;
margin-bottom: 12rpx;
transition: all 0.2s;
}
.selected-book-item:last-child {
margin-bottom: 0;
}
.book-item-left {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
}
.book-thumb {
width: 80rpx;
height: 110rpx;
border-radius: 6rpx;
background-color: #e5e5e5;
margin-right: 15rpx;
flex-shrink: 0;
}
.book-info-compact {
flex: 1;
display: flex;
flex-direction: column;
gap: 6rpx;
min-width: 0;
}
.book-name-compact {
font-size: 26rpx;
font-weight: 500;
color: #333;
line-height: 36rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.book-isbn {
font-size: 22rpx;
color: #999;
}
.book-price-compact {
font-size: 24rpx;
font-weight: bold;
color: #ff5722;
margin-top: 4rpx;
}
.book-item-right {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8rpx;
flex-shrink: 0;
margin-left: 15rpx;
min-width: 140rpx;
}
/* 迷你库存控制器 */
.inventory-control-mini {
display: flex;
align-items: center;
gap: 8rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
padding: 4rpx;
}
.inventory-control-mini.disabled-control {
opacity: 0.5;
}
.mini-btn {
width: 44rpx;
height: 44rpx;
background-color: #fff;
border: 1rpx solid #e0e0e0;
border-radius: 6rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #3c9cff;
padding: 0;
line-height: 1;
flex-shrink: 0;
transition: all 0.2s;
}
.mini-btn:active {
background-color: #3c9cff;
color: #fff;
transform: scale(0.95);
}
.mini-btn.disabled {
opacity: 0.3;
background-color: #f5f5f5;
}
.mini-input:disabled {
opacity: 0.5;
background-color: #e5e5e5;
}
.mini-input {
width: 70rpx;
height: 44rpx;
text-align: center;
font-size: 24rpx;
font-weight: bold;
color: #333;
background-color: #fff;
border: 1rpx solid #e0e0e0;
border-radius: 6rpx;
padding: 0;
flex-shrink: 0;
}
.stock-info-row {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 20rpx;
}
.original-stock {
color: #999;
}
.new-stock {
color: #999;
}
.new-stock.stock-changed {
color: #ff5722;
font-weight: 500;
}
.stock-change {
color: #999;
background-color: #f5f5f5;
padding: 2rpx 8rpx;
border-radius: 4rpx;
}
.stock-change.changed {
color: #fff;
background-color: #3c9cff;
font-weight: 500;
}
/* 书籍选择模态框 */
.book-modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx;
}
.book-modal {
width: 100%;
max-width: 660rpx;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
display: flex;
flex-direction: column;
max-height: 80vh;
}
.book-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #e5e5e5;
background-color: #fff;
flex-shrink: 0;
}
.book-modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.close-modal-btn {
font-size: 40rpx;
color: #999;
padding: 0 10rpx;
line-height: 1;
}
.book-modal-content {
flex: 1;
overflow-y: auto;
padding: 20rpx 30rpx;
background-color: #f9f9f9;
width: 100%;
box-sizing: border-box;
}
.book-list {
display: flex;
flex-direction: column;
gap: 20rpx;
width: 100%;
box-sizing: border-box;
}
.book-item {
display: flex;
padding: 20rpx;
padding-right: 80rpx;
background-color: #fff;
border-radius: 12rpx;
border: 2rpx solid transparent;
transition: all 0.3s;
position: relative;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
width: 100%;
box-sizing: border-box;
overflow: hidden;
}
.book-item.selected-book {
border-color: #3c9cff;
background-color: #f0f7ff;
box-shadow: 0 4rpx 12rpx rgba(60, 156, 255, 0.2);
}
.book-image-wrapper {
width: 140rpx;
height: 200rpx;
margin-right: 20rpx;
position: relative;
flex-shrink: 0;
flex-grow: 0;
}
.book-image {
width: 100%;
height: 100%;
border-radius: 8rpx;
background-color: #e5e5e5;
}
.stock-badge {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
font-size: 20rpx;
text-align: center;
padding: 6rpx 0;
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.stock-badge.in-stock {
background-color: rgba(76, 175, 80, 0.9);
}
.book-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 6rpx;
min-width: 0;
overflow: hidden;
flex-shrink: 1;
}
.book-name {
font-size: 26rpx;
font-weight: bold;
color: #333;
line-height: 36rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.book-detail-item {
display: flex;
align-items: center;
font-size: 22rpx;
line-height: 30rpx;
}
.detail-label {
color: #999;
margin-right: 6rpx;
flex-shrink: 0;
}
.detail-value {
color: #666;
flex: 1;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.condition-tag {
display: inline-block;
padding: 2rpx 10rpx;
background-color: #fff3cd;
color: #856404;
border-radius: 4rpx;
font-size: 20rpx;
}
.price-text {
color: #ff5722;
font-weight: bold;
font-size: 26rpx;
}
.art-no {
font-family: monospace;
font-size: 18rpx;
color: #999;
}
.book-action {
position: absolute;
top: 20rpx;
right: 20rpx;
flex-shrink: 0;
width: 45rpx;
height: 45rpx;
}
.select-indicator {
width: 45rpx;
height: 45rpx;
background-color: #3c9cff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 28rpx;
font-weight: bold;
}
/* 加载更多按钮 */
.load-more-container {
padding: 20rpx 0;
display: flex;
justify-content: center;
}
.load-more-btn {
width: 100%;
height: 70rpx;
line-height: 70rpx;
background-color: #fff;
border: 2rpx solid #3c9cff;
color: #3c9cff;
font-size: 28rpx;
border-radius: 10rpx;
text-align: center;
}
.load-more-btn:active {
background-color: #3c9cff;
color: #fff;
}
/* 没有更多提示 */
.no-more-tip {
padding: 20rpx 0;
text-align: center;
}
.no-more-text {
font-size: 24rpx;
color: #999;
}
.book-modal-footer {
display: flex;
gap: 20rpx;
padding: 20rpx 30rpx;
border-top: 1rpx solid #e5e5e5;
background-color: #fff;
flex-shrink: 0;
}
.modal-btn {
flex: 1;
height: 80rpx;
line-height: 80rpx;
border-radius: 10rpx;
font-size: 28rpx;
border: none;
}
.cancel-btn {
background-color: #f5f5f5;
color: #666;
}
.confirm-btn {
background-color: #3c9cff;
color: #fff;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
background-color: #fff;
border-radius: 20rpx;
margin-top: 30rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
/* 地址输入框 */
.address-input-wrapper {
position: relative;
display: flex;
align-items: center;
}
.address-arrow {
position: absolute;
right: 20rpx;
font-size: 40rpx;
color: #999;
font-weight: bold;
line-height: 80rpx;
pointer-events: none;
}
/* 地址输入弹窗 */
.address-modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx;
}
.address-modal {
width: 100%;
max-width: 600rpx;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
display: flex;
flex-direction: column;
}
.address-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #e5e5e5;
background-color: #fff;
flex-shrink: 0;
}
.address-modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.address-modal-content {
padding: 30rpx;
background-color: #fff;
}
.address-form-item {
margin-bottom: 25rpx;
}
.address-form-item:last-child {
margin-bottom: 0;
}
.modal-label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
font-weight: 500;
}
.modal-input {
width: 100%;
height: 80rpx;
border: 1rpx solid #e5e5e5;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 28rpx;
box-sizing: border-box;
background-color: #f9f9f9;
}
.address-modal-footer {
display: flex;
gap: 20rpx;
padding: 20rpx 30rpx;
border-top: 1rpx solid #e5e5e5;
background-color: #fff;
flex-shrink: 0;
}
</style>