From 95fce9ecb77e9615df925eee143ce34647c694ce Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 16 四月 2026 17:20:22 +0800
Subject: [PATCH] 扫码出库和扫码入库功能开发
---
src/pages/inventoryManagement/receiptManagement/index.vue | 593 +++++++++++++------------
src/pages.json | 16
src/pages/inventoryManagement/scanOut/index.vue | 339 ++++++++++++++
src/pages/works.vue | 86 ++
src/pages/inventoryManagement/scanIn/index.vue | 307 +++++++++++++
5 files changed, 1,032 insertions(+), 309 deletions(-)
diff --git a/src/pages.json b/src/pages.json
index 1699610..8ba3857 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -747,6 +747,20 @@
}
},
{
+ "path": "pages/inventoryManagement/scanIn/index",
+ "style": {
+ "navigationBarTitleText": "鎵爜鍏ュ簱",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/inventoryManagement/scanOut/index",
+ "style": {
+ "navigationBarTitleText": "鎵爜鍑哄簱",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/safeProduction/safeQualifications/index",
"style": {
"navigationBarTitleText": "瑙勭▼涓庤祫璐�",
@@ -1337,4 +1351,4 @@
"navigationBarTitleText": "RuoYi",
"navigationBarBackgroundColor": "#FFFFFF"
}
-}
+}
\ No newline at end of file
diff --git a/src/pages/inventoryManagement/receiptManagement/index.vue b/src/pages/inventoryManagement/receiptManagement/index.vue
index 257ea3e..da08367 100644
--- a/src/pages/inventoryManagement/receiptManagement/index.vue
+++ b/src/pages/inventoryManagement/receiptManagement/index.vue
@@ -1,34 +1,42 @@
<template>
<view class="stock-in-page">
- <PageHeader title="鑷畾涔夊叆搴�" @back="goBack" />
-
+ <PageHeader title="鑷畾涔夊叆搴�"
+ @back="goBack" />
<!-- 鎼滅储鍖哄煙 -->
<view class="search-section">
<view class="search-bar">
<view class="search-input">
- <up-input
- v-model="searchForm.supplierName"
- placeholder="璇疯緭鍏ヤ緵搴斿晢鍚嶇О"
- clearable
- />
+ <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 class="search-button"
+ @click="handleQuery">
+ <up-icon name="search"
+ size="24"
+ color="#999"></up-icon>
</view>
</view>
- <view class="date-filter" @click="openDatePickerHandler">
+ <view class="date-filter"
+ @click="openDatePickerHandler">
<text class="date-text">{{ searchForm.timeStr || '閫夋嫨鏃ユ湡' }}</text>
- <up-icon name="calendar" size="18" color="#999"></up-icon>
+ <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="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>
+ <up-icon name="file-text"
+ size="16"
+ color="#ffffff"></up-icon>
</view>
<text class="batch-text">{{ item.inboundBatches }}</text>
</view>
@@ -37,7 +45,6 @@
</view>
</view>
<up-divider></up-divider>
-
<view class="item-details">
<view class="detail-row">
<text class="detail-label">渚涘簲鍟嗗悕绉�</text>
@@ -76,331 +83,341 @@
<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>
+ <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">
+ <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 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 :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" />
+ <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"
+ 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 userStore = useUserStore();
-const tableData = ref([])
-const showDatePicker = ref(false)
-const dateValue = ref(new Date().getTime())
-const formDiaManual = ref(null)
+ 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 page = reactive({
+ current: 1,
+ size: 20,
+ });
+ const total = ref(0);
-const data = reactive({
- searchForm: {
- supplierName: '',
- timeStr: '',
- },
-})
-const { searchForm } = toRefs(data)
+ const data = reactive({
+ searchForm: {
+ supplierName: "",
+ timeStr: "",
+ },
+ });
+ const { searchForm } = toRefs(data);
-// 缁熶竴鐢� dayjs 杈撳嚭 YYYY-MM-DD
-const formatYMDLocal = (ts) => dayjs(Number(ts)).format('YYYY-MM-DD')
+ // 缁熶竴鐢� dayjs 杈撳嚭 YYYY-MM-DD
+ const formatYMDLocal = ts => dayjs(Number(ts)).format("YYYY-MM-DD");
-// 杩斿洖涓婁竴椤�
-const goBack = () => {
- uni.navigateBack()
-}
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
-// 鏌ヨ鍒楄〃
-const handleQuery = () => {
- page.current = 1
- getList()
-}
+ // 鏌ヨ鍒楄〃
+ 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 getList = () => {
+ uni.showLoading({
+ title: "鍔犺浇涓�...",
+ mask: true,
+ });
-// 鎵撳紑鏃ユ湡閫夋嫨鍣紙绠�鍗曞彲闈狅級
-const openDatePickerHandler = () => {
- // 鑻ュ凡鏈夐�変腑鏃ユ湡锛岀敤瀹冨垵濮嬪寲锛涘惁鍒欑敤浠婂ぉ
- dateValue.value = searchForm.value.timeStr
- ? dayjs(searchForm.value.timeStr, 'YYYY-MM-DD').valueOf()
- : Date.now()
- showDatePicker.value = true
-}
+ const params = {
+ ...page,
+ supplierName: searchForm.value.supplierName,
+ timeStr: searchForm.value.timeStr,
+ };
-// 鏃ユ湡閫夋嫨纭锛堜笌鍏朵粬椤典竴鑷达細鎷挎椂闂存埑 -> YYYY-MM-DD锛�
-const onDateConfirm = (e) => {
- searchForm.value.timeStr = formatDateToYMD(e.value)
- showDatePicker.value = false
- handleQuery()
-}
+ 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 handleAdd = () => {
- formDiaManual.value?.openDialog('add')
-}
+ // 鎵撳紑鏃ユ湡閫夋嫨鍣紙绠�鍗曞彲闈狅級
+ const openDatePickerHandler = () => {
+ // 鑻ュ凡鏈夐�変腑鏃ユ湡锛岀敤瀹冨垵濮嬪寲锛涘惁鍒欑敤浠婂ぉ
+ dateValue.value = searchForm.value.timeStr
+ ? dayjs(searchForm.value.timeStr, "YYYY-MM-DD").valueOf()
+ : Date.now();
+ showDatePicker.value = true;
+ };
-// 缂栬緫
-const handleEdit = (item) => {
- formDiaManual.value?.openDialog('edit', item)
-}
+ // 鏃ユ湡閫夋嫨纭锛堜笌鍏朵粬椤典竴鑷达細鎷挎椂闂存埑 -> YYYY-MM-DD锛�
+ const onDateConfirm = e => {
+ searchForm.value.timeStr = formatDateToYMD(e.value);
+ showDatePicker.value = false;
+ handleQuery();
+ };
-// 鍒犻櫎鍗曟潯
-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'
- })
- })
- }
+ // 鏂板鍏ュ簱
+ 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;
}
- })
-}
-onShow(() => {
- getList()
-})
+ 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;
-}
+ .stock-in-page {
+ min-height: 100vh;
+ background: #f5f5f5;
+ padding-bottom: 80px;
+ }
-.search-section {
- background: #fff;
- padding: 16px;
- margin-bottom: 12px;
-}
+ .search-section {
+ background: #fff;
+ padding: 16px;
+ margin-bottom: 12px;
+ }
-.search-bar {
- display: flex;
- align-items: center;
- gap: 12px;
- margin-bottom: 12px;
-}
+ .search-bar {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 12px;
+ }
-.search-input {
- flex: 1;
-}
+ .search-input {
+ flex: 1;
+ }
-.search-button {
- width: 44px;
- height: 44px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: #f5f5f5;
- border-radius: 8px;
-}
+ .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-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;
-}
+ .date-text {
+ font-size: 14px;
+ color: #666;
+ }
-.stock-list {
- padding: 0 16px;
-}
+ .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);
-}
+ .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-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ }
-.item-left {
- display: flex;
- align-items: center;
- gap: 8px;
-}
+ .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-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;
-}
+ .batch-text {
+ font-size: 14px;
+ font-weight: 500;
+ color: #333;
+ }
-.time-text {
- font-size: 12px;
- color: #999;
-}
+ .time-text {
+ font-size: 12px;
+ color: #999;
+ }
-.item-details {
- margin: 12px 0;
-}
+ .item-details {
+ margin: 12px 0;
+ }
-.detail-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 8px 0;
-}
+ .detail-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 0;
+ }
-.detail-label {
- font-size: 14px;
- color: #666;
-}
+ .detail-label {
+ font-size: 14px;
+ color: #666;
+ }
-.detail-value {
- font-size: 14px;
- color: #333;
- text-align: right;
- flex: 1;
- margin-left: 12px;
-}
+ .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.highlight {
+ color: #2979ff;
+ font-weight: 500;
+ }
-.detail-value.price {
- color: #ff6b00;
- 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;
-}
+ .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;
-}
+ .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;
-}
+ .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>
diff --git a/src/pages/inventoryManagement/scanIn/index.vue b/src/pages/inventoryManagement/scanIn/index.vue
new file mode 100644
index 0000000..3e17f31
--- /dev/null
+++ b/src/pages/inventoryManagement/scanIn/index.vue
@@ -0,0 +1,307 @@
+<template>
+ <view class="scan-container">
+ <PageHeader title="鎵爜鍏ュ簱"
+ @back="goBack" />
+ <view class="module-selector"
+ v-if="!showForm">
+ <view class="module-card"
+ @click="startScan('qualified')">
+ <view class="module-icon qualified">
+ <u-icon name="checkbox-mark"
+ color="#fff"
+ size="40"></u-icon>
+ </view>
+ <view class="module-info">
+ <text class="module-label">鍚堟牸鍏ュ簱</text>
+ <text class="module-desc">鎵弿鍚堟牸浜у搧淇℃伅</text>
+ </view>
+ </view>
+ <view class="module-card"
+ @click="startScan('unqualified')">
+ <view class="module-icon unqualified">
+ <u-icon name="close"
+ color="#fff"
+ size="40"></u-icon>
+ </view>
+ <view class="module-info">
+ <text class="module-label">涓嶅悎鏍煎叆搴�</text>
+ <text class="module-desc">褰曞叆涓嶅悎鏍煎搧璁板綍</text>
+ </view>
+ </view>
+ </view>
+ <view class="form-content"
+ v-if="showForm">
+ <u-form ref="formRef"
+ :model="form"
+ :rules="formRules"
+ label-width="100px">
+ <u-form-item label="鍏ュ簱绫诲瀷"
+ border-bottom>
+ <u-tag :text="type === 'qualified' ? '鍚堟牸鍏ュ簱' : '涓嶅悎鏍煎叆搴�'"
+ :type="type === 'qualified' ? 'success' : 'error'"></u-tag>
+ </u-form-item>
+ <u-form-item label="浜у搧鍚嶇О"
+ border-bottom>
+ <u-input v-model="form.productName"
+ readonly
+ border="none"></u-input>
+ </u-form-item>
+ <u-form-item label="瑙勬牸鍨嬪彿"
+ border-bottom>
+ <u-input v-model="form.productModelName"
+ readonly
+ border="none"></u-input>
+ </u-form-item>
+ <u-form-item label="鍗曚綅"
+ border-bottom>
+ <u-input v-model="form.unit"
+ readonly
+ border="none"></u-input>
+ </u-form-item>
+ <u-form-item label="鍏ュ簱鏁伴噺"
+ prop="qualitity"
+ required
+ border-bottom>
+ <u-number-box v-model="form.qualitity"
+ :min="1"
+ :step="1"></u-number-box>
+ </u-form-item>
+ <u-form-item label="棰勮鏁伴噺"
+ prop="warnNum"
+ v-if="type === 'qualified'"
+ border-bottom>
+ <u-number-box v-model="form.warnNum"
+ :min="0"
+ :step="1"></u-number-box>
+ </u-form-item>
+ <u-form-item label="澶囨敞"
+ prop="remark"
+ border-bottom>
+ <u-textarea v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉�"
+ count></u-textarea>
+ </u-form-item>
+ </u-form>
+ <view class="footer-btns">
+ <u-button class="cancel-btn"
+ @click="cancelForm">鍙栨秷</u-button>
+ <u-button class="save-btn"
+ @click="handleSubmit"
+ :loading="loading">纭鍏ュ簱</u-button>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive } from "vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import {
+ createStockInventory,
+ getStockInventoryListPage,
+ } from "@/api/inventoryManagement/stockInventory.js";
+ import {
+ createStockUnInventory,
+ getStockUninventoryListPage,
+ } from "@/api/inventoryManagement/stockUninventory.js";
+ import modal from "@/plugins/modal";
+
+ const showForm = ref(false);
+ const type = ref("qualified"); // qualified | unqualified
+ const loading = ref(false);
+ const formRef = ref(null);
+
+ const form = ref({
+ productId: undefined,
+ productModelId: undefined,
+ productName: "",
+ productModelName: "",
+ unit: "",
+ qualitity: 1,
+ warnNum: 0,
+ remark: "",
+ });
+
+ const formRules = {
+ qualitity: [
+ {
+ required: true,
+ type: "number",
+ message: "璇疯緭鍏ュ叆搴撴暟閲�",
+ trigger: ["blur", "change"],
+ },
+ ],
+ };
+
+ const goBack = () => {
+ if (showForm.value) {
+ showForm.value = false;
+ } else {
+ uni.navigateBack();
+ }
+ };
+
+ const cancelForm = () => {
+ showForm.value = false;
+ };
+
+ const startScan = scanType => {
+ type.value = scanType;
+ uni.scanCode({
+ success: res => {
+ handleScanResult(res.result);
+ },
+ fail: err => {
+ modal.msgError("鎵爜澶辫触");
+ },
+ });
+ };
+
+ const handleScanResult = async result => {
+ try {
+ // 瑙f瀽浜岀淮鐮佹暟鎹�
+ const scanData = JSON.parse(result);
+ if (!scanData.id) {
+ modal.msgError("鏃犳晥鐨勪簩缁寸爜鏁版嵁");
+ return;
+ }
+
+ // 鐩存帴浠庝簩缁寸爜淇℃伅涓幏鍙栦骇鍝佽鎯�
+ form.value.productId = scanData.productId; // 濡傛灉浜岀淮鐮佷腑鏈� productId
+ form.value.productName = scanData.productName;
+ form.value.productModelId = scanData.id; // 浜岀淮鐮佷腑鐨� id 鏄骇鍝佸瀷鍙� ID
+ form.value.productModelName = scanData.model;
+ form.value.unit = scanData.unit;
+ form.value.qualitity = 1;
+ form.value.warnNum = 0;
+ form.value.remark = "";
+
+ showForm.value = true;
+ } catch (error) {
+ console.error("瑙f瀽浜岀淮鐮佸け璐�", error);
+ modal.msgError("瑙f瀽浜岀淮鐮佸け璐ワ紝璇风‘淇濇壂鐮佸唴瀹规纭�");
+ }
+ };
+
+ const handleSubmit = async () => {
+ try {
+ const valid = await formRef.value.validate();
+ if (!valid) return;
+
+ loading.value = true;
+ const apiCall =
+ type.value === "qualified"
+ ? createStockInventory
+ : createStockUnInventory;
+
+ const res = await apiCall(form.value);
+ if (res.code === 200) {
+ modal.msgSuccess("鍏ュ簱鎴愬姛");
+ setTimeout(() => {
+ showForm.value = false;
+ }, 1500);
+ }
+ } catch (error) {
+ console.error("鎻愪氦澶辫触", error);
+ } finally {
+ loading.value = false;
+ }
+ };
+</script>
+
+<style scoped lang="scss">
+ .scan-container {
+ min-height: 100vh;
+ background-color: #f5f7fa;
+ }
+
+ .module-selector {
+ display: flex;
+ flex-direction: column;
+ padding: 40rpx;
+ height: 80vh;
+ justify-content: center;
+ }
+
+ .module-card {
+ display: flex;
+ align-items: center;
+ background-color: #fff;
+ padding: 80rpx 50rpx;
+ border-radius: 32rpx;
+ box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
+ margin-bottom: 50rpx;
+ transition: all 0.3s ease;
+ border: 2rpx solid transparent;
+
+ &:active {
+ transform: scale(0.98);
+ background-color: #f9f9f9;
+ }
+ }
+
+ .module-icon {
+ width: 140rpx;
+ height: 140rpx;
+ border-radius: 32rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 40rpx;
+
+ &.qualified {
+ background: linear-gradient(135deg, #52c41a, #73d13d);
+ box-shadow: 0 10rpx 20rpx rgba(82, 196, 26, 0.2);
+ }
+
+ &.unqualified {
+ background: linear-gradient(135deg, #ff4d4f, #ff7875);
+ box-shadow: 0 10rpx 20rpx rgba(255, 77, 79, 0.2);
+ }
+ }
+
+ .module-info {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .module-label {
+ font-size: 40rpx;
+ font-weight: 700;
+ color: #1a1a1a;
+ margin-bottom: 12rpx;
+ }
+
+ .module-desc {
+ font-size: 28rpx;
+ color: #999;
+ }
+
+ .form-content {
+ background-color: #fff;
+ margin: 20rpx;
+ padding: 30rpx;
+ border-radius: 16rpx;
+ }
+
+ .footer-btns {
+ margin-top: 60rpx;
+ display: flex;
+ justify-content: space-between;
+ padding-bottom: 40rpx;
+ }
+
+ .cancel-btn {
+ width: 30%;
+ background-color: #f5f5f5;
+ color: #666;
+ border: none;
+ }
+
+ .save-btn {
+ width: 65%;
+ background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
+ color: #fff;
+ border: none;
+ }
+</style>
diff --git a/src/pages/inventoryManagement/scanOut/index.vue b/src/pages/inventoryManagement/scanOut/index.vue
new file mode 100644
index 0000000..fadc34a
--- /dev/null
+++ b/src/pages/inventoryManagement/scanOut/index.vue
@@ -0,0 +1,339 @@
+<template>
+ <view class="scan-container">
+ <PageHeader title="鎵爜鍑哄簱"
+ @back="goBack" />
+ <view class="module-selector"
+ v-if="!showForm">
+ <view class="module-card"
+ @click="startScan('qualified')">
+ <view class="module-icon qualified">
+ <u-icon name="checkbox-mark"
+ color="#fff"
+ size="40"></u-icon>
+ </view>
+ <view class="module-info">
+ <text class="module-label">鍚堟牸鍑哄簱</text>
+ <text class="module-desc">鎵弿鍚堟牸鍝佽繘琛岄鐢ㄥ嚭搴�</text>
+ </view>
+ </view>
+ <view class="module-card"
+ @click="startScan('unqualified')">
+ <view class="module-icon unqualified">
+ <u-icon name="close"
+ color="#fff"
+ size="40"></u-icon>
+ </view>
+ <view class="module-info">
+ <text class="module-label">涓嶅悎鏍煎嚭搴�</text>
+ <text class="module-desc">璁板綍涓嶅悎鏍煎搧鐨勫嚭搴撴祦鍚�</text>
+ </view>
+ </view>
+ </view>
+ <view class="form-content"
+ v-if="showForm">
+ <u-form ref="formRef"
+ :model="form"
+ :rules="formRules"
+ label-width="100px">
+ <u-form-item label="鍑哄簱绫诲瀷"
+ border-bottom>
+ <u-tag :text="type === 'qualified' ? '鍚堟牸鍑哄簱' : '涓嶅悎鏍煎嚭搴�'"
+ :type="type === 'qualified' ? 'success' : 'error'"></u-tag>
+ </u-form-item>
+ <u-form-item label="浜у搧鍚嶇О"
+ border-bottom>
+ <u-input v-model="form.productName"
+ readonly
+ border="none"></u-input>
+ </u-form-item>
+ <u-form-item label="瑙勬牸鍨嬪彿"
+ border-bottom>
+ <u-input v-model="form.model"
+ readonly
+ border="none"></u-input>
+ </u-form-item>
+ <u-form-item label="鍙敤搴撳瓨"
+ border-bottom>
+ <u-input v-model="form.unLockedQuantity"
+ readonly
+ border="none"></u-input>{{form.unit}}
+ </u-form-item>
+ <u-form-item label="鍑哄簱鏁伴噺"
+ prop="qualitity"
+ required
+ border-bottom>
+ <u-number-box v-model="form.qualitity"
+ :min="1"
+ :max="form.unLockedQuantity"
+ :step="1"></u-number-box>
+ <text class="limit-tip">鏈�澶у彲棰嗙敤: {{form.unLockedQuantity}}</text>
+ </u-form-item>
+ <u-form-item label="澶囨敞"
+ prop="remark"
+ border-bottom>
+ <u-textarea v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉�"
+ count></u-textarea>
+ </u-form-item>
+ </u-form>
+ <view class="footer-btns">
+ <u-button class="cancel-btn"
+ @click="cancelForm">鍙栨秷</u-button>
+ <u-button class="save-btn"
+ @click="handleSubmit"
+ :loading="loading">纭鍑哄簱</u-button>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive } from "vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import {
+ subtractStockInventory,
+ getStockInventoryListPage,
+ } from "@/api/inventoryManagement/stockInventory.js";
+ import {
+ subtractStockUnInventory,
+ getStockUninventoryListPage,
+ } from "@/api/inventoryManagement/stockUninventory.js";
+ import modal from "@/plugins/modal";
+
+ const showForm = ref(false);
+ const type = ref("qualified"); // qualified | unqualified
+ const loading = ref(false);
+ const formRef = ref(null);
+
+ const form = ref({
+ id: undefined,
+ productId: undefined,
+ productModelId: undefined,
+ productName: "",
+ model: "",
+ unit: "",
+ qualitity: 1,
+ unLockedQuantity: 0,
+ remark: "",
+ });
+
+ const formRules = {
+ qualitity: [
+ {
+ required: true,
+ type: "number",
+ message: "璇疯緭鍏ュ嚭搴撴暟閲�",
+ trigger: ["blur", "change"],
+ },
+ {
+ validator: (rule, value, callback) => {
+ if (value > form.value.unLockedQuantity) {
+ callback(new Error("涓嶈兘瓒呰繃鍙敤搴撳瓨"));
+ } else {
+ callback();
+ }
+ },
+ trigger: ["blur", "change"],
+ },
+ ],
+ };
+
+ const goBack = () => {
+ if (showForm.value) {
+ showForm.value = false;
+ } else {
+ uni.navigateBack();
+ }
+ };
+
+ const cancelForm = () => {
+ showForm.value = false;
+ };
+
+ const startScan = scanType => {
+ type.value = scanType;
+ uni.scanCode({
+ success: res => {
+ handleScanResult(res.result);
+ },
+ fail: err => {
+ modal.msgError("鎵爜澶辫触");
+ },
+ });
+ };
+
+ const handleScanResult = async result => {
+ try {
+ // 瑙f瀽浜岀淮鐮佹暟鎹�
+ const scanData = JSON.parse(result);
+ if (!scanData.id) {
+ modal.msgError("鏃犳晥鐨勪簩缁寸爜鏁版嵁");
+ return;
+ }
+
+ // 鑾峰彇瀹炴椂搴撳瓨璇︽儏
+ modal.loading("鑾峰彇浜у搧搴撳瓨璇︽儏...");
+ const apiCall =
+ type.value === "qualified"
+ ? getStockInventoryListPage
+ : getStockUninventoryListPage;
+
+ const res = await apiCall({ productModelId: scanData.id });
+ modal.closeLoading();
+
+ if (res.code === 200 && res.data.records && res.data.records.length > 0) {
+ const detail = res.data.records[0];
+ form.value.id = detail.id;
+ form.value.productId = detail.productId;
+ form.value.productName = detail.productName;
+ form.value.productModelId = detail.productModelId;
+ form.value.model = detail.model;
+ form.value.unit = detail.unit;
+ form.value.unLockedQuantity = detail.unLockedQuantity;
+ form.value.qualitity = 1;
+ form.value.remark = "";
+
+ if (form.value.unLockedQuantity <= 0) {
+ modal.msgError("褰撳墠搴撳瓨涓嶈冻锛屾棤娉曞嚭搴�");
+ return;
+ }
+
+ showForm.value = true;
+ } else {
+ modal.msgError("鏈壘鍒拌浜у搧鍨嬪彿鐨勫簱瀛樿褰�");
+ }
+ } catch (error) {
+ modal.closeLoading();
+ console.error("澶勭悊鎵爜缁撴灉澶辫触", error);
+ modal.msgError("鎵爜澶勭悊澶辫触锛岃閲嶈瘯");
+ }
+ };
+
+ const handleSubmit = async () => {
+ try {
+ const valid = await formRef.value.validate();
+ if (!valid) return;
+
+ loading.value = true;
+ const apiCall =
+ type.value === "qualified"
+ ? subtractStockInventory
+ : subtractStockUnInventory;
+
+ const res = await apiCall(form.value);
+ if (res.code === 200) {
+ modal.msgSuccess("鍑哄簱鎴愬姛");
+ setTimeout(() => {
+ showForm.value = false;
+ }, 1500);
+ }
+ } catch (error) {
+ console.error("鎻愪氦澶辫触", error);
+ } finally {
+ loading.value = false;
+ }
+ };
+</script>
+
+<style scoped lang="scss">
+ .scan-container {
+ min-height: 100vh;
+ background-color: #f5f7fa;
+ }
+
+ .module-selector {
+ display: flex;
+ flex-direction: column;
+ padding: 40rpx;
+ height: 80vh;
+ justify-content: center;
+ }
+
+ .module-card {
+ display: flex;
+ align-items: center;
+ background-color: #fff;
+ padding: 80rpx 50rpx;
+ border-radius: 32rpx;
+ box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
+ margin-bottom: 50rpx;
+ transition: all 0.3s ease;
+ border: 2rpx solid transparent;
+
+ &:active {
+ transform: scale(0.98);
+ background-color: #f9f9f9;
+ }
+ }
+
+ .module-icon {
+ width: 140rpx;
+ height: 140rpx;
+ border-radius: 32rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 40rpx;
+
+ &.qualified {
+ background: linear-gradient(135deg, #52c41a, #73d13d);
+ box-shadow: 0 10rpx 20rpx rgba(82, 196, 26, 0.2);
+ }
+
+ &.unqualified {
+ background: linear-gradient(135deg, #ff4d4f, #ff7875);
+ box-shadow: 0 10rpx 20rpx rgba(255, 77, 79, 0.2);
+ }
+ }
+
+ .module-info {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .module-label {
+ font-size: 40rpx;
+ font-weight: 700;
+ color: #1a1a1a;
+ margin-bottom: 12rpx;
+ }
+
+ .module-desc {
+ font-size: 28rpx;
+ color: #999;
+ }
+
+ .form-content {
+ background-color: #fff;
+ margin: 20rpx;
+ padding: 30rpx;
+ border-radius: 16rpx;
+ }
+
+ .limit-tip {
+ font-size: 24rpx;
+ color: #999;
+ margin-left: 20rpx;
+ }
+
+ .footer-btns {
+ margin-top: 60rpx;
+ display: flex;
+ justify-content: space-between;
+ padding-bottom: 40rpx;
+ }
+
+ .cancel-btn {
+ width: 30%;
+ background-color: #f5f5f5;
+ color: #666;
+ border: none;
+ }
+
+ .save-btn {
+ width: 65%;
+ background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
+ color: #fff;
+ border: none;
+ }
+</style>
diff --git a/src/pages/works.vue b/src/pages/works.vue
index 7948ada..9ddae95 100644
--- a/src/pages/works.vue
+++ b/src/pages/works.vue
@@ -15,7 +15,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -37,7 +38,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -59,7 +61,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -81,9 +84,24 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
+ </up-grid-item>
+ <up-grid-item @click="jumpUrl('/pages/inventoryManagement/scanIn/index')">
+ <view class="icon-container">
+ <image src="/static/images/icon/xiaoshoutaizhang.svg"
+ class="item-icon"></image>
+ </view>
+ <text class="item-label">鎵爜鍏ュ簱</text>
+ </up-grid-item>
+ <up-grid-item @click="jumpUrl('/pages/inventoryManagement/scanOut/index')">
+ <view class="icon-container">
+ <image src="/static/images/icon/xiaoshoutaizhang.svg"
+ class="item-icon"></image>
+ </view>
+ <text class="item-label">鎵爜鍑哄簱</text>
</up-grid-item>
</up-grid>
</view>
@@ -149,7 +167,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -171,7 +190,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -193,7 +213,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -215,7 +236,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -237,7 +259,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -259,7 +282,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -532,8 +556,8 @@
// 璁惧绠$悊鍔熻兘鏁版嵁
const equipmentItems = reactive([
{
- icon: '/static/images/icon/shengchanbaogong.svg',
- label: '璁惧鍙拌处',
+ icon: "/static/images/icon/shengchanbaogong.svg",
+ label: "璁惧鍙拌处",
},
{
icon: "/static/images/icon/yunxingguanli.svg",
@@ -912,6 +936,11 @@
});
}
};
+ const jumpUrl = url => {
+ uni.navigateTo({
+ url: url,
+ });
+ };
// 鍒涘缓瀵瑰瓙缁勪欢鐨勫紩鐢�
const uToastRef = ref(null);
@@ -1092,10 +1121,16 @@
// 瀹氫箟鑿滃崟閰嶇疆鏄犲皠
const menuMapping = {
- collaboration: { target: collaborationItems, specialMapping: { "瑙勭珷鍒跺害": "瑙勭珷鍒跺害绠$悊" } },
- archiveManagement: { target: archiveManagementItems, specialMapping: { "渚涘簲鍟嗘。妗�": "渚涘簲鍟嗙鐞�" } },
+ collaboration: {
+ target: collaborationItems,
+ specialMapping: { 瑙勭珷鍒跺害: "瑙勭珷鍒跺害绠$悊" },
+ },
+ archiveManagement: {
+ target: archiveManagementItems,
+ specialMapping: { 渚涘簲鍟嗘。妗�: "渚涘簲鍟嗙鐞�" },
+ },
};
- console.log(allowedMenuTitles)
+ console.log(allowedMenuTitles);
// 閫氱敤杩囨护鍑芥暟
const filterArray = (targetArray, specialMapping) => {
const filtered = targetArray.filter(item => {
@@ -1112,7 +1147,10 @@
filterArray(marketingItems);
filterArray(purchaseItems);
filterArray(financeManagementItems);
- filterArray(archiveManagementItems, menuMapping.archiveManagement.specialMapping);
+ filterArray(
+ archiveManagementItems,
+ menuMapping.archiveManagement.specialMapping
+ );
filterArray(collaborationItems, menuMapping.collaboration.specialMapping);
filterArray(safetyItems);
filterArray(humanResourcesItems);
@@ -1125,14 +1163,22 @@
// 妫�鏌ユā鍧楁槸鍚︽湁鑿滃崟椤归渶瑕佹樉绀�
const hasMarketingItems = computed(() => marketingItems.length > 0);
const hasPurchaseItems = computed(() => purchaseItems.length > 0);
- const hasFinanceManagementItems = computed(() => financeManagementItems.length > 0);
- const hasArchiveManagementItems = computed(() => archiveManagementItems.length > 0);
- const hasAfterSalesServiceItems = computed(() => afterSalesServiceItems.length > 0);
+ const hasFinanceManagementItems = computed(
+ () => financeManagementItems.length > 0
+ );
+ const hasArchiveManagementItems = computed(
+ () => archiveManagementItems.length > 0
+ );
+ const hasAfterSalesServiceItems = computed(
+ () => afterSalesServiceItems.length > 0
+ );
const hasCollaborationItems = computed(() => collaborationItems.length > 0);
const hasSafetyItems = computed(() => safetyItems.length > 0);
const hasQualityItems = computed(() => qualityItems.length > 0);
// const hasHumanResourcesItems = computed(() => humanResourcesItems.length > 0);
- const hasWarehouseLogisticsItems = computed(() => warehouseLogisticsItems.length > 0);
+ const hasWarehouseLogisticsItems = computed(
+ () => warehouseLogisticsItems.length > 0
+ );
// const hasProductionItems = computed(() => productionItems.length > 0);
const hasEquipmentItems = computed(() => equipmentItems.length > 0);
--
Gitblit v1.9.3