From b973bcee308e99b5fd8a69640f11069e810346f4 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期二, 27 一月 2026 16:37:55 +0800
Subject: [PATCH] 采购台账重构

---
 src/pages/sales/salesAccount/goOut.vue |  657 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 657 insertions(+), 0 deletions(-)

diff --git a/src/pages/sales/salesAccount/goOut.vue b/src/pages/sales/salesAccount/goOut.vue
new file mode 100644
index 0000000..9980e5f
--- /dev/null
+++ b/src/pages/sales/salesAccount/goOut.vue
@@ -0,0 +1,657 @@
+<template>
+  <view class="account-detail">
+    <PageHeader title="鍙戣揣"
+                @back="goBack" />
+    <!-- 琛ㄥ崟鍖哄煙 -->
+    <u-form ref="formRef"
+            @submit="submitForm"
+            :rules="rules"
+            :model="form"
+            label-width="140rpx">
+      <u-form-item prop="typeValue"
+                   label="鍙戣揣绫诲瀷"
+                   required>
+        <u-input v-model="typeValue"
+                 readonly
+                 placeholder="璇烽�夋嫨鍙戣揣鏂瑰紡"
+                 @click="showPicker = true" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="showPicker = true"></up-icon>
+        </template>
+      </u-form-item>
+    </u-form>
+    <!-- 閫夋嫨鍣ㄥ脊绐� -->
+    <up-action-sheet :show="showPicker"
+                     :actions="productOptions"
+                     title="鍙戣揣鏂瑰紡"
+                     @select="onConfirm"
+                     @close="showPicker = false" />
+    <!-- 瀹℃牳娴佺▼鍖哄煙 -->
+    <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 approverNodes"
+              :key="stepIndex"
+              class="approval-step">
+          <view class="step-dot"></view>
+          <view class="step-title">
+            <text>瀹℃壒浜�</text>
+          </view>
+          <view class="approver-container">
+            <view v-if="step.nickName"
+                  class="approver-item">
+              <view class="approver-avatar">
+                <text class="avatar-text">{{ step.nickName.charAt(0) }}</text>
+                <view class="status-dot"></view>
+              </view>
+              <view class="approver-info">
+                <text class="approver-name">{{ step.nickName }}</text>
+              </view>
+              <view class="delete-approver-btn"
+                    @click="removeApprover(stepIndex)">脳</view>
+            </view>
+            <view v-else
+                  class="add-approver-btn"
+                  @click="addApprover(stepIndex)">
+              <view class="add-circle">+</view>
+              <text class="add-label">閫夋嫨瀹℃壒浜�</text>
+            </view>
+          </view>
+          <view class="step-line"
+                v-if="stepIndex < approverNodes.length - 1"></view>
+          <view class="delete-step-btn"
+                v-if="approverNodes.length > 1"
+                @click="removeApprovalStep(stepIndex)">鍒犻櫎鑺傜偣</view>
+        </view>
+      </view>
+      <view class="add-step-btn">
+        <u-button icon="plus"
+                  plain
+                  type="primary"
+                  style="width: 100%"
+                  @click="addApprovalStep">鏂板鑺傜偣</u-button>
+      </view>
+    </view>
+    <!-- 搴曢儴鎸夐挳 -->
+    <view class="footer-btns">
+      <u-button class="cancel-btn"
+                @click="goBack">鍙栨秷</u-button>
+      <u-button class="save-btn"
+                @click="submitForm">鍙戣揣</u-button>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
+  import PageHeader from "@/components/PageHeader.vue";
+  import { addShippingInfo } from "@/api/salesManagement/salesLedger";
+  const showToast = message => {
+    uni.showToast({
+      title: message,
+      icon: "none",
+    });
+  };
+  import { userListNoPageByTenantId } from "@/api/system/user";
+
+  const data = reactive({
+    form: {
+      approveTime: "",
+      approveId: "",
+      approveUser: "",
+      approveUserName: "",
+      approveDeptName: "",
+      approveDeptId: "",
+      approveReason: "",
+      checkResult: "",
+      tempFileIds: [],
+      approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
+      startDate: "",
+      endDate: "",
+      location: "",
+      price: "",
+    },
+    rules: {
+      typeValue: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+    },
+  });
+  const { form, rules } = toRefs(data);
+  const showPicker = ref(false);
+  const productOptions = ref([
+    {
+      value: "璐ц溅",
+      name: "璐ц溅",
+    },
+    {
+      value: "蹇��",
+      name: "蹇��",
+    },
+  ]);
+  const operationType = ref("");
+  const currentApproveStatus = ref("");
+  const approverNodes = ref([]);
+  const userList = ref([]);
+  const formRef = ref(null);
+  const approveType = ref(0);
+  const goOutData = ref({});
+  onMounted(async () => {
+    try {
+      userListNoPageByTenantId().then(res => {
+        userList.value = res.data;
+      });
+      // 浠庢湰鍦板瓨鍌ㄨ幏鍙栧彂璐ц鎯�
+      goOutData.value = JSON.parse(uni.getStorageSync("goOutData"));
+      console.log(goOutData.value, "goOutData.value");
+
+      // 鍒濆鍖栧鎵规祦绋嬭妭鐐癸紝榛樿涓�涓妭鐐�
+      approverNodes.value = [{ id: 1, userId: null }];
+
+      // 鐩戝惉鑱旂郴浜洪�夋嫨浜嬩欢
+      uni.$on("selectContact", handleSelectContact);
+    } catch (error) {
+      console.error("鑾峰彇澶辫触:", error);
+    }
+  });
+
+  onUnmounted(() => {
+    // 绉婚櫎浜嬩欢鐩戝惉
+    uni.$off("selectContact", handleSelectContact);
+  });
+  const typeValue = ref("璐ц溅");
+  const onConfirm = item => {
+    // 璁剧疆閫変腑鐨勯儴闂�
+    typeValue.value = item.name;
+    showPicker.value = false;
+  };
+
+  const goBack = () => {
+    // 娓呴櫎鏈湴瀛樺偍鐨勬暟鎹�
+    uni.removeStorageSync("operationType");
+    uni.removeStorageSync("invoiceLedgerEditRow");
+    uni.removeStorageSync("approveType");
+    uni.navigateBack();
+  };
+
+  const submitForm = () => {
+    // 妫�鏌ユ瘡涓鎵规楠ゆ槸鍚﹂兘鏈夊鎵逛汉
+    const hasEmptyStep = approverNodes.value.some(step => !step.nickName);
+    if (hasEmptyStep) {
+      showToast("璇蜂负姣忎釜瀹℃壒姝ラ閫夋嫨瀹℃壒浜�");
+      return;
+    }
+    formRef.value
+      .validate()
+      .then(valid => {
+        if (valid) {
+          // 琛ㄥ崟鏍¢獙閫氳繃锛屽彲浠ユ彁浜ゆ暟鎹�
+          // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
+          console.log("approverNodes---", approverNodes.value);
+          const approveUserIds = approverNodes.value
+            .map(node => node.userId)
+            .join(",");
+          const params = {
+            salesLedgerId: goOutData.value.salesLedgerId,
+            salesLedgerProductId: goOutData.value.id,
+            type: typeValue.value,
+            approveUserIds,
+          };
+          console.log(params, "params");
+
+          addShippingInfo(params).then(res => {
+            showToast("鍙戣揣鎴愬姛");
+            setTimeout(() => {
+              goBack();
+            }, 500);
+          });
+        }
+      })
+      .catch(error => {
+        console.error("琛ㄥ崟鏍¢獙澶辫触:", error);
+        // 灏濊瘯鑾峰彇鍏蜂綋鐨勯敊璇瓧娈�
+        if (error && error.errors) {
+          const firstError = error.errors[0];
+          if (firstError) {
+            uni.showToast({
+              title: firstError.message || "琛ㄥ崟鏍¢獙澶辫触锛岃妫�鏌ュ繀濉」",
+              icon: "none",
+            });
+            return;
+          }
+        }
+        // 鏄剧ず閫氱敤閿欒淇℃伅
+        uni.showToast({
+          title: "琛ㄥ崟鏍¢獙澶辫触锛岃妫�鏌ュ繀濉」",
+          icon: "none",
+        });
+      });
+  };
+
+  // 澶勭悊鑱旂郴浜洪�夋嫨缁撴灉
+  const handleSelectContact = data => {
+    const { stepIndex, contact } = data;
+    // 灏嗛�変腑鐨勮仈绯讳汉璁剧疆涓哄搴斿鎵规楠ょ殑瀹℃壒浜�
+    approverNodes.value[stepIndex].userId = contact.userId;
+    approverNodes.value[stepIndex].nickName = contact.nickName;
+  };
+
+  const addApprover = stepIndex => {
+    // 璺宠浆鍒拌仈绯讳汉閫夋嫨椤甸潰
+    uni.setStorageSync("stepIndex", stepIndex);
+    uni.navigateTo({
+      url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect",
+    });
+  };
+
+  const addApprovalStep = () => {
+    // 娣诲姞鏂扮殑瀹℃壒姝ラ
+    approverNodes.value.push({ userId: null, nickName: null });
+  };
+
+  const removeApprover = stepIndex => {
+    // 绉婚櫎瀹℃壒浜�
+    approverNodes.value[stepIndex].userId = null;
+    approverNodes.value[stepIndex].nickName = null;
+  };
+
+  const removeApprovalStep = stepIndex => {
+    // 纭繚鑷冲皯淇濈暀涓�涓鎵规楠�
+    if (approverNodes.value.length > 1) {
+      approverNodes.value.splice(stepIndex, 1);
+    } else {
+      uni.showToast({
+        title: "鑷冲皯闇�瑕佷竴涓鎵规楠�",
+        icon: "none",
+      });
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  @import "@/static/scss/form-common.scss";
+
+  .approval-process {
+    background: #fff;
+    margin: 16px;
+    border-radius: 16px;
+    padding: 16px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+  }
+
+  .approval-header {
+    margin-bottom: 16px;
+  }
+
+  .approval-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #333;
+    display: block;
+    margin-bottom: 4px;
+  }
+
+  .approval-desc {
+    font-size: 12px;
+    color: #999;
+  }
+
+  /* 鏍峰紡澧炲己涓衡�滅畝娲佸皬鍦嗗湀椋庢牸鈥� */
+  .approval-steps {
+    padding-left: 22px;
+    position: relative;
+
+    &::before {
+      content: "";
+      position: absolute;
+      left: 11px;
+      top: 40px;
+      bottom: 40px;
+      width: 2px;
+      background: linear-gradient(
+        to bottom,
+        #e6f7ff 0%,
+        #bae7ff 50%,
+        #91d5ff 100%
+      );
+      border-radius: 1px;
+    }
+  }
+
+  .approval-step {
+    position: relative;
+    margin-bottom: 24px;
+
+    &::before {
+      content: "";
+      position: absolute;
+      left: -18px;
+      top: 14px; // 浠� 8px 璋冩暣涓� 14px锛屼笌鏂囧瓧涓績瀵归綈
+      width: 12px;
+      height: 12px;
+      background: #fff;
+      border: 3px solid #006cfb;
+      border-radius: 50%;
+      z-index: 2;
+      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+    }
+  }
+
+  .step-title {
+    top: 12px;
+    margin-bottom: 12px;
+    position: relative;
+    margin-left: 6px;
+  }
+
+  .step-title text {
+    font-size: 14px;
+    color: #666;
+    background: #f0f0f0;
+    padding: 4px 12px;
+    border-radius: 12px;
+    position: relative;
+    line-height: 1.4; // 纭繚鏂囧瓧琛岄珮涓�鑷�
+  }
+
+  .approver-item {
+    display: flex;
+    align-items: center;
+    background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+    border-radius: 16px;
+    padding: 16px;
+    gap: 12px;
+    position: relative;
+    border: 1px solid #e6f7ff;
+    box-shadow: 0 4px 12px rgba(0, 108, 251, 0.08);
+    transition: all 0.3s ease;
+  }
+
+  .approver-avatar {
+    width: 48px;
+    height: 48px;
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: relative;
+    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+  }
+
+  .avatar-text {
+    color: #fff;
+    font-size: 18px;
+    font-weight: 600;
+    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+  }
+
+  .approver-info {
+    flex: 1;
+    position: relative;
+  }
+
+  .approver-name {
+    display: block;
+    font-size: 16px;
+    color: #333;
+    font-weight: 500;
+    position: relative;
+  }
+
+  .approver-dept {
+    font-size: 12px;
+    color: #999;
+    background: rgba(0, 108, 251, 0.05);
+    padding: 2px 8px;
+    border-radius: 8px;
+    display: inline-block;
+    position: relative;
+
+    &::before {
+      content: "";
+      position: absolute;
+      left: 4px;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 2px;
+      height: 2px;
+      background: #006cfb;
+      border-radius: 50%;
+    }
+  }
+
+  .delete-approver-btn {
+    font-size: 16px;
+    color: #ff4d4f;
+    background: linear-gradient(
+      135deg,
+      rgba(255, 77, 79, 0.1) 0%,
+      rgba(255, 77, 79, 0.05) 100%
+    );
+    width: 28px;
+    height: 28px;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    transition: all 0.3s ease;
+    position: relative;
+  }
+
+  .add-approver-btn {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%);
+    border: 2px dashed #006cfb;
+    border-radius: 16px;
+    padding: 20px;
+    color: #006cfb;
+    font-size: 14px;
+    position: relative;
+    transition: all 0.3s ease;
+
+    &::before {
+      content: "";
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      transform: translate(-50%, -50%);
+      width: 32px;
+      height: 32px;
+      border: 2px solid #006cfb;
+      border-radius: 50%;
+      opacity: 0;
+      transition: all 0.3s ease;
+    }
+  }
+
+  .delete-step-btn {
+    color: #ff4d4f;
+    font-size: 12px;
+    background: linear-gradient(
+      135deg,
+      rgba(255, 77, 79, 0.1) 0%,
+      rgba(255, 77, 79, 0.05) 100%
+    );
+    padding: 6px 12px;
+    border-radius: 12px;
+    display: inline-block;
+    position: relative;
+    transition: all 0.3s ease;
+
+    &::before {
+      content: "";
+      position: absolute;
+      left: 6px;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 4px;
+      height: 4px;
+      background: #ff4d4f;
+      border-radius: 50%;
+    }
+  }
+
+  .step-line {
+    display: none; // 闅愯棌鍘熸潵鐨勭嚎鏉★紝浣跨敤浼厓绱犱唬鏇�
+  }
+
+  .add-step-btn {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  .footer-btns {
+    position: fixed;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: #fff;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    padding: 0.75rem 0;
+    box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05);
+    z-index: 1000;
+  }
+
+  .cancel-btn {
+    font-weight: 400;
+    font-size: 1rem;
+    color: #ffffff;
+    width: 6.375rem;
+    background: #c7c9cc;
+    box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
+    border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
+  }
+
+  .save-btn {
+    font-weight: 400;
+    font-size: 1rem;
+    color: #ffffff;
+    width: 14rem;
+    background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
+    box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
+    border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
+  }
+
+  // 鍔ㄧ敾瀹氫箟
+  @keyframes pulse {
+    0% {
+      transform: scale(1);
+      opacity: 1;
+    }
+    50% {
+      transform: scale(1.2);
+      opacity: 0.7;
+    }
+    100% {
+      transform: scale(1);
+      opacity: 1;
+    }
+  }
+
+  @keyframes rotate {
+    0% {
+      transform: rotate(0deg);
+    }
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+
+  @keyframes ripple {
+    0% {
+      transform: translate(-50%, -50%) scale(0.8);
+      opacity: 1;
+    }
+    100% {
+      transform: translate(-50%, -50%) scale(1.6);
+      opacity: 0;
+    }
+  }
+
+  /* 濡傛灉宸叉湁 .step-line锛岃繖閲屾洿绮惧噯瀹氫綅鍒板乏渚т笌灏忓渾鐐瑰榻� */
+  .step-line {
+    position: absolute;
+    left: 4px;
+    top: 48px;
+    width: 2px;
+    height: calc(100% - 48px);
+    background: #e5e7eb;
+  }
+
+  .approver-container {
+    display: flex;
+    align-items: center;
+    background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+    border-radius: 16px;
+    gap: 12px;
+    padding: 10px 0;
+    background: transparent;
+    border: none;
+    box-shadow: none;
+  }
+
+  .approver-item {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    padding: 8px 10px;
+    background: transparent;
+    border: none;
+    box-shadow: none;
+    border-radius: 0;
+  }
+
+  .approver-avatar {
+    position: relative;
+    width: 40px;
+    height: 40px;
+    border-radius: 50%;
+    background: #f3f4f6;
+    border: 2px solid #e5e7eb;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    animation: none; /* 绂佺敤鏃嬭浆绛夊姩鐢伙紝鍥炲綊绠�娲� */
+  }
+
+  .avatar-text {
+    font-size: 14px;
+    color: #374151;
+    font-weight: 600;
+  }
+
+  .add-approver-btn {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    background: transparent;
+    border: none;
+    box-shadow: none;
+    padding: 0;
+  }
+
+  .add-approver-btn .add-circle {
+    width: 40px;
+    height: 40px;
+    border: 2px dashed #a0aec0;
+    border-radius: 50%;
+    color: #6b7280;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 22px;
+    line-height: 1;
+  }
+
+  .add-approver-btn .add-label {
+    color: #3b82f6;
+    font-size: 14px;
+  }
+</style>
\ No newline at end of file

--
Gitblit v1.9.3