From bd712637441c43d6e23a8577b748a5d2b3365745 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 21 十一月 2025 15:03:59 +0800
Subject: [PATCH] 添加自定义出库页面

---
 src/api/inventoryManagement/stockManage.js              |   76 ++++++
 src/pages/index.vue                                     |   11 
 src/pages.json                                          |    7 
 src/pages/inventoryManagement/issueManagement/index.vue |  564 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 657 insertions(+), 1 deletions(-)

diff --git a/src/api/inventoryManagement/stockManage.js b/src/api/inventoryManagement/stockManage.js
new file mode 100644
index 0000000..4f5d957
--- /dev/null
+++ b/src/api/inventoryManagement/stockManage.js
@@ -0,0 +1,76 @@
+import request from "@/utils/request";
+
+// 鏌ヨ搴撳瓨淇℃伅鍒楄〃
+export const getStockManagePage = (params) => {
+    return request({
+        url: "/stockin/listPageCopy",
+        method: "get",
+        params,
+    });
+};
+
+// 鏌ヨ鐢熶骇鍏ュ簱搴撳瓨淇℃伅鍒楄〃
+export const getStockManagePageByProduction = (params) => {
+    return request({
+        url: "/stockin/listPageCopyByProduction",
+        method: "get",
+        params,
+    });
+};
+
+// 鏌ヨ鑷畾涔夊叆搴撳簱瀛樹俊鎭垪琛�
+export const getStockManagePageByCustom = (params) => {
+    return request({
+        url: "/stockin/listPageCopyByCustom",
+        method: "get",
+        params,
+    });
+};
+
+
+// 淇敼搴撳瓨淇℃伅
+export const updateStockManage = (data) => {
+    return request({
+        url: "/stockmanagement/update",
+        method: "put",
+        data,
+    });
+};
+
+// 鍒犻櫎搴撳瓨淇℃伅
+export function delStockManage(ids) {
+    return request({
+        url: '/stockin/del',
+        method: 'post',
+        data: ids
+    })
+}
+
+// 瀵煎嚭搴撳瓨淇℃伅
+export function exportStockManage(query) {
+    return request({
+        url: '/stockmanagement/export',
+        method: 'get',
+        params: query,
+        responseType: 'blob'
+    })
+}
+
+// 鍑哄簱绠$悊-棰嗙敤鎺ュ彛
+export const stockOut = (data) => {
+    return request({
+        url: '/stockmanagement/stockout',
+        method: 'post',
+        data: data
+    })
+}
+
+//鏍规嵁id鑾峰彇搴撳瓨淇℃伅
+export function getStockManageById(id) {
+    return request({
+        url: '/stockmanagement/' + id,
+        method: 'get'
+    })
+}
+
+//
\ No newline at end of file
diff --git a/src/pages.json b/src/pages.json
index 654c337..68210cf 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -469,6 +469,13 @@
         "navigationBarTitleText": "鏂板鍏ュ簱",
         "navigationStyle": "custom"
       }
+    },
+    {
+      "path": "pages/inventoryManagement/issueManagement/index",
+      "style": {
+        "navigationBarTitleText": "鑷畾涔夊嚭搴�",
+        "navigationStyle": "custom"
+      }
     }
   ],
   "subPackages": [
diff --git a/src/pages/index.vue b/src/pages/index.vue
index 04da41f..67cb6b4 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -125,7 +125,7 @@
 		<view class="common-module collaboration-module">
 			<view class="module-header">
 				<view class="module-title-container">
-					<text class="module-title">鍏ュ簱绠$悊</text>
+					<text class="module-title">浠撳偍鐗╂祦</text>
 				</view>
 			</view>
 			<view class="module-content">
@@ -328,6 +328,10 @@
 		icon: '/static/images/icon/rukuguanli@2x.png',
 		label: '鑷畾涔夊叆搴�',
 	},
+	{
+		icon: '/static/images/icon/rukuguanli@2x.png',
+		label: '鑷畾涔夊嚭搴�',
+	},
 ]);
 
 // 鐢熶骇绠℃帶鍔熻兘鏁版嵁
@@ -493,6 +497,11 @@
 				url: '/pages/inventoryManagement/receiptManagement/index'
 			});
 			break;
+		case '鑷畾涔夊嚭搴�':
+			uni.navigateTo({
+				url: '/pages/inventoryManagement/issueManagement/index'
+			});
+			break;
 		case '鐢熶骇璁㈠崟':
 			uni.navigateTo({
 				url: '/pages/productionManagement/productionOrder/index'
diff --git a/src/pages/inventoryManagement/issueManagement/index.vue b/src/pages/inventoryManagement/issueManagement/index.vue
new file mode 100644
index 0000000..5a2b4a1
--- /dev/null
+++ b/src/pages/inventoryManagement/issueManagement/index.vue
@@ -0,0 +1,564 @@
+<template>
+  <view class="stock-out-page">
+    <!-- 椤甸潰鏍囬 -->
+    <PageHeader title="鑷畾涔夊嚭搴�" @back="goBack" />
+    
+    <!-- 鎼滅储鍖哄煙 -->
+    <view class="search-section">
+      <view class="search-bar">
+        <view class="search-input">
+          <u-input 
+            v-model="searchForm.supplierName" 
+            placeholder="璇疯緭鍏ヤ緵搴斿晢鍚嶇О" 
+            border="none"
+            clearable
+          />
+        </view>
+        <view class="search-button" @click="handleQuery">
+          <u-icon name="search" size="24" color="#999"></u-icon>
+        </view>
+      </view>
+      <view class="date-filter" @click="openDatePickerHandler">
+        <text class="date-text">{{ searchForm.timeStr || '閫夋嫨鏃ユ湡' }}</text>
+        <up-icon name="calendar" size="18" color="#999"></up-icon>
+      </view>
+    </view>
+    
+    <!-- 鏃ユ湡閫夋嫨鍣� -->
+    <up-popup :show="showDatePicker" mode="bottom" @close="showDatePicker = false">
+      <up-datetime-picker
+        :show="true"
+        v-model="dateValue"
+        @confirm="onDateConfirm"
+        @cancel="showDatePicker = false"
+        mode="date"
+      />
+    </up-popup>
+    
+    <!-- 鏁版嵁鍒楄〃 -->
+    <view class="stock-list" v-if="tableData.length > 0">
+      <view v-for="(item, index) in tableData" :key="item.id" class="stock-item">
+        <view class="item-header">
+          <view class="item-left">
+            <view class="batch-icon">
+              <u-icon name="file-text" size="16" color="#ffffff"></u-icon>
+            </view>
+            <text class="batch-text">{{ item.inboundBatches || '鏈煡鎵规' }}</text>
+          </view>
+          <view class="item-right">
+            <text class="time-text">{{ item.inboundDate || item.createTime || '-' }}</text>
+          </view>
+        </view>
+        <u-divider></u-divider>
+        
+        <view class="item-details">
+          <view class="detail-row">
+            <text class="detail-label">渚涘簲鍟嗗悕绉�</text>
+            <text class="detail-value">{{ item.supplierName || '鏈煡渚涘簲鍟�' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">浜у搧澶х被</text>
+            <text class="detail-value">{{ item.productCategory || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">瑙勬牸鍨嬪彿</text>
+            <text class="detail-value">{{ item.specificationModel || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鍏ュ簱鏁伴噺</text>
+            <text class="detail-value highlight">{{ item.inboundNum || 0 }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鍚◣鍗曚环</text>
+            <text class="detail-value">楼{{ item.taxInclusiveUnitPrice || 0 }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鍚◣鎬讳环</text>
+            <text class="detail-value price">楼{{ item.taxInclusiveTotalPrice || 0 }}</text>
+          </view>
+        </view>
+        
+        <view class="item-actions">
+          <u-button type="primary" size="small" @click="openForm(item)">棰嗙敤</u-button>
+          <u-button type="warning" size="small" plain @click="handleOut">瀵煎嚭</u-button>
+        </view>
+      </view>
+    </view>
+    
+    <view v-else class="no-data">
+      <text>鏆傛棤鏁版嵁</text>
+    </view>
+    
+    <!-- 鍔犺浇鏇村 -->
+    <view class="load-more" v-if="tableData.length > 0">
+      <u-loadmore :status="loadStatus" @loadmore="loadMore" />
+    </view>
+    
+    <!-- 鍑哄簱琛ㄥ崟寮圭獥 -->
+    <u-popup 
+      v-model="dialogFormVisible" 
+      mode="center" 
+      :closeable="true"
+      @close="closeDia"
+      round="10"
+    >
+      <view class="popup-content">
+        <view class="popup-header">
+          <text class="popup-title">鏂板鍑哄簱</text>
+        </view>
+        
+        <view class="popup-body">
+          <u-form :model="form" :rules="rules" ref="formRef" labelWidth="80">
+            <u-form-item label="鍑哄簱鏁伴噺" prop="inboundQuantity" borderBottom>
+              <u-input 
+                v-model="form.inboundQuantity" 
+                placeholder="璇疯緭鍏ュ嚭搴撴暟閲�" 
+                type="number"
+                border="none"
+              />
+            </u-form-item>
+            
+            <u-form-item label="鍑哄簱鏃ユ湡" prop="inboundTime" borderBottom>
+              <u-input 
+                v-model="form.inboundTime" 
+                placeholder="璇烽�夋嫨鍑哄簱鏃ユ湡" 
+                border="none"
+                readonly
+                @click="showOutDatePicker = true"
+              >
+                <template #suffix>
+                  <u-icon name="calendar" size="18"></u-icon>
+                </template>
+              </u-input>
+            </u-form-item>
+            
+            <u-form-item label="鍑哄簱浜�" prop="nickName" borderBottom>
+              <u-select 
+                v-model="form.nickName" 
+                :list="userList" 
+                labelName="nickName" 
+                valueName="userId"
+                placeholder="璇烽�夋嫨鍑哄簱浜�"
+              ></u-select>
+            </u-form-item>
+          </u-form>
+        </view>
+        
+        <view class="popup-footer">
+          <u-button type="primary" @click="submitForm" size="normal">纭</u-button>
+          <u-button @click="closeDia" size="normal" plain>鍙栨秷</u-button>
+        </view>
+      </view>
+    </u-popup>
+    
+    <!-- 鍑哄簱鏃ユ湡閫夋嫨鍣� -->
+    <u-datetime-picker 
+      v-model="form.inboundTime" 
+      :show="showOutDatePicker" 
+      mode="date" 
+      @confirm="showOutDatePicker = false" 
+      @cancel="showOutDatePicker = false"
+    />
+  </view>
+</template>
+
+<script setup>
+import { ref, reactive, toRefs, onMounted, getCurrentInstance, nextTick } from 'vue'
+import dayjs from 'dayjs'
+import PageHeader from '@/components/PageHeader.vue'
+import useUserStore from '@/store/modules/user'
+import { formatDateToYMD } from '@/utils/ruoyi'
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+import { getInPageByCustom } from "@/api/inventoryManagement/stockIn.js";
+import { stockOut } from "@/api/inventoryManagement/stockManage.js";
+
+const userStore = useUserStore()
+const { proxy } = getCurrentInstance()
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+  uni.navigateBack()
+}
+
+// 鎵撳紑鏃ユ湡閫夋嫨鍣紙绠�鍗曞彲闈狅級
+const openDatePickerHandler = () => {
+  // 鑻ュ凡鏈夐�変腑鏃ユ湡锛岀敤瀹冨垵濮嬪寲锛涘惁鍒欑敤浠婂ぉ
+  dateValue.value = searchForm.value.timeStr
+    ? dayjs(searchForm.value.timeStr, 'YYYY-MM-DD').valueOf()
+    : Date.now()
+  showDatePicker.value = true
+}
+
+// 鏃ユ湡閫夋嫨纭锛堜笌鍏朵粬椤典竴鑷达細鎷挎椂闂存埑 -> YYYY-MM-DD锛�
+const onDateConfirm = (e) => {
+  searchForm.value.timeStr = formatDateToYMD(e.value)
+  showDatePicker.value = false
+  handleQuery()
+}
+
+// 鍝嶅簲寮忔暟鎹�
+const tableData = ref([])
+const userList = ref([])
+const tableLoading = ref(false)
+const dialogFormVisible = ref(false)
+const showDatePicker = ref(false)
+const showOutDatePicker = ref(false)
+const loadStatus = ref('loadmore')
+const dateValue = ref(new Date().getTime())
+
+const page = reactive({
+  current: 1,
+  size: 10,
+})
+
+const total = ref(0)
+const currentRowId = ref(null)
+const currentRowNum = ref(0)
+
+const data = reactive({
+  searchForm: {
+    supplierName: '',
+    timeStr: '',
+  },
+  form: {
+    inboundQuantity: '',
+    inboundTime: '',
+    nickName: '',
+  },
+  rules: {
+    inboundQuantity: [
+      { required: true, message: '璇疯緭鍏ュ嚭搴撴暟閲�', trigger: 'blur' },
+      { 
+        validator: (rule, value, callback) => {
+          if (value && (value <= 0 || value > currentRowNum.value)) {
+            callback(new Error('璇峰~鍏ユ湁鏁堟暟瀛�'))
+          } else {
+            callback()
+          }
+        }, 
+        trigger: 'blur' 
+      }
+    ],
+    inboundTime: [
+      { required: true, message: '璇烽�夋嫨鍑哄簱鏃ユ湡', trigger: 'change' }
+    ],
+    nickName: [
+      { required: true, message: '璇烽�夋嫨鍑哄簱浜�', trigger: 'change' }
+    ]
+  }
+})
+
+const { searchForm, form, rules } = toRefs(data)
+
+
+
+// 鏌ヨ鍒楄〃
+const handleQuery = () => {
+  page.current = 1
+  getList()
+}
+
+const getList = () => {
+  tableLoading.value = true
+  const params = { 
+    ...page,
+    supplierName: searchForm.value.supplierName,
+    timeStr: searchForm.value.timeStr
+  }
+  
+  getInPageByCustom(params).then(res => {
+    tableLoading.value = false
+    if (page.current === 1) {
+      tableData.value = res.data.records || []
+    } else {
+      tableData.value = [...tableData.value, ...(res.data.records || [])]
+    }
+    total.value = res.data.total || 0
+    
+    // 鏇存柊鍔犺浇鐘舵��
+    if (tableData.value.length >= total.value) {
+      loadStatus.value = 'nomore'
+    } else {
+      loadStatus.value = 'loadmore'
+    }
+  }).catch(() => {
+    tableLoading.value = false
+    loadStatus.value = 'error'
+  })
+}
+
+// 鍔犺浇鏇村
+const loadMore = () => {
+  if (loadStatus.value === 'nomore') return
+  
+  loadStatus.value = 'loading'
+  page.current++
+  getList()
+}
+
+// 鎵撳紑鍑哄簱琛ㄥ崟
+const openForm = async (row) => {
+  dialogFormVisible.value = true
+  currentRowId.value = row.id
+  currentRowNum.value = row.inboundNum || 0
+  
+  // 鍒濆鍖栬〃鍗曟暟鎹�
+  form.value = {
+    inboundQuantity: '',
+    inboundTime: getCurrentDate(),
+    nickName: '',
+  }
+  
+  // 鍔犺浇鐢ㄦ埛鍒楄〃
+  try {
+    const userLists = await userListNoPageByTenantId()
+    userList.value = userLists.data.map(item => ({
+      ...item,
+      label: item.nickName,
+      value: item.userId
+    }))
+  } catch (error) {
+    console.error('鍔犺浇鐢ㄦ埛鍒楄〃澶辫触:', error)
+    userList.value = []
+  }
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+  proxy.$refs.formRef.validate().then(valid => {
+    if (valid && currentRowId.value) {
+      const outData = {
+        id: currentRowId.value,
+        salesLedgerProductId: 0,
+        quantity: form.value.inboundQuantity,
+        time: form.value.inboundTime,
+        userId: form.value.nickName,
+        type: 3 // 鑷畾涔夊嚭搴撶被鍨�
+      }
+      
+      stockOut(outData).then(res => {
+        uni.$u.toast('鎻愪氦鎴愬姛')
+        closeDia()
+        getList()
+      }).catch(err => {
+        uni.$u.toast('鍑哄簱澶辫触')
+      })
+    }
+  }).catch(err => {
+    console.log('琛ㄥ崟楠岃瘉澶辫触:', err)
+  })
+}
+
+// 鍏抽棴寮圭獥
+const closeDia = () => {
+  dialogFormVisible.value = false
+  proxy.$refs.formRef.resetFields()
+}
+
+// 瀵煎嚭
+const handleOut = () => {
+  uni.showModal({
+    title: '瀵煎嚭',
+    content: '鏄惁纭瀵煎嚭锛�',
+    success: (res) => {
+      if (res.confirm) {
+        proxy.download("/stockin/exportTwo", {}, '鑷畾涔夊嚭搴撳彴璐�.xlsx')
+      }
+    }
+  })
+}
+
+// 鑾峰彇褰撳墠鏃ユ湡
+function getCurrentDate() {
+  const today = new Date()
+  const year = today.getFullYear()
+  const month = String(today.getMonth() + 1).padStart(2, '0')
+  const day = String(today.getDate()).padStart(2, '0')
+  return `${year}-${month}-${day}`
+}
+
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped lang="scss">
+.stock-out-page {
+  min-height: 100vh;
+  background: #f5f5f5;
+  padding-bottom: 80px;
+}
+
+.search-section {
+  background: #fff;
+  padding: 16px;
+  margin-bottom: 12px;
+}
+
+.search-bar {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin-bottom: 12px;
+}
+
+.search-input {
+  flex: 1;
+}
+
+.search-button {
+  width: 44px;
+  height: 44px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: #f5f5f5;
+  border-radius: 8px;
+}
+
+.date-filter {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 12px 16px;
+  background: #f5f5f5;
+  border-radius: 8px;
+}
+
+.date-text {
+  font-size: 14px;
+  color: #666;
+}
+
+.stock-list {
+  padding: 0 16px;
+}
+
+.stock-item {
+  background: #fff;
+  border-radius: 12px;
+  padding: 16px;
+  margin-bottom: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+}
+
+.item-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 12px;
+}
+
+.item-left {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.batch-icon {
+  width: 32px;
+  height: 32px;
+  background: #2979ff;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.batch-text {
+  font-size: 14px;
+  font-weight: 500;
+  color: #333;
+}
+
+.time-text {
+  font-size: 12px;
+  color: #999;
+}
+
+.item-details {
+  margin: 12px 0;
+}
+
+.detail-row {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 8px 0;
+}
+
+.detail-label {
+  font-size: 14px;
+  color: #666;
+}
+
+.detail-value {
+  font-size: 14px;
+  color: #333;
+  text-align: right;
+  flex: 1;
+  margin-left: 12px;
+}
+
+.detail-value.highlight {
+  color: #2979ff;
+  font-weight: 500;
+}
+
+.detail-value.price {
+  color: #ff6b00;
+  font-weight: 500;
+}
+
+.item-actions {
+  display: flex;
+  gap: 12px;
+  margin-top: 12px;
+  padding-top: 12px;
+  border-top: 1px solid #f5f5f5;
+}
+
+.no-data {
+  text-align: center;
+  padding: 60px 0;
+  color: #999;
+  font-size: 14px;
+}
+
+.load-more {
+  padding: 20px 16px;
+}
+
+.popup-content {
+  width: 600rpx;
+  background: #ffffff;
+  border-radius: 20rpx;
+  
+  .popup-header {
+    padding: 40rpx 30rpx 20rpx;
+    text-align: center;
+    border-bottom: 1rpx solid #f0f0f0;
+    
+    .popup-title {
+      font-size: 32rpx;
+      font-weight: bold;
+      color: #333;
+    }
+  }
+  
+  .popup-body {
+    padding: 30rpx;
+  }
+  
+  .popup-footer {
+    padding: 30rpx;
+    display: flex;
+    gap: 20rpx;
+    border-top: 1rpx solid #f0f0f0;
+    
+    .u-button {
+      flex: 1;
+    }
+  }
+}
+</style>

--
Gitblit v1.9.3