yyb
2 天以前 8218d918da290861fa402414b5b155270d582106
合格/不合格入库新增审批流程功能
已修改2个文件
244 ■■■■■ 文件已修改
src/pages/inventoryManagement/scanIn/index.vue 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inventoryManagement/scanIn/scanIn.submit.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inventoryManagement/scanIn/index.vue
@@ -109,6 +109,51 @@
          </view>
        </view>
      </view>
      <view class="approval-process">
        <view class="approval-header">
          <text class="approval-title">审核流程</text>
          <text class="approval-desc">每个步骤只能选择一个审批人</text>
        </view>
        <view class="approval-steps">
          <view v-for="(step, stepIndex) in stockApproverNodes"
                :key="step.id"
                class="approval-step">
            <view class="step-title">
              <text>审批人</text>
            </view>
            <view class="approver-container">
              <view v-if="step.userName"
                    class="approver-item">
                <view class="approver-avatar">
                  <text class="avatar-text">{{ step.userName.charAt(0) }}</text>
                </view>
                <view class="approver-info">
                  <text class="approver-name">{{ step.userName }}</text>
                </view>
                <view class="delete-approver-btn"
                      @click="removeApprover(stepIndex)">×</view>
              </view>
              <view v-else
                    class="add-approver-btn"
                    @click="openApproverPicker(stepIndex)">
                <view class="add-circle">+</view>
                <text class="add-label">选择审批人</text>
              </view>
            </view>
            <view class="delete-step-btn"
                  v-if="stockApproverNodes.length > 1"
                  @click="removeStockApproverNode(stepIndex)">删除节点</view>
          </view>
        </view>
        <view class="add-step-btn">
          <u-button icon="plus"
                    plain
                    type="primary"
                    style="width: 100%"
                    @click="addStockApproverNode">新增节点</u-button>
        </view>
      </view>
      <view class="footer-btns">
        <u-button class="footer-cancel-btn"
                  @click="cancelForm">返回</u-button>
@@ -117,11 +162,12 @@
                  @click="confirmInbound">确认入库</u-button>
      </view>
    </scroll-view>
  </view>
</template>
<script setup>
  import { ref, computed } from "vue";
  import { ref, computed, onMounted, onUnmounted } from "vue";
  import PageHeader from "@/components/PageHeader.vue";
  import { productList as salesProductList } from "@/api/salesManagement/salesLedger";
  import modal from "@/plugins/modal";
@@ -146,6 +192,8 @@
  const contractKind = ref(CONTRACT_KIND.sales);
  const scanLedgerId = ref(null);
  const submitLoading = ref(false);
  const stockApproverNodes = ref([{ id: 1, userId: null, userName: "" }]);
  let nextApproverNodeId = 2;
  const submitConfigByScene = createSubmitConfig(scanLedgerId);
  const cardTitleMain = computed(() => {
@@ -315,9 +363,65 @@
    scanLedgerId.value = null;
    expandedByIndex.value = {};
    recordList.value = [];
    stockApproverNodes.value = [{ id: 1, userId: null, userName: "" }];
  };
  const confirmInbound = async () => {
  onMounted(() => {
    uni.$on("selectContact", handleSelectContact);
  });
  onUnmounted(() => {
    uni.$off("selectContact", handleSelectContact);
  });
  const addStockApproverNode = () => {
    stockApproverNodes.value.push({
      id: nextApproverNodeId++,
      userId: null,
      userName: "",
    });
  };
  const removeStockApproverNode = index => {
    if (stockApproverNodes.value.length <= 1) {
      modal.msgError("至少保留一个审批节点");
      return;
    }
    stockApproverNodes.value.splice(index, 1);
  };
  const removeApprover = stepIndex => {
    if (!stockApproverNodes.value[stepIndex]) return;
    stockApproverNodes.value[stepIndex].userId = null;
    stockApproverNodes.value[stepIndex].userName = "";
  };
  const openApproverPicker = index => {
    uni.setStorageSync("stepIndex", index);
    uni.navigateTo({
      url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect?approveType=9",
    });
  };
  const handleSelectContact = data => {
    const { stepIndex, contact } = data || {};
    if (stepIndex === null || stepIndex === undefined) return;
    const idx = Number(stepIndex);
    if (Number.isNaN(idx) || !stockApproverNodes.value[idx]) return;
    stockApproverNodes.value[idx].userId = contact?.userId ?? null;
    stockApproverNodes.value[idx].userName = contact?.nickName || contact?.userName || "";
  };
  const validateApproverNodes = () => {
    const hasEmptyNode = stockApproverNodes.value.some(node => !node.userId);
    if (hasEmptyNode) {
      modal.msgError("请为每个审批节点选择审批人");
      return false;
    }
    return true;
  };
  const submitInbound = async () => {
    if (scanLedgerId.value == null || scanLedgerId.value === "") {
      modal.msgError("缺少订单信息,请重新扫码");
      return;
@@ -334,7 +438,8 @@
      return;
    }
    const runApi = currentSubmitConfig.runApi;
    const payload = currentSubmitConfig.payloadBuilder(salesLedgerProductList);
    const approveUserIds = stockApproverNodes.value.map(node => node.userId).join(",");
    const payload = currentSubmitConfig.payloadBuilder(salesLedgerProductList, approveUserIds);
    try {
      submitLoading.value = true;
      modal.loading("提交中...");
@@ -352,6 +457,11 @@
    } finally {
      submitLoading.value = false;
    }
  };
  const confirmInbound = () => {
    if (!validateApproverNodes()) return;
    submitInbound();
  };
  const goBack = () => {
@@ -638,4 +748,118 @@
    color: #fff;
    border: none;
  }
  .approval-process {
    background: #fff;
    margin: 20rpx;
    border-radius: 16rpx;
    padding: 24rpx;
  }
  .approval-header {
    margin-bottom: 16rpx;
  }
  .approval-title {
    font-size: 30rpx;
    font-weight: 600;
    color: #333;
    display: block;
  }
  .approval-desc {
    font-size: 24rpx;
    color: #999;
    margin-top: 6rpx;
  }
  .approval-step {
    margin-bottom: 18rpx;
  }
  .step-title text {
    font-size: 24rpx;
    color: #666;
  }
  .approver-container {
    display: flex;
    align-items: center;
    margin-top: 10rpx;
  }
  .approver-item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 12rpx;
    padding: 12rpx 0;
  }
  .approver-avatar {
    width: 64rpx;
    height: 64rpx;
    border-radius: 50%;
    background: #f3f4f6;
    border: 2rpx solid #e5e7eb;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .avatar-text {
    font-size: 24rpx;
    color: #374151;
    font-weight: 600;
  }
  .approver-info {
    flex: 1;
  }
  .approver-name {
    font-size: 28rpx;
    color: #333;
  }
  .delete-approver-btn {
    font-size: 32rpx;
    color: #ff4d4f;
    padding: 0 8rpx;
  }
  .add-approver-btn {
    display: flex;
    align-items: center;
    gap: 10rpx;
    color: #3b82f6;
    padding: 10rpx 0;
  }
  .add-circle {
    width: 52rpx;
    height: 52rpx;
    border: 2rpx dashed #a0aec0;
    border-radius: 50%;
    color: #6b7280;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 34rpx;
    line-height: 1;
  }
  .add-label {
    font-size: 26rpx;
  }
  .delete-step-btn {
    color: #ff4d4f;
    font-size: 24rpx;
    margin-top: 8rpx;
  }
  .add-step-btn {
    margin-top: 8rpx;
  }
</style>
src/pages/inventoryManagement/scanIn/scanIn.submit.ts
@@ -16,37 +16,41 @@
type SubmitConfig = {
  runApi: (data: any) => Promise<any>;
  payloadBuilder: (list: AnyRow[]) => any;
  payloadBuilder: (list: AnyRow[], approveUserIds: string) => any;
};
export function createSubmitConfig(scanLedgerIdRef: AnyRef<string | number | null>) {
  const cfg: Record<string, SubmitConfig> = {
    [`${CONTRACT_KIND.sales}-${QUALITY_TYPE.qualified}`]: {
      runApi: scanInboundSales,
      payloadBuilder: (list: AnyRow[]) => ({
      payloadBuilder: (list: AnyRow[], approveUserIds: string) => ({
        salesLedgerId: scanLedgerIdRef.value,
        salesLedgerProductList: list,
        approveUserIds,
      }),
    },
    [`${CONTRACT_KIND.sales}-${QUALITY_TYPE.unqualified}`]: {
      runApi: scanInboundSalesUnqualified,
      payloadBuilder: (list: AnyRow[]) => ({
      payloadBuilder: (list: AnyRow[], approveUserIds: string) => ({
        salesLedgerId: scanLedgerIdRef.value,
        salesLedgerProductList: list,
        approveUserIds,
      }),
    },
    [`${CONTRACT_KIND.purchase}-${QUALITY_TYPE.qualified}`]: {
      runApi: scanInboundPurchase,
      payloadBuilder: (list: AnyRow[]) => ({
      payloadBuilder: (list: AnyRow[], approveUserIds: string) => ({
        purchaseLedgerId: scanLedgerIdRef.value,
        salesLedgerProductList: list,
        approveUserIds,
      }),
    },
    [`${CONTRACT_KIND.purchase}-${QUALITY_TYPE.unqualified}`]: {
      runApi: scanInboundPurchaseUnqualified,
      payloadBuilder: (list: AnyRow[]) => ({
      payloadBuilder: (list: AnyRow[], approveUserIds: string) => ({
        purchaseLedgerId: scanLedgerIdRef.value,
        salesLedgerProductList: list,
        approveUserIds,
      }),
    },
  };