RegularRequisition.vue 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. <!-- 常用领料 -->
  2. <template>
  3. <div class="page-container">
  4. <!-- 顶部信息栏 -->
  5. <PageHeader :is-go-home="false" title="常用领料">
  6. <template #actions>
  7. <button class="action-btn cart-btn" @click="openStockOutCar">
  8. <van-badge :content="count" :show-zero="true">
  9. <i class="fas fa-shopping-cart text-xl" />
  10. </van-badge>
  11. <span class="ml-2">领料车</span>
  12. </button>
  13. </template>
  14. </PageHeader>
  15. <!-- 主内容区域 -->
  16. <main class="main-content">
  17. <!-- 页面标题 -->
  18. <!-- <div class="page-title">
  19. <h2>常用领料</h2>
  20. </div> -->
  21. <!-- 筛选区域 -->
  22. <FilterPanel :default-collapsed="false" :enable-collapse="false" :active-count="getActiveFilterCount()">
  23. <div class="filter-form">
  24. <div class="filter-item">
  25. <label class="filter-label">仓库</label>
  26. <v-select
  27. v-model="warehouseId" :options="warehouseList" :reduce="item => item.id" label="name"
  28. placeholder="选择仓库" :clearable="true" :filterable="true" class="filter-select"
  29. @update:model-value="getDatas"
  30. >
  31. <template #no-options>
  32. <span>无该选项数据</span>
  33. </template>
  34. </v-select>
  35. </div>
  36. <div class="filter-item">
  37. <label class="filter-label">名称</label>
  38. <van-field v-model="inventoryName" placeholder="输入名称" class="filter-input" @keyup.enter="getDatas" />
  39. </div>
  40. <div class="filter-item">
  41. <label class="filter-label">编号</label>
  42. <van-field v-model="inventoryNo" placeholder="输入编号" class="filter-input" @keyup.enter="getDatas" />
  43. </div>
  44. <div class="filter-item">
  45. <label class="filter-label">类型</label>
  46. <v-select
  47. v-model="inventoryType" :options="inventoryTypeList" :reduce="item => item.value" label="label"
  48. placeholder="选择类型" :clearable="true" :filterable="true" class="filter-select"
  49. @update:model-value="getDatas"
  50. >
  51. <template #no-options>
  52. <span>无该选项数据</span>
  53. </template>
  54. </v-select>
  55. </div>
  56. <div class="filter-item filter-buttons">
  57. <van-button type="primary" @click="getDatas">
  58. <i class="fas fa-search mr-1" /> 搜索
  59. </van-button>
  60. <van-button class="ml-2" @click="handleReset">
  61. <i class="fas fa-redo mr-1" /> 重置
  62. </van-button>
  63. </div>
  64. </div>
  65. </FilterPanel>
  66. <!-- 卡片容器 -->
  67. <div class="card-container">
  68. <!-- 全选控制栏 -->
  69. <div v-if="stockRequisitionList.length > 0" class="select-all-bar">
  70. <van-checkbox :model-value="isAllSelected" @update:model-value="toggleSelectAll">
  71. <span class="select-all-text">全选当前页(已选 {{ selectedIds.length }} 项)</span>
  72. </van-checkbox>
  73. </div>
  74. <!-- 卡片列表区域 -->
  75. <div class="card-list-wrapper">
  76. <!-- 空状态 -->
  77. <van-empty v-if="stockRequisitionList.length === 0" description="暂无数据" />
  78. <!-- 卡片列表 -->
  79. <div v-else class="card-list">
  80. <div
  81. v-for="(item, index) in stockRequisitionList" :key="item.id || index"
  82. :class="{ 'selected-card': selectedIds.includes(item.id) }" class="inventory-card"
  83. @click="toggleSelect(item.id)"
  84. >
  85. <div class="card-header">
  86. <div class="card-header-left">
  87. <van-checkbox
  88. :model-value="selectedIds.includes(item.id)" @click.stop
  89. @update:model-value="checked => handleCheckboxChange(checked, item.id)"
  90. />
  91. <div class="custom-avatar ml-3">
  92. <i :class="getInventoryIcon(item.inventoryType)" />
  93. </div>
  94. <div class="ml-4 card-title-info">
  95. <div class="card-name">{{ item.inventoryName }}</div>
  96. <div class="card-subtitle">编号: {{ item.inventoryNo }}</div>
  97. </div>
  98. </div>
  99. <div class="card-header-center">
  100. <div class="card-stats">
  101. <div class="stat-item">
  102. <i class="fas fa-history" />
  103. <span class="stat-label">借用次数</span>
  104. <span class="stat-value">{{ item.borrowTotal || 0 }}次</span>
  105. </div>
  106. <div class="stat-item">
  107. <i class="fas fa-clock" />
  108. <span class="stat-label">上次借用</span>
  109. <span class="stat-value">{{ item.lastBorrowTime || '暂无记录' }}</span>
  110. </div>
  111. </div>
  112. </div>
  113. <div class="card-header-right">
  114. <div class="card-location">
  115. <i class="fas fa-map-marker-alt mr-2" />
  116. <span>{{ item.inventoryActulPosition || item.inventoryPosition || '-' }}</span>
  117. <span class="mx-2">/</span>
  118. <span>{{ item.inventoryWarehouse || '-' }}</span>
  119. </div>
  120. </div>
  121. </div>
  122. </div>
  123. </div>
  124. </div>
  125. <!-- 分页区域(固定底部) -->
  126. <div v-if="stockRequisitionList.length > 0" class="pagination-wrapper">
  127. <div class="pagination-info">
  128. 第{{ (pagination.current_page - 1) * pagination.per_page + 1 }}-{{ Math.min(pagination.current_page *
  129. pagination.per_page, pagination.total) }}条,共{{ pagination.total }}条
  130. </div>
  131. <div class="pagination-controls">
  132. <button
  133. class="pagination-btn" :disabled="pagination.current_page === 1"
  134. @click="handlePageChange(pagination.current_page - 1)"
  135. >
  136. &lt;
  137. </button>
  138. <template v-for="page in getPageNumbers()" :key="page">
  139. <button
  140. v-if="page !== '...'" class="pagination-btn" :class="{ active: page === pagination.current_page }"
  141. @click="handlePageChange(page)"
  142. >
  143. {{ page }}
  144. </button>
  145. <span v-else class="pagination-ellipsis">...</span>
  146. </template>
  147. <button
  148. class="pagination-btn"
  149. :disabled="pagination.current_page === Math.ceil(pagination.total / pagination.per_page)"
  150. @click="handlePageChange(pagination.current_page + 1)"
  151. >
  152. &gt;
  153. </button>
  154. </div>
  155. <div class="pagination-size">
  156. <v-select
  157. v-model="pagination.per_page" :options="pageSizeOptions" :clearable="false" :searchable="false"
  158. :append-to-body="true" :calculate-position="withPopper" class="size-select"
  159. @update:model-value="handlePageSizeChange"
  160. >
  161. <template #selected-option="{ label }">
  162. {{ label }}条/页
  163. </template>
  164. <template #option="{ label }">
  165. {{ label }}条/页
  166. </template>
  167. </v-select>
  168. </div>
  169. </div>
  170. </div>
  171. </main>
  172. <!-- 底部操作按钮 -->
  173. <div class="bottom-actions">
  174. <van-button
  175. type="primary" size="large" :disabled="selectedIds.length === 0" class="add-to-cart-btn"
  176. @click="submitStock"
  177. >
  178. <i class="fas fa-cart-plus mr-2" />
  179. 加入领料车
  180. </van-button>
  181. </div>
  182. <Loading v-if="loading" />
  183. </div>
  184. </template>
  185. <script setup>
  186. import { ref, computed, onMounted } from 'vue';
  187. import { useRouter } from 'vue-router';
  188. import Common from '../common/Common.js';
  189. import { showNotify } from 'vant';
  190. import vSelect from 'vue-select';
  191. import 'vue-select/dist/vue-select.css';
  192. import { createPopper } from '@popperjs/core';
  193. // 为 vue-select 配置 popper
  194. const withPopper = (dropdownList, component, { width }) => {
  195. dropdownList.style.width = width;
  196. const popper = createPopper(component.$refs.toggle, dropdownList, {
  197. placement: 'bottom',
  198. modifiers: [
  199. {
  200. name: 'offset',
  201. options: { offset: [0, -1] },
  202. },
  203. {
  204. name: 'toggleClass',
  205. enabled: true,
  206. phase: 'write',
  207. fn({ state }) {
  208. component.$el.classList.toggle('drop-up', state.placement === 'top');
  209. },
  210. },
  211. ],
  212. });
  213. return () => popper.destroy();
  214. };
  215. import PageHeader from '../common/PageHeader.vue';
  216. import FilterPanel from '../common/FilterPanel.vue';
  217. import { getWarehouseList } from '../api/stock.js';
  218. import { queryCommonUse, createStockOutPrepareLine, queryPickingCarNumber } from '../api/stockOut.js';
  219. const router = useRouter();
  220. const warehouseId = ref(undefined);
  221. const inventoryName = ref('');
  222. const inventoryNo = ref('');
  223. const inventoryType = ref(undefined);
  224. const count = ref(0);
  225. const warehouseList = ref([]);
  226. const stockRequisitionList = ref([]);
  227. const selectedIds = ref([]);
  228. // 计算是否全选
  229. const isAllSelected = computed(() => {
  230. if (stockRequisitionList.value.length === 0) return false;
  231. return stockRequisitionList.value.every(item => selectedIds.value.includes(item.id));
  232. });
  233. const loading = ref(false);
  234. const inventoryTypeList = ref([
  235. { value: 'Clamp', label: '工装' },
  236. { value: 'Instrument', label: '设备' },
  237. { value: 'FinishProduct', label: '成品' },
  238. ]);
  239. const pagination = ref({
  240. total: 0,
  241. current_page: 1,
  242. per_page: 20,
  243. });
  244. const pageSizeOptions = [10, 20, 50, 100, 200, 500];
  245. // 计算激活的筛选条件数量
  246. const getActiveFilterCount = () => {
  247. let count = 0;
  248. if (warehouseId.value) count++;
  249. if (inventoryName.value) count++;
  250. if (inventoryNo.value) count++;
  251. if (inventoryType.value) count++;
  252. return count;
  253. };
  254. // 打开领料车
  255. const openStockOutCar = () => {
  256. router.push('/stock-picking-car?isRegular=true');
  257. };
  258. // 切换选择
  259. const toggleSelect = id => {
  260. const index = selectedIds.value.indexOf(id);
  261. if (index > -1) {
  262. selectedIds.value.splice(index, 1);
  263. } else {
  264. selectedIds.value.push(id);
  265. }
  266. };
  267. // 处理复选框变化
  268. const handleCheckboxChange = (checked, id) => {
  269. if (checked) {
  270. if (!selectedIds.value.includes(id)) {
  271. selectedIds.value.push(id);
  272. }
  273. } else {
  274. const index = selectedIds.value.indexOf(id);
  275. if (index > -1) {
  276. selectedIds.value.splice(index, 1);
  277. }
  278. }
  279. };
  280. // 全选/取消全选
  281. const toggleSelectAll = checked => {
  282. if (checked) {
  283. // 全选当前页所有项(累加模式)
  284. stockRequisitionList.value.forEach(item => {
  285. if (!selectedIds.value.includes(item.id)) {
  286. selectedIds.value.push(item.id);
  287. }
  288. });
  289. } else {
  290. // 取消全选当前页
  291. const currentPageIds = stockRequisitionList.value.map(item => item.id);
  292. selectedIds.value = selectedIds.value.filter(id => !currentPageIds.includes(id));
  293. }
  294. };
  295. // 获取设备类型图标
  296. const getInventoryIcon = type => {
  297. const iconMap = {
  298. '工装': 'fas fa-cube',
  299. '设备': 'fas fa-cogs',
  300. '成品': 'fas fa-box',
  301. };
  302. return iconMap[type] || 'fas fa-cube';
  303. };
  304. // 分页改变
  305. const handlePageChange = page => {
  306. pagination.value.current_page = page;
  307. getStockRequisitionList();
  308. };
  309. // 每页大小改变
  310. const handlePageSizeChange = pageSize => {
  311. pagination.value.per_page = pageSize;
  312. pagination.value.current_page = 1;
  313. getStockRequisitionList();
  314. };
  315. // 获取分页页码数组
  316. const getPageNumbers = () => {
  317. const totalPages = Math.ceil(pagination.value.total / pagination.value.per_page);
  318. const current = pagination.value.current_page;
  319. const pages = [];
  320. if (totalPages <= 7) {
  321. for (let i = 1; i <= totalPages; i++) {
  322. pages.push(i);
  323. }
  324. } else {
  325. if (current <= 3) {
  326. for (let i = 1; i <= 5; i++) pages.push(i);
  327. pages.push('...');
  328. pages.push(totalPages);
  329. } else if (current >= totalPages - 2) {
  330. pages.push(1);
  331. pages.push('...');
  332. for (let i = totalPages - 4; i <= totalPages; i++) pages.push(i);
  333. } else {
  334. pages.push(1);
  335. pages.push('...');
  336. for (let i = current - 1; i <= current + 1; i++) pages.push(i);
  337. pages.push('...');
  338. pages.push(totalPages);
  339. }
  340. }
  341. return pages;
  342. };
  343. // 查询物料数据
  344. const getStockRequisitionList = async () => {
  345. loading.value = true;
  346. const params = {
  347. inventoryName: inventoryName.value,
  348. inventoryNo: inventoryNo.value,
  349. inventoryType: inventoryType.value,
  350. warehouseId: warehouseId.value,
  351. range: {
  352. start: (pagination.value.current_page - 1) * pagination.value.per_page,
  353. length: pagination.value.per_page,
  354. },
  355. };
  356. try {
  357. const res = await queryCommonUse(params);
  358. if (res.errorCode == 0) {
  359. if (res.datas && res.datas.length > 0) {
  360. stockRequisitionList.value = res.datas;
  361. pagination.value.total = res.total;
  362. } else {
  363. stockRequisitionList.value = [];
  364. pagination.value.total = 0;
  365. }
  366. }
  367. } catch (error) {
  368. showNotify({ type: 'danger', message: '查询常用物料API失败' });
  369. console.error('查询常用物料API失败', error);
  370. } finally {
  371. loading.value = false;
  372. }
  373. };
  374. // 获取仓库列表
  375. const getWarehouses = async () => {
  376. try {
  377. const res = await getWarehouseList();
  378. if (res.errorCode == 0) {
  379. if (res.datas && res.datas.length > 0) {
  380. warehouseList.value = res.datas;
  381. } else {
  382. warehouseList.value = [];
  383. }
  384. } else {
  385. showNotify({ type: 'danger', message: res.errorMessage });
  386. }
  387. } catch (error) {
  388. console.error('获取仓库数据失败:', error);
  389. showNotify({ type: 'danger', message: '获取库存数据失败' });
  390. }
  391. };
  392. // 重置筛选条件
  393. const handleReset = () => {
  394. warehouseId.value = undefined;
  395. inventoryName.value = '';
  396. inventoryNo.value = '';
  397. inventoryType.value = undefined;
  398. getDatas();
  399. };
  400. // 查询
  401. const getDatas = () => {
  402. pagination.value.current_page = 1;
  403. getStockRequisitionList();
  404. };
  405. // 加入领料车
  406. const submitStock = async () => {
  407. if (selectedIds.value.length == 0) {
  408. showNotify({ type: 'warning', message: '请至少选择一个物料' });
  409. return;
  410. }
  411. loading.value = true;
  412. const params = {
  413. inventoryIds: selectedIds.value,
  414. };
  415. try {
  416. const res = await createStockOutPrepareLine(params);
  417. if (res.errorCode == 0) {
  418. selectedIds.value = [];
  419. getDatas();
  420. queryPickingCarCount();
  421. showNotify({ type: 'success', message: '已添加到领料车' });
  422. }
  423. } catch (error) {
  424. console.error('添加领料车失败:', error);
  425. showNotify({ type: 'danger', message: '添加到领料车失败' });
  426. } finally {
  427. loading.value = false;
  428. }
  429. };
  430. /**
  431. * 查询领料车中的数量
  432. */
  433. const queryPickingCarCount = async () => {
  434. try {
  435. const res = await queryPickingCarNumber();
  436. if (res.errorCode == 0) {
  437. if (res.data) {
  438. count.value = res.data;
  439. } else {
  440. count.value = 0;
  441. }
  442. } else {
  443. showNotify({ type: 'warning', message: res.errorMessage });
  444. }
  445. } catch (error) {
  446. console.error('查询领料车数量失败:', error);
  447. showNotify({ type: 'danger', message: '查询领料车数量失败' });
  448. }
  449. };
  450. onMounted(() => {
  451. getDatas();
  452. getWarehouses();
  453. queryPickingCarCount();
  454. });
  455. </script>
  456. <style scoped>
  457. /* 页面容器 - 固定高度布局 */
  458. .page-container {
  459. display: flex;
  460. flex-direction: column;
  461. height: 100vh;
  462. background-color: #f9fafb;
  463. overflow: hidden;
  464. }
  465. /* 主内容区 - 可滚动 */
  466. .main-content {
  467. flex: 1;
  468. overflow-y: auto;
  469. padding: 1rem;
  470. display: flex;
  471. flex-direction: column;
  472. }
  473. /* 页面标题 */
  474. .page-title {
  475. margin-bottom: 1.5rem;
  476. }
  477. .page-title h2 {
  478. font-size: 1.5rem;
  479. font-weight: 700;
  480. color: #111827;
  481. margin: 0;
  482. }
  483. /* 卡片容器 */
  484. .card-container {
  485. flex: 1;
  486. background-color: white;
  487. border-radius: 0.5rem;
  488. box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
  489. display: flex;
  490. flex-direction: column;
  491. overflow: hidden;
  492. min-height: 0;
  493. }
  494. /* 卡片列表包装器(可滚动区域) */
  495. .card-list-wrapper {
  496. flex: 1;
  497. overflow-y: auto;
  498. padding: 1.5rem;
  499. min-height: 0;
  500. }
  501. /* 卡片列表(单列布局) */
  502. .card-list {
  503. display: flex;
  504. flex-direction: column;
  505. font-weight: 600;
  506. }
  507. .card-list > * {
  508. margin-bottom: 1rem;
  509. }
  510. .card-list > *:last-child {
  511. margin-bottom: 0;
  512. }
  513. /* 分页包装器(固定底部) */
  514. .pagination-wrapper {
  515. padding: 0.5rem 1.5rem;
  516. border-top: 1px solid #e5e7eb;
  517. background-color: #fafafa;
  518. display: flex;
  519. justify-content: flex-end;
  520. align-items: center;
  521. }
  522. .pagination-wrapper > * {
  523. margin-left: 1rem;
  524. }
  525. .pagination-wrapper > *:first-child {
  526. margin-left: 0;
  527. }
  528. .pagination-info {
  529. font-size: 14px;
  530. color: #6b7280;
  531. white-space: nowrap;
  532. }
  533. .pagination-controls {
  534. display: flex;
  535. align-items: center;
  536. }
  537. .pagination-controls > * {
  538. margin-right: 0.25rem;
  539. }
  540. .pagination-controls > *:last-child {
  541. margin-right: 0;
  542. }
  543. .pagination-btn {
  544. min-width: 32px;
  545. height: 32px;
  546. padding: 0 8px;
  547. border: 1px solid #d1d5db;
  548. background: white;
  549. color: #374151;
  550. border-radius: 4px;
  551. cursor: pointer;
  552. font-size: 14px;
  553. transition: all 0.2s;
  554. }
  555. .pagination-btn:hover:not(:disabled) {
  556. border-color: #3b82f6;
  557. color: #3b82f6;
  558. }
  559. .pagination-btn.active {
  560. background: #3b82f6;
  561. border-color: #3b82f6;
  562. color: white;
  563. }
  564. .pagination-btn:disabled {
  565. opacity: 0.5;
  566. cursor: not-allowed;
  567. }
  568. .pagination-ellipsis {
  569. padding: 0 4px;
  570. color: #6b7280;
  571. }
  572. .pagination-size {
  573. display: flex;
  574. align-items: center;
  575. }
  576. .pagination-size > * {
  577. margin-right: 0.5rem;
  578. }
  579. .pagination-size > *:last-child {
  580. margin-right: 0;
  581. }
  582. .size-select {
  583. width: 150px;
  584. position: relative;
  585. z-index: 50;
  586. }
  587. :deep(.size-select .vs__dropdown-menu) {
  588. z-index: 9999 !important;
  589. }
  590. /* 底部操作按钮 */
  591. .bottom-actions {
  592. position: sticky;
  593. bottom: 0;
  594. padding: 0 1rem 1rem 1rem;
  595. background-color: #f9fafb;
  596. display: flex;
  597. justify-content: flex-end;
  598. z-index: 10;
  599. }
  600. /* 领料车按钮样式 */
  601. .cart-btn {
  602. background-color: #d1fae5;
  603. color: #065f46;
  604. padding: 0.5rem 1rem;
  605. border-radius: 0.5rem;
  606. font-weight: 500;
  607. transition: all 0.2s;
  608. display: inline-flex;
  609. align-items: center;
  610. border: none;
  611. cursor: pointer;
  612. font-size: 14px;
  613. }
  614. .cart-btn:hover {
  615. background-color: #a7f3d0;
  616. }
  617. /* 筛选表单 */
  618. .filter-form {
  619. display: flex;
  620. flex-wrap: wrap;
  621. align-items: center;
  622. }
  623. .filter-form > * {
  624. margin-right: 1rem;
  625. margin-bottom: 0.4rem;
  626. }
  627. .filter-item {
  628. display: flex;
  629. align-items: center;
  630. }
  631. .filter-item > * {
  632. margin-right: 0.5rem;
  633. }
  634. .filter-item > *:last-child {
  635. margin-right: 0;
  636. }
  637. .filter-label {
  638. font-size: 14px;
  639. font-weight: 600;
  640. white-space: nowrap;
  641. color: #374151;
  642. }
  643. .filter-select {
  644. width: 200px;
  645. position: relative;
  646. z-index: 100;
  647. }
  648. .filter-input {
  649. width: 200px;
  650. }
  651. :deep(.filter-input .van-cell) {
  652. padding: 8px 12px !important;
  653. border: 1px solid #d1d5db !important;
  654. border-radius: 4px !important;
  655. background-color: white !important;
  656. min-height: 32px !important;
  657. box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
  658. }
  659. :deep(.filter-input .van-cell::after) {
  660. display: none !important;
  661. }
  662. :deep(.filter-input .van-field__body) {
  663. border: none !important;
  664. }
  665. :deep(.filter-input .van-field__control) {
  666. font-size: 14px;
  667. }
  668. /* Vue Select 样式 */
  669. :deep(.v-select) {
  670. font-size: 14px;
  671. }
  672. :deep(.v-select .vs__dropdown-toggle) {
  673. border: 1px solid #d1d5db;
  674. border-radius: 4px;
  675. padding: 4px 8px;
  676. min-height: 32px;
  677. background: white;
  678. }
  679. :deep(.v-select .vs__dropdown-menu) {
  680. z-index: 9999 !important;
  681. border: 1px solid #d1d5db;
  682. box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  683. }
  684. :deep(.v-select .vs__selected) {
  685. margin: 2px;
  686. padding: 0 4px;
  687. }
  688. :deep(.v-select .vs__search) {
  689. padding: 0;
  690. margin: 0;
  691. }
  692. :deep(.v-select .vs__actions) {
  693. padding: 0 4px;
  694. }
  695. .filter-buttons {
  696. display: flex;
  697. }
  698. .filter-buttons > * {
  699. margin-right: 0.5rem;
  700. }
  701. .filter-buttons > *:last-child {
  702. margin-right: 0;
  703. }
  704. /* 库存卡片样式 */
  705. .inventory-card {
  706. cursor: pointer;
  707. transition: all 0.25s ease;
  708. border: 2px solid #e5e7eb;
  709. background-color: #ffffff;
  710. border-radius: 0.5rem;
  711. }
  712. .inventory-card:hover {
  713. border-color: #93c5fd;
  714. box-shadow: 0 4px 12px rgba(59, 130, 246, 0.1);
  715. }
  716. .inventory-card.selected-card {
  717. border-color: #3b82f6 !important;
  718. background-color: #eff6ff;
  719. box-shadow: 0 4px 16px rgba(59, 130, 246, 0.2);
  720. }
  721. /* 卡片标题区域 */
  722. .card-header {
  723. display: flex;
  724. align-items: center;
  725. justify-content: space-between;
  726. width: 100%;
  727. }
  728. .card-header > * {
  729. margin-right: 1.5rem;
  730. }
  731. .card-header > *:last-child {
  732. margin-right: 0;
  733. }
  734. .card-header-left {
  735. display: flex;
  736. align-items: center;
  737. flex: 1;
  738. min-width: 0;
  739. }
  740. .card-header-center {
  741. display: flex;
  742. align-items: center;
  743. flex-shrink: 0;
  744. }
  745. .card-header-right {
  746. display: flex;
  747. align-items: center;
  748. flex-shrink: 0;
  749. }
  750. .card-header-right > * {
  751. margin-left: 1rem;
  752. }
  753. .card-header-right > *:first-child {
  754. margin-left: 0;
  755. }
  756. /* 卡片标题信息 */
  757. .card-title-info {
  758. display: flex;
  759. flex-direction: column;
  760. min-width: 0;
  761. }
  762. .card-title-info > * {
  763. margin-bottom: 0.25rem;
  764. }
  765. .card-title-info > *:last-child {
  766. margin-bottom: 0;
  767. }
  768. .card-name {
  769. font-size: 1.2rem;
  770. font-weight: 600;
  771. color: #111827;
  772. line-height: 1.5;
  773. white-space: nowrap;
  774. overflow: hidden;
  775. text-overflow: ellipsis;
  776. }
  777. .card-subtitle {
  778. font-size: 0.875rem;
  779. color: #6b7280;
  780. line-height: 1.4;
  781. }
  782. /* 卡片位置信息 */
  783. .card-location {
  784. display: flex;
  785. align-items: center;
  786. padding: 0.5rem 1rem;
  787. background-color: #f3f4f6;
  788. border-radius: 0.375rem;
  789. font-size: 0.875rem;
  790. color: #374151;
  791. white-space: nowrap;
  792. }
  793. .card-location i {
  794. color: #3b82f6;
  795. }
  796. .card-location {
  797. flex-shrink: 0;
  798. }
  799. /* 卡片统计信息 */
  800. .card-stats {
  801. display: flex;
  802. align-items: center;
  803. padding: 0.4rem 1.25rem;
  804. background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
  805. border-radius: 0.5rem;
  806. margin-top: 0.75rem;
  807. }
  808. .card-stats > * {
  809. margin-right: 1.5rem;
  810. }
  811. .card-stats > *:last-child {
  812. margin-right: 0;
  813. }
  814. .stat-item {
  815. display: flex;
  816. align-items: center;
  817. color: #0369a1;
  818. }
  819. .stat-item > * {
  820. margin-right: 0.5rem;
  821. }
  822. .stat-item > *:last-child {
  823. margin-right: 0;
  824. }
  825. .stat-item i {
  826. font-size: 0.875rem;
  827. color: #0284c7;
  828. }
  829. .stat-label {
  830. font-size: 0.75rem;
  831. color: #64748b;
  832. font-weight: 500;
  833. }
  834. .stat-value {
  835. font-size: 0.875rem;
  836. font-weight: 600;
  837. color: #0c4a6e;
  838. }
  839. /* 自定义头像 */
  840. .custom-avatar {
  841. width: 42px;
  842. height: 42px;
  843. border-radius: 50%;
  844. background-color: #3b82f6;
  845. display: flex;
  846. align-items: center;
  847. justify-content: center;
  848. color: white;
  849. font-size: 20px;
  850. flex-shrink: 0;
  851. }
  852. /* 卡片内部样式 */
  853. .inventory-card .card-header {
  854. padding: 1rem 1.5rem;
  855. }
  856. /* 按钮样式 */
  857. :deep(.van-button--primary) {
  858. background-color: #3b82f6;
  859. border-color: #3b82f6;
  860. }
  861. :deep(.van-button--primary:active) {
  862. background-color: #2563eb;
  863. border-color: #2563eb;
  864. }
  865. .add-to-cart-btn {
  866. background-color: #10b981 !important;
  867. border-color: #10b981 !important;
  868. width: auto;
  869. padding: 0 24px;
  870. }
  871. :deep(.add-to-cart-btn.van-button--primary) {
  872. background-color: #10b981 !important;
  873. border-color: #10b981 !important;
  874. }
  875. :deep(.add-to-cart-btn.van-button--primary:active) {
  876. background-color: #059669 !important;
  877. border-color: #059669 !important;
  878. }
  879. :deep(.van-button--disabled) {
  880. opacity: 0.5;
  881. cursor: not-allowed;
  882. }
  883. .action-btn {
  884. padding: 0.5rem 1rem;
  885. border-radius: 0.5rem;
  886. font-weight: 500;
  887. transition: all 0.2s;
  888. display: inline-flex;
  889. align-items: center;
  890. border: none;
  891. cursor: pointer;
  892. }
  893. :deep(.van-cell) {
  894. border: 1px solid #ddd;
  895. border-radius: 6px;
  896. padding: 6px 8px !important;
  897. }
  898. :deep(.v-select .vs__dropdown-toggle) {
  899. padding: 6px 8px !important;
  900. }
  901. /* 全选控制栏 */
  902. .select-all-bar {
  903. background-color: white;
  904. padding: 1rem 1.5rem;
  905. border-bottom: 1px solid #e5e7eb;
  906. display: flex;
  907. align-items: center;
  908. }
  909. .select-all-text {
  910. font-size: 0.95rem;
  911. color: #374151;
  912. font-weight: 500;
  913. }
  914. </style>