gaoluyang
2025-11-17 c46bf7475742e9b50a8fdcc8d592ba3a86b2754d
海川开心-添加请假管理、出差管理、公出管理页面
已添加12个文件
已修改5个文件
1714 ■■■■■ 文件已修改
src/api/inventoryManagement/stockIn.js 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cooperativeOffice/collaborativeApproval/approve.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cooperativeOffice/collaborativeApproval/detail.vue 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cooperativeOffice/collaborativeApproval/index.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cooperativeOffice/collaborativeApproval/index1.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cooperativeOffice/collaborativeApproval/index2.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cooperativeOffice/collaborativeApproval/index3.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cooperativeOffice/collaborativeApproval/index4.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/index.vue 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inventoryManagement/receiptManagement/components/formDiaManual.vue 412 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inventoryManagement/receiptManagement/detail.vue 364 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inventoryManagement/receiptManagement/index.vue 406 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/static/images/icon/chuchaiguanli@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/static/images/icon/gongchuguanli@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/static/images/icon/qingjiaguanli@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/static/images/icon/rukuguanli@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockIn.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
import request from "@/utils/request";
// æŸ¥è¯¢å…¥åº“信息列表
export const getStockInPage = (params) => {
    return request({
        url: "/stockin/listPage",
        method: "get",
        params,
    });
};
// æŸ¥è¯¢ç”Ÿäº§å…¥åº“信息列表
export const getStockInPageByProduction = (params) => {
    return request({
        url: "/stockin/listPageByProduction",
        method: "get",
        params,
    });
};
// å‡ºåº“台账-查询自定义入库信息列表
export const getStockInPageByCustom = (params) => {
    return request({
        url: "/stockmanagement/listPageByCustom",
        method: "get",
        params,
    });
};
// å…¥åº“管理-查询自定义入库信息列表
export const getInPageByCustom = (params) => {
    return request({
        url: "/stockin/listPageByCustom",
        method: "get",
        params,
    });
};
// å‡ºåº“台账-查询生产出库信息列表
export const getStockInPageByProduct = (params) => {
    return request({
        url: "/stockmanagement/listPageByProduct",
        method: "get",
        params,
    });
};
// ä¿®æ”¹å…¥åº“存信息
export const updateStockIn = (data) => {
    return request({
        url: "/stockin/update",
        method: "post",
        data,
    });
};
// ä¿®æ”¹åº“存信息
export const updateManagement = (data) => {
    return request({
        url: "/stockin/updateManagement",
        method: "post",
        data,
    });
};
// æ–°å¢žå•†å“å…¥åº“信息
export function addSutockIn(data) {
    return request({
        url: '/stockin/add',
        method: 'post',
        data: data
    })
}
// æ–°å¢žè‡ªå®šä¹‰å…¥åº“信息
export function addStockInCustom(data) {
    return request({
        url: '/stockin/addCustom',
        method: 'post',
        data: data
    })
}
// ç¼–辑自定义入库信息
export function updateStockInCustom(data) {
    return request({
        url: '/stockin/updateCustom',
        method: 'post',
        data: data
    })
}
// åˆ é™¤å…¥åº“信息
export function delStockIn(ids) {
    return request({
        url: '/stockin/del',
        method: 'post',
        data: ids
    })
}
// åˆ é™¤è‡ªå®šä¹‰å…¥åº“信息
export function delStockInCustom(ids) {
    return request({
        url: '/stockin/delteCustom',
        method: 'post',
        data: ids
    })
}
// å¯¼å‡ºå…¥åº“信息
export function exportStockIn(query) {
    return request({
        url: '/stockin/export',
        method: 'get',
        params: query,
        responseType: 'blob'
    })
}
export function selectProductRecordListByPuechaserId(query) {
    return request({
        url: '/stockin/productlist',
        method: 'get',
        params: query
    })
}
//
src/pages.json
@@ -247,6 +247,27 @@
      }
    },
    {
      "path": "pages/cooperativeOffice/collaborativeApproval/index1",
      "style": {
        "navigationBarTitleText": "公出管理",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/cooperativeOffice/collaborativeApproval/index2",
      "style": {
        "navigationBarTitleText": "请假管理",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/cooperativeOffice/collaborativeApproval/index3",
      "style": {
        "navigationBarTitleText": "出差管理",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/cooperativeOffice/collaborativeApproval/index",
      "style": {
        "navigationBarTitleText": "审批管理",
@@ -434,6 +455,20 @@
        "navigationBarTitleText": "生产核算",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/inventoryManagement/receiptManagement/index",
      "style": {
        "navigationBarTitleText": "自定义入库",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/inventoryManagement/receiptManagement/detail",
      "style": {
        "navigationBarTitleText": "新增入库",
        "navigationStyle": "custom"
      }
    }
  ],
  "subPackages": [
src/pages/cooperativeOffice/collaborativeApproval/approve.vue
@@ -25,6 +25,30 @@
          <text class="info-label">申请日期</text>
          <text class="info-value">{{ approvalData.approveTime }}</text>
        </view>
        <!-- approveType=2 è¯·å‡ç›¸å…³å­—段 -->
        <template v-if="approvalData.approveType === 2">
          <view class="info-row">
            <text class="info-label">请假开始时间</text>
            <text class="info-value">{{ approvalData.startDate || '-' }}</text>
          </view>
          <view class="info-row">
            <text class="info-label">请假结束时间</text>
            <text class="info-value">{{ approvalData.endDate || '-' }}</text>
          </view>
        </template>
        <!-- approveType=3 å‡ºå·®ç›¸å…³å­—段 -->
        <view v-if="approvalData.approveType === 3" class="info-row">
          <text class="info-label">出差地点</text>
          <text class="info-value">{{ approvalData.location || '-' }}</text>
        </view>
        <!-- approveType=4 æŠ¥é”€ç›¸å…³å­—段 -->
        <view v-if="approvalData.approveType === 4" class="info-row">
          <text class="info-label">报销金额</text>
          <text class="info-value">{{ approvalData.price ? `Â¥${approvalData.price}` : '-' }}</text>
        </view>
      </view>
    </view>
src/pages/cooperativeOffice/collaborativeApproval/detail.vue
@@ -39,9 +39,48 @@
      <u-form-item prop="approveTime" label="申请日期" required>
        <u-input
          v-model="form.approveTime"
          placeholder="请选择"
          readonly
          placeholder="请选择"
          @click="showDatePicker"
        />
      </u-form-item>
      <!-- approveType=2 è¯·å‡ç›¸å…³å­—段 -->
      <template v-if="approveType === 2">
        <u-form-item prop="startDate" label="请假开始时间" required>
          <u-input
            v-model="form.startDate"
            readonly
            placeholder="请选择开始时间"
            @click="showStartDatePicker"
          />
        </u-form-item>
        <u-form-item prop="endDate" label="请假结束时间" required>
          <u-input
            v-model="form.endDate"
            readonly
            placeholder="请选择结束时间"
            @click="showEndDatePicker"
          />
        </u-form-item>
      </template>
      <!-- approveType=3 å‡ºå·®ç›¸å…³å­—段 -->
      <u-form-item v-if="approveType === 3" prop="location" label="出差地点" required>
        <u-input
          v-model="form.location"
          placeholder="请输入出差地点"
          clearable
        />
      </u-form-item>
      <!-- approveType=4 æŠ¥é”€ç›¸å…³å­—段 -->
      <u-form-item v-if="approveType === 4" prop="price" label="报销金额" required>
        <u-input
          v-model="form.price"
          type="number"
          placeholder="请输入报销金额"
          clearable
        />
      </u-form-item>
    </u-form>
@@ -56,15 +95,37 @@
    />
    <!-- æ—¥æœŸé€‰æ‹©å™¨ -->
    <u-popup v-model="showDate" mode="bottom">
      <u-datetime-picker
    <up-popup :show="showDate" mode="bottom" @close="showDate = false">
      <up-datetime-picker
        :show="true"
        v-model="currentDate"
        title="选择日期"
        mode="date"
        @confirm="onDateConfirm"
        @cancel="showDate = false"
        mode="date"
      />
    </u-popup>
    </up-popup>
    <!-- è¯·å‡å¼€å§‹æ—¶é—´é€‰æ‹©å™¨ -->
    <up-popup :show="showStartDate" mode="bottom" @close="showStartDate = false">
      <up-datetime-picker
        :show="true"
        v-model="startDateValue"
        @confirm="onStartDateConfirm"
        @cancel="showStartDate = false"
        mode="date"
      />
    </up-popup>
    <!-- è¯·å‡ç»“束时间选择器 -->
    <up-popup :show="showEndDate" mode="bottom" @close="showEndDate = false">
      <up-datetime-picker
        :show="true"
        v-model="endDateValue"
        @confirm="onEndDateConfirm"
        @cancel="showEndDate = false"
        mode="date"
      />
    </up-popup>
    <!-- å®¡æ ¸æµç¨‹åŒºåŸŸ -->
    <view class="approval-process">
      <view class="approval-header">
@@ -137,7 +198,11 @@
        approveReason: "",
        checkResult: "",
        tempFileIds: [],
        approverList: [] // æ–°å¢žå­—段,存储所有节点的审批人id
        approverList: [], // æ–°å¢žå­—段,存储所有节点的审批人id
        startDate: "",
        endDate: "",
        location: "",
        price: ""
    },
    rules: {
        approveTime: [{ required: false, message: "请输入", trigger: "change" },],
@@ -145,6 +210,10 @@
        approveDeptId: [{ required: true, message: "请输入", trigger: "blur" }],
        approveReason: [{ required: true, message: "请输入", trigger: "blur" }],
        checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
        startDate: [{ required: false, message: "请选择开始时间", trigger: "change" }],
        endDate: [{ required: false, message: "请选择结束时间", trigger: "change" }],
        location: [{ required: false, message: "请输入出差地点", trigger: "blur" }],
        price: [{ required: false, message: "请输入报销金额", trigger: "blur" }],
    },
});
const { form, rules } = toRefs(data);
@@ -158,8 +227,13 @@
const formRef = ref(null);
const message = ref("");
const showDate = ref(false)
const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
const currentDate = ref(Date.now())
const showStartDate = ref(false)
const startDateValue = ref(Date.now())
const showEndDate = ref(false)
const endDateValue = ref(Date.now())
const userStore = useUserStore()
const approveType = ref(0)
const getProductOptions = () => {
    getDept().then((res) => {
@@ -184,6 +258,7 @@
        
        // ä»Žæœ¬åœ°å­˜å‚¨èŽ·å–å‚æ•°
        operationType.value = uni.getStorageSync('operationType') || 'add';
        approveType.value = uni.getStorageSync('approveType') || 0;
        
        // å¦‚果是编辑模式,从本地存储获取数据
        if (operationType.value === 'edit') {
@@ -250,6 +325,7 @@
    // æ¸…除本地存储的数据
  uni.removeStorageSync('operationType');
    uni.removeStorageSync('invoiceLedgerEditRow');
    uni.removeStorageSync('approveType');
  uni.navigateBack();
};
@@ -283,7 +359,7 @@
      // æ”¶é›†æ‰€æœ‰èŠ‚ç‚¹çš„å®¡æ‰¹äººid
      console.log('approverNodes---', approverNodes.value)
      form.value.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
      form.value.approveType = 0
      form.value.approveType = approveType.value
      if (operationType.value === "add" || currentApproveStatus.value == 3) {
        approveProcessAdd(form.value).then(res => {
          showToast("提交成功");
@@ -366,6 +442,28 @@
    currentDate.value = formatDateToYMD(e.value)
    showDate.value = false;
}
// æ˜¾ç¤ºè¯·å‡å¼€å§‹æ—¶é—´é€‰æ‹©å™¨
const showStartDatePicker = () => {
    showStartDate.value = true
}
// ç¡®è®¤è¯·å‡å¼€å§‹æ—¶é—´é€‰æ‹©
const onStartDateConfirm = (e) => {
  form.value.startDate = formatDateToYMD(e.value)
    showStartDate.value = false
}
const showEndDatePicker = () => {
    showEndDate.value = true
}
// ç¡®è®¤è¯·å‡ç»“束时间选择
const onEndDateConfirm = (e) => {
  form.value.endDate = formatDateToYMD(e.value)
    showEndDate.value = false
}
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
@@ -374,6 +472,7 @@
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
</script>
<style scoped lang="scss">
src/pages/cooperativeOffice/collaborativeApproval/index.vue
@@ -55,6 +55,31 @@
                            <text class="detail-label">申请日期</text>
                            <text class="detail-value">{{ item.approveTime }}</text>
                        </view>
                        <!-- approveType=2 è¯·å‡ç›¸å…³å­—段 -->
                        <template v-if="item.approveType === 2">
                            <view class="detail-row">
                                <text class="detail-label">请假开始时间</text>
                                <text class="detail-value">{{ item.startDate || '-' }}</text>
                            </view>
                            <view class="detail-row">
                                <text class="detail-label">请假结束时间</text>
                                <text class="detail-value">{{ item.endDate || '-' }}</text>
                            </view>
                        </template>
                        <!-- approveType=3 å‡ºå·®ç›¸å…³å­—段 -->
                        <view v-if="item.approveType === 3" class="detail-row">
                            <text class="detail-label">出差地点</text>
                            <text class="detail-value">{{ item.location || '-' }}</text>
                        </view>
                        <!-- approveType=4 æŠ¥é”€ç›¸å…³å­—段 -->
                        <view v-if="item.approveType === 4" class="detail-row">
                            <text class="detail-label">报销金额</text>
                            <text class="detail-value highlightYellow">{{ item.price ? `Â¥${item.price}` : '-' }}</text>
                        </view>
                        <view class="detail-row">
                            <text class="detail-label">结束日期</text>
                            <text class="detail-value">{{ item.approveOverTime }}</text>
@@ -117,6 +142,14 @@
    import {onShow} from "@dcloudio/uni-app";
    import useUserStore from "@/store/modules/user";
    
    // æŽ¥æ”¶çˆ¶ç»„件传递的 approveType å‚æ•°
    const props = defineProps({
        approveType: {
            type: Number,
            default: 0
        }
    });
    const userStore = useUserStore()
    // æ•°æ®
    const ledgerList = ref([]);
@@ -139,7 +172,7 @@
            size: -1,
        };
        approveProcessListPage({
                ...page,approveType: 0,...searchForm.value
                ...page,approveType: props.approveType,...searchForm.value
            })
            .then((res) => {
                ledgerList.value = res.data.records;
@@ -206,6 +239,7 @@
        uni.setStorageSync('invoiceLedgerEditRow', JSON.stringify(item));
        uni.setStorageSync('operationType', 'edit');
        uni.setStorageSync('approveId', item.approveId);
        uni.setStorageSync('approveType', props.approveType);
        uni.navigateTo({
            url: "/pages/cooperativeOffice/collaborativeApproval/detail",
        });
@@ -214,15 +248,17 @@
    // æ·»åŠ æ–°è®°å½•
    const handleAdd = () => {
        uni.setStorageSync('operationType', 'add');
        uni.setStorageSync('approveType', props.approveType);
        uni.navigateTo({
            url: "/pages/cooperativeOffice/collaborativeApproval/detail",
            url: `/pages/cooperativeOffice/collaborativeApproval/detail?approveType=${props.approveType}`,
        });
    };
    // ç‚¹å‡»å®¡æ ¸
    const approve = (item) => {
        uni.setStorageSync('approveId', item.approveId);
        uni.setStorageSync('approveType', props.approveType);
        uni.navigateTo({
            url: "/pages/cooperativeOffice/collaborativeApproval/approve"
            url: "/pages/cooperativeOffice/collaborativeApproval/approve?approveType=" + props.approveType
        })
    }
src/pages/cooperativeOffice/collaborativeApproval/index1.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
<template>
  <view class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <ApprovalProcessIndex :approveType="1" />
  </view>
</template>
<script setup>
import ApprovalProcessIndex from './index.vue'
</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
}
</style>
src/pages/cooperativeOffice/collaborativeApproval/index2.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
<template>
  <view class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <ApprovalProcessIndex :approveType="2" />
  </view>
</template>
<script setup>
import ApprovalProcessIndex from './index.vue'
</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
}
</style>
src/pages/cooperativeOffice/collaborativeApproval/index3.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
<template>
  <view class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <ApprovalProcessIndex :approveType="3" />
  </view>
</template>
<script setup>
import ApprovalProcessIndex from './index.vue'
</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
}
</style>
src/pages/cooperativeOffice/collaborativeApproval/index4.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
<template>
  <view class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <ApprovalProcessIndex :approveType="4" />
  </view>
</template>
<script setup>
import ApprovalProcessIndex from './index.vue'
</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
}
</style>
src/pages/index.vue
@@ -121,6 +121,35 @@
                </up-grid>
            </view>
        </view>
        <!-- å…¥åº“管理 -->
        <view class="common-module collaboration-module">
            <view class="module-header">
                <view class="module-title-container">
                    <text class="module-title">入库管理</text>
                </view>
            </view>
            <view class="module-content">
                <up-grid
                    :border="false"
                    col="4"
                >
                    <up-grid-item
                        v-for="(item, index) in inventoryManagement"
                        :key="index"
                        @click="handleCommonItemClick(item)"
                    >
                        <view class="icon-container" :style="{ background: item.bgColor }">
                            <up-icon
                                :name="item.icon"
                                :size="58"
                                color="#ffffff"
                            ></up-icon>
                        </view>
                        <text class="item-label">{{item.label}}</text>
                    </up-grid-item>
                </up-grid>
            </view>
        </view>
        
        <!-- ç”Ÿäº§ç®¡æŽ§æ¨¡å— -->
        <view class="common-module production-module">
@@ -273,6 +302,18 @@
// ååŒåŠžå…¬åŠŸèƒ½æ•°æ®
const collaborationItems = reactive([
    {
        icon: '/static/images/icon/gongchuguanli@2x.png',
        label: '公出管理',
    },
    {
        icon: '/static/images/icon/qingjiaguanli@2x.png',
        label: '请假管理',
    },
    {
        icon: '/static/images/icon/chuchaiguanli@2x.png',
        label: '出差管理',
    },
    {
        icon: '/static/images/icon/xietongshenpi@2x.png',
        label: '协同审批',
    },
@@ -280,6 +321,13 @@
        icon: '/static/images/icon/kehubaifang@2x.png',
        label: '客户拜访',
    }
]);
// ååŒåŠžå…¬åŠŸèƒ½æ•°æ®inventoryManagement/receiptManagement
const inventoryManagement = reactive([
    {
        icon: '/static/images/icon/rukuguanli@2x.png',
        label: '自定义入库',
    },
]);
// ç”Ÿäº§ç®¡æŽ§åŠŸèƒ½æ•°æ®
@@ -313,10 +361,10 @@
// è®¾å¤‡ç®¡ç†åŠŸèƒ½æ•°æ®
const equipmentItems = reactive([
    // {
    //     icon: '/static/images/icon/shebeitaizhang@2x.png',
    //     label: '设备台账',
    // },
    {
        icon: '/static/images/icon/shebeitaizhang@2x.png',
        label: '设备台账',
    },
    {
        icon: '/static/images/icon/shbeibaoxiu@2x.png',
        label: '设备报修',
@@ -329,26 +377,26 @@
        icon: '/static/images/icon/xunjianshangchuan@2x.png',
        label: '巡检上传',
    },
    {
        icon: '/static/images/icon/guzhangfenxi@2x.png',
        label: '分析追溯',
        bgColor: '#ff9800'
    },
    {
        icon: '/static/images/icon/zhinengpaidan@2x.png',
        label: '智能派单',
        bgColor: '#ff6b35'
    },
    {
        icon: '/static/images/icon/zuoyezhidao@2x.png',
        label: '作业指导',
        bgColor: '#4caf50'
    },
    {
        icon: '/static/images/icon/jieguoyanzheng@2x.png',
        label: '结果验证',
        bgColor: '#9c27b0'
    }
    // {
    //     icon: '/static/images/icon/guzhangfenxi@2x.png',
    //     label: '分析追溯',
    //     bgColor: '#ff9800'
    // },
    // {
    //     icon: '/static/images/icon/zhinengpaidan@2x.png',
    //     label: '智能派单',
    //     bgColor: '#ff6b35'
    // },
    // {
    //     icon: '/static/images/icon/zuoyezhidao@2x.png',
    //     label: '作业指导',
    //     bgColor: '#4caf50'
    // },
    // {
    //     icon: '/static/images/icon/jieguoyanzheng@2x.png',
    //     label: '结果验证',
    //     bgColor: '#9c27b0'
    // }
]);
// å¤„理常用功能点击
@@ -415,6 +463,21 @@
                url: '/pages/procurementManagement/paymentLedger/index'
            });
            break;
        case '公出管理':
            uni.navigateTo({
                url: '/pages/cooperativeOffice/collaborativeApproval/index1'
            });
            break;
        case '请假管理':
            uni.navigateTo({
                url: '/pages/cooperativeOffice/collaborativeApproval/index2'
            });
            break;
        case '出差管理':
            uni.navigateTo({
                url: '/pages/cooperativeOffice/collaborativeApproval/index3'
            });
            break;
        case '协同审批':
            uni.navigateTo({
                url: '/pages/cooperativeOffice/collaborativeApproval/index'
@@ -425,6 +488,11 @@
                url: '/pages/cooperativeOffice/clientVisit/index'
            });
            break;
        case '自定义入库':
            uni.navigateTo({
                url: '/pages/inventoryManagement/receiptManagement/index'
            });
            break;
        case '生产订单':
            uni.navigateTo({
                url: '/pages/productionManagement/productionOrder/index'
src/pages/inventoryManagement/receiptManagement/components/formDiaManual.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,412 @@
<template>
  <up-popup :show="dialogFormVisible" mode="bottom" :round="10" closeable @close="closeDia">
    <view class="form-popup">
      <view class="popup-header">
        <text class="popup-title">{{ operationType === 'add' ? '新增自定义入库' : '编辑自定义入库' }}</text>
      </view>
      <scroll-view class="popup-content" scroll-y>
        <view v-if="operationType === 'add'" class="add-btn-section">
          <u-button type="primary" @click="addProductRow">新增产品</u-button>
        </view>
        <view v-for="(item, index) in productList" :key="index" class="product-item">
          <view class="item-header">
            <text class="item-title">产品 {{ index + 1 }}</text>
            <u-button v-if="operationType === 'add'" type="error" size="mini" class="delete-btn" @click="removeProductRow(index)">删除</u-button>
          </view>
          <view class="item-form">
            <view class="form-field">
              <text class="field-label required">产品大类</text>
              <up-input v-model="item.productCategory" placeholder="请输入产品大类" />
            </view>
            <view class="form-field">
              <text class="field-label required">规格型号</text>
              <up-input v-model="item.specificationModel" placeholder="请输入规格型号" />
            </view>
            <view class="form-field">
              <text class="field-label required">单位</text>
              <up-input v-model="item.unit" placeholder="请输入单位" />
            </view>
            <view class="form-field">
              <text class="field-label">供应商</text>
              <up-input v-model="item.supplierName" placeholder="请输入供应商" />
            </view>
            <view class="form-field">
              <text class="field-label required">物品类型</text>
              <up-input v-model="item.itemType" readonly placeholder="请选择物品类型" @click="openItemTypePicker(index)" />
            </view>
            <view class="form-field">
              <text class="field-label required">入库数量</text>
              <up-input v-model="item.inboundNum" type="number" placeholder="请输入入库数量" @blur="calculateTotalPrice(item)" />
            </view>
            <view class="form-field">
              <text class="field-label required">入库日期</text>
              <up-input v-model="item.inboundDate" readonly placeholder="请选择入库日期" @click="openDatePicker(index)" />
            </view>
            <view class="form-field">
              <text class="field-label">税率(%)</text>
              <up-input v-model="item.taxRate" readonly placeholder="请选择税率" @click="openTaxRatePicker(index)" />
            </view>
            <view class="form-field">
              <text class="field-label">含税单价(元)</text>
              <up-input v-model="item.taxInclusiveUnitPrice" type="digit" placeholder="请输入含税单价" @blur="calculateTotalPrice(item)" />
            </view>
            <view class="form-field">
              <text class="field-label">含税总价(元)</text>
              <up-input v-model="item.taxInclusiveTotalPrice" type="digit" placeholder="自动计算" @blur="calculateExclusivePrice(item)" />
            </view>
            <view class="form-field">
              <text class="field-label">不含税总价(元)</text>
              <up-input v-model="item.taxExclusiveTotalPrice" type="digit" placeholder="自动计算" disabled />
            </view>
          </view>
        </view>
      </scroll-view>
      <view class="popup-footer">
        <u-button class="btn-cancel" @click="closeDia">取消</u-button>
        <u-button class="btn-confirm" type="primary" @click="submitForm">确认</u-button>
      </view>
    </view>
    <up-action-sheet :show="showItemTypePicker" :actions="itemTypeActions" title="选择物品类型" @select="onItemTypeSelect" @close="showItemTypePicker = false" />
    <up-action-sheet :show="showTaxRatePicker" :actions="taxRateActions" title="选择税率" @select="onTaxRateSelect" @close="showTaxRatePicker = false" />
    <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>
  </up-popup>
</template>
<script setup>
import { ref, reactive, toRefs } from 'vue'
import useUserStore from '@/store/modules/user'
import { formatDateToYMD } from '@/utils/ruoyi'
import { addStockInCustom, updateStockInCustom } from '@/api/inventoryManagement/stockIn.js'
const userStore = useUserStore()
const emit = defineEmits(['close', 'success'])
const operationType = ref('')
const dialogFormVisible = ref(false)
const productList = ref([])
const loading = ref(false)
const showItemTypePicker = ref(false)
const showTaxRatePicker = ref(false)
const showDatePicker = ref(false)
const dateValue = ref(new Date().getTime())
const currentEditIndex = ref(0)
function formatDateTime(date = new Date(), includeTime = true) {
  const d = new Date(date)
  const year = d.getFullYear()
  const month = String(d.getMonth() + 1).padStart(2, '0')
  const day = String(d.getDate()).padStart(2, '0')
  if (!includeTime) return `${year}-${month}-${day}`
  const hours = String(d.getHours()).padStart(2, '0')
  const minutes = String(d.getMinutes()).padStart(2, '0')
  const seconds = String(d.getSeconds()).padStart(2, '0')
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
function getCurrentDate() {
  return formatDateTime(new Date(), false)
}
const itemTypeActions = ref([
  { name: '物料', value: '物料' },
  { name: '原料', value: '原料' },
  { name: '成品', value: '成品' },
  { name: '其他', value: '其他' }
])
const taxRateActions = ref([
  { name: '1%', value: 1 },
  { name: '6%', value: 6 },
  { name: '13%', value: 13 }
])
const data = reactive({
  form: {
    id: null,
    supplierId: null,
    supplierName: '',
    recorderId: userStore.userId,
    recorderName: userStore.name,
    entryDate: getCurrentDate(),
    remark: ''
  }
})
const { form } = toRefs(data)
const addProductRow = () => {
  productList.value.push({
    id: null,
    productCategory: '',
    specificationModel: '',
    unit: '',
    supplierName: form.value.supplierName || '',
    itemType: '',
    inboundNum: 0,
    inboundDate: '',
    taxRate: null,
    taxInclusiveUnitPrice: 0,
    taxInclusiveTotalPrice: 0,
    taxExclusiveTotalPrice: 0
  })
}
const removeProductRow = (index) => {
  productList.value.splice(index, 1)
}
const openItemTypePicker = (index) => {
  currentEditIndex.value = index
  showItemTypePicker.value = true
}
const onItemTypeSelect = (item) => {
  productList.value[currentEditIndex.value].itemType = item.value
  showItemTypePicker.value = false
}
const openTaxRatePicker = (index) => {
  currentEditIndex.value = index
  showTaxRatePicker.value = true
}
const onTaxRateSelect = (item) => {
  productList.value[currentEditIndex.value].taxRate = item.value
  calculateExclusivePrice(productList.value[currentEditIndex.value])
  showTaxRatePicker.value = false
}
const openDatePicker = (index) => {
  currentEditIndex.value = index
  showDatePicker.value = true
}
const onDateConfirm = (e) => {
  productList.value[currentEditIndex.value].inboundDate = formatDateToYMD(e.value)
  showDatePicker.value = false
}
const calculateTotalPrice = (row) => {
  const unitPrice = Number(row.taxInclusiveUnitPrice || 0)
  const quantity = Number(row.inboundNum || 0)
  row.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2)
  calculateExclusivePrice(row)
}
const calculateExclusivePrice = (row) => {
  const totalPrice = Number(row.taxInclusiveTotalPrice || 0)
  const taxRate = Number(row.taxRate || 0)
  row.taxExclusiveTotalPrice = (totalPrice / (1 + taxRate / 100)).toFixed(2)
}
const submitForm = async () => {
  try {
    if (!productList.value.length) {
      uni.showToast({ title: '请至少添加一条产品数据', icon: 'none' })
      return
    }
    for (let i = 0; i < productList.value.length; i++) {
      const product = productList.value[i]
      if (!product.productCategory || !product.specificationModel || !product.unit) {
        uni.showToast({ title: ç¬¬è¡Œäº§å“æ•°æ®æœªå¡«å†™å®Œæ•´, icon: 'none' })
        return
      }
      if (!product.itemType) {
        uni.showToast({ title: ç¬¬è¡Œè¯·é€‰æ‹©ç‰©å“ç±»åž‹, icon: 'none' })
        return
      }
      if (!product.inboundDate) {
        uni.showToast({ title: ç¬¬è¡Œè¯·é€‰æ‹©å…¥åº“日期, icon: 'none' })
        return
      }
      const stock = Number(product?.inboundNum ?? 0)
      if (!Number.isFinite(stock) || stock <= 0) {
        uni.showToast({ title: ç¬¬è¡Œå…¥åº“数量需大于0, icon: 'none' })
        return
      }
    }
    const payloadList = productList.value.map(product => ({
      id: product.id ?? null,
      inboundNum: Number(product.inboundNum),
      productCategory: product.productCategory,
      specificationModel: product.specificationModel,
      unit: product.unit,
      supplierName: product.supplierName || form.value.supplierName,
      itemType: product.itemType,
      inboundDate: formatDateTime(product.inboundDate, false),
      taxRate: Number(product.taxRate || 0),
      taxInclusiveUnitPrice: Number(product.taxInclusiveUnitPrice || 0),
      taxInclusiveTotalPrice: Number(product.taxInclusiveTotalPrice || 0),
      taxExclusiveTotalPrice: Number(product.taxExclusiveTotalPrice || 0)
    }))
    loading.value = true
    uni.showLoading({ title: '提交中...', mask: true })
    if (operationType.value === 'edit') {
      await updateStockInCustom(payloadList[0])
    } else {
      await addStockInCustom(payloadList)
    }
    uni.hideLoading()
    uni.showToast({ title: operationType.value === 'edit' ? '编辑成功' : '新增成功', icon: 'success' })
    closeDia()
    emit('success')
  } catch (error) {
    console.error('提交失败:', error)
    uni.hideLoading()
    uni.showToast({ title: '操作失败,请重试', icon: 'none' })
  } finally {
    loading.value = false
  }
}
const closeDia = () => {
  dialogFormVisible.value = false
  productList.value = []
  emit('close')
}
const openDialog = async (type, row) => {
  operationType.value = type
  dialogFormVisible.value = true
  if (type === 'add') {
    form.value = {
      id: null,
      supplierId: null,
      supplierName: '',
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: ''
    }
    productList.value = []
  } else {
    form.value = {
      id: row?.id ?? null,
      supplierId: row?.supplierId ?? null,
      supplierName: row?.supplierName ?? '',
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: row?.remark ?? ''
    }
    productList.value = [{
      id: row?.id ?? null,
      productCategory: row?.productCategory ?? '',
      specificationModel: row?.specificationModel ?? '',
      unit: row?.unit ?? '',
      supplierName: row?.supplierName ?? '',
      itemType: row?.itemType ?? '',
      inboundNum: Number(row?.inboundNum ?? row?.inboundQuantity ?? 0),
      inboundDate: row?.inboundDate ?? row?.createTime ?? '',
      taxRate: Number(row?.taxRate ?? 0),
      taxInclusiveUnitPrice: Number(row?.taxInclusiveUnitPrice ?? 0),
      taxInclusiveTotalPrice: Number(row?.taxInclusiveTotalPrice ?? 0),
      taxExclusiveTotalPrice: Number(row?.taxExclusiveTotalPrice ?? 0)
    }]
  }
}
defineExpose({ openDialog })
</script>
<style scoped lang="scss">
.form-popup {
  height: 80vh;
  display: flex;
  flex-direction: column;
  background: #f5f5f5;
}
.popup-header {
  padding: 16px;
  background: #fff;
  border-bottom: 1px solid #f0f0f0;
}
.popup-title {
  font-size: 18px;
  font-weight: 600;
  color: #333;
}
.popup-content {
  flex: 1;
  height: 0;
  padding: 12px;
}
.add-btn-section {
  margin-bottom: 12px;
}
.product-item {
  background: #fff;
  border-radius: 12px;
  margin-bottom: 12px;
  overflow: hidden;
}
.item-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: #f8f8f8;
  border-bottom: 1px solid #f0f0f0;
}
.item-title {
  font-size: 16px;
  font-weight: 600;
  width: 100%;
  color: #333;
}
.item-form {
  padding: 16px;
}
.form-field {
  margin-bottom: 16px;
}
.field-label {
  display: block;
  font-size: 14px;
  color: #666;
  margin-bottom: 8px;
}
.field-label.required::before {
  content: '*';
  color: #ff0000;
  margin-right: 4px;
}
.popup-footer {
  display: flex;
  gap: 12px;
  padding: 12px 16px;
  background: #fff;
  border-top: 1px solid #f0f0f0;
}
.btn-cancel, .btn-confirm {
  flex: 1;
}
.btn-cancel {
  background: #f5f5f5;
  color: #666;
  border: none;
}
.delete-btn {
  padding: 2px 8px !important;
  height: 24px !important;
  font-size: 11px !important;
  width: 20px !important;
}
</style>
src/pages/inventoryManagement/receiptManagement/detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,364 @@
<template>
  <view class="stock-detail-page">
    <PageHeader :title="operationType === 'add' ? '新增入库' : '编辑入库'" @back="goBack" />
    <u-form ref="formRef" :model="form" :rules="rules" label-width="140rpx">
      <!-- åŸºæœ¬ä¿¡æ¯ -->
      <view class="form-section">
        <view class="section-title">基本信息</view>
        <u-form-item prop="supplierName" label="供应商" required>
          <u-input
            v-model="form.supplierName"
            placeholder="请输入供应商名称"
            clearable
          />
        </u-form-item>
        <u-form-item prop="productCategory" label="产品大类" required>
          <u-input
            v-model="form.productCategory"
            placeholder="请输入产品大类"
            clearable
          />
        </u-form-item>
        <u-form-item prop="specificationModel" label="规格型号" required>
          <u-input
            v-model="form.specificationModel"
            placeholder="请输入规格型号"
            clearable
          />
        </u-form-item>
        <u-form-item prop="unit" label="单位" required>
          <u-input
            v-model="form.unit"
            placeholder="请输入单位"
            clearable
          />
        </u-form-item>
        <u-form-item prop="itemType" label="物品类型" required>
          <u-input
            v-model="form.itemType"
            readonly
            placeholder="请选择物品类型"
            @click="showItemTypePicker = true"
          />
          <template #right>
            <up-icon name="arrow-right" @click="showItemTypePicker = true"></up-icon>
          </template>
        </u-form-item>
        <u-form-item prop="inboundDate" label="入库日期" required>
          <u-input
            v-model="form.inboundDate"
            readonly
            placeholder="请选择入库日期"
            @click="showDatePicker = true"
          />
          <template #right>
            <up-icon name="calendar" @click="showDatePicker = true"></up-icon>
          </template>
        </u-form-item>
      </view>
      <!-- æ•°é‡å’Œä»·æ ¼ -->
      <view class="form-section">
        <view class="section-title">数量和价格</view>
        <u-form-item prop="inboundNum" label="入库数量" required>
          <u-input
            v-model="form.inboundNum"
            type="number"
            placeholder="请输入入库数量"
            @blur="calculateTotalPrice"
          />
        </u-form-item>
        <u-form-item prop="taxInclusiveUnitPrice" label="含税单价" required>
          <u-input
            v-model="form.taxInclusiveUnitPrice"
            type="digit"
            placeholder="请输入含税单价"
            @blur="calculateTotalPrice"
          />
        </u-form-item>
        <u-form-item prop="taxInclusiveTotalPrice" label="含税总价">
          <u-input
            v-model="form.taxInclusiveTotalPrice"
            type="digit"
            placeholder="自动计算"
            disabled
          />
        </u-form-item>
        <u-form-item prop="taxRate" label="税率(%)" required>
          <u-input
            v-model="form.taxRate"
            readonly
            placeholder="请选择税率"
            @click="showTaxRatePicker = true"
          />
          <template #right>
            <up-icon name="arrow-right" @click="showTaxRatePicker = true"></up-icon>
          </template>
        </u-form-item>
        <u-form-item prop="taxExclusiveTotalPrice" label="不含税总价">
          <u-input
            v-model="form.taxExclusiveTotalPrice"
            type="digit"
            placeholder="自动计算"
            disabled
          />
        </u-form-item>
      </view>
    </u-form>
    <!-- åº•部按钮 -->
    <view class="footer-btns">
      <u-button class="cancel-btn" @click="goBack">取消</u-button>
      <u-button class="save-btn" @click="submitForm">保存</u-button>
    </view>
    <!-- ç‰©å“ç±»åž‹é€‰æ‹©å™¨ -->
    <up-action-sheet
      :show="showItemTypePicker"
      :actions="itemTypeOptions"
      title="选择物品类型"
      @select="onItemTypeSelect"
      @close="showItemTypePicker = false"
    />
    <!-- ç¨ŽçŽ‡é€‰æ‹©å™¨ -->
    <up-action-sheet
      :show="showTaxRatePicker"
      :actions="taxRateOptions"
      title="选择税率"
      @select="onTaxRateSelect"
      @close="showTaxRatePicker = false"
    />
    <!-- æ—¥æœŸé€‰æ‹©å™¨ -->
    <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>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import { formatDateToYMD } from '@/utils/ruoyi'
import {
  addStockInCustom,
  updateStockInCustom
} from "@/api/inventoryManagement/stockIn.js"
const formRef = ref(null)
const operationType = ref('add')
const showItemTypePicker = ref(false)
const showTaxRatePicker = ref(false)
const showDatePicker = ref(false)
const dateValue = ref(new Date().getTime())
const data = reactive({
  form: {
    supplierName: '',
    productCategory: '',
    specificationModel: '',
    unit: '',
    itemType: '',
    inboundDate: '',
    inboundNum: '',
    taxInclusiveUnitPrice: '',
    taxInclusiveTotalPrice: '',
    taxRate: '',
    taxExclusiveTotalPrice: '',
  },
  rules: {
    supplierName: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }],
    productCategory: [{ required: true, message: '请输入产品大类', trigger: 'blur' }],
    specificationModel: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
    unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
    itemType: [{ required: true, message: '请选择物品类型', trigger: 'change' }],
    inboundDate: [{ required: true, message: '请选择入库日期', trigger: 'change' }],
    inboundNum: [{ required: true, message: '请输入入库数量', trigger: 'blur' }],
    taxInclusiveUnitPrice: [{ required: true, message: '请输入含税单价', trigger: 'blur' }],
    taxRate: [{ required: true, message: '请选择税率', trigger: 'change' }],
  },
})
const { form, rules } = toRefs(data)
// ç‰©å“ç±»åž‹é€‰é¡¹
const itemTypeOptions = ref([
  { name: '原材料', value: '原材料' },
  { name: '半成品', value: '半成品' },
  { name: '成品', value: '成品' },
  { name: '辅料', value: '辅料' },
  { name: '包装物', value: '包装物' },
  { name: '其他', value: '其他' },
])
// ç¨Žçއ选项
const taxRateOptions = ref([
  { name: '0%', value: 0 },
  { name: '1%', value: 1 },
  { name: '3%', value: 3 },
  { name: '6%', value: 6 },
  { name: '9%', value: 9 },
  { name: '13%', value: 13 },
])
// è¿”回上一页
const goBack = () => {
  uni.removeStorageSync('stockInEditRow')
  uni.navigateBack()
}
// ç‰©å“ç±»åž‹é€‰æ‹©
const onItemTypeSelect = (item) => {
  form.value.itemType = item.value
  showItemTypePicker.value = false
}
// ç¨ŽçŽ‡é€‰æ‹©
const onTaxRateSelect = (item) => {
  form.value.taxRate = item.value
  showTaxRatePicker.value = false
  calculateExclusivePrice()
}
// æ—¥æœŸé€‰æ‹©ç¡®è®¤
const onDateConfirm = (e) => {
  form.value.inboundDate = formatDateToYMD(e.value)
  showDatePicker.value = false
}
// è®¡ç®—含税总价
const calculateTotalPrice = () => {
  const num = parseFloat(form.value.inboundNum) || 0
  const price = parseFloat(form.value.taxInclusiveUnitPrice) || 0
  form.value.taxInclusiveTotalPrice = (num * price).toFixed(2)
  calculateExclusivePrice()
}
// è®¡ç®—不含税总价
const calculateExclusivePrice = () => {
  const totalPrice = parseFloat(form.value.taxInclusiveTotalPrice) || 0
  const taxRate = parseFloat(form.value.taxRate) || 0
  form.value.taxExclusiveTotalPrice = (totalPrice / (1 + taxRate / 100)).toFixed(2)
}
// æäº¤è¡¨å•
const submitForm = () => {
  formRef.value.validate().then(() => {
    uni.showLoading({
      title: '保存中...',
      mask: true
    })
    const apiCall = operationType.value === 'add' ? addStockInCustom : updateStockInCustom
    apiCall(form.value).then(() => {
      uni.hideLoading()
      uni.showToast({
        title: '保存成功',
        icon: 'success'
      })
      setTimeout(() => {
        goBack()
      }, 1500)
    }).catch(() => {
      uni.hideLoading()
      uni.showToast({
        title: '保存失败',
        icon: 'none'
      })
    })
  }).catch(() => {
    uni.showToast({
      title: '请填写完整信息',
      icon: 'none'
    })
  })
}
onLoad((options) => {
  if (options.type) {
    operationType.value = options.type
  }
  // ç¼–辑模式,加载数据
  if (operationType.value === 'edit') {
    const storedData = uni.getStorageSync('stockInEditRow')
    if (storedData) {
      const row = JSON.parse(storedData)
      form.value = { ...row }
    }
  } else {
    // æ–°å¢žæ¨¡å¼ï¼Œè®¾ç½®é»˜è®¤æ—¥æœŸ
    form.value.inboundDate = formatDateToYMD(new Date())
  }
})
</script>
<style scoped lang="scss">
.stock-detail-page {
  min-height: 100vh;
  background: #f5f5f5;
  padding-bottom: 80px;
}
.form-section {
  background: #fff;
  margin-bottom: 12px;
  padding: 16px;
}
.section-title {
  font-size: 16px;
  font-weight: 600;
  color: #333;
  margin-bottom: 16px;
  padding-left: 12px;
  border-left: 4px solid #2979ff;
}
.footer-btns {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  gap: 12px;
  padding: 12px 16px;
  background: #fff;
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.04);
  z-index: 999;
}
.cancel-btn {
  flex: 1;
  background: #f5f5f5;
  color: #666;
  border: none;
}
.save-btn {
  flex: 1;
  background: #2979ff;
  color: #fff;
  border: none;
}
</style>
src/pages/inventoryManagement/receiptManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,406 @@
<template>
  <view class="stock-in-page">
    <PageHeader title="自定义入库" @back="goBack" />
    <!-- æœç´¢åŒºåŸŸ -->
    <view class="search-section">
      <view class="search-bar">
        <view class="search-input">
          <up-input
            v-model="searchForm.supplierName"
            placeholder="请输入供应商名称"
            clearable
          />
        </view>
        <view class="search-button" @click="handleQuery">
          <up-icon name="search" size="24" color="#999"></up-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>
    <!-- åˆ—表 -->
    <view class="stock-list" v-if="tableData.length > 0">
      <view v-for="(item, index) in tableData" :key="index" class="stock-item">
        <view class="item-header">
          <view class="item-left">
            <view class="batch-icon">
              <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
            </view>
            <text class="batch-text">{{ item.inboundBatches }}</text>
          </view>
          <view class="item-right">
            <text class="time-text">{{ item.inboundDate }}</text>
          </view>
        </view>
        <up-divider></up-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">{{ item.itemType }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">入库数量</text>
            <text class="detail-value highlight">{{ item.inboundNum }} {{ item.unit }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">含税单价</text>
            <text class="detail-value">Â¥{{ item.taxInclusiveUnitPrice }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">含税总价</text>
            <text class="detail-value price">Â¥{{ item.taxInclusiveTotalPrice }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">税率</text>
            <text class="detail-value">{{ item.taxRate }}%</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">入库人</text>
            <text class="detail-value">{{ item.createBy }}</text>
          </view>
        </view>
        <view class="item-actions">
          <u-button type="primary" size="small" @click="handleEdit(item)">编辑</u-button>
          <u-button type="error" size="small" plain @click="handleDeleteSingle(item)">删除</u-button>
        </view>
      </view>
    </view>
    <view v-else class="no-data">
      <text>暂无数据</text>
    </view>
    <!-- æµ®åŠ¨æ“ä½œæŒ‰é’® -->
    <view class="fab-button" @click="handleAdd">
      <up-icon name="plus" size="24" color="#ffffff"></up-icon>
    </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>
    <!-- è¡¨å•弹窗 -->
    <form-dia-manual ref="formDiaManual" @close="getList" @success="getList" />
  </view>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import dayjs from 'dayjs'
import PageHeader from '@/components/PageHeader.vue'
import FormDiaManual from './components/formDiaManual.vue'
import useUserStore from '@/store/modules/user'
import { formatDateToYMD } from '@/utils/ruoyi'
import {
  getInPageByCustom,
  delStockInCustom
} from "@/api/inventoryManagement/stockIn.js"
const userStore = useUserStore()
const tableData = ref([])
const showDatePicker = ref(false)
const dateValue = ref(new Date().getTime())
const formDiaManual = ref(null)
const page = reactive({
  current: 1,
  size: 20,
})
const total = ref(0)
const data = reactive({
  searchForm: {
    supplierName: '',
    timeStr: '',
  },
})
const { searchForm } = toRefs(data)
// ç»Ÿä¸€ç”¨ dayjs è¾“出 YYYY-MM-DD
const formatYMDLocal = (ts) => dayjs(Number(ts)).format('YYYY-MM-DD')
// è¿”回上一页
const goBack = () => {
  uni.navigateBack()
}
// æŸ¥è¯¢åˆ—表
const handleQuery = () => {
  page.current = 1
  getList()
}
const getList = () => {
  uni.showLoading({
    title: '加载中...',
    mask: true
  })
  const params = {
    ...page,
    supplierName: searchForm.value.supplierName,
    timeStr: searchForm.value.timeStr
  }
  getInPageByCustom(params).then(res => {
    uni.hideLoading()
    tableData.value = res.data.records || []
    total.value = res.data.total || 0
  }).catch(() => {
    uni.hideLoading()
    uni.showToast({
      title: '加载失败',
      icon: 'none'
    })
  })
}
// æ‰“开日期选择器(简单可靠)
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 handleAdd = () => {
  formDiaManual.value?.openDialog('add')
}
// ç¼–辑
const handleEdit = (item) => {
  formDiaManual.value?.openDialog('edit', item)
}
// åˆ é™¤å•条
const handleDeleteSingle = (item) => {
  // æ£€æŸ¥æ˜¯å¦æ˜¯æœ¬äººåˆ›å»º
  if (item.createBy !== userStore.nickName) {
    uni.showToast({
      title: '不可删除他人维护的数据',
      icon: 'none'
    })
    return
  }
  uni.showModal({
    title: '删除',
    content: '确认删除该入库记录吗?',
    success: (res) => {
      if (res.confirm) {
        delStockInCustom({ ids: [item.id] }).then(() => {
          uni.showToast({
            title: '删除成功',
            icon: 'success'
          })
          getList()
        }).catch(() => {
          uni.showToast({
            title: '删除失败',
            icon: 'none'
          })
        })
      }
    }
  })
}
onShow(() => {
  getList()
})
</script>
<style scoped lang="scss">
.stock-in-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;
}
.fab-button {
  position: fixed;
  right: 20px;
  bottom: 80px;
  width: 56px;
  height: 56px;
  background: #2979ff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 12px rgba(41, 121, 255, 0.4);
  z-index: 999;
}
</style>
src/static/images/icon/chuchaiguanli@2x.png
src/static/images/icon/gongchuguanli@2x.png
src/static/images/icon/qingjiaguanli@2x.png
src/static/images/icon/rukuguanli@2x.png