|
@@ -1,280 +1,165 @@
|
|
|
<template>
|
|
<template>
|
|
|
<view class="page">
|
|
<view class="page">
|
|
|
- <!-- 黄色导航栏 -->
|
|
|
|
|
- <view class="yellow-header">
|
|
|
|
|
- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
|
|
|
|
- <view class="header-content">
|
|
|
|
|
- <view class="back-btn" @click="goBack">
|
|
|
|
|
- <view class="back-arrow"></view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="header-title">补货单详情</text>
|
|
|
|
|
- <view class="header-actions">
|
|
|
|
|
- <view class="action-dot"></view>
|
|
|
|
|
- <view class="action-dot"></view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="header-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
|
|
|
|
|
|
+ <NavBar title="补货单详情" :showBack="true" />
|
|
|
|
|
|
|
|
- <view v-if="loading" class="loading-container">
|
|
|
|
|
- <view class="loading-spinner"></view>
|
|
|
|
|
- <text class="loading-text">加载中...</text>
|
|
|
|
|
|
|
+ <view class="load-tip" v-if="loading">
|
|
|
|
|
+ <view class="dot-row"><view class="pulse-dot"></view><view class="pulse-dot"></view><view class="pulse-dot"></view></view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<template v-else-if="order">
|
|
<template v-else-if="order">
|
|
|
- <!-- 蓝牙打印机提示 -->
|
|
|
|
|
- <view class="printer-bar">
|
|
|
|
|
- <text class="printer-text">暂未连接蓝牙打印机</text>
|
|
|
|
|
- <text class="printer-link" @click="goPrinterConfig">去配置</text>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 订单头部信息 -->
|
|
|
|
|
- <view class="order-header-card">
|
|
|
|
|
- <view class="order-title-row">
|
|
|
|
|
- <text class="order-title">{{ order.deviceName || order.deviceId }}</text>
|
|
|
|
|
- <text class="order-status" :class="statusClass(order.status)">{{ statusLabel(order.status) }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-info-row">
|
|
|
|
|
- <text class="info-label">门店名称:</text>
|
|
|
|
|
- <text class="info-value">{{ order.shopName || order.deviceName || '-' }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-info-row">
|
|
|
|
|
- <text class="info-label">任务编号:</text>
|
|
|
|
|
- <text class="info-value">{{ order.taskNo || order.orderNo || '-' }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-info-row">
|
|
|
|
|
- <text class="info-label">创建时间:</text>
|
|
|
|
|
- <text class="info-value">{{ order.createTime || '-' }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="export-btn" @click="handleExport">
|
|
|
|
|
- <text>导出/打印</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 待下架商品 -->
|
|
|
|
|
- <view class="section-block">
|
|
|
|
|
- <view class="section-title-row">
|
|
|
|
|
- <text class="section-title">待下架商品</text>
|
|
|
|
|
- <text class="section-count">(共{{ pendingRemovalItems.length }}种商品)</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="empty-tip" v-if="pendingRemovalItems.length === 0">
|
|
|
|
|
- <text>暂无待下架商品</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view
|
|
|
|
|
- class="product-row"
|
|
|
|
|
- v-for="(item, index) in pendingRemovalItems"
|
|
|
|
|
- :key="'remove-' + index"
|
|
|
|
|
- >
|
|
|
|
|
- <image v-if="item.productImage" :src="item.productImage" class="product-img" mode="aspectFill" />
|
|
|
|
|
- <view v-else class="product-img-placeholder">
|
|
|
|
|
- <text class="placeholder-text">无图</text>
|
|
|
|
|
|
|
+ <scroll-view class="detail-scroll" scroll-y>
|
|
|
|
|
+ <!-- 订单头 -->
|
|
|
|
|
+ <view class="header-card">
|
|
|
|
|
+ <view class="head-row">
|
|
|
|
|
+ <text class="head-title">{{ order.deviceName || order.deviceId }}</text>
|
|
|
|
|
+ <text class="head-status" :class="statusClass(order.status)">{{ statusLabel(order.status) }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="product-info">
|
|
|
|
|
- <text class="product-name">{{ item.productName || '未知商品' }}</text>
|
|
|
|
|
|
|
+ <view class="head-info">
|
|
|
|
|
+ <text class="hi-label">门店</text>
|
|
|
|
|
+ <text class="hi-value">{{ order.shopName || '-' }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <text class="product-qty">{{ item.plannedQuantity || item.quantity || 0 }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 待补货商品 -->
|
|
|
|
|
- <view class="section-block">
|
|
|
|
|
- <view class="section-title-row">
|
|
|
|
|
- <view class="section-title-left">
|
|
|
|
|
- <text class="section-title">待补货商品</text>
|
|
|
|
|
- <text class="section-count">(共{{ replenishItems.length }}个商品)</text>
|
|
|
|
|
|
|
+ <view class="head-info">
|
|
|
|
|
+ <text class="hi-label">编号</text>
|
|
|
|
|
+ <text class="hi-value">{{ order.orderNo || '-' }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="show-zero-toggle" @click="showZeroQty = !showZeroQty">
|
|
|
|
|
- <text class="toggle-label">展示待补货数量为0商品</text>
|
|
|
|
|
- <view class="toggle-switch" :class="{ on: showZeroQty }">
|
|
|
|
|
- <view class="toggle-knob"></view>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
+ <view class="head-info">
|
|
|
|
|
+ <text class="hi-label">创建时间</text>
|
|
|
|
|
+ <text class="hi-value">{{ order.createTime || '-' }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <!-- 排序栏 -->
|
|
|
|
|
- <view class="sort-bar">
|
|
|
|
|
- <view class="sort-item" :class="{ active: sortField === 'name' }" @click="sortField = 'name'">
|
|
|
|
|
- <text>商品名称</text>
|
|
|
|
|
- <text class="sort-icon">{{ sortField === 'name' ? (sortAsc ? '↑' : '↓') : '' }}</text>
|
|
|
|
|
|
|
+ <!-- 待下架商品 -->
|
|
|
|
|
+ <view class="section">
|
|
|
|
|
+ <view class="section-head">
|
|
|
|
|
+ <text class="section-title">待下架商品</text>
|
|
|
|
|
+ <text class="section-count">{{ pendingRemovalItems.length }} 种</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="sort-item" :class="{ active: sortField === 'shelfNum' }" @click="sortField = 'shelfNum'">
|
|
|
|
|
- <text>层数</text>
|
|
|
|
|
- <text class="sort-icon">{{ sortField === 'shelfNum' ? (sortAsc ? '↑' : '↓') : '⇅' }}</text>
|
|
|
|
|
|
|
+ <view class="empty-tip" v-if="pendingRemovalItems.length === 0">
|
|
|
|
|
+ <text>暂无待下架商品</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="sort-item" :class="{ active: sortField === 'quantity' }" @click="sortField = 'quantity'">
|
|
|
|
|
- <text>待补货数量</text>
|
|
|
|
|
- <text class="sort-icon">{{ sortField === 'quantity' ? (sortAsc ? '↑' : '↓') : '⇅' }}</text>
|
|
|
|
|
|
|
+ <view class="product-row" v-for="(item, i) in pendingRemovalItems" :key="'rm-' + i">
|
|
|
|
|
+ <image v-if="item.productImage" :src="item.productImage" class="product-img" mode="aspectFill" />
|
|
|
|
|
+ <view v-else class="product-img-placeholder"><text>无图</text></view>
|
|
|
|
|
+ <view class="product-info">
|
|
|
|
|
+ <text class="product-name">{{ item.productName || '未知商品' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="product-qty">{{ item.plannedQuantity || item.quantity || 0 }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <view
|
|
|
|
|
- class="product-row"
|
|
|
|
|
- v-for="(item, index) in sortedReplenishItems"
|
|
|
|
|
- :key="'replenish-' + index"
|
|
|
|
|
- >
|
|
|
|
|
- <image v-if="item.productImage" :src="item.productImage" class="product-img" mode="aspectFill" />
|
|
|
|
|
- <view v-else class="product-img-placeholder">
|
|
|
|
|
- <text class="placeholder-text">无图</text>
|
|
|
|
|
|
|
+ <!-- 待补货商品 -->
|
|
|
|
|
+ <view class="section">
|
|
|
|
|
+ <view class="section-head">
|
|
|
|
|
+ <view class="section-head-left">
|
|
|
|
|
+ <text class="section-title">待补货商品</text>
|
|
|
|
|
+ <text class="section-count">{{ displayReplenishItems.length }} 种</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="zero-toggle" @click="showZeroQty = !showZeroQty">
|
|
|
|
|
+ <text class="toggle-label">含零</text>
|
|
|
|
|
+ <view class="toggle-switch" :class="{ on: showZeroQty }"><view class="toggle-knob"></view></view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="sort-bar">
|
|
|
|
|
+ <view class="sort-item" :class="{ active: sortField === 'name' }" @click="toggleSort('name')">
|
|
|
|
|
+ <text>名称</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="sort-item" :class="{ active: sortField === 'shelfNum' }" @click="toggleSort('shelfNum')">
|
|
|
|
|
+ <text>层数</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="sort-item" :class="{ active: sortField === 'quantity' }" @click="toggleSort('quantity')">
|
|
|
|
|
+ <text>数量</text>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="product-info">
|
|
|
|
|
- <text class="product-name">{{ item.productName || '未知商品' }}</text>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <view class="product-row" v-for="(item, i) in displayReplenishItems" :key="'rp-' + i">
|
|
|
|
|
+ <image v-if="item.productImage" :src="item.productImage" class="product-img" mode="aspectFill" />
|
|
|
|
|
+ <view v-else class="product-img-placeholder"><text>无图</text></view>
|
|
|
|
|
+ <view class="product-info">
|
|
|
|
|
+ <text class="product-name">{{ item.productName || '未知商品' }}</text>
|
|
|
|
|
+ <text class="product-shelf" v-if="item.shelfNum">层 {{ item.shelfNum }} · {{ item.position || '' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="product-qty">{{ item.plannedQuantity || item.quantity || 0 }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <text class="product-qty">{{ item.plannedQuantity || item.quantity || 0 }}</text>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
- </template>
|
|
|
|
|
|
|
|
|
|
- <!-- 底部操作栏 -->
|
|
|
|
|
- <view class="bottom-bar" v-if="order && !loading">
|
|
|
|
|
- <view class="bottom-btn secondary" @click="handleRemoteOpen">
|
|
|
|
|
- <text>远程开门补货</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="bottom-btn secondary" @click="handleCancel">
|
|
|
|
|
- <text>撤销</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="bottom-btn primary" @click="handleEdit">
|
|
|
|
|
- <text>修改</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
+ <view class="bottom-spacer" v-if="canExecute(order)"></view>
|
|
|
|
|
+ </scroll-view>
|
|
|
|
|
|
|
|
- <CustomTabBar />
|
|
|
|
|
|
|
+ <!-- 底部操作 -->
|
|
|
|
|
+ <view class="bottom-bar" v-if="canExecute(order)">
|
|
|
|
|
+ <view class="action-btn" @click="goReplenish"><text>按补货单执行补货</text></view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </template>
|
|
|
</view>
|
|
</view>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
import { ref, computed, onMounted } from 'vue';
|
|
import { ref, computed, onMounted } from 'vue';
|
|
|
-import CustomTabBar from '@/components/CustomTabBar.vue';
|
|
|
|
|
|
|
+import NavBar from '@/components/NavBar.vue';
|
|
|
import { getReplenisherOrderDetail } from '@/api/replenish';
|
|
import { getReplenisherOrderDetail } from '@/api/replenish';
|
|
|
|
|
|
|
|
-const statusBarHeight = ref(0);
|
|
|
|
|
const loading = ref(true);
|
|
const loading = ref(true);
|
|
|
const order = ref<any>(null);
|
|
const order = ref<any>(null);
|
|
|
const items = ref<any[]>([]);
|
|
const items = ref<any[]>([]);
|
|
|
const showZeroQty = ref(false);
|
|
const showZeroQty = ref(false);
|
|
|
-const sortField = ref('name');
|
|
|
|
|
|
|
+const sortField = ref('shelfNum');
|
|
|
const sortAsc = ref(true);
|
|
const sortAsc = ref(true);
|
|
|
|
|
|
|
|
-const statusMap: Record<number, string> = {
|
|
|
|
|
- 0: '草稿', 1: '待补货', 2: '已同步', 3: '已完成', 4: '已取消'
|
|
|
|
|
-};
|
|
|
|
|
|
|
+const statusMap: Record<number, string> = { 0: '草稿', 1: '已提交', 2: '已同步', 3: '已完成', 4: '已取消' };
|
|
|
|
|
|
|
|
-function statusLabel(status: number): string { return statusMap[status] || '未知'; }
|
|
|
|
|
-function statusClass(status: number): string {
|
|
|
|
|
- const map: Record<number, string> = { 0: 'draft', 1: 'pending', 2: 'synced', 3: 'done', 4: 'cancelled' };
|
|
|
|
|
- return map[status] || '';
|
|
|
|
|
|
|
+function statusLabel(s: number) { return statusMap[s] || '未知'; }
|
|
|
|
|
+function statusClass(s: number) {
|
|
|
|
|
+ const m: Record<number, string> = { 0: 'draft', 1: 'submitted', 2: 'synced', 3: 'done', 4: 'cancelled' };
|
|
|
|
|
+ return m[s] || '';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 待下架商品(数量为0或标记为下架的商品)
|
|
|
|
|
-const pendingRemovalItems = computed(() => {
|
|
|
|
|
- return items.value.filter((item: any) => {
|
|
|
|
|
- const qty = item.plannedQuantity || item.quantity || 0;
|
|
|
|
|
- return item.isRemoval || qty <= 0;
|
|
|
|
|
- });
|
|
|
|
|
-});
|
|
|
|
|
|
|
+function canExecute(o: any) { return o.status === 0 || o.status === 1; }
|
|
|
|
|
|
|
|
-// 待补货商品
|
|
|
|
|
-const replenishItems = computed(() => {
|
|
|
|
|
- let list = items.value.filter((item: any) => {
|
|
|
|
|
- const qty = item.plannedQuantity || item.quantity || 0;
|
|
|
|
|
- return !item.isRemoval && qty > 0;
|
|
|
|
|
- });
|
|
|
|
|
- if (!showZeroQty.value) {
|
|
|
|
|
- list = list.filter((item: any) => (item.plannedQuantity || item.quantity || 0) > 0);
|
|
|
|
|
- }
|
|
|
|
|
- return list;
|
|
|
|
|
-});
|
|
|
|
|
|
|
+const pendingRemovalItems = computed(() =>
|
|
|
|
|
+ items.value.filter((item: any) => item.isRemoval || (item.plannedQuantity || item.quantity || 0) <= 0)
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
|
|
+const replenishItems = computed(() =>
|
|
|
|
|
+ items.value.filter((item: any) => !item.isRemoval && (item.plannedQuantity || item.quantity || 0) > 0)
|
|
|
|
|
+);
|
|
|
|
|
|
|
|
-// 排序
|
|
|
|
|
-const sortedReplenishItems = computed(() => {
|
|
|
|
|
- const list = [...replenishItems.value];
|
|
|
|
|
|
|
+const displayReplenishItems = computed(() => {
|
|
|
|
|
+ let list = showZeroQty.value ? [...replenishItems.value] : replenishItems.value.filter((i: any) => (i.plannedQuantity || i.quantity || 0) > 0);
|
|
|
const dir = sortAsc.value ? 1 : -1;
|
|
const dir = sortAsc.value ? 1 : -1;
|
|
|
- list.sort((a, b) => {
|
|
|
|
|
- if (sortField.value === 'name') {
|
|
|
|
|
- return dir * ((a.productName || '').localeCompare(b.productName || ''));
|
|
|
|
|
- }
|
|
|
|
|
- if (sortField.value === 'shelfNum') {
|
|
|
|
|
- return dir * ((a.shelfNum || 0) - (b.shelfNum || 0));
|
|
|
|
|
- }
|
|
|
|
|
- if (sortField.value === 'quantity') {
|
|
|
|
|
- return dir * ((a.plannedQuantity || a.quantity || 0) - (b.plannedQuantity || b.quantity || 0));
|
|
|
|
|
- }
|
|
|
|
|
- return 0;
|
|
|
|
|
|
|
+ list.sort((a: any, b: any) => {
|
|
|
|
|
+ if (sortField.value === 'name') return dir * (a.productName || '').localeCompare(b.productName || '');
|
|
|
|
|
+ if (sortField.value === 'shelfNum') return dir * ((a.shelfNum || 0) - (b.shelfNum || 0));
|
|
|
|
|
+ return dir * ((a.plannedQuantity || a.quantity || 0) - (b.plannedQuantity || b.quantity || 0));
|
|
|
});
|
|
});
|
|
|
return list;
|
|
return list;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-function goBack() {
|
|
|
|
|
- const pages = getCurrentPages();
|
|
|
|
|
- if (pages.length > 1) {
|
|
|
|
|
- uni.navigateBack();
|
|
|
|
|
- } else {
|
|
|
|
|
- uni.switchTab({ url: '/pages/replenish/index' });
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function goPrinterConfig() {
|
|
|
|
|
- uni.showToast({ title: '蓝牙打印机配置功能开发中', icon: 'none' });
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function handleExport() {
|
|
|
|
|
- uni.showToast({ title: '导出/打印功能开发中', icon: 'none' });
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function handleRemoteOpen() {
|
|
|
|
|
- if (!order.value) return;
|
|
|
|
|
- uni.navigateTo({
|
|
|
|
|
- url: `/pages/replenish/operation?deviceId=${order.value.deviceId}&orderId=${order.value.id}`
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function handleCancel() {
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '确认撤销',
|
|
|
|
|
- content: '确定要撤销此补货单吗?',
|
|
|
|
|
- success: (res) => {
|
|
|
|
|
- if (res.confirm) {
|
|
|
|
|
- uni.showToast({ title: '撤销功能开发中', icon: 'none' });
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+function toggleSort(field: string) {
|
|
|
|
|
+ if (sortField.value === field) { sortAsc.value = !sortAsc.value; } else { sortField.value = field; sortAsc.value = true; }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function handleEdit() {
|
|
|
|
|
|
|
+function goReplenish() {
|
|
|
if (!order.value) return;
|
|
if (!order.value) return;
|
|
|
const orderData = {
|
|
const orderData = {
|
|
|
- orderId: order.value.id,
|
|
|
|
|
- orderNo: order.value.orderNo,
|
|
|
|
|
|
|
+ orderId: order.value.id, orderNo: order.value.orderNo,
|
|
|
items: items.value.map((item: any) => ({
|
|
items: items.value.map((item: any) => ({
|
|
|
- productCode: item.productCode,
|
|
|
|
|
- productName: item.productName,
|
|
|
|
|
|
|
+ productCode: item.productCode, productName: item.productName,
|
|
|
plannedQuantity: item.plannedQuantity || item.quantity || 0,
|
|
plannedQuantity: item.plannedQuantity || item.quantity || 0,
|
|
|
- shelfNum: item.shelfNum,
|
|
|
|
|
- position: item.position
|
|
|
|
|
|
|
+ shelfNum: item.shelfNum, position: item.position
|
|
|
}))
|
|
}))
|
|
|
};
|
|
};
|
|
|
uni.setStorageSync('pendingReplenishOrder', JSON.stringify(orderData));
|
|
uni.setStorageSync('pendingReplenishOrder', JSON.stringify(orderData));
|
|
|
- uni.navigateTo({
|
|
|
|
|
- url: `/pages/replenish/operation?deviceId=${order.value.deviceId}&orderId=${order.value.id}`
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ uni.navigateTo({ url: `/pages/replenish/operation?deviceId=${order.value.deviceId}&orderId=${order.value.id}` });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
|
- const systemInfo = uni.getSystemInfoSync();
|
|
|
|
|
- statusBarHeight.value = systemInfo.statusBarHeight || 20;
|
|
|
|
|
-
|
|
|
|
|
try {
|
|
try {
|
|
|
const pages = getCurrentPages();
|
|
const pages = getCurrentPages();
|
|
|
- const currentPage = pages[pages.length - 1] as any;
|
|
|
|
|
- const id = currentPage?.options?.id;
|
|
|
|
|
- if (!id) {
|
|
|
|
|
- uni.showToast({ title: '参数错误', icon: 'none' });
|
|
|
|
|
- loading.value = false;
|
|
|
|
|
- setTimeout(() => uni.navigateBack(), 800);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const id = (pages[pages.length - 1] as any)?.options?.id;
|
|
|
|
|
+ if (!id) { uni.showToast({ title: '参数错误', icon: 'none' }); loading.value = false; setTimeout(() => uni.navigateBack(), 800); return; }
|
|
|
const data = await getReplenisherOrderDetail(id);
|
|
const data = await getReplenisherOrderDetail(id);
|
|
|
- order.value = data?.order;
|
|
|
|
|
- items.value = data?.items || [];
|
|
|
|
|
|
|
+ order.value = data?.order; items.value = data?.items || [];
|
|
|
} catch (e: any) {
|
|
} catch (e: any) {
|
|
|
uni.showToast({ title: e.message || '加载失败', icon: 'none' });
|
|
uni.showToast({ title: e.message || '加载失败', icon: 'none' });
|
|
|
} finally {
|
|
} finally {
|
|
@@ -284,174 +169,79 @@ onMounted(async () => {
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
|
-.page {
|
|
|
|
|
- min-height: 100vh;
|
|
|
|
|
- background: $bg-color-page;
|
|
|
|
|
- padding-bottom: 180rpx;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// ====== 黄色导航栏 ======
|
|
|
|
|
-.yellow-header {
|
|
|
|
|
- position: fixed; top: 0; left: 0; right: 0; z-index: 999;
|
|
|
|
|
- background: $primary-color;
|
|
|
|
|
- padding-top: constant(safe-area-inset-top);
|
|
|
|
|
- padding-top: env(safe-area-inset-top);
|
|
|
|
|
-}
|
|
|
|
|
-.header-content {
|
|
|
|
|
- display: flex; align-items: center; justify-content: space-between;
|
|
|
|
|
- height: 44px; padding: 0 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.back-btn {
|
|
|
|
|
- width: 64rpx; height: 64rpx; display: flex; align-items: center; justify-content: center;
|
|
|
|
|
- &:active { opacity: 0.7; }
|
|
|
|
|
-}
|
|
|
|
|
-.back-arrow {
|
|
|
|
|
- width: 20rpx; height: 20rpx;
|
|
|
|
|
- border-left: 4rpx solid #1e293b; border-bottom: 4rpx solid #1e293b;
|
|
|
|
|
- transform: rotate(45deg); margin-left: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.header-title { font-size: 34rpx; font-weight: 700; color: #1e293b; }
|
|
|
|
|
-.header-actions { display: flex; gap: 6rpx; padding: 8rpx;
|
|
|
|
|
- .action-dot { width: 6rpx; height: 6rpx; border-radius: 50%; background: #1e293b; }
|
|
|
|
|
-}
|
|
|
|
|
-.header-placeholder { width: 100%; }
|
|
|
|
|
-
|
|
|
|
|
-// ====== 加载 ======
|
|
|
|
|
-.loading-container {
|
|
|
|
|
- display: flex; flex-direction: column; align-items: center; padding: 100rpx 0;
|
|
|
|
|
- .loading-text { margin-top: 16rpx; font-size: 26rpx; color: $text-color-muted; }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// ====== 蓝牙打印机提示 ======
|
|
|
|
|
-.printer-bar {
|
|
|
|
|
- display: flex; align-items: center; justify-content: space-between;
|
|
|
|
|
- padding: 16rpx 24rpx; background: #fffbe6; border-bottom: 1rpx solid $border-color-light;
|
|
|
|
|
- .printer-text { font-size: 24rpx; color: $warning-color; }
|
|
|
|
|
- .printer-link { font-size: 24rpx; color: $primary-color-dark; font-weight: 600; }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// ====== 订单头部卡片 ======
|
|
|
|
|
-.order-header-card {
|
|
|
|
|
- margin: 16rpx 24rpx; padding: 24rpx;
|
|
|
|
|
- background: $bg-color-card; border-radius: $radius-md;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
- position: relative;
|
|
|
|
|
-}
|
|
|
|
|
-.order-title-row {
|
|
|
|
|
- display: flex; align-items: center; justify-content: space-between; margin-bottom: 16rpx;
|
|
|
|
|
- .order-title { font-size: 32rpx; font-weight: 700; color: $text-color-primary; }
|
|
|
|
|
- .order-status {
|
|
|
|
|
- padding: 4rpx 16rpx; border-radius: 6rpx; font-size: 22rpx; font-weight: 600;
|
|
|
|
|
- &.draft { background: #f0f0f0; color: #999; }
|
|
|
|
|
- &.pending { background: #fff3e0; color: #ff9800; }
|
|
|
|
|
- &.synced { background: #e3f2fd; color: #2196f3; }
|
|
|
|
|
- &.done { background: #e8f5e9; color: #4caf50; }
|
|
|
|
|
- &.cancelled { background: #ffebee; color: #f44336; }
|
|
|
|
|
|
|
+.page { height: 100vh; background: $bg-color-page; display: flex; flex-direction: column; overflow: hidden; }
|
|
|
|
|
+.detail-scroll { flex: 1; height: 0; overflow: hidden; }
|
|
|
|
|
+
|
|
|
|
|
+// ====== Loading ======
|
|
|
|
|
+.load-tip { display: flex; justify-content: center; padding: 200rpx 0; }
|
|
|
|
|
+.dot-row { display: flex; gap: $spacing-1; }
|
|
|
|
|
+.pulse-dot { width: 10rpx; height: 10rpx; background: $text-color-placeholder; border-radius: 50%; animation: pulse 1.2s ease-in-out infinite;
|
|
|
|
|
+ &:nth-child(2) { animation-delay: 0.2s; } &:nth-child(3) { animation-delay: 0.4s; }
|
|
|
|
|
+}
|
|
|
|
|
+@keyframes pulse { 0%,80%,100% { transform: scale(0.5); opacity: 0.4; } 40% { transform: scale(1); opacity: 1; } }
|
|
|
|
|
+
|
|
|
|
|
+// ====== Header card ======
|
|
|
|
|
+.header-card { margin: 16rpx 24rpx; padding: $spacing-3; background: $bg-color-card; border-radius: $radius-md; box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); }
|
|
|
|
|
+.head-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16rpx;
|
|
|
|
|
+ .head-title { font-size: $font-size-lg; font-weight: 700; color: $text-color-primary; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
|
|
+ .head-status { padding: 4rpx 16rpx; border-radius: $radius-sm; font-size: 22rpx; font-weight: 500;
|
|
|
|
|
+ &.draft { background: $bg-color-secondary; color: $text-color-muted; }
|
|
|
|
|
+ &.submitted { background: $warning-color-bg; color: $warning-color; }
|
|
|
|
|
+ &.synced { background: $info-color-bg; color: $info-color; }
|
|
|
|
|
+ &.done { background: $success-color-bg; color: $success-color; }
|
|
|
|
|
+ &.cancelled { background: $error-color-bg; color: $error-color; }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-.order-info-row {
|
|
|
|
|
- padding: 6rpx 0;
|
|
|
|
|
- .info-label { font-size: 24rpx; color: $text-color-tertiary; }
|
|
|
|
|
- .info-value { font-size: 24rpx; color: $text-color-primary; font-weight: 500; }
|
|
|
|
|
-}
|
|
|
|
|
-.export-btn {
|
|
|
|
|
- position: absolute; right: 24rpx; bottom: 24rpx;
|
|
|
|
|
- padding: 10rpx 24rpx; background: $primary-color; border-radius: $radius-base;
|
|
|
|
|
- font-size: 22rpx; font-weight: 600; color: #1e293b;
|
|
|
|
|
- &:active { opacity: 0.8; }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// ====== 区块 ======
|
|
|
|
|
-.section-block {
|
|
|
|
|
- padding: 0 24rpx; margin-top: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.section-title-row {
|
|
|
|
|
- display: flex; align-items: center; justify-content: space-between;
|
|
|
|
|
- padding: 16rpx 0;
|
|
|
|
|
- .section-title { font-size: 28rpx; font-weight: 700; color: $text-color-primary; }
|
|
|
|
|
- .section-count { font-size: 24rpx; color: $text-color-muted; }
|
|
|
|
|
|
|
+.head-info { padding: 6rpx 0; display: flex;
|
|
|
|
|
+ .hi-label { font-size: $font-size-sm; color: $text-color-muted; margin-right: $spacing-1; flex-shrink: 0; }
|
|
|
|
|
+ .hi-value { font-size: $font-size-sm; color: $text-color-primary; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
}
|
|
}
|
|
|
-.section-title-left { display: flex; align-items: baseline; gap: 8rpx; }
|
|
|
|
|
|
|
|
|
|
-.empty-tip {
|
|
|
|
|
- text-align: center; padding: 40rpx 0;
|
|
|
|
|
- text { font-size: 26rpx; color: $text-color-muted; }
|
|
|
|
|
|
|
+// ====== Section ======
|
|
|
|
|
+.section { padding: 0 24rpx; margin-top: 16rpx; width: 100%; box-sizing: border-box; overflow: hidden; }
|
|
|
|
|
+.section-head { display: flex; align-items: center; justify-content: space-between; padding: 16rpx 0;
|
|
|
|
|
+ .section-title { font-size: $font-size-base; font-weight: 700; color: $text-color-primary; }
|
|
|
|
|
+ .section-count { font-size: $font-size-sm; color: $text-color-muted; }
|
|
|
}
|
|
}
|
|
|
|
|
+.section-head-left { display: flex; align-items: baseline; gap: 8rpx; }
|
|
|
|
|
+.empty-tip { text-align: center; padding: 40rpx 0; text { font-size: 26rpx; color: $text-color-muted; } }
|
|
|
|
|
|
|
|
-// ====== 商品行 ======
|
|
|
|
|
-.product-row {
|
|
|
|
|
- display: flex; align-items: center; padding: 16rpx 0;
|
|
|
|
|
- border-bottom: 1rpx solid $border-color-light;
|
|
|
|
|
- &:last-child { border-bottom: none; }
|
|
|
|
|
-}
|
|
|
|
|
-.product-img {
|
|
|
|
|
- width: 80rpx; height: 80rpx; border-radius: 10rpx;
|
|
|
|
|
- background: $bg-color-page; margin-right: 16rpx; flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.product-img-placeholder {
|
|
|
|
|
- width: 80rpx; height: 80rpx; border-radius: 10rpx;
|
|
|
|
|
- background: $bg-color-page; margin-right: 16rpx; flex-shrink: 0;
|
|
|
|
|
- display: flex; align-items: center; justify-content: center;
|
|
|
|
|
- .placeholder-text { font-size: 20rpx; color: $text-color-muted; }
|
|
|
|
|
-}
|
|
|
|
|
-.product-info { flex: 1;
|
|
|
|
|
- .product-name { font-size: 26rpx; color: $text-color-primary; line-height: 1.4; }
|
|
|
|
|
|
|
+// ====== Toggle ======
|
|
|
|
|
+.zero-toggle { display: flex; align-items: center; gap: 8rpx;
|
|
|
|
|
+ .toggle-label { font-size: 20rpx; color: $text-color-muted; }
|
|
|
}
|
|
}
|
|
|
-.product-qty {
|
|
|
|
|
- font-size: 30rpx; font-weight: 700; color: $text-color-primary;
|
|
|
|
|
- margin-left: 16rpx; flex-shrink: 0; min-width: 48rpx; text-align: right;
|
|
|
|
|
|
|
+.toggle-switch { width: 56rpx; height: 32rpx; border-radius: 16rpx; background: $border-color; position: relative; transition: background 0.2s;
|
|
|
|
|
+ .toggle-knob { width: 26rpx; height: 26rpx; border-radius: 50%; background: $bg-color-card; position: absolute; top: 3rpx; left: 3rpx; transition: left 0.2s; box-shadow: 0 1rpx 4rpx rgba(0,0,0,0.1); }
|
|
|
|
|
+ &.on { background: $primary-color; .toggle-knob { left: 27rpx; } }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// ====== 排序栏 ======
|
|
|
|
|
-.sort-bar {
|
|
|
|
|
- display: flex; background: $bg-color-card; border-radius: $radius-sm;
|
|
|
|
|
- border: 1rpx solid $border-color; overflow: hidden; margin-bottom: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.sort-item {
|
|
|
|
|
- flex: 1; display: flex; align-items: center; justify-content: center; gap: 4rpx;
|
|
|
|
|
- padding: 14rpx 0; font-size: 22rpx; color: $text-color-tertiary;
|
|
|
|
|
- border-right: 1rpx solid $border-color-light;
|
|
|
|
|
|
|
+// ====== Sort bar ======
|
|
|
|
|
+.sort-bar { display: flex; background: $bg-color-card; border-radius: $radius-sm; border: 1rpx solid $border-color; overflow: hidden; margin-bottom: 8rpx; }
|
|
|
|
|
+.sort-item { flex: 1; padding: 12rpx 0; text-align: center; font-size: 22rpx; color: $text-color-muted; border-right: 1rpx solid $border-color-light;
|
|
|
&:last-child { border-right: none; }
|
|
&:last-child { border-right: none; }
|
|
|
- &.active { color: $primary-color-dark; font-weight: 600; }
|
|
|
|
|
- .sort-icon { font-size: 18rpx; }
|
|
|
|
|
- &:active { background: $bg-color-page; }
|
|
|
|
|
|
|
+ &.active { color: $primary-color-dark; font-weight: 600; background: $primary-color-bg; }
|
|
|
|
|
+ &:active { opacity: 0.7; }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// ====== 展示0商品开关 ======
|
|
|
|
|
-.show-zero-toggle {
|
|
|
|
|
- display: flex; align-items: center; gap: 8rpx;
|
|
|
|
|
- .toggle-label { font-size: 20rpx; color: $text-color-muted; }
|
|
|
|
|
|
|
+// ====== Product row ======
|
|
|
|
|
+.product-row { display: flex; align-items: center; padding: 16rpx 0; border-bottom: 1rpx solid $border-color-light; width: 100%; box-sizing: border-box;
|
|
|
|
|
+ &:last-child { border-bottom: none; }
|
|
|
}
|
|
}
|
|
|
-.toggle-switch {
|
|
|
|
|
- width: 64rpx; height: 36rpx; border-radius: 18rpx;
|
|
|
|
|
- background: $border-color; position: relative; transition: background 0.3s;
|
|
|
|
|
- .toggle-knob {
|
|
|
|
|
- width: 30rpx; height: 30rpx; border-radius: 50%; background: #fff;
|
|
|
|
|
- position: absolute; top: 3rpx; left: 3rpx; transition: left 0.3s;
|
|
|
|
|
- box-shadow: 0 2rpx 4rpx rgba(0,0,0,0.15);
|
|
|
|
|
- }
|
|
|
|
|
- &.on { background: $primary-color;
|
|
|
|
|
- .toggle-knob { left: 31rpx; }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+.product-img { width: 72rpx; height: 72rpx; border-radius: $radius-sm; background: $bg-color-page; margin-right: 16rpx; flex-shrink: 0; }
|
|
|
|
|
+.product-img-placeholder { width: 72rpx; height: 72rpx; border-radius: $radius-sm; background: $bg-color-page; margin-right: 16rpx; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
|
|
|
|
|
+ text { font-size: 20rpx; color: $text-color-muted; }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-// ====== 底部操作栏 ======
|
|
|
|
|
-.bottom-bar {
|
|
|
|
|
- position: fixed; bottom: 0; left: 0; right: 0;
|
|
|
|
|
- display: flex; align-items: center; gap: 16rpx;
|
|
|
|
|
- padding: 16rpx 24rpx; padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
|
|
|
|
|
- background: $bg-color-card; border-top: 1rpx solid $border-color-light;
|
|
|
|
|
- z-index: 100;
|
|
|
|
|
|
|
+.product-info { flex: 1; min-width: 0; overflow: hidden;
|
|
|
|
|
+ .product-name { display: block; font-size: 26rpx; color: $text-color-primary; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
|
|
|
|
|
+ .product-shelf { font-size: 20rpx; color: $text-color-muted; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
}
|
|
}
|
|
|
-.bottom-btn {
|
|
|
|
|
- flex: 1; height: 80rpx; line-height: 80rpx; text-align: center;
|
|
|
|
|
- border-radius: $radius-base; font-size: $font-size-base; font-weight: 600;
|
|
|
|
|
|
|
+.product-qty { font-size: 30rpx; font-weight: 700; color: $text-color-primary; margin-left: 16rpx; flex-shrink: 0; min-width: 40rpx; text-align: right; }
|
|
|
|
|
+
|
|
|
|
|
+// ====== Bottom ======
|
|
|
|
|
+.bottom-spacer { height: 120rpx; }
|
|
|
|
|
+.bottom-bar { position: fixed; bottom: 0; left: 0; right: 0; padding: $spacing-3 $spacing-4; padding-bottom: calc($spacing-3 + env(safe-area-inset-bottom)); background: $bg-color-card; border-top: 1rpx solid $border-color-light; }
|
|
|
|
|
+.action-btn { display: flex; align-items: center; justify-content: center; width: 100%; padding: 26rpx 0; border-radius: $radius-xl; background: $primary-color;
|
|
|
|
|
+ text { font-size: $font-size-md; font-weight: 600; color: $text-color-primary; }
|
|
|
&:active { opacity: 0.8; }
|
|
&:active { opacity: 0.8; }
|
|
|
- &.secondary {
|
|
|
|
|
- background: $bg-color-secondary; color: $text-color-secondary;
|
|
|
|
|
- }
|
|
|
|
|
- &.primary {
|
|
|
|
|
- background: $primary-color; color: #1e293b;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|