From 3ea1ff641e1c680a5a1727fb4034797bfe65d93e Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期三, 18 三月 2026 15:29:17 +0800
Subject: [PATCH] fix: 质量、耗材物流
---
src/pages.json | 21
src/pages/inventoryManagement/stockManagement/add.vue | 16
src/pages/qualityManagement/InspectItem/index.vue | 22
src/pages/inventoryManagement/receiptManagement/view.vue | 13
src/pages/consumablesLogistics/dispatchLog/index.vue | 59 -
src/pages/consumablesLogistics/receiptManagement/Record.vue | 9
src/pages/qualityManagement/rawMaterial/form.vue | 418 +++++++++++
src/pages/qualityManagement/rawMaterial/index.vue | 50 +
src/pages/index.vue | 60 -
src/pages/inventoryManagement/receiptManagement/index.vue | 70 -
src/pages/consumablesLogistics/receiptManagement/index.vue | 59 -
src/pages/inventoryManagement/dispatchLog/view.vue | 13
src/pages/qualityManagement/nonconformingManagement/index.vue | 457 +++++++----
src/pages/inventoryManagement/stockManagement/index.vue | 70 -
src/pages/consumablesLogistics/dispatchLog/view.vue | 9
src/pages/consumablesLogistics/stockManagement/subtract.vue | 22
src/pages/inventoryManagement/stockManagement/subtract.vue | 10
src/pages/consumablesLogistics/stockManagement/view.vue | 7
/dev/null | 91 --
src/pages/consumablesLogistics/stockManagement/add.vue | 14
src/pages/qualityManagement/nonconformingManagement/form.vue | 458 ++++++++++++
src/pages/consumablesLogistics/receiptManagement/view.vue | 9
src/pages/consumablesLogistics/stockManagement/index.vue | 45 -
src/pages/consumablesLogistics/dispatchLog/Record.vue | 9
src/pages/inventoryManagement/dispatchLog/index.vue | 59 -
src/pages/qualityManagement/rawMaterial/files.vue | 159 ++++
26 files changed, 1,528 insertions(+), 701 deletions(-)
diff --git a/src/pages.json b/src/pages.json
index 4d5fa2b..5c83101 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -1142,6 +1142,13 @@
}
},
{
+ "path": "pages/qualityManagement/nonconformingManagement/form",
+ "style": {
+ "navigationBarTitleText": "涓嶅悎鏍煎搧绠$悊",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/qualityManagement/rawMaterial/index",
"style": {
"navigationBarTitleText": "鍘熸潗鏂�",
@@ -1149,6 +1156,20 @@
}
},
{
+ "path": "pages/qualityManagement/rawMaterial/form",
+ "style": {
+ "navigationBarTitleText": "鍘熸枡妫�",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/qualityManagement/rawMaterial/files",
+ "style": {
+ "navigationBarTitleText": "闄勪欢绠$悊",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/qualityManagement/visualization/qualityDashboard",
"style": {
"navigationBarTitleText": "璐ㄩ噺鐪嬫澘",
diff --git a/src/pages/consumablesLogistics/dispatchLog/Record.vue b/src/pages/consumablesLogistics/dispatchLog/Record.vue
index a4afc5e..868f7ed 100644
--- a/src/pages/consumablesLogistics/dispatchLog/Record.vue
+++ b/src/pages/consumablesLogistics/dispatchLog/Record.vue
@@ -86,7 +86,6 @@
} from "@/api/consumablesLogistics/consumablesOutRecord.js";
import {
findAllQualifiedStockOutRecordTypeOptions,
- findAllUnQualifiedStockOutRecordTypeOptions,
} from "@/api/basicData/enum.js";
const props = defineProps({
@@ -122,11 +121,7 @@
};
const fetchStockRecordTypeOptions = () => {
- const api =
- props.type === "1"
- ? findAllUnQualifiedStockOutRecordTypeOptions
- : findAllQualifiedStockOutRecordTypeOptions;
- api()
+ findAllQualifiedStockOutRecordTypeOptions()
.then((res) => {
stockRecordTypeOptions.value = res.data || [];
})
@@ -148,7 +143,7 @@
const getList = () => {
tableLoading.value = true;
- getConsumablesOutRecordPage({ ...searchForm.value, ...page, type: props.type })
+ getConsumablesOutRecordPage({ ...searchForm.value, ...page, type: "0" })
.then(res => {
tableData.value = res?.data?.records || [];
total.value = res?.data?.total || 0;
diff --git a/src/pages/consumablesLogistics/dispatchLog/index.vue b/src/pages/consumablesLogistics/dispatchLog/index.vue
index 132494f..11d524e 100644
--- a/src/pages/consumablesLogistics/dispatchLog/index.vue
+++ b/src/pages/consumablesLogistics/dispatchLog/index.vue
@@ -1,17 +1,6 @@
<template>
<view class="dispatch-page">
<PageHeader title="鍑哄簱鍙拌处" @back="goBack" />
- <view class="tabs-wrap">
- <view
- v-for="tab in tabs"
- :key="tab.name"
- class="tab-item"
- :class="{ active: activeTab === tab.name }"
- @click="activeTab = tab.name"
- >
- <text>{{ tab.label }}</text>
- </view>
- </view>
<view class="search-section">
<view class="search-row">
<view class="search-input-wrap">
@@ -63,26 +52,20 @@
</template>
<script setup>
-import { reactive, ref, toRefs, watch } from "vue";
+import { reactive, ref, toRefs } from "vue";
import { onReachBottom, onShow } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
import { getConsumablesOutRecordPage, delConsumablesOutRecord } from "@/api/consumablesLogistics/consumablesOutRecord.js";
-import { findAllQualifiedStockOutRecordTypeOptions, findAllUnQualifiedStockOutRecordTypeOptions } from "@/api/basicData/enum.js";
+import { findAllQualifiedStockOutRecordTypeOptions } from "@/api/basicData/enum.js";
-const activeTab = ref("qualified");
const stockRecordTypeOptions = ref([]);
-const tabs = [
- { label: "鍚堟牸鍑哄簱", name: "qualified", type: "0" },
- { label: "涓嶅悎鏍煎嚭搴�", name: "unqualified", type: "1" },
-];
const tableData = ref([]);
const total = ref(0);
const loadStatus = ref("loadmore");
const page = reactive({ current: 1, size: 20 });
const data = reactive({ searchForm: { productName: "" } });
const { searchForm } = toRefs(data);
-
-const currentType = () => tabs.find((t) => t.name === activeTab.value)?.type || "0";
+const currentType = () => "0";
function getRecordType(recordType) {
if (recordType == null || recordType === "") return "";
@@ -90,11 +73,7 @@
}
function fetchRecordTypeOptions() {
- const api =
- currentType() === "1"
- ? findAllUnQualifiedStockOutRecordTypeOptions
- : findAllQualifiedStockOutRecordTypeOptions;
- api()
+ findAllQualifiedStockOutRecordTypeOptions()
.then((res) => {
const list = res.data != null ? res.data : res;
stockRecordTypeOptions.value = Array.isArray(list) ? list : [];
@@ -143,13 +122,6 @@
getList();
};
-watch(activeTab, () => {
- page.current = 1;
- loadStatus.value = "loadmore";
- stockRecordTypeOptions.value = [];
- getList();
-});
-
const handleQuery = () => {
page.current = 1;
loadStatus.value = "loadmore";
@@ -163,7 +135,7 @@
"dispatchDetailItem",
JSON.stringify({
item,
- type: currentType(),
+ type: "0",
})
);
} catch (e) {}
@@ -198,9 +170,6 @@
<style lang="scss" scoped>
.dispatch-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 40rpx; }
-.tabs-wrap { display: flex; background: #fff; padding: 24rpx; gap: 24rpx; }
-.tab-item { flex: 1; text-align: center; padding: 20rpx; border-radius: 12rpx; background: #f0f0f0; font-size: 28rpx; color: #666; }
-.tab-item.active { background: #2979ff; color: #fff; }
.search-section { background: #fff; margin: 24rpx; padding: 24rpx; border-radius: 16rpx; }
.search-row { display: flex; align-items: center; }
.search-input-wrap { flex: 1; margin-right: 20rpx; min-width: 0; }
@@ -216,8 +185,22 @@
.card-body .l { color: #666; }
.card-body .r { color: #333; }
.card-body .r.highlight { color: #2979ff; font-weight: 500; }
-.card-actions { display: flex; justify-content: flex-end; margin-top: 12rpx; }
-.btn-delete { color: #f56c6c; font-size: 28rpx; }
+.card-actions {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 16rpx;
+ padding-top: 16rpx;
+ border-top: 1rpx solid #eee;
+}
+.btn-delete {
+ color: #f56c6c;
+ font-size: 28rpx;
+ padding: 12rpx 36rpx;
+ border-radius: 999rpx;
+ border: 1rpx solid rgba(245, 108, 108, 0.55);
+ background: rgba(245, 108, 108, 0.08);
+}
.no-data { text-align: center; padding: 60rpx 0; color: #999; font-size: 28rpx; }
.load-more-wrap { padding: 24rpx 24rpx 8rpx; }
</style>
diff --git a/src/pages/consumablesLogistics/dispatchLog/view.vue b/src/pages/consumablesLogistics/dispatchLog/view.vue
index 0008348..546c134 100644
--- a/src/pages/consumablesLogistics/dispatchLog/view.vue
+++ b/src/pages/consumablesLogistics/dispatchLog/view.vue
@@ -92,7 +92,7 @@
import { ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
-import { findAllQualifiedStockOutRecordTypeOptions, findAllUnQualifiedStockOutRecordTypeOptions } from "@/api/basicData/enum.js";
+import { findAllQualifiedStockOutRecordTypeOptions } from "@/api/basicData/enum.js";
const detail = ref(null);
const loading = ref(true);
@@ -126,9 +126,7 @@
}
function fetchRecordTypeOptions(type) {
- const api =
- type === "1" ? findAllUnQualifiedStockOutRecordTypeOptions : findAllQualifiedStockOutRecordTypeOptions;
- api()
+ findAllQualifiedStockOutRecordTypeOptions()
.then((res) => {
const data = res.data != null ? res.data : res;
stockRecordTypeOptions.value = Array.isArray(data) ? data : [];
@@ -144,9 +142,8 @@
try {
const payload = typeof cached === "string" ? JSON.parse(cached) : cached;
const item = payload && payload.item != null ? payload.item : payload;
- const type = payload && payload.type != null ? payload.type : "0";
detail.value = normalizeDetail({ ...item, index: 1 });
- fetchRecordTypeOptions(type);
+ fetchRecordTypeOptions("0");
uni.removeStorageSync("dispatchDetailItem");
} catch (e) {
uni.removeStorageSync("dispatchDetailItem");
diff --git a/src/pages/consumablesLogistics/receiptManagement/Record.vue b/src/pages/consumablesLogistics/receiptManagement/Record.vue
index 9770be2..29cce52 100644
--- a/src/pages/consumablesLogistics/receiptManagement/Record.vue
+++ b/src/pages/consumablesLogistics/receiptManagement/Record.vue
@@ -86,7 +86,6 @@
} from "@/api/consumablesLogistics/consumablesInRecord.js";
import {
findAllQualifiedStockInRecordTypeOptions,
- findAllUnQualifiedStockInRecordTypeOptions,
} from "@/api/basicData/enum.js";
const props = defineProps({
@@ -122,11 +121,7 @@
};
const fetchStockRecordTypeOptions = () => {
- const api =
- props.type === "1"
- ? findAllUnQualifiedStockInRecordTypeOptions
- : findAllQualifiedStockInRecordTypeOptions;
- api()
+ findAllQualifiedStockInRecordTypeOptions()
.then((res) => {
stockRecordTypeOptions.value = res.data || [];
})
@@ -148,7 +143,7 @@
const getList = () => {
tableLoading.value = true;
- getConsumablesInRecordListPage({ ...searchForm.value, ...page, type: props.type })
+ getConsumablesInRecordListPage({ ...searchForm.value, ...page, type: "0" })
.then(res => {
tableData.value = res?.data?.records || [];
total.value = res?.data?.total || 0;
diff --git a/src/pages/consumablesLogistics/receiptManagement/index.vue b/src/pages/consumablesLogistics/receiptManagement/index.vue
index db0f94e..9a515e0 100644
--- a/src/pages/consumablesLogistics/receiptManagement/index.vue
+++ b/src/pages/consumablesLogistics/receiptManagement/index.vue
@@ -1,17 +1,6 @@
<template>
<view class="receipt-page">
<PageHeader title="鍏ュ簱绠$悊" @back="goBack" />
- <view class="tabs-wrap">
- <view
- v-for="tab in tabs"
- :key="tab.name"
- class="tab-item"
- :class="{ active: activeTab === tab.name }"
- @click="activeTab = tab.name"
- >
- <text>{{ tab.label }}</text>
- </view>
- </view>
<view class="search-section">
<view class="search-row">
<view class="search-input-wrap">
@@ -58,26 +47,20 @@
</template>
<script setup>
-import { reactive, ref, toRefs, watch } from "vue";
+import { reactive, ref, toRefs } from "vue";
import { onReachBottom, onShow } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
import request from "@/utils/request";
-import { findAllQualifiedStockInRecordTypeOptions, findAllUnQualifiedStockInRecordTypeOptions } from "@/api/basicData/enum.js";
+import { findAllQualifiedStockInRecordTypeOptions } from "@/api/basicData/enum.js";
-const activeTab = ref("qualified");
const stockRecordTypeOptions = ref([]);
-const tabs = [
- { label: "鍚堟牸鍏ュ簱", name: "qualified", type: "0" },
- { label: "涓嶅悎鏍煎叆搴�", name: "unqualified", type: "1" },
-];
const tableData = ref([]);
const total = ref(0);
const loadStatus = ref("loadmore");
const page = reactive({ current: 1, size: 4 });
const data = reactive({ searchForm: { productName: "" } });
const { searchForm } = toRefs(data);
-
-const currentType = () => tabs.find((t) => t.name === activeTab.value)?.type || "0";
+const currentType = () => "0";
function getRecordType(recordType) {
if (recordType == null || recordType === "") return "";
@@ -85,11 +68,7 @@
}
function fetchRecordTypeOptions() {
- const api =
- currentType() === "1"
- ? findAllUnQualifiedStockInRecordTypeOptions
- : findAllQualifiedStockInRecordTypeOptions;
- api()
+ findAllQualifiedStockInRecordTypeOptions()
.then((res) => {
const data = res.data != null ? res.data : res;
stockRecordTypeOptions.value = Array.isArray(data) ? data : [];
@@ -142,13 +121,6 @@
getList();
};
-watch(activeTab, () => {
- page.current = 1;
- loadStatus.value = "loadmore";
- stockRecordTypeOptions.value = [];
- getList();
-});
-
const handleQuery = () => {
page.current = 1;
loadStatus.value = "loadmore";
@@ -162,7 +134,7 @@
"receiptDetailItem",
JSON.stringify({
item,
- type: currentType(),
+ type: "0",
})
);
} catch (e) {}
@@ -206,9 +178,6 @@
<style lang="scss" scoped>
.receipt-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 40rpx; }
-.tabs-wrap { display: flex; background: #fff; padding: 24rpx; gap: 24rpx; }
-.tab-item { flex: 1; text-align: center; padding: 20rpx; border-radius: 12rpx; background: #f0f0f0; font-size: 28rpx; color: #666; }
-.tab-item.active { background: #2979ff; color: #fff; }
.search-section { background: #fff; margin: 24rpx; padding: 24rpx; border-radius: 16rpx; }
.search-row { display: flex; align-items: center; }
.search-input-wrap { flex: 1; margin-right: 20rpx; min-width: 0; }
@@ -224,8 +193,22 @@
.card-body .l { color: #666; }
.card-body .r { color: #333; }
.card-body .r.highlight { color: #2979ff; font-weight: 500; }
-.card-actions { display: flex; justify-content: flex-end; margin-top: 12rpx; }
-.btn-delete { color: #f56c6c; font-size: 28rpx; }
+.card-actions {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 16rpx;
+ padding-top: 16rpx;
+ border-top: 1rpx solid #eee;
+}
+.btn-delete {
+ color: #f56c6c;
+ font-size: 28rpx;
+ padding: 12rpx 36rpx;
+ border-radius: 999rpx;
+ border: 1rpx solid rgba(245, 108, 108, 0.55);
+ background: rgba(245, 108, 108, 0.08);
+}
.no-data { text-align: center; padding: 60rpx 0; color: #999; font-size: 28rpx; }
.load-more-wrap { padding: 24rpx 24rpx 8rpx; }
</style>
diff --git a/src/pages/consumablesLogistics/receiptManagement/view.vue b/src/pages/consumablesLogistics/receiptManagement/view.vue
index 180eb9a..d603419 100644
--- a/src/pages/consumablesLogistics/receiptManagement/view.vue
+++ b/src/pages/consumablesLogistics/receiptManagement/view.vue
@@ -92,7 +92,7 @@
import { ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
-import { findAllQualifiedStockInRecordTypeOptions, findAllUnQualifiedStockInRecordTypeOptions } from "@/api/basicData/enum.js";
+import { findAllQualifiedStockInRecordTypeOptions } from "@/api/basicData/enum.js";
const detail = ref(null);
const loading = ref(true);
@@ -126,9 +126,7 @@
}
function fetchRecordTypeOptions(type) {
- const api =
- type === "1" ? findAllUnQualifiedStockInRecordTypeOptions : findAllQualifiedStockInRecordTypeOptions;
- api()
+ findAllQualifiedStockInRecordTypeOptions()
.then((res) => {
const data = res.data != null ? res.data : res;
stockRecordTypeOptions.value = Array.isArray(data) ? data : [];
@@ -144,9 +142,8 @@
try {
const payload = typeof cached === "string" ? JSON.parse(cached) : cached;
const item = payload && payload.item != null ? payload.item : payload;
- const type = payload && payload.type != null ? payload.type : "0";
detail.value = normalizeDetail({ ...item, index: 1 });
- fetchRecordTypeOptions(type);
+ fetchRecordTypeOptions("0");
uni.removeStorageSync("receiptDetailItem");
} catch (e) {
uni.removeStorageSync("receiptDetailItem");
diff --git a/src/pages/consumablesLogistics/stockManagement/Unqualified.vue b/src/pages/consumablesLogistics/stockManagement/Unqualified.vue
deleted file mode 100644
index 76b193e..0000000
--- a/src/pages/consumablesLogistics/stockManagement/Unqualified.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<template>
- <div class="app-container">
- <div class="search_form">
- <div>
- <span class="search_title ml10">浜у搧鍚嶇О锛�</span>
- <el-input
- v-model="searchForm.productName"
- style="width: 240px"
- placeholder="璇疯緭鍏�"
- clearable
- @keyup.enter="handleQuery"
- />
- <el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button>
- </div>
- </div>
-
- <div class="table_list">
- <el-table
- :data="tableData"
- border
- v-loading="tableLoading"
- style="width: 100%"
- height="calc(100vh - 18.5em)"
- >
- <el-table-column align="center" label="搴忓彿" type="index" width="60" />
- <el-table-column label="浜у搧鍚嶇О" prop="productName" min-width="180" show-overflow-tooltip />
- <el-table-column label="瑙勬牸鍨嬪彿" prop="model" min-width="160" show-overflow-tooltip />
- <el-table-column label="鍗曚綅" prop="unit" width="100" show-overflow-tooltip />
- <el-table-column label="搴撳瓨鏁伴噺" prop="qualitity" width="110" show-overflow-tooltip />
- <el-table-column label="鍐荤粨鏁伴噺" prop="lockedQuantity" width="110" show-overflow-tooltip />
- <el-table-column label="鏈�杩戞洿鏂版椂闂�" prop="updateTime" width="180" show-overflow-tooltip />
- <el-table-column label="澶囨敞" prop="remark" min-width="140" show-overflow-tooltip />
- </el-table>
-
- <Pagination
- v-show="total > 0"
- :total="total"
- :page="page.current"
- :limit="page.size"
- @pagination="paginationChange"
- />
- </div>
- </div>
-</template>
-
-<script setup>
-import { reactive, ref, toRefs } from "vue";
-import Pagination from "@/components/PIMTable/Pagination.vue";
-import { getConsumablesUninventoryListPage } from "@/api/consumablesLogistics/consumablesUninventory.js";
-
-const tableData = ref([]);
-const tableLoading = ref(false);
-
-const page = reactive({
- current: 1,
- size: 100,
-});
-const total = ref(0);
-
-const data = reactive({
- searchForm: {
- productName: "",
- },
-});
-const { searchForm } = toRefs(data);
-
-const handleQuery = () => {
- page.current = 1;
- getList();
-};
-
-const paginationChange = obj => {
- page.current = obj.page;
- page.size = obj.limit;
- getList();
-};
-
-const getList = () => {
- tableLoading.value = true;
- getConsumablesUninventoryListPage({ ...searchForm.value, ...page })
- .then(res => {
- tableData.value = res?.data?.records || [];
- total.value = res?.data?.total || 0;
- })
- .finally(() => {
- tableLoading.value = false;
- });
-};
-
-getList();
-</script>
diff --git a/src/pages/consumablesLogistics/stockManagement/add.vue b/src/pages/consumablesLogistics/stockManagement/add.vue
index ed14b7d..38f9711 100644
--- a/src/pages/consumablesLogistics/stockManagement/add.vue
+++ b/src/pages/consumablesLogistics/stockManagement/add.vue
@@ -23,7 +23,7 @@
</view>
</view>
- <view v-if="isQualified" class="form-section">
+ <view class="form-section">
<view class="section-title">杩囩淇℃伅</view>
<view class="form-row">
<text class="form-label">杞︾墝鍙�</text>
@@ -114,7 +114,6 @@
import dayjs from "dayjs";
import PageHeader from "@/components/PageHeader.vue";
import { createConsumablesIn } from "@/api/consumablesLogistics/consumablesIn.js";
-import { createConsumablesUnInventory } from "@/api/consumablesLogistics/consumablesUninventory.js";
import { productModelList } from "@/api/basicData/productModel.js";
const form = reactive({
@@ -133,8 +132,8 @@
remark: "",
});
-const type = ref("0");
-const isQualified = computed(() => type.value === "0");
+const type = ref("0"); // 鍥哄畾鍚堟牸搴撳瓨
+const isQualified = computed(() => true);
const showProductPopup = ref(false);
const productQuery = reactive({
@@ -148,9 +147,7 @@
const weighingDateValue = ref(Date.now());
onLoad((options) => {
- if (options && options.type != null) {
- type.value = options.type;
- }
+ type.value = "0";
});
const openProductSelector = () => {
@@ -238,8 +235,7 @@
weighingOperator: form.weighingOperator,
remark: form.remark,
};
- const api = isQualified.value ? createConsumablesIn : createConsumablesUnInventory;
- api(payload)
+ createConsumablesIn(payload)
.then(() => {
uni.showToast({ title: "鏂板鎴愬姛", icon: "success" });
setTimeout(() => {
diff --git a/src/pages/consumablesLogistics/stockManagement/index.vue b/src/pages/consumablesLogistics/stockManagement/index.vue
index 85021f6..a18ef5f 100644
--- a/src/pages/consumablesLogistics/stockManagement/index.vue
+++ b/src/pages/consumablesLogistics/stockManagement/index.vue
@@ -2,18 +2,6 @@
<view class="stock-mgmt-page">
<PageHeader title="搴撳瓨绠$悊" @back="goBack" />
- <view class="tabs-wrap">
- <view
- v-for="tab in tabs"
- :key="tab.name"
- class="tab-item"
- :class="{ active: activeTab === tab.name }"
- @click="activeTab = tab.name"
- >
- <text>{{ tab.label }}</text>
- </view>
- </view>
-
<view class="search-section">
<view class="search-row">
<view class="search-input-wrap">
@@ -102,17 +90,11 @@
</template>
<script setup>
-import { computed, reactive, ref, toRefs, watch } from "vue";
+import { computed, reactive, ref, toRefs } from "vue";
import { onReachBottom, onShow } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
import { frozenConsumablesIn, getConsumablesInListPage, thawConsumablesIn } from "@/api/consumablesLogistics/consumablesIn.js";
-import { frozenConsumablesUninventory, getConsumablesUninventoryListPage, thawConsumablesUninventory } from "@/api/consumablesLogistics/consumablesUninventory.js";
-const activeTab = ref("qualified");
-const tabs = [
- { label: "鍚堟牸搴撳瓨", name: "qualified" },
- { label: "涓嶅悎鏍煎簱瀛�", name: "unqualified" },
-];
const tableData = ref([]);
const total = ref(0);
const loadStatus = ref("loadmore");
@@ -126,15 +108,13 @@
});
const { searchForm, quantityForm } = toRefs(data);
-const isQualified = () => activeTab.value === "qualified";
const getList = () => {
const isFirstPage = page.current === 1;
if (isFirstPage) {
uni.showLoading({ title: "鍔犺浇涓�...", mask: true });
}
const params = { ...page, productName: searchForm.value.productName };
- const api = isQualified() ? getConsumablesInListPage : getConsumablesUninventoryListPage;
- api(params)
+ getConsumablesInListPage(params)
.then((res) => {
uni.hideLoading();
const records = res.data?.records || [];
@@ -163,12 +143,6 @@
getList();
};
-watch(activeTab, () => {
- page.current = 1;
- loadStatus.value = "loadmore";
- getList();
-});
-
const handleQuery = () => {
page.current = 1;
loadStatus.value = "loadmore";
@@ -176,9 +150,8 @@
};
const goAdd = () => {
- const type = isQualified() ? "0" : "1";
uni.navigateTo({
- url: `/pages/consumablesLogistics/stockManagement/add?type=${type}`,
+ url: `/pages/consumablesLogistics/stockManagement/add?type=0`,
});
};
@@ -202,13 +175,12 @@
"stockSubtractRecord",
JSON.stringify({
item: row,
- type: isQualified() ? "0" : "1",
+ type: "0",
})
);
} catch (e) {}
- const typeParam = isQualified() ? "0" : "1";
uni.navigateTo({
- url: `/pages/consumablesLogistics/stockManagement/subtract?type=${typeParam}&id=${row.id}`,
+ url: `/pages/consumablesLogistics/stockManagement/subtract?type=0&id=${row.id}`,
});
};
@@ -243,9 +215,9 @@
const base = { id, lockedQuantity: num };
let promise;
if (quantityOp.value === "frozen") {
- promise = isQualified() ? frozenConsumablesIn(base) : frozenConsumablesUninventory(base);
+ promise = frozenConsumablesIn(base);
} else {
- promise = isQualified() ? thawConsumablesIn(base) : thawConsumablesUninventory(base);
+ promise = thawConsumablesIn(base);
}
promise
.then(() => {
@@ -283,9 +255,6 @@
<style lang="scss" scoped>
.stock-mgmt-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 120rpx; }
-.tabs-wrap { display: flex; background: #fff; padding: 24rpx; gap: 24rpx; }
-.tab-item { flex: 1; text-align: center; padding: 20rpx; border-radius: 12rpx; background: #f0f0f0; font-size: 28rpx; color: #666; }
-.tab-item.active { background: #2979ff; color: #fff; }
.search-section { background: #fff; margin: 24rpx; padding: 24rpx; border-radius: 16rpx; }
.search-row { display: flex; align-items: center; }
.search-input-wrap { flex: 1; margin-right: 20rpx; min-width: 0; }
diff --git a/src/pages/consumablesLogistics/stockManagement/subtract.vue b/src/pages/consumablesLogistics/stockManagement/subtract.vue
index 81fea14..f8bd2f3 100644
--- a/src/pages/consumablesLogistics/stockManagement/subtract.vue
+++ b/src/pages/consumablesLogistics/stockManagement/subtract.vue
@@ -25,23 +25,23 @@
<text class="form-label required">鍑哄簱鏁伴噺</text>
<up-input v-model="form.stockOutNum" type="number" :placeholder="'鏈�澶�' + stockRecord.unLockedQuantity" />
</view>
- <view class="form-row" v-if="isQualified">
+ <view class="form-row">
<text class="form-label">杞︾墝鍙�</text>
<up-input v-model="form.licensePlateNo" placeholder="璇疯緭鍏ヨ溅鐗屽彿" />
</view>
- <view class="form-row" v-if="isQualified">
+ <view class="form-row">
<text class="form-label">姣涢噸(鍚�)</text>
<up-input v-model="form.grossWeight" type="number" placeholder="璇疯緭鍏ユ瘺閲�" />
</view>
- <view class="form-row" v-if="isQualified">
+ <view class="form-row">
<text class="form-label">鐨噸(鍚�)</text>
<up-input v-model="form.tareWeight" type="number" placeholder="璇疯緭鍏ョ毊閲�" />
</view>
- <view class="form-row" v-if="isQualified">
+ <view class="form-row">
<text class="form-label">鍑�閲�(鍚�)</text>
<up-input v-model="form.netWeight" type="number" disabled placeholder="鑷姩璁$畻" />
</view>
- <view class="form-row" v-if="isQualified">
+ <view class="form-row">
<text class="form-label">杩囩鏃ユ湡</text>
<view class="selector-trigger" @click="openWeighingDatePicker">
<text class="selector-text" :class="{ placeholder: !form.weighingDate }">
@@ -50,7 +50,7 @@
<up-icon name="calendar" size="16" color="#999"></up-icon>
</view>
</view>
- <view class="form-row" v-if="isQualified">
+ <view class="form-row">
<text class="form-label">杩囩鍛�</text>
<up-input v-model="form.weighingOperator" placeholder="璇疯緭鍏ヨ繃纾呭憳" />
</view>
@@ -83,10 +83,9 @@
import dayjs from "dayjs";
import PageHeader from "@/components/PageHeader.vue";
import { subtractConsumablesIn } from "@/api/consumablesLogistics/consumablesIn.js";
-import { subtractConsumablesUnInventory } from "@/api/consumablesLogistics/consumablesUninventory.js";
const type = ref("0");
-const isQualified = computed(() => type.value === "0");
+const isQualified = computed(() => true);
const stockRecord = reactive({
id: "",
@@ -110,9 +109,7 @@
const weighingDateValue = ref(Date.now());
onLoad((options) => {
- if (options && options.type != null) {
- type.value = options.type;
- }
+ type.value = "0";
const cached = uni.getStorageSync("stockSubtractRecord");
if (cached) {
try {
@@ -176,8 +173,7 @@
return;
}
}
- const api = isQualified.value ? subtractConsumablesIn : subtractConsumablesUnInventory;
- api({
+ subtractConsumablesIn({
id: stockRecord.id,
stockOutNum: outNum,
licensePlateNo: form.licensePlateNo,
diff --git a/src/pages/consumablesLogistics/stockManagement/view.vue b/src/pages/consumablesLogistics/stockManagement/view.vue
index fd68338..d809a93 100644
--- a/src/pages/consumablesLogistics/stockManagement/view.vue
+++ b/src/pages/consumablesLogistics/stockManagement/view.vue
@@ -64,7 +64,7 @@
const detail = ref(null);
const loading = ref(true);
-function normalizeDetail(raw, type) {
+function normalizeDetail(raw) {
if (!raw) return null;
const d = typeof raw === "object" ? raw : {};
return {
@@ -76,7 +76,7 @@
lockedQuantity: d.lockedQuantity,
unLockedQuantity: d.unLockedQuantity ?? (d.qualitity - (d.lockedQuantity || 0)),
updateTime: d.updateTime,
- typeLabel: type === "1" ? "涓嶅悎鏍煎簱瀛�" : "鍚堟牸搴撳瓨",
+ typeLabel: "鍚堟牸搴撳瓨",
};
}
@@ -86,8 +86,7 @@
try {
const payload = typeof cached === "string" ? JSON.parse(cached) : cached;
const item = payload && payload.item != null ? payload.item : payload;
- const type = payload && payload.type != null ? payload.type : "0";
- detail.value = normalizeDetail({ ...item, index: 1 }, type);
+ detail.value = normalizeDetail({ ...item, index: 1 });
uni.removeStorageSync("stockDetailItem");
} catch (e) {
uni.removeStorageSync("stockDetailItem");
diff --git a/src/pages/index.vue b/src/pages/index.vue
index 5e09c1d..c27a0b7 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -426,18 +426,18 @@
// 璐ㄩ噺绠$悊
const qualityItems = reactive([
- {
- icon: "/static/images/icon/caigoutaizhang@2x.png",
- label: "鍘熸潗鏂欐楠�",
- },
- {
- icon: "/static/images/icon/caigoutaizhang@2x.png",
- label: "杩囩▼妫�楠�",
- },
- {
- icon: "/static/images/icon/caigoutaizhang@2x.png",
- label: "鍑哄巶妫�楠�",
- },
+ // {
+ // icon: "/static/images/icon/caigoutaizhang@2x.png",
+ // label: "鍘熸潗鏂欐楠�",
+ // },
+ // {
+ // icon: "/static/images/icon/caigoutaizhang@2x.png",
+ // label: "杩囩▼妫�楠�",
+ // },
+ // {
+ // icon: "/static/images/icon/caigoutaizhang@2x.png",
+ // label: "鍑哄巶妫�楠�",
+ // },
]);
// 鍗忓悓鍔炲叕鍔熻兘鏁版嵁
const collaborationItems = reactive([
@@ -949,7 +949,7 @@
url: "/pages/qualityManagement/nonconformingManagement/index",
});
break;
- case "鍘熸潗鏂�":
+ case "鍘熸枡妫�":
uni.navigateTo({
url: "/pages/qualityManagement/rawMaterial/index",
});
@@ -1206,41 +1206,13 @@
});
purchaseItems.splice(0, purchaseItems.length, ...filteredPurchase);
- // 杩囨护璐ㄩ噺绠$悊鑿滃崟
+ // 璐ㄩ噺绠$悊鑿滃崟锛氬浐瀹氬彧灞曠ず 3 涓叆鍙�
const originalQuality = [
- { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "鍘熸潗鏂欐楠�" },
- { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "杩囩▼妫�楠�" },
- { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "鍑哄巶妫�楠�" },
{ icon: "/static/images/icon/caigoutaizhang@2x.png", label: "妫�娴嬮」缁存姢" },
- { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "鎸囨爣缁存姢" },
- { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "鎸囨爣缁戝畾" },
+ { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "鍘熸枡妫�" },
{ icon: "/static/images/icon/caigoutaizhang@2x.png", label: "涓嶅悎鏍煎搧绠$悊" },
- { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "鍘熸潗鏂�" },
- { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "杩戞晥鏈熼��璐�" },
- { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "璐ㄩ噺鐪嬫澘" },
];
- const hasAllowedTitleLike = label => {
- if (allowedMenuTitles.has(label)) return true;
- return Array.from(allowedMenuTitles).some(
- title =>
- typeof title === "string" &&
- (title.includes(label) || label.includes(title))
- );
- };
- const filteredQuality = originalQuality.filter(item => {
- return hasAllowedTitleLike(item.label);
- });
- const hasQualityModulePermission = Array.from(allowedMenuTitles).some(
- title =>
- typeof title === "string" &&
- (title.includes("璐ㄩ噺") || title.includes("妫�楠�"))
- );
- const finalQualityItems = filteredQuality.length
- ? filteredQuality
- : hasQualityModulePermission
- ? originalQuality
- : [];
- qualityItems.splice(0, qualityItems.length, ...finalQualityItems);
+ qualityItems.splice(0, qualityItems.length, ...originalQuality);
// 杩囨护瀹夊叏鐢熶骇鑿滃崟
const originalSafety = [
diff --git a/src/pages/inventoryManagement/dispatchLog/index.vue b/src/pages/inventoryManagement/dispatchLog/index.vue
index 1ab16fe..702ccf5 100644
--- a/src/pages/inventoryManagement/dispatchLog/index.vue
+++ b/src/pages/inventoryManagement/dispatchLog/index.vue
@@ -2,19 +2,6 @@
<view class="dispatch-page">
<PageHeader title="鍑哄簱鍙拌处" @back="goBack" />
- <!-- 鏍囩锛氬悎鏍煎嚭搴� / 涓嶅悎鏍煎嚭搴� -->
- <view class="tabs-wrap">
- <view
- v-for="tab in tabs"
- :key="tab.name"
- class="tab-item"
- :class="{ active: activeTab === tab.name }"
- @click="activeTab = tab.name"
- >
- <text>{{ tab.label }}</text>
- </view>
- </view>
-
<!-- 鎼滅储鍖哄煙 -->
<view class="search-section">
<view class="search-row">
@@ -78,21 +65,16 @@
</template>
<script setup>
-import { ref, reactive, toRefs, watch } from 'vue'
+import { ref, reactive, toRefs } from 'vue'
import { onShow, onReachBottom } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import { getStockOutPage, delStockOut } from '@/api/inventoryManagement/stockOutRecord.js'
import {
- findAllQualifiedStockOutRecordTypeOptions,
- findAllUnQualifiedStockOutRecordTypeOptions
+ findAllQualifiedStockOutRecordTypeOptions
} from '@/api/basicData/enum.js'
-const activeTab = ref('qualified')
const stockRecordTypeOptions = ref([])
-const tabs = [
- { label: '鍚堟牸鍑哄簱', name: 'qualified', type: '0' },
- { label: '涓嶅悎鏍煎嚭搴�', name: 'unqualified', type: '1' }
-]
+const currentType = () => '0'
const tableData = ref([])
const total = ref(0)
const loadStatus = ref('loadmore')
@@ -104,18 +86,13 @@
})
const { searchForm } = toRefs(data)
-const currentType = () => tabs.find(t => t.name === activeTab.value)?.type || '0'
-
function getRecordType(recordType) {
if (recordType == null || recordType === '') return ''
return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
}
function fetchRecordTypeOptions() {
- const api = currentType() === '1'
- ? findAllUnQualifiedStockOutRecordTypeOptions
- : findAllQualifiedStockOutRecordTypeOptions
- api()
+ findAllQualifiedStockOutRecordTypeOptions()
.then(res => {
const list = res.data != null ? res.data : res
stockRecordTypeOptions.value = Array.isArray(list) ? list : []
@@ -168,13 +145,6 @@
getList()
}
-watch(activeTab, () => {
- page.current = 1
- loadStatus.value = 'loadmore'
- stockRecordTypeOptions.value = []
- getList()
-})
-
const handleQuery = () => {
page.current = 1
loadStatus.value = 'loadmore'
@@ -186,7 +156,7 @@
try {
uni.setStorageSync('dispatchDetailItem', JSON.stringify({
item,
- type: currentType()
+ type: '0'
}))
} catch (e) {}
uni.navigateTo({
@@ -224,25 +194,6 @@
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 40rpx;
-}
-.tabs-wrap {
- display: flex;
- background: #fff;
- padding: 24rpx;
- gap: 24rpx;
-}
-.tab-item {
- flex: 1;
- text-align: center;
- padding: 20rpx;
- border-radius: 12rpx;
- background: #f0f0f0;
- font-size: 28rpx;
- color: #666;
-}
-.tab-item.active {
- background: #2979ff;
- color: #fff;
}
.search-section {
background: #fff;
diff --git a/src/pages/inventoryManagement/dispatchLog/view.vue b/src/pages/inventoryManagement/dispatchLog/view.vue
index ea41965..e764082 100644
--- a/src/pages/inventoryManagement/dispatchLog/view.vue
+++ b/src/pages/inventoryManagement/dispatchLog/view.vue
@@ -95,8 +95,7 @@
import { onLoad } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import {
- findAllQualifiedStockOutRecordTypeOptions,
- findAllUnQualifiedStockOutRecordTypeOptions
+ findAllQualifiedStockOutRecordTypeOptions
} from '@/api/basicData/enum.js'
const detail = ref(null)
@@ -130,11 +129,8 @@
return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
}
-function fetchRecordTypeOptions(type) {
- const api = type === '1'
- ? findAllUnQualifiedStockOutRecordTypeOptions
- : findAllQualifiedStockOutRecordTypeOptions
- api()
+function fetchRecordTypeOptions() {
+ findAllQualifiedStockOutRecordTypeOptions()
.then(res => {
const data = res.data != null ? res.data : res
stockRecordTypeOptions.value = Array.isArray(data) ? data : []
@@ -150,9 +146,8 @@
try {
const payload = typeof cached === 'string' ? JSON.parse(cached) : cached
const item = payload && payload.item != null ? payload.item : payload
- const type = payload && payload.type != null ? payload.type : '0'
detail.value = normalizeDetail({ ...item, index: 1 })
- fetchRecordTypeOptions(type)
+ fetchRecordTypeOptions()
uni.removeStorageSync('dispatchDetailItem')
} catch (e) {
uni.removeStorageSync('dispatchDetailItem')
diff --git a/src/pages/inventoryManagement/receiptManagement/index.vue b/src/pages/inventoryManagement/receiptManagement/index.vue
index 60a3ccc..e3625b6 100644
--- a/src/pages/inventoryManagement/receiptManagement/index.vue
+++ b/src/pages/inventoryManagement/receiptManagement/index.vue
@@ -2,19 +2,6 @@
<view class="receipt-page">
<PageHeader title="鍏ュ簱绠$悊" @back="goBack" />
- <!-- 鏍囩锛氬悎鏍煎叆搴� / 涓嶅悎鏍煎叆搴� -->
- <view class="tabs-wrap">
- <view
- v-for="tab in tabs"
- :key="tab.name"
- class="tab-item"
- :class="{ active: activeTab === tab.name }"
- @click="activeTab = tab.name"
- >
- <text>{{ tab.label }}</text>
- </view>
- </view>
-
<!-- 鎼滅储鍖哄煙 -->
<view class="search-section">
<view class="search-row">
@@ -34,7 +21,7 @@
</view>
</view>
- <!-- 鍒楄〃锛堝悎鏍�/涓嶅悎鏍煎叡鐢ㄦ帴鍙� type 鍖哄垎锛� -->
+ <!-- 鍒楄〃 -->
<view class="list-section" v-if="activeTab !== 'custom'">
<view v-if="tableData.length > 0">
<view
@@ -73,7 +60,7 @@
</template>
<script setup>
-import { ref, reactive, toRefs, watch } from 'vue'
+import { ref, reactive, toRefs } from 'vue'
import { onShow, onReachBottom } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import {
@@ -81,16 +68,11 @@
batchDeleteStockInRecords
} from '@/api/inventoryManagement/stockInRecord.js'
import {
- findAllQualifiedStockInRecordTypeOptions,
- findAllUnQualifiedStockInRecordTypeOptions
+ findAllQualifiedStockInRecordTypeOptions
} from '@/api/basicData/enum.js'
-const activeTab = ref('qualified')
const stockRecordTypeOptions = ref([])
-const tabs = [
- { label: '鍚堟牸鍏ュ簱', name: 'qualified', type: '0' },
- { label: '涓嶅悎鏍煎叆搴�', name: 'unqualified', type: '1' }
-]
+const currentType = () => '0'
const tableData = ref([])
const total = ref(0)
@@ -104,18 +86,13 @@
})
const { searchForm } = toRefs(data)
-const currentType = () => tabs.find(t => t.name === activeTab.value)?.type || '0'
-
function getRecordType(recordType) {
if (recordType == null || recordType === '') return ''
return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
}
function fetchRecordTypeOptions() {
- const api = currentType() === '1'
- ? findAllUnQualifiedStockInRecordTypeOptions
- : findAllQualifiedStockInRecordTypeOptions
- api()
+ findAllQualifiedStockInRecordTypeOptions()
.then(res => {
const data = res.data != null ? res.data : res
stockRecordTypeOptions.value = Array.isArray(data) ? data : []
@@ -126,7 +103,6 @@
}
const getList = () => {
- if (activeTab.value === 'custom') return
const isFirstPage = page.current === 1
if (isFirstPage) {
uni.showLoading({ title: '鍔犺浇涓�...', mask: true })
@@ -170,13 +146,6 @@
getList()
}
-watch(activeTab, () => {
- page.current = 1
- loadStatus.value = 'loadmore'
- stockRecordTypeOptions.value = []
- getList()
-})
-
const handleQuery = () => {
page.current = 1
loadStatus.value = 'loadmore'
@@ -188,7 +157,7 @@
try {
uni.setStorageSync('receiptDetailItem', JSON.stringify({
item,
- type: currentType()
+ type: '0'
}))
} catch (e) {}
uni.navigateTo({
@@ -218,7 +187,7 @@
const goBack = () => uni.navigateBack()
onShow(() => {
- if (activeTab.value !== 'custom') getList()
+ getList()
})
onReachBottom(() => {
@@ -231,25 +200,6 @@
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 40rpx;
-}
-.tabs-wrap {
- display: flex;
- background: #fff;
- padding: 24rpx;
- gap: 24rpx;
-}
-.tab-item {
- flex: 1;
- text-align: center;
- padding: 20rpx;
- border-radius: 12rpx;
- background: #f0f0f0;
- font-size: 28rpx;
- color: #666;
-}
-.tab-item.active {
- background: #2979ff;
- color: #fff;
}
.search-section {
background: #fff;
@@ -340,11 +290,17 @@
display: flex;
justify-content: center;
align-items: center;
+ width: 100%;
+ text-align: center;
}
.btn-delete {
font-size: 28rpx;
color: #f56c6c;
padding: 12rpx 32rpx;
+ margin: 0 auto;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
}
.no-data {
text-align: center;
diff --git a/src/pages/inventoryManagement/receiptManagement/view.vue b/src/pages/inventoryManagement/receiptManagement/view.vue
index e5daea9..2833283 100644
--- a/src/pages/inventoryManagement/receiptManagement/view.vue
+++ b/src/pages/inventoryManagement/receiptManagement/view.vue
@@ -95,8 +95,7 @@
import { onLoad } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import {
- findAllQualifiedStockInRecordTypeOptions,
- findAllUnQualifiedStockInRecordTypeOptions
+ findAllQualifiedStockInRecordTypeOptions
} from '@/api/basicData/enum.js'
const detail = ref(null)
@@ -131,11 +130,8 @@
return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
}
-function fetchRecordTypeOptions(type) {
- const api = type === '1'
- ? findAllUnQualifiedStockInRecordTypeOptions
- : findAllQualifiedStockInRecordTypeOptions
- api()
+function fetchRecordTypeOptions() {
+ findAllQualifiedStockInRecordTypeOptions()
.then(res => {
const data = res.data != null ? res.data : res
stockRecordTypeOptions.value = Array.isArray(data) ? data : []
@@ -151,9 +147,8 @@
try {
const payload = typeof cached === 'string' ? JSON.parse(cached) : cached
const item = payload && payload.item != null ? payload.item : payload
- const type = payload && payload.type != null ? payload.type : '0'
detail.value = normalizeDetail({ ...item, index: 1 })
- fetchRecordTypeOptions(type)
+ fetchRecordTypeOptions()
uni.removeStorageSync('receiptDetailItem')
} catch (e) {
uni.removeStorageSync('receiptDetailItem')
diff --git a/src/pages/inventoryManagement/stockManagement/add.vue b/src/pages/inventoryManagement/stockManagement/add.vue
index f8c698f..c27d958 100644
--- a/src/pages/inventoryManagement/stockManagement/add.vue
+++ b/src/pages/inventoryManagement/stockManagement/add.vue
@@ -23,8 +23,8 @@
</view>
</view>
- <!-- 鍚堟牸搴撳瓨鏃舵樉绀鸿繃纾呯浉鍏冲瓧娈� -->
- <view v-if="isQualified" class="form-section">
+ <!-- 杩囩鐩稿叧瀛楁 -->
+ <view class="form-section">
<view class="section-title">杩囩淇℃伅</view>
<view class="form-row">
<text class="form-label">杞︾墝鍙�</text>
@@ -145,7 +145,6 @@
import dayjs from 'dayjs'
import PageHeader from '@/components/PageHeader.vue'
import { createStockInventory } from '@/api/inventoryManagement/stockInventory.js'
-import { createStockUnInventory } from '@/api/inventoryManagement/stockUninventory.js'
import { productModelList } from '@/api/basicData/productModel.js'
const form = reactive({
@@ -165,8 +164,8 @@
remark: ''
})
-const type = ref('0') // 0 鍚堟牸搴撳瓨锛�1 涓嶅悎鏍煎簱瀛�
-const isQualified = computed(() => type.value === '0')
+const type = ref('0') // 鍥哄畾鍚堟牸搴撳瓨
+const isQualified = computed(() => true)
const showProductPopup = ref(false)
const productQuery = reactive({
@@ -180,9 +179,7 @@
const weighingDateValue = ref(Date.now())
onLoad((options) => {
- if (options && options.type != null) {
- type.value = options.type
- }
+ type.value = '0'
})
const openProductSelector = () => {
@@ -286,8 +283,7 @@
weighingOperator: form.weighingOperator,
remark: form.remark
}
- const api = isQualified.value ? createStockInventory : createStockUnInventory
- api(payload)
+ createStockInventory(payload)
.then(() => {
uni.showToast({ title: '鏂板鎴愬姛', icon: 'success' })
setTimeout(() => {
diff --git a/src/pages/inventoryManagement/stockManagement/index.vue b/src/pages/inventoryManagement/stockManagement/index.vue
index e36fac0..3b707a7 100644
--- a/src/pages/inventoryManagement/stockManagement/index.vue
+++ b/src/pages/inventoryManagement/stockManagement/index.vue
@@ -2,19 +2,6 @@
<view class="stock-mgmt-page">
<PageHeader title="搴撳瓨绠$悊" @back="goBack" />
- <!-- 鏍囩锛氬悎鏍煎簱瀛� / 涓嶅悎鏍煎簱瀛� -->
- <view class="tabs-wrap">
- <view
- v-for="tab in tabs"
- :key="tab.name"
- class="tab-item"
- :class="{ active: activeTab === tab.name }"
- @click="activeTab = tab.name"
- >
- <text>{{ tab.label }}</text>
- </view>
- </view>
-
<!-- 鎼滅储鍖哄煙 -->
<view class="search-section">
<view class="search-row">
@@ -107,7 +94,7 @@
</template>
<script setup>
-import { ref, reactive, toRefs, watch, computed } from 'vue'
+import { ref, reactive, toRefs, computed } from 'vue'
import { onShow, onReachBottom } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import {
@@ -117,19 +104,7 @@
frozenStockInventory,
thawStockInventory
} from '@/api/inventoryManagement/stockInventory.js'
-import {
- getStockUninventoryListPage,
- createStockUnInventory,
- subtractStockUnInventory,
- frozenStockUninventory,
- thawStockUninventory
-} from '@/api/inventoryManagement/stockUninventory.js'
-const activeTab = ref('qualified')
-const tabs = [
- { label: '鍚堟牸搴撳瓨', name: 'qualified' },
- { label: '涓嶅悎鏍煎簱瀛�', name: 'unqualified' }
-]
const tableData = ref([])
const total = ref(0)
const loadStatus = ref('loadmore') // loadmore | loading | nomore | error
@@ -143,15 +118,13 @@
})
const { searchForm, quantityForm } = toRefs(data)
-const isQualified = () => activeTab.value === 'qualified'
const getList = () => {
const isFirstPage = page.current === 1
if (isFirstPage) {
uni.showLoading({ title: '鍔犺浇涓�...', mask: true })
}
const params = { ...page, productName: searchForm.value.productName }
- const api = isQualified() ? getStockInventoryListPage : getStockUninventoryListPage
- api(params)
+ getStockInventoryListPage(params)
.then(res => {
uni.hideLoading()
const records = res.data?.records || []
@@ -184,12 +157,6 @@
getList()
}
-watch(activeTab, () => {
- page.current = 1
- loadStatus.value = 'loadmore'
- getList()
-})
-
const handleQuery = () => {
page.current = 1
loadStatus.value = 'loadmore'
@@ -197,9 +164,8 @@
}
const goAdd = () => {
- const type = isQualified() ? '0' : '1'
uni.navigateTo({
- url: `/pages/inventoryManagement/stockManagement/add?type=${type}`
+ url: `/pages/inventoryManagement/stockManagement/add?type=0`
})
}
@@ -220,12 +186,11 @@
try {
uni.setStorageSync('stockSubtractRecord', JSON.stringify({
item: row,
- type: isQualified() ? '0' : '1'
+ type: '0'
}))
} catch (e) {}
- const typeParam = isQualified() ? '0' : '1'
uni.navigateTo({
- url: `/pages/inventoryManagement/stockManagement/subtract?type=${typeParam}&id=${row.id}`
+ url: `/pages/inventoryManagement/stockManagement/subtract?type=0&id=${row.id}`
})
}
const openFrozen = (row) => {
@@ -256,9 +221,9 @@
const base = { id, lockedQuantity: num }
let promise
if (quantityOp.value === 'frozen') {
- promise = isQualified() ? frozenStockInventory(base) : frozenStockUninventory(base)
+ promise = frozenStockInventory(base)
} else {
- promise = isQualified() ? thawStockInventory(base) : thawStockUninventory(base)
+ promise = thawStockInventory(base)
}
promise.then(() => {
uni.showToast({ title: '鎿嶄綔鎴愬姛', icon: 'success' })
@@ -272,7 +237,7 @@
try {
uni.setStorageSync('stockDetailItem', JSON.stringify({
item,
- type: isQualified() ? '0' : '1'
+ type: '0'
}))
} catch (e) {}
if (!item.id) {
@@ -294,25 +259,6 @@
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 120rpx;
-}
-.tabs-wrap {
- display: flex;
- background: #fff;
- padding: 24rpx;
- gap: 24rpx;
-}
-.tab-item {
- flex: 1;
- text-align: center;
- padding: 20rpx;
- border-radius: 12rpx;
- background: #f0f0f0;
- font-size: 28rpx;
- color: #666;
-}
-.tab-item.active {
- background: #2979ff;
- color: #fff;
}
.search-section {
background: #fff;
diff --git a/src/pages/inventoryManagement/stockManagement/subtract.vue b/src/pages/inventoryManagement/stockManagement/subtract.vue
index 1a7ebf7..2452e04 100644
--- a/src/pages/inventoryManagement/stockManagement/subtract.vue
+++ b/src/pages/inventoryManagement/stockManagement/subtract.vue
@@ -92,7 +92,6 @@
import dayjs from 'dayjs'
import PageHeader from '@/components/PageHeader.vue'
import { subtractStockInventory } from '@/api/inventoryManagement/stockInventory.js'
-import { subtractStockUnInventory } from '@/api/inventoryManagement/stockUninventory.js'
const form = reactive({
id: undefined,
@@ -124,7 +123,7 @@
remark: ''
})
-const type = ref('0') // 0 鍚堟牸搴撳瓨锛�1 涓嶅悎鏍煎簱瀛�
+const type = ref('0') // 鍥哄畾鍚堟牸搴撳瓨
const showWeighingDatePicker = ref(false)
const weighingDateValue = ref(Date.now())
@@ -135,9 +134,7 @@
})
onLoad((options) => {
- if (options && options.type != null) {
- type.value = options.type
- }
+ type.value = '0'
const cached = uni.getStorageSync('stockSubtractRecord')
if (cached) {
try {
@@ -195,8 +192,7 @@
return
}
const payload = { ...form }
- const api = type.value === '0' ? subtractStockInventory : subtractStockUnInventory
- api(payload)
+ subtractStockInventory(payload)
.then(() => {
uni.showToast({ title: '鍑哄簱鎴愬姛', icon: 'success' })
setTimeout(() => {
diff --git a/src/pages/qualityManagement/InspectItem/index.vue b/src/pages/qualityManagement/InspectItem/index.vue
index 92ae99c..822abb6 100644
--- a/src/pages/qualityManagement/InspectItem/index.vue
+++ b/src/pages/qualityManagement/InspectItem/index.vue
@@ -64,7 +64,7 @@
<view class="dialog-header">
<text class="dialog-title">{{ operationType === 'add' ? '鏂板妫�娴嬮」鐩�' : '淇敼妫�娴嬮」鐩�' }}</text>
</view>
- <up-form :model="form" ref="formRef" label-width="100" label-position="top">
+ <up-form :model="form" :rules="rules" ref="formRef" label-width="100" label-position="top">
<up-form-item label="妫�娴嬮」鐩�" prop="name" required borderBottom>
<up-input v-model="form.name" placeholder="璇疯緭鍏ユ娴嬮」鐩悕绉�" border="surround" />
</up-form-item>
@@ -204,25 +204,27 @@
dialogVisible.value = false;
};
-const submitForm = () => {
- formRef.value.validate().then(res => {
- submitLoading.value = true;
- qualityInspectItemSave(form).then(() => {
+const submitForm = async () => {
+ if (!formRef.value) return
+ const valid = await formRef.value.validate().catch(() => false)
+ if (!valid) return
+ submitLoading.value = true;
+ qualityInspectItemSave(form)
+ .then(() => {
toast(operationType.value === 'add' ? '鏂板鎴愬姛' : '淇敼鎴愬姛');
dialogVisible.value = false;
handleQuery();
- }).finally(() => {
+ })
+ .finally(() => {
submitLoading.value = false;
});
- }).catch(errors => {
- console.log('楠岃瘉澶辫触', errors);
- });
};
const handleDelete = (row) => {
showConfirm('纭鍒犻櫎璇ユ娴嬮」鐩悧锛�').then(res => {
if (res.confirm) {
- qualityInspectItemDelete({ id: row.id }).then(() => {
+ // 瀵归綈 PC 绔細鍒犻櫎鎺ュ彛鎺ユ敹 id 鏁扮粍
+ qualityInspectItemDelete([row.id]).then(() => {
toast('鍒犻櫎鎴愬姛');
handleQuery();
});
diff --git a/src/pages/qualityManagement/nonconformingManagement/form.vue b/src/pages/qualityManagement/nonconformingManagement/form.vue
new file mode 100644
index 0000000..7acb67d
--- /dev/null
+++ b/src/pages/qualityManagement/nonconformingManagement/form.vue
@@ -0,0 +1,458 @@
+<template>
+ <view class="nonconforming-form-page">
+ <PageHeader :title="pageTitle" @back="goBack" />
+
+ <scroll-view scroll-y class="content-scroll">
+ <view class="form-section">
+ <view class="form-row">
+ <text class="form-label required">浜у搧鍚嶇О</text>
+ <view class="selector-trigger" @click="openProductSelector" :class="{ disabled: isEdit }">
+ <text class="selector-text" :class="{ placeholder: !form.productName }">
+ {{ form.productName || '璇烽�夋嫨' }}
+ </text>
+ <up-icon name="arrow-right" size="16" color="#999"></up-icon>
+ </view>
+ </view>
+ <view class="form-row">
+ <text class="form-label required">瑙勬牸鍨嬪彿</text>
+ <view class="selector-trigger" @click="openModelSelector" :class="{ disabled: !form.productId || isEdit }">
+ <text class="selector-text" :class="{ placeholder: !form.model }">
+ {{ form.model || '璇烽�夋嫨' }}
+ </text>
+ <up-icon name="arrow-right" size="16" color="#999"></up-icon>
+ </view>
+ </view>
+ <view class="form-row">
+ <text class="form-label">鍗曚綅</text>
+ <up-input v-model="form.unit" disabled placeholder="鑷姩甯﹀嚭" />
+ </view>
+ <view class="form-row">
+ <text class="form-label required">鎵瑰彿</text>
+ <up-input v-model="form.batchNo" placeholder="璇疯緭鍏�" />
+ </view>
+ <view class="form-row">
+ <text class="form-label required">妫�楠岀被鍨�</text>
+ <view class="selector-trigger" @click="showTypeSelect = true">
+ <text class="selector-text" :class="{ placeholder: form.checkType === undefined || form.checkType === '' }">
+ {{ checkTypeLabel || '璇烽�夋嫨' }}
+ </text>
+ <up-icon name="arrow-down" size="14" color="#999"></up-icon>
+ </view>
+ </view>
+ <view class="form-row">
+ <text class="form-label required">妫�楠屽憳</text>
+ <view class="selector-trigger" @click="showUserSelect = true">
+ <text class="selector-text" :class="{ placeholder: !form.checkName }">
+ {{ form.checkName || '璇烽�夋嫨' }}
+ </text>
+ <up-icon name="arrow-down" size="14" color="#999"></up-icon>
+ </view>
+ </view>
+ <view class="form-row">
+ <text class="form-label required">妫�娴嬫棩鏈�</text>
+ <view class="selector-trigger" @click="openCheckTimePicker">
+ <text class="selector-text" :class="{ placeholder: !form.checkTime }">
+ {{ form.checkTime || '璇烽�夋嫨' }}
+ </text>
+ <up-icon name="calendar" size="16" color="#999"></up-icon>
+ </view>
+ </view>
+ <view class="form-row">
+ <text class="form-label required">涓嶅悎鏍肩幇璞�</text>
+ <up-textarea v-model="form.defectivePhenomena" placeholder="璇疯緭鍏�" count border="surround" />
+ </view>
+ </view>
+
+ <view class="form-section">
+ <view class="section-title">榛樿澶勭悊淇℃伅</view>
+ <view class="form-row">
+ <text class="form-label required">澶勭悊缁撴灉</text>
+ <up-input :modelValue="dealResultLabel || '鎶ュ簾'" disabled />
+ </view>
+ <view class="form-row">
+ <text class="form-label">澶勭悊浜�</text>
+ <up-input v-model="form.dealName" placeholder="閫夊~" />
+ </view>
+ <view class="form-row">
+ <text class="form-label">澶勭悊鏃ユ湡</text>
+ <view class="selector-trigger" @click="openDealTimePicker">
+ <text class="selector-text" :class="{ placeholder: !form.dealTime }">
+ {{ form.dealTime || '璇烽�夋嫨' }}
+ </text>
+ <up-icon name="calendar" size="16" color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+
+ <view class="bottom-bar">
+ <view class="btn-submit" @click="handleSubmit" :class="{ disabled: submitting }">
+ {{ submitting ? '鎻愪氦涓�...' : '鎻愪氦' }}
+ </view>
+ </view>
+
+ <!-- 浜у搧閫夋嫨 -->
+ <up-popup :show="showProductPopup" mode="bottom" @close="showProductPopup = false">
+ <view class="popup">
+ <view class="popup-header">
+ <text class="popup-title">閫夋嫨浜у搧</text>
+ </view>
+ <scroll-view scroll-y class="popup-list">
+ <view
+ v-for="(item, idx) in productOptions"
+ :key="item.value || idx"
+ class="popup-item"
+ @click="selectProduct(item)"
+ >
+ <text class="popup-item-title">{{ item.label }}</text>
+ </view>
+ <view v-if="!productLoading && productOptions.length === 0" class="no-data">鏆傛棤鏁版嵁</view>
+ </scroll-view>
+ </view>
+ </up-popup>
+
+ <!-- 鍨嬪彿閫夋嫨 -->
+ <up-popup :show="showModelPopup" mode="bottom" @close="showModelPopup = false">
+ <view class="popup">
+ <view class="popup-header">
+ <text class="popup-title">閫夋嫨瑙勬牸鍨嬪彿</text>
+ </view>
+ <scroll-view scroll-y class="popup-list">
+ <view
+ v-for="(item, idx) in modelOptions"
+ :key="item.id || idx"
+ class="popup-item"
+ @click="selectModel(item)"
+ >
+ <view class="popup-item-row">
+ <text class="popup-item-title">{{ item.model }}</text>
+ <text class="popup-item-sub">{{ item.unit }}</text>
+ </view>
+ </view>
+ <view v-if="!modelLoading && modelOptions.length === 0" class="no-data">鏆傛棤鏁版嵁</view>
+ </scroll-view>
+ </view>
+ </up-popup>
+
+ <!-- 妫�楠岀被鍨� -->
+ <up-action-sheet
+ :actions="checkTypeActions"
+ :show="showTypeSelect"
+ @close="showTypeSelect = false"
+ @select="selectCheckType"
+ title="璇烽�夋嫨妫�楠岀被鍨�"
+ />
+
+ <!-- 妫�楠屽憳 -->
+ <up-action-sheet
+ :actions="userActions"
+ :show="showUserSelect"
+ @close="showUserSelect = false"
+ @select="selectUser"
+ title="璇烽�夋嫨妫�楠屽憳"
+ />
+
+ <!-- 鏃ユ湡閫夋嫨鍣細妫�娴嬫棩鏈� -->
+ <up-datetime-picker
+ :show="showCheckTimePicker"
+ v-model="checkTimeValue"
+ mode="date"
+ @confirm="confirmCheckTime"
+ @cancel="showCheckTimePicker = false"
+ />
+ <!-- 鏃ユ湡閫夋嫨鍣細澶勭悊鏃ユ湡 -->
+ <up-datetime-picker
+ :show="showDealTimePicker"
+ v-model="dealTimeValue"
+ mode="date"
+ @confirm="confirmDealTime"
+ @cancel="showDealTimePicker = false"
+ />
+ </view>
+</template>
+
+<script setup>
+import { computed, reactive, ref } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import dayjs from 'dayjs'
+import PageHeader from '@/components/PageHeader.vue'
+import { useDict } from '@/utils/dict'
+import { toast } from '@/utils/common'
+import { productTreeList, modelList } from '@/api/basicData/product.js'
+import { userListNoPage } from '@/api/system/user.js'
+import {
+ getQualityUnqualifiedInfo,
+ qualityUnqualifiedAdd,
+ qualityUnqualifiedUpdate
+} from '@/api/qualityManagement/nonconformingManagement.js'
+
+const pageType = ref('add') // add | edit
+const id = ref('')
+const submitting = ref(false)
+
+const isEdit = computed(() => pageType.value === 'edit')
+const pageTitle = computed(() => (isEdit.value ? '缂栬緫涓嶅悎鏍肩鐞�' : '鏂板涓嶅悎鏍肩鐞�'))
+
+const { rejection_handling } = useDict('rejection_handling')
+const dealResultLabel = computed(() => {
+ const list = rejection_handling?.value || []
+ const v = form.dealResult
+ return (list || []).find(it => String(it.value) === String(v))?.label || ''
+})
+const getScrapDealResultValue = () => {
+ const list = rejection_handling?.value || []
+ const scrap = (list || []).find(it => String(it?.label ?? '') === '鎶ュ簾')
+ return scrap?.value ?? ''
+}
+
+const form = reactive({
+ id: undefined,
+ productId: '',
+ productName: '',
+ productModelId: '',
+ model: '',
+ unit: '',
+ batchNo: '',
+ checkType: undefined,
+ checkName: '',
+ checkTime: '',
+ defectivePhenomena: '',
+ dealResult: '',
+ dealName: '',
+ dealTime: ''
+})
+
+// 閫夋嫨鍣ㄦ暟鎹�
+const showProductPopup = ref(false)
+const productLoading = ref(false)
+const productOptions = ref([])
+
+const showModelPopup = ref(false)
+const modelLoading = ref(false)
+const modelOptions = ref([])
+
+const showTypeSelect = ref(false)
+const checkTypeActions = [
+ { name: '鍏ュ巶妫�', value: 0 },
+ { name: '杞﹂棿妫�', value: 1 },
+ { name: '鍑哄巶妫�', value: 2 }
+]
+const checkTypeLabel = computed(() => {
+ const v = form.checkType
+ return checkTypeActions.find(it => String(it.value) === String(v))?.name || ''
+})
+
+const showUserSelect = ref(false)
+const userActions = ref([])
+
+const showCheckTimePicker = ref(false)
+const checkTimeValue = ref(Date.now())
+const showDealTimePicker = ref(false)
+const dealTimeValue = ref(Date.now())
+
+const loadProducts = async () => {
+ productLoading.value = true
+ try {
+ const res = await productTreeList()
+ const list =
+ (Array.isArray(res) ? res : null) ||
+ (Array.isArray(res?.data) ? res.data : null) ||
+ (Array.isArray(res?.records) ? res.records : null) ||
+ (Array.isArray(res?.data?.records) ? res.data.records : null) ||
+ []
+ // 浠呭彇鍙跺瓙鑺傜偣锛堝彲閫変骇鍝侊級锛涘苟杞负 action 鍒楄〃
+ const flat = []
+ const walk = (nodes) => {
+ ;(nodes || []).forEach(n => {
+ const children = Array.isArray(n?.children) ? n.children : []
+ if (children.length > 0) walk(children)
+ else {
+ const value = n?.value ?? n?.id
+ const label = n?.label || n?.productName || n?.name || ''
+ flat.push({ label, value })
+ }
+ })
+ }
+ walk(list)
+ productOptions.value = flat.filter(it => it.value)
+ } finally {
+ productLoading.value = false
+ }
+}
+
+const openProductSelector = async () => {
+ if (isEdit.value) return
+ showProductPopup.value = true
+ if (productOptions.value.length === 0) {
+ await loadProducts()
+ }
+}
+const selectProduct = (item) => {
+ if (isEdit.value) return
+ form.productId = item.value
+ form.productName = item.label
+ form.productModelId = ''
+ form.model = ''
+ form.unit = ''
+ modelOptions.value = []
+ showProductPopup.value = false
+}
+
+const loadModels = async () => {
+ if (!form.productId) return
+ modelLoading.value = true
+ try {
+ const res = await modelList({ id: form.productId })
+ modelOptions.value = Array.isArray(res) ? res : (res?.data || [])
+ } finally {
+ modelLoading.value = false
+ }
+}
+const openModelSelector = async () => {
+ if (isEdit.value) return
+ if (!form.productId) {
+ toast('璇峰厛閫夋嫨浜у搧鍚嶇О')
+ return
+ }
+ showModelPopup.value = true
+ if (modelOptions.value.length === 0) {
+ await loadModels()
+ }
+}
+const selectModel = (item) => {
+ if (isEdit.value) return
+ form.productModelId = item?.id
+ form.model = item?.model || ''
+ form.unit = item?.unit || ''
+ showModelPopup.value = false
+}
+
+const selectCheckType = (e) => {
+ form.checkType = e.value
+ showTypeSelect.value = false
+}
+
+const loadUsers = async () => {
+ const res = await userListNoPage()
+ const list = res?.data || []
+ userActions.value = (list || []).map(u => ({ name: u.nickName, value: u.nickName })).filter(it => it.value)
+}
+const selectUser = (e) => {
+ form.checkName = e.value
+ showUserSelect.value = false
+}
+
+const openCheckTimePicker = () => {
+ checkTimeValue.value = form.checkTime ? dayjs(form.checkTime, 'YYYY-MM-DD').valueOf() : Date.now()
+ showCheckTimePicker.value = true
+}
+const confirmCheckTime = (e) => {
+ const ts = e?.value ?? checkTimeValue.value
+ form.checkTime = dayjs(ts).format('YYYY-MM-DD')
+ showCheckTimePicker.value = false
+}
+
+const openDealTimePicker = () => {
+ dealTimeValue.value = form.dealTime ? dayjs(form.dealTime, 'YYYY-MM-DD').valueOf() : Date.now()
+ showDealTimePicker.value = true
+}
+const confirmDealTime = (e) => {
+ const ts = e?.value ?? dealTimeValue.value
+ form.dealTime = dayjs(ts).format('YYYY-MM-DD')
+ showDealTimePicker.value = false
+}
+
+const loadDetail = async () => {
+ if (!id.value) return
+ const res = await getQualityUnqualifiedInfo(id.value)
+ const d = res?.data || {}
+ Object.assign(form, {
+ id: d.id,
+ productId: d.productId,
+ productName: d.productName,
+ productModelId: d.productModelId,
+ model: d.model,
+ unit: d.unit,
+ batchNo: d.batchNo,
+ // 鍏煎鍚庣杩斿洖瀛楁锛氫紭鍏� checkType锛屽叾娆� inspectType
+ checkType: d.checkType ?? d.inspectType,
+ checkName: d.checkName,
+ checkTime: d.checkTime,
+ defectivePhenomena: d.defectivePhenomena,
+ dealResult: d.dealResult,
+ dealName: d.dealName,
+ dealTime: d.dealTime
+ })
+}
+
+const validate = () => {
+ if (!form.productId) return toast('璇烽�夋嫨浜у搧鍚嶇О'), false
+ if (!form.productModelId) return toast('璇烽�夋嫨瑙勬牸鍨嬪彿'), false
+ if (!form.batchNo) return toast('璇疯緭鍏ユ壒鍙�'), false
+ if (form.checkType === undefined || form.checkType === '') return toast('璇烽�夋嫨妫�楠岀被鍨�'), false
+ if (!form.checkName) return toast('璇烽�夋嫨妫�楠屽憳'), false
+ if (!form.checkTime) return toast('璇烽�夋嫨妫�娴嬫棩鏈�'), false
+ if (!form.defectivePhenomena) return toast('璇疯緭鍏ヤ笉鍚堟牸鐜拌薄'), false
+ return true
+}
+
+const handleSubmit = async () => {
+ if (submitting.value) return
+ if (!validate()) return
+ submitting.value = true
+ try {
+ // 澶勭悊缁撴灉榛樿鎶ュ簾
+ if (!form.dealResult) form.dealResult = getScrapDealResultValue()
+ const payload = { ...form }
+ delete payload.inspectState
+ const api = isEdit.value ? qualityUnqualifiedUpdate : qualityUnqualifiedAdd
+ await api(payload)
+ toast('鎻愪氦鎴愬姛')
+ setTimeout(() => uni.navigateBack(), 400)
+ } catch (e) {
+ toast('鎻愪氦澶辫触')
+ } finally {
+ submitting.value = false
+ }
+}
+
+const goBack = () => uni.navigateBack()
+
+onLoad(async (options) => {
+ pageType.value = options?.type || 'add'
+ id.value = options?.id || ''
+ form.dealResult = getScrapDealResultValue()
+ await loadUsers()
+ if (isEdit.value) {
+ await loadDetail()
+ }
+})
+</script>
+
+<style lang="scss" scoped>
+.nonconforming-form-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 120rpx; }
+.content-scroll { height: calc(100vh - 100rpx); }
+.form-section { background: #fff; margin: 24rpx; padding: 24rpx; border-radius: 16rpx; }
+.section-title { font-size: 28rpx; font-weight: 500; color: #333; margin-bottom: 12rpx; }
+.form-row { margin-bottom: 24rpx; }
+.form-label { display: block; font-size: 26rpx; color: #666; margin-bottom: 12rpx; }
+.form-label.required:before { content: "*"; color: #f56c6c; margin-right: 6rpx; }
+.selector-trigger { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 24rpx; background: #f5f5f5; border-radius: 12rpx; }
+.selector-trigger.disabled { opacity: 0.6; }
+.selector-text { font-size: 28rpx; color: #333; }
+.selector-text.placeholder { color: #999; }
+.bottom-bar { position: fixed; left: 0; right: 0; bottom: 0; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); background: #fff; box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.04); }
+.btn-submit { height: 88rpx; border-radius: 999rpx; background: #2979ff; color: #fff; font-size: 30rpx; display: flex; align-items: center; justify-content: center; }
+.btn-submit.disabled { opacity: 0.6; }
+
+.popup { background: #fff; border-top-left-radius: 16rpx; border-top-right-radius: 16rpx; overflow: hidden; }
+.popup-header { padding: 24rpx; border-bottom: 1rpx solid #eee; }
+.popup-title { font-size: 30rpx; font-weight: 500; color: #333; }
+.popup-list { max-height: 60vh; padding: 12rpx 0; }
+.popup-item { padding: 22rpx 24rpx; border-bottom: 1rpx solid #f0f0f0; }
+.popup-item-title { font-size: 28rpx; color: #333; }
+.popup-item-row { display: flex; justify-content: space-between; gap: 16rpx; }
+.popup-item-sub { font-size: 24rpx; color: #999; }
+.no-data { text-align: center; padding: 60rpx 0; color: #999; font-size: 28rpx; }
+</style>
+
diff --git a/src/pages/qualityManagement/nonconformingManagement/index.vue b/src/pages/qualityManagement/nonconformingManagement/index.vue
index 5efa7ae..f3cc42e 100644
--- a/src/pages/qualityManagement/nonconformingManagement/index.vue
+++ b/src/pages/qualityManagement/nonconformingManagement/index.vue
@@ -2,74 +2,69 @@
<view class="nonconforming-management-page">
<PageHeader title="涓嶅悎鏍煎搧绠$悊" @back="goBack" />
- <!-- 鎼滅储涓庣瓫閫� -->
+ <!-- 鎼滅储涓庣瓫閫夛紙鏍峰紡鍙傜収浠撳偍鐗╂祦妯″潡锛� -->
<view class="search-section">
- <up-search
- placeholder="璇疯緭鍏ヤ骇鍝佸悕绉版悳绱�"
- v-model="searchForm.productName"
- @search="handleQuery"
- @custom="handleQuery"
- @clear="handleQuery"
- :show-action="true"
- action-text="鎼滅储"
- :animation="true"
- customStyle="margin-bottom: 20rpx"
- ></up-search>
+ <view class="search-row">
+ <view class="search-input-wrap">
+ <up-input v-model="searchForm.productName" placeholder="浜у搧鍚嶇О" clearable />
+ </view>
+ <view class="btn-search" @click="handleQuery">
+ <view class="btn-search-inner">
+ <up-icon name="search" size="22" color="#fff"></up-icon>
+ <text>鎼滅储</text>
+ </view>
+ </view>
+ </view>
<view class="filter-row">
<view class="filter-item" @click="showTypeSelect = true">
<text>{{ typeLabel }}</text>
<up-icon name="arrow-down" size="14" color="#999"></up-icon>
</view>
- <view class="filter-item" @click="showStatusSelect = true">
- <text>{{ statusLabel }}</text>
- <up-icon name="arrow-down" size="14" color="#999"></up-icon>
+ <view class="filter-item" @click="openDateRange">
+ <text>{{ dateRangeLabel }}</text>
+ <up-icon name="calendar" size="14" color="#999"></up-icon>
</view>
</view>
</view>
<!-- 鍒楄〃鍖哄煙 -->
- <view class="list-container" v-if="tableData.length > 0">
- <view v-for="(item, index) in tableData" :key="index" class="list-item">
- <view class="item-header">
- <text class="product-name">{{ item.productName }}</text>
- <up-tag :text="getStatusText(item.inspectState)" :type="getStatusType(item.inspectState)" size="mini"></up-tag>
- </view>
- <view class="item-content">
- <view class="item-row">
- <text class="item-label">绫诲埆锛�</text>
- <text class="item-value">{{ getInspectTypeText(item.inspectType) }}</text>
+ <view class="list-section">
+ <view v-if="tableData.length > 0">
+ <view v-for="(item, index) in tableData" :key="item.id || index" class="card-item">
+ <view class="card-click" @click="openForm('edit', item)">
+ <view class="card-header">
+ <view class="header-main">
+ <text class="product-name">{{ item.productName }}</text>
+ </view>
+ <view class="header-sub">
+ <text class="sub-title">{{ item.model || '-' }}</text>
+ <up-tag :text="getInspectTypeText(item.checkType ?? item.inspectType)" type="primary" size="mini" />
+ </view>
+ </view>
+ <up-divider />
+ <view class="card-body">
+ <view class="row"><text class="l">妫�娴嬫棩鏈�</text><text class="r">{{ item.checkTime || '-' }}</text></view>
+ <view class="row"><text class="l">鎵瑰彿</text><text class="r">{{ item.batchNo || '-' }}</text></view>
+ <view class="row"><text class="l">妫�楠屽憳</text><text class="r">{{ item.checkName || '-' }}</text></view>
+ <view class="row"><text class="l">涓嶅悎鏍肩幇璞�</text><text class="r text-error">{{ item.defectivePhenomena || '-' }}</text></view>
+ <view class="row" v-if="item.inspectState == 1"><text class="l">澶勭悊缁撴灉</text><text class="r text-success">{{ getDealResultLabel(item.dealResult) || item.dealResult || '-' }}</text></view>
+ <view class="row" v-if="item.inspectState == 1"><text class="l">澶勭悊浜�</text><text class="r">{{ item.dealName || '-' }}</text></view>
+ <view class="row" v-if="item.inspectState == 1"><text class="l">澶勭悊鏃ユ湡</text><text class="r">{{ item.dealTime || '-' }}</text></view>
+ </view>
</view>
- <view class="item-row">
- <text class="item-label">妫�娴嬫棩鏈燂細</text>
- <text class="item-value">{{ item.checkTime || '-' }}</text>
- </view>
- <view class="item-row">
- <text class="item-label">瑙勬牸鍨嬪彿锛�</text>
- <text class="item-value">{{ item.model || '-' }}</text>
- </view>
- <view class="item-row">
- <text class="item-label">涓嶅悎鏍肩幇璞★細</text>
- <text class="item-value text-error">{{ item.defectivePhenomena || '-' }}</text>
- </view>
- <view class="item-row" v-if="item.inspectState === 1">
- <text class="item-label">澶勭悊缁撴灉锛�</text>
- <text class="item-value text-success">{{ item.dealResult || '-' }}</text>
+ <view class="card-actions">
+ <view class="btn-link btn-link-primary" v-if="item.inspectState == 0" @click.stop="openDealDialog(item)">澶勭悊</view>
+ <view class="btn-link btn-link-plain" v-if="item.inspectState == 0" @click.stop="openForm('edit', item)">缂栬緫</view>
+ <view class="btn-link btn-link-warn" @click.stop="handleDelete(item)">鍒犻櫎</view>
</view>
</view>
- <view class="item-actions">
- <up-button v-if="item.inspectState === 0" type="primary" size="mini" @click.stop="openDealDialog(item)">澶勭悊</up-button>
- <up-button type="error" size="mini" @click.stop="handleDelete(item)">鍒犻櫎</up-button>
+ <view class="load-more-wrap">
+ <u-loadmore :status="loadStatus" @loadmore="loadMore" />
</view>
</view>
- <view class="pagination-container">
- <up-loadmore :status="loadStatus" @loadmore="getList" />
- </view>
+ <view v-else class="no-data">鏆傛棤鏁版嵁</view>
</view>
- <view v-else class="no-data">
- <up-empty mode="data" text="鏆傛棤鏁版嵁"></up-empty>
- </view>
-
<!-- 绫诲瀷閫夋嫨鍣� -->
<up-action-sheet
:actions="typeActions"
@@ -77,15 +72,6 @@
@close="showTypeSelect = false"
@select="selectType"
title="璇烽�夋嫨绫诲埆"
- ></up-action-sheet>
-
- <!-- 鐘舵�侀�夋嫨鍣� -->
- <up-action-sheet
- :actions="statusActions"
- :show="showStatusSelect"
- @close="showStatusSelect = false"
- @select="selectStatus"
- title="璇烽�夋嫨鐘舵��"
></up-action-sheet>
<!-- 澶勭悊寮圭獥 -->
@@ -97,10 +83,19 @@
<up-form :model="dealForm" ref="dealFormRef" label-width="100" label-position="top">
<view class="info-summary">
<text class="summary-text">浜у搧锛歿{ currentItem?.productName }}</text>
- <text class="summary-text">涓嶅悎鏍肩幇璞★細{{ currentItem?.defectivePhenomena }}</text>
+ <text class="summary-text">妫�娴嬫棩鏈燂細{{ currentItem?.checkTime || '-' }}</text>
</view>
+ <up-form-item label="涓嶅悎鏍肩幇璞�" prop="defectivePhenomena" required borderBottom>
+ <up-textarea v-model="dealForm.defectivePhenomena" placeholder="璇疯緭鍏ヤ笉鍚堟牸鐜拌薄" count border="surround" />
+ </up-form-item>
<up-form-item label="澶勭悊缁撴灉" prop="dealResult" required borderBottom>
- <up-textarea v-model="dealForm.dealResult" placeholder="璇疯緭鍏ュ鐞嗙粨鏋�" count border="surround" />
+ <view class="selector-trigger" @click="showDealResultSelect = true">
+ <text class="selector-text" :class="{ placeholder: !dealResultLabel }">{{ dealResultLabel || '璇烽�夋嫨澶勭悊缁撴灉' }}</text>
+ <up-icon name="arrow-down" size="14" color="#999"></up-icon>
+ </view>
+ </up-form-item>
+ <up-form-item label="澶勭悊浜�" prop="dealName" required borderBottom>
+ <up-input v-model="dealForm.dealName" placeholder="璇疯緭鍏ュ鐞嗕汉" border="surround" />
</up-form-item>
<up-form-item label="澶勭悊鏃ユ湡" prop="dealTime" required borderBottom>
<up-input
@@ -119,6 +114,15 @@
</view>
</up-popup>
+ <!-- 澶勭悊缁撴灉閫夋嫨鍣� -->
+ <up-action-sheet
+ :actions="dealResultActions"
+ :show="showDealResultSelect"
+ @close="showDealResultSelect = false"
+ @select="selectDealResult"
+ title="璇烽�夋嫨澶勭悊缁撴灉"
+ />
+
<!-- 鏃ユ湡閫夋嫨鍣� -->
<up-datetime-picker
:show="showDatePicker"
@@ -127,23 +131,49 @@
@confirm="confirmDate"
@cancel="showDatePicker = false"
></up-datetime-picker>
+
+ <!-- 褰曞叆鏃ユ湡鑼冨洿閫夋嫨锛氬紑濮� -->
+ <up-datetime-picker
+ :show="showEntryStartPicker"
+ v-model="entryStartValue"
+ mode="date"
+ @confirm="confirmEntryStart"
+ @cancel="showEntryStartPicker = false"
+ />
+ <!-- 褰曞叆鏃ユ湡鑼冨洿閫夋嫨锛氱粨鏉� -->
+ <up-datetime-picker
+ :show="showEntryEndPicker"
+ v-model="entryEndValue"
+ mode="date"
+ @confirm="confirmEntryEnd"
+ @cancel="showEntryEndPicker = false"
+ />
+
+ <!-- 鍙充笅瑙掓柊澧炴寜閽� -->
+ <view class="fab-button" @click="openForm('add')">
+ <up-icon name="plus" size="24" color="#ffffff"></up-icon>
+ </view>
</view>
</template>
<script setup>
-import { ref, reactive, onMounted, computed } from 'vue';
+import { ref, reactive, computed } from 'vue';
import {
qualityUnqualifiedListPage,
qualityUnqualifiedDeal,
qualityUnqualifiedDel
} from '@/api/qualityManagement/nonconformingManagement.js';
import { toast, showConfirm } from '@/utils/common';
+import { useDict } from '@/utils/dict'
import dayjs from 'dayjs';
+import PageHeader from '@/components/PageHeader.vue'
+import { onReachBottom, onShow } from '@dcloudio/uni-app'
const searchForm = reactive({
productName: '',
- inspectType: '',
- inspectState: ''
+ checkType: '',
+ entryDateStart: undefined,
+ entryDateEnd: undefined
});
const tableData = ref([]);
@@ -162,54 +192,67 @@
{ name: '鍑哄巶妫�', value: '2' }
];
const typeLabel = computed(() => {
- const action = typeActions.find(a => a.value === searchForm.inspectType);
+ const action = typeActions.find(a => a.value === String(searchForm.checkType ?? ''));
return action ? action.name : '鍏ㄩ儴绫诲埆';
});
-const showStatusSelect = ref(false);
-const statusActions = [
- { name: '鍏ㄩ儴', value: '' },
- { name: '寰呭鐞�', value: '0' },
- { name: '宸插鐞�', value: '1' }
-];
-const statusLabel = computed(() => {
- const action = statusActions.find(a => a.value === searchForm.inspectState);
- return action ? action.name : '鍏ㄩ儴鐘舵��';
-});
+const dateRangeLabel = computed(() => {
+ if (searchForm.entryDateStart && searchForm.entryDateEnd) return `${searchForm.entryDateStart}~${searchForm.entryDateEnd}`
+ if (searchForm.entryDateStart) return `${searchForm.entryDateStart}~`
+ if (searchForm.entryDateEnd) return `~${searchForm.entryDateEnd}`
+ return '妫�娴嬫棩鏈�'
+})
const dealDialogVisible = ref(false);
const submitLoading = ref(false);
const currentItem = ref(null);
const dealForm = reactive({
id: null,
+ defectivePhenomena: '',
dealResult: '',
+ dealName: '',
dealTime: dayjs().format('YYYY-MM-DD')
});
+
+const { rejection_handling } = useDict('rejection_handling')
+const showDealResultSelect = ref(false)
+const dealResultActions = computed(() => {
+ const list = rejection_handling?.value || []
+ return (list || []).map(it => ({ name: it.label, value: it.value }))
+})
+const dealResultLabel = computed(() => {
+ const list = rejection_handling?.value || []
+ const v = dealForm.dealResult
+ return (list || []).find(it => String(it.value) === String(v))?.label || ''
+})
+function getDealResultLabel(value) {
+ const list = rejection_handling?.value || []
+ return (list || []).find(it => String(it.value) === String(value))?.label || ''
+}
const showDatePicker = ref(false);
const dateValue = ref(Number(new Date()));
+const showEntryStartPicker = ref(false)
+const showEntryEndPicker = ref(false)
+const entryStartValue = ref(Date.now())
+const entryEndValue = ref(Date.now())
+
const getInspectTypeText = (type) => {
const types = { '0': '鍏ュ巶妫�', '1': '杞﹂棿妫�', '2': '鍑哄巶妫�' };
- return types[type] || '-';
-};
-
-const getStatusText = (state) => {
- return state === 1 ? '宸插鐞�' : '寰呭鐞�';
-};
-
-const getStatusType = (state) => {
- return state === 1 ? 'success' : 'warning';
+ return types[String(type ?? '')] || '-';
};
const getList = () => {
- if (loadStatus.value === 'loading' || (page.total > 0 && tableData.value.length >= page.total)) return;
-
- loadStatus.value = 'loading';
+ const isFirstPage = page.current === 1
+ if (loadStatus.value === 'loading' || (!isFirstPage && page.total > 0 && tableData.value.length >= page.total)) return
+
+ loadStatus.value = 'loading'
const params = {
productName: searchForm.productName || null,
- inspectType: searchForm.inspectType || null,
- inspectState: searchForm.inspectState || null,
+ checkType: searchForm.checkType === '' ? null : searchForm.checkType,
+ entryDateStart: searchForm.entryDateStart,
+ entryDateEnd: searchForm.entryDateEnd,
current: page.current,
size: page.size
};
@@ -227,12 +270,18 @@
loadStatus.value = 'nomore';
} else {
loadStatus.value = 'loadmore';
- page.current++;
}
}).catch(() => {
- loadStatus.value = 'loadmore';
+ loadStatus.value = 'error';
});
};
+
+const loadMore = () => {
+ if (loadStatus.value === 'nomore' || loadStatus.value === 'loading') return
+ loadStatus.value = 'loading'
+ page.current++
+ getList()
+}
const handleQuery = () => {
page.current = 1;
@@ -243,27 +292,55 @@
};
const selectType = (e) => {
- searchForm.inspectType = e.value;
+ searchForm.checkType = e.value;
handleQuery();
};
-const selectStatus = (e) => {
- searchForm.inspectState = e.value;
- handleQuery();
-};
+const openDateRange = () => {
+ entryStartValue.value = searchForm.entryDateStart ? dayjs(searchForm.entryDateStart, 'YYYY-MM-DD').valueOf() : Date.now()
+ showEntryStartPicker.value = true
+}
+const confirmEntryStart = (e) => {
+ const ts = e?.value ?? entryStartValue.value
+ searchForm.entryDateStart = dayjs(ts).format('YYYY-MM-DD')
+ showEntryStartPicker.value = false
+ entryEndValue.value = searchForm.entryDateEnd ? dayjs(searchForm.entryDateEnd, 'YYYY-MM-DD').valueOf() : Date.now()
+ showEntryEndPicker.value = true
+}
+const confirmEntryEnd = (e) => {
+ const ts = e?.value ?? entryEndValue.value
+ searchForm.entryDateEnd = dayjs(ts).format('YYYY-MM-DD')
+ showEntryEndPicker.value = false
+ handleQuery()
+}
const openDealDialog = (item) => {
currentItem.value = item;
dealForm.id = item.id;
- dealForm.dealResult = '';
+ dealForm.defectivePhenomena = item.defectivePhenomena || ''
+ dealForm.dealResult = item.dealResult || '';
+ dealForm.dealName = item.dealName || ''
dealForm.dealTime = dayjs().format('YYYY-MM-DD');
dealDialogVisible.value = true;
};
+const selectDealResult = (e) => {
+ dealForm.dealResult = e.value
+ showDealResultSelect.value = false
+}
+
const submitDeal = () => {
- if (!dealForm.dealResult) {
- toast('璇疯緭鍏ュ鐞嗙粨鏋�');
+ if (!dealForm.defectivePhenomena) {
+ toast('璇疯緭鍏ヤ笉鍚堟牸鐜拌薄')
return;
+ }
+ if (!dealForm.dealResult) {
+ toast('璇烽�夋嫨澶勭悊缁撴灉')
+ return
+ }
+ if (!dealForm.dealName) {
+ toast('璇疯緭鍏ュ鐞嗕汉')
+ return
}
submitLoading.value = true;
qualityUnqualifiedDeal(dealForm).then(() => {
@@ -291,89 +368,115 @@
showDatePicker.value = false;
};
+const openForm = (type, row) => {
+ if (type !== 'add' && row?.inspectState == 1) {
+ toast('宸插鐞嗙殑鏁版嵁涓嶈兘鍐嶇紪杈�')
+ return
+ }
+ const id = row?.id
+ uni.navigateTo({
+ url: `/pages/qualityManagement/nonconformingManagement/form?type=${type}${id ? `&id=${id}` : ''}`
+ })
+}
+
const goBack = () => {
uni.navigateBack();
};
-onMounted(() => {
- handleQuery();
-});
+onShow(() => {
+ handleQuery()
+})
+
+onReachBottom(() => {
+ loadMore()
+})
</script>
<style lang="scss" scoped>
.nonconforming-management-page {
- padding-bottom: 20rpx;
- background-color: #f5f7fa;
min-height: 100vh;
+ background: #f5f5f5;
+ padding-bottom: 120rpx;
}
.search-section {
- padding: 20rpx 30rpx;
- background-color: #ffffff;
- position: sticky;
- top: 0;
- z-index: 10;
+ background: #fff;
+ margin: 24rpx;
+ padding: 24rpx;
+ border-radius: 16rpx;
}
+
+.search-row { display: flex; align-items: center; margin-bottom: 20rpx; }
+.search-input-wrap { flex: 1; margin-right: 20rpx; min-width: 0; }
+.btn-search {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 180rpx;
+ min-height: 72rpx;
+ flex-shrink: 0;
+ padding: 20rpx 24rpx;
+ background: #2979ff;
+ color: #fff;
+ border-radius: 12rpx;
+ font-size: 28rpx;
+ box-sizing: border-box;
+ text-align: center;
+}
+.btn-search-inner { display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 8rpx; }
.filter-row {
display: flex;
- justify-content: space-around;
- padding: 10rpx 0;
+ gap: 20rpx;
}
.filter-item {
- display: flex;
- align-items: center;
- gap: 10rpx;
- font-size: 28rpx;
- color: #606266;
-}
-
-.list-container {
- padding: 20rpx;
-}
-
-.list-item {
- background-color: #ffffff;
- border-radius: 16rpx;
- padding: 30rpx;
- margin-bottom: 20rpx;
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-}
-
-.item-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 20rpx;
-}
-
-.product-name {
- font-size: 30rpx;
- font-weight: bold;
- color: #303133;
-}
-
-.item-content {
- margin-bottom: 20rpx;
-}
-
-.item-row {
- display: flex;
- margin-bottom: 10rpx;
-}
-
-.item-label {
- color: #909399;
- width: 180rpx;
- font-size: 28rpx;
-}
-
-.item-value {
flex: 1;
- color: #303133;
- font-size: 28rpx;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 18rpx 20rpx;
+ background: #f5f5f5;
+ border-radius: 12rpx;
+ font-size: 26rpx;
+ color: #666;
}
+
+.list-section { padding: 0 24rpx; }
+.card-item {
+ background: #fff;
+ border-radius: 16rpx;
+ padding: 16rpx 20rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
+}
+.card-header { padding: 2rpx 0 6rpx; }
+.header-main { display: flex; justify-content: space-between; align-items: center; gap: 16rpx; }
+.product-name { font-size: 30rpx; font-weight: 500; color: #333; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.header-sub { display: flex; justify-content: space-between; gap: 16rpx; margin-top: 6rpx; }
+.sub-title { font-size: 24rpx; color: #999; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.sub-right { font-size: 24rpx; color: #999; flex-shrink: 0; }
+.card-body .row { display: flex; justify-content: space-between; padding: 6rpx 0; font-size: 26rpx; }
+.card-body .l { color: #666; width: 180rpx; flex-shrink: 0; }
+.card-body .r { color: #333; flex: 1; text-align: right; word-break: break-all; }
+.card-actions {
+ display: flex;
+ gap: 16rpx;
+ justify-content: flex-end;
+ align-items: center;
+ margin-top: 12rpx;
+ padding-top: 14rpx;
+ border-top: 1rpx solid #eee;
+}
+.btn-link {
+ font-size: 28rpx;
+ padding: 10rpx 22rpx;
+ border-radius: 999rpx;
+ border: 1rpx solid transparent;
+}
+.btn-link-primary { color: #2979ff; border-color: rgba(41, 121, 255, 0.4); background: rgba(41, 121, 255, 0.08); }
+.btn-link-plain { color: #606266; border-color: rgba(96, 98, 102, 0.35); background: rgba(96, 98, 102, 0.06); }
+.btn-link-warn { color: #f56c6c; border-color: rgba(245, 108, 108, 0.55); background: rgba(245, 108, 108, 0.08); }
.text-error {
color: #f56c6c;
@@ -383,17 +486,8 @@
color: #67c23a;
}
-.item-actions {
- display: flex;
- justify-content: flex-end;
- gap: 20rpx;
- border-top: 1rpx solid #ebeef5;
- padding-top: 20rpx;
-}
-
-.no-data {
- padding-top: 200rpx;
-}
+.no-data { text-align: center; padding: 60rpx 0; color: #999; font-size: 28rpx; }
+.load-more-wrap { padding: 24rpx 24rpx 8rpx; }
.dialog-content {
width: 650rpx;
@@ -431,4 +525,23 @@
.dialog-footer {
margin-top: 40rpx;
}
+
+.selector-trigger { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 24rpx; background: #f5f5f5; border-radius: 12rpx; }
+.selector-text { font-size: 28rpx; color: #333; }
+.selector-text.placeholder { color: #999; }
+
+.fab-button {
+ position: fixed;
+ right: 36rpx;
+ bottom: 72rpx;
+ width: 104rpx;
+ height: 104rpx;
+ border-radius: 52rpx;
+ background: #2979ff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 10rpx 26rpx rgba(41, 121, 255, 0.35);
+ z-index: 20;
+}
</style>
diff --git a/src/pages/qualityManagement/rawMaterial/files.vue b/src/pages/qualityManagement/rawMaterial/files.vue
new file mode 100644
index 0000000..32842f4
--- /dev/null
+++ b/src/pages/qualityManagement/rawMaterial/files.vue
@@ -0,0 +1,159 @@
+<template>
+ <view class="file-page">
+ <PageHeader title="闄勪欢绠$悊" @back="goBack" />
+
+ <view class="file-list">
+ <view v-if="files.length > 0">
+ <view v-for="(f, idx) in files" :key="f.id || idx" class="file-item">
+ <view class="file-info">
+ <text class="file-name">{{ f.name }}</text>
+ </view>
+ <view class="file-actions">
+ <view class="btn-link" @click="previewFile(f)">棰勮</view>
+ <view class="btn-link danger" @click="confirmDelete(f)">鍒犻櫎</view>
+ </view>
+ </view>
+ </view>
+ <view v-else class="empty">鏆傛棤闄勪欢</view>
+ </view>
+
+ <view class="upload-bar">
+ <view class="btn-upload" @click="chooseFile">涓婁紶闄勪欢</view>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import { onLoad, onShow } from '@dcloudio/uni-app'
+import PageHeader from '@/components/PageHeader.vue'
+import config from '@/config'
+import { getToken } from '@/utils/auth'
+import { qualityInspectFileAdd, qualityInspectFileDel, qualityInspectFileListPage } from '@/api/qualityManagement/qualityInspectFile.js'
+
+const inspectId = ref('')
+const files = ref([])
+
+const getList = () => {
+ if (!inspectId.value) return
+ qualityInspectFileListPage({ inspectId: inspectId.value, current: 1, size: 200 })
+ .then(res => {
+ files.value = res?.data?.records || []
+ })
+ .catch(() => { files.value = [] })
+}
+
+const chooseFile = () => {
+ if (!inspectId.value) {
+ uni.showToast({ title: '缂哄皯妫�楠岃褰旾D', icon: 'none' })
+ return
+ }
+ uni.chooseFile({
+ count: 1,
+ success: (res) => {
+ const path = res?.tempFiles?.[0]?.path
+ const name = res?.tempFiles?.[0]?.name
+ if (!path) return
+ uploadOne(path, name)
+ }
+ })
+}
+
+const uploadOne = (filePath, originalName) => {
+ const token = getToken()
+ if (!token) {
+ uni.showToast({ title: '鏈櫥褰�', icon: 'none' })
+ return
+ }
+ uni.showLoading({ title: '涓婁紶涓�...', mask: true })
+ uni.uploadFile({
+ url: config.baseUrl + '/file/upload',
+ filePath,
+ name: 'file',
+ header: { Authorization: 'Bearer ' + token },
+ success: (res) => {
+ uni.hideLoading()
+ try {
+ const resp = JSON.parse(res.data || '{}')
+ if (resp.code !== 200) throw new Error('upload fail')
+ const fileRow = {
+ inspectId: inspectId.value,
+ name: resp.data?.originalName || originalName || '闄勪欢',
+ url: resp.data?.tempPath
+ }
+ qualityInspectFileAdd(fileRow).then(() => {
+ uni.showToast({ title: '涓婁紶鎴愬姛', icon: 'success' })
+ getList()
+ })
+ } catch (e) {
+ uni.showToast({ title: '涓婁紶澶辫触', icon: 'none' })
+ }
+ },
+ fail: () => {
+ uni.hideLoading()
+ uni.showToast({ title: '涓婁紶澶辫触', icon: 'none' })
+ }
+ })
+}
+
+const previewFile = (f) => {
+ const url = f?.url
+ if (!url) return
+ // H5/APP 缁熶竴鐢ㄥ閮ㄦ墦寮�
+ uni.navigateTo({
+ url: `/pages/inspectionUpload/filePreview?url=${encodeURIComponent(url)}`
+ })
+}
+
+const confirmDelete = (f) => {
+ if (!f?.id) return
+ uni.showModal({
+ title: '鍒犻櫎',
+ content: '纭鍒犻櫎璇ラ檮浠讹紵',
+ success: (r) => {
+ if (!r.confirm) return
+ qualityInspectFileDel([f.id]).then(() => {
+ uni.showToast({ title: '鍒犻櫎鎴愬姛', icon: 'success' })
+ getList()
+ })
+ }
+ })
+}
+
+onLoad((options) => {
+ inspectId.value = options?.id || ''
+ if (!inspectId.value) {
+ const cached = uni.getStorageSync('rawMaterialFilesCtx')
+ if (cached) {
+ try {
+ const payload = typeof cached === 'string' ? JSON.parse(cached) : cached
+ inspectId.value = payload?.id || ''
+ uni.removeStorageSync('rawMaterialFilesCtx')
+ } catch (e) {
+ uni.removeStorageSync('rawMaterialFilesCtx')
+ }
+ }
+ }
+})
+
+onShow(() => {
+ getList()
+})
+
+const goBack = () => uni.navigateBack()
+</script>
+
+<style lang="scss" scoped>
+.file-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 120rpx; }
+.file-list { margin: 24rpx; background: #fff; border-radius: 16rpx; padding: 12rpx 24rpx; }
+.file-item { padding: 20rpx 0; border-bottom: 1rpx solid #eee; display: flex; justify-content: space-between; align-items: center; gap: 16rpx; }
+.file-item:last-child { border-bottom: 0; }
+.file-name { font-size: 28rpx; color: #333; }
+.file-actions { display: flex; gap: 20rpx; }
+.btn-link { color: #2979ff; font-size: 26rpx; }
+.btn-link.danger { color: #f56c6c; }
+.empty { text-align: center; padding: 60rpx 0; color: #999; font-size: 28rpx; }
+.upload-bar { position: fixed; left: 0; right: 0; bottom: 0; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); background: #fff; box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.04); }
+.btn-upload { height: 88rpx; border-radius: 999rpx; background: #2979ff; color: #fff; font-size: 30rpx; display: flex; align-items: center; justify-content: center; }
+</style>
+
diff --git a/src/pages/qualityManagement/rawMaterial/form.vue b/src/pages/qualityManagement/rawMaterial/form.vue
new file mode 100644
index 0000000..148dcd6
--- /dev/null
+++ b/src/pages/qualityManagement/rawMaterial/form.vue
@@ -0,0 +1,418 @@
+<template>
+ <view class="rm-form-page">
+ <PageHeader :title="operationType === 'add' ? '鏂板鍘熸枡妫�' : '缂栬緫鍘熸枡妫�'" @back="goBack" />
+
+ <scroll-view scroll-y class="content-scroll">
+ <view class="section-card">
+ <view class="section-title">鍩虹淇℃伅</view>
+
+ <view class="form-row">
+ <text class="form-label required">浜у搧鍚嶇О</text>
+ <view class="selector-trigger" @click="openProductSelector" :class="{ disabled: operationType === 'edit' }">
+ <text class="selector-text" :class="{ placeholder: !form.productName }">
+ {{ form.productName || '璇烽�夋嫨浜у搧' }}
+ </text>
+ <up-icon name="arrow-right" size="16" color="#999"></up-icon>
+ </view>
+ </view>
+
+ <view class="form-row">
+ <text class="form-label required">瑙勬牸鍨嬪彿</text>
+ <up-input v-model="form.model" disabled placeholder="璇烽�夋嫨浜у搧鍚庤嚜鍔ㄥ甫鍑�" />
+ </view>
+
+ <view class="form-row">
+ <text class="form-label">鍗曚綅</text>
+ <up-input v-model="form.unit" disabled placeholder="鑷姩甯﹀嚭" />
+ </view>
+
+ <view class="form-row">
+ <text class="form-label required">鎵瑰彿</text>
+ <up-input v-model="form.batchNo" placeholder="璇疯緭鍏ユ壒鍙�" />
+ </view>
+
+ <view class="form-row">
+ <text class="form-label required">妫�楠岀被鍨�</text>
+ <view class="selector-trigger" @click="showCheckTypeSheet = true">
+ <text class="selector-text" :class="{ placeholder: form.checkType === '' || form.checkType == null }">
+ {{ checkTypeLabel }}
+ </text>
+ <up-icon name="arrow-down" size="14" color="#999"></up-icon>
+ </view>
+ </view>
+
+ <view class="form-row">
+ <text class="form-label required">妫�娴嬬粨鏋�</text>
+ <view class="selector-trigger" @click="showCheckResultSheet = true">
+ <text class="selector-text" :class="{ placeholder: form.checkResult === '' || form.checkResult == null }">
+ {{ checkResultLabel }}
+ </text>
+ <up-icon name="arrow-down" size="14" color="#999"></up-icon>
+ </view>
+ </view>
+
+ <view class="form-row">
+ <text class="form-label required">妫�楠屽憳</text>
+ <up-input v-model="form.checkUserName" placeholder="璇疯緭鍏ユ楠屽憳" />
+ </view>
+
+ <view class="form-row">
+ <text class="form-label required">妫�娴嬫棩鏈�</text>
+ <view class="selector-trigger" @click="openCheckDatePicker">
+ <text class="selector-text" :class="{ placeholder: !form.checkTime }">
+ {{ form.checkTime || '璇烽�夋嫨妫�娴嬫棩鏈�' }}
+ </text>
+ <up-icon name="calendar" size="16" color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+
+ <view class="section-card">
+ <view class="section-title row-between">
+ <text>妫�娴嬮」鐩�</text>
+ <view class="btn-inline" @click="openItemSelector">娣诲姞妫�娴嬮」鐩�</view>
+ </view>
+
+ <view v-if="inspectItems.length > 0">
+ <view v-for="(it, idx) in inspectItems" :key="it.id || idx" class="item-card">
+ <view class="item-head">
+ <text class="item-name">{{ it.name }}</text>
+ <view class="item-del" @click="removeItem(it.id)">鍒犻櫎</view>
+ </view>
+ <view class="item-row"><text class="l">鍗曚綅</text><text class="r">{{ it.unit || '-' }}</text></view>
+ <view class="item-row"><text class="l">鏍囧噯鍊�</text><text class="r">{{ it.standardValue || '-' }}</text></view>
+ <view class="item-row"><text class="l">鍐呮帶鍊�</text><text class="r">{{ it.internalControl || '-' }}</text></view>
+ <view class="item-row input-row">
+ <text class="l">鍖栭獙鍊�</text>
+ <up-input v-model="it.testValue" placeholder="璇疯緭鍏�" class="test-value-input" />
+ </view>
+ </view>
+ </view>
+ <view v-else class="no-data">璇锋坊鍔犳娴嬮」鐩�</view>
+ </view>
+ </scroll-view>
+
+ <view class="bottom-bar">
+ <view class="btn-submit" :class="{ disabled: submitLoading }" @click="handleSubmit">
+ {{ submitLoading ? '鎻愪氦涓�...' : '淇濆瓨' }}
+ </view>
+ </view>
+
+ <!-- 浜у搧閫夋嫨寮圭獥锛堝鐢� pageModel 鎺ュ彛锛� -->
+ <up-popup :show="showProductPopup" mode="bottom" @close="showProductPopup = false">
+ <view class="popup-wrap">
+ <view class="popup-header">
+ <text class="popup-title">閫夋嫨浜у搧</text>
+ </view>
+ <view class="popup-search">
+ <up-input v-model="productQuery.productName" placeholder="浜у搧澶х被" clearable />
+ <up-input v-model="productQuery.model" placeholder="鍨嬪彿鍚嶇О" clearable />
+ <view class="popup-search-btn" @click="loadProductList">鎼滅储</view>
+ </view>
+ <scroll-view scroll-y class="popup-list">
+ <view v-for="row in productList" :key="row.id" class="popup-item" @click="selectProduct(row)">
+ <view class="popup-item-top">
+ <text class="popup-item-name">{{ row.productName }}</text>
+ <text class="popup-item-unit">{{ row.unit }}</text>
+ </view>
+ <view class="popup-item-sub">鍨嬪彿锛歿{ row.model }}</view>
+ </view>
+ <view v-if="!productLoading && productList.length === 0" class="no-data">鏆傛棤鏁版嵁</view>
+ </scroll-view>
+ </view>
+ </up-popup>
+
+ <!-- 妫�娴嬮」鐩�夋嫨寮圭獥锛堢畝鍖栵細浠庢娴嬮」缁存姢琛ㄩ噷閫夛級 -->
+ <up-popup :show="showItemPopup" mode="bottom" @close="showItemPopup = false">
+ <view class="popup-wrap">
+ <view class="popup-header">
+ <text class="popup-title">閫夋嫨妫�娴嬮」鐩�</text>
+ </view>
+ <view class="popup-search">
+ <up-input v-model="itemQuery.name" placeholder="妫�娴嬮」鐩悕绉�" clearable />
+ <view class="popup-search-btn" @click="loadItemList">鎼滅储</view>
+ </view>
+ <scroll-view scroll-y class="popup-list">
+ <view
+ v-for="row in itemList"
+ :key="row.id"
+ class="popup-item"
+ :class="{ selected: isItemSelected(row.id) }"
+ @click="toggleItem(row)"
+ >
+ <view class="popup-item-top">
+ <text class="popup-item-name">{{ row.name }}</text>
+ <view class="right-wrap">
+ <text class="popup-item-unit">{{ row.unit }}</text>
+ <up-icon
+ v-if="isItemSelected(row.id)"
+ name="checkbox-mark"
+ size="18"
+ color="#2979ff"
+ ></up-icon>
+ </view>
+ </view>
+ <view class="popup-item-sub">鏍囧噯鍊硷細{{ row.standardValue || '-' }}锝滃唴鎺у�硷細{{ row.internalControl || '-' }}</view>
+ </view>
+ <view v-if="!itemLoading && itemList.length === 0" class="no-data">鏆傛棤鏁版嵁</view>
+ </scroll-view>
+ <view class="popup-footer">
+ <view class="btn-cancel" @click="showItemPopup = false">鍙栨秷</view>
+ <view class="btn-ok" @click="confirmItems">纭畾</view>
+ </view>
+ </view>
+ </up-popup>
+
+ <!-- 閫夋嫨鍣細妫�楠岀被鍨�/缁撴灉 -->
+ <up-action-sheet :actions="checkTypeActions" :show="showCheckTypeSheet" @close="showCheckTypeSheet = false" @select="onSelectCheckType" title="妫�楠岀被鍨�" />
+ <up-action-sheet :actions="checkResultActions" :show="showCheckResultSheet" @close="showCheckResultSheet = false" @select="onSelectCheckResult" title="妫�娴嬬粨鏋�" />
+
+ <!-- 鏃ユ湡閫夋嫨 -->
+ <up-popup :show="showCheckDatePicker" mode="bottom" @close="showCheckDatePicker = false">
+ <up-datetime-picker :show="true" v-model="checkDateValue" mode="date" @confirm="onCheckDateConfirm" @cancel="showCheckDatePicker = false" />
+ </up-popup>
+ </view>
+</template>
+
+<script setup>
+import { computed, reactive, ref } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import dayjs from 'dayjs'
+import PageHeader from '@/components/PageHeader.vue'
+import useUserStore from '@/store/modules/user'
+import { productModelList } from '@/api/basicData/productModel.js'
+import { qualityInspectItemListPage } from '@/api/qualityManagement/inspectItem.js'
+import { createRawMaterial, findRawMaterialDetail, updateRawMaterial } from '@/api/qualityManagement/rawMaterial.js'
+
+const userStore = useUserStore()
+
+const operationType = ref('add')
+const submitLoading = ref(false)
+
+const form = reactive({
+ id: null,
+ productId: '',
+ productModelId: '',
+ productName: '',
+ model: '',
+ unit: '',
+ batchNo: '',
+ checkType: '',
+ checkResult: '',
+ checkUserName: userStore?.nickName || '',
+ checkTime: ''
+})
+
+const inspectItems = ref([]) // qualityInspectItem
+
+// 妫�楠岀被鍨�/缁撴灉
+const checkTypeActions = [
+ { name: '鍏ュ巶妫�', value: 0 },
+ { name: '杞﹂棿妫�', value: 1 },
+ { name: '鍑哄巶妫�', value: 2 }
+]
+const checkResultActions = [
+ { name: '鍚堟牸', value: 1 },
+ { name: '涓嶅悎鏍�', value: 0 }
+]
+const showCheckTypeSheet = ref(false)
+const showCheckResultSheet = ref(false)
+const checkTypeLabel = computed(() => checkTypeActions.find(a => a.value === form.checkType)?.name || '璇烽�夋嫨')
+const checkResultLabel = computed(() => checkResultActions.find(a => a.value === form.checkResult)?.name || '璇烽�夋嫨')
+const onSelectCheckType = (e) => { form.checkType = e.value; showCheckTypeSheet.value = false }
+const onSelectCheckResult = (e) => { form.checkResult = e.value; showCheckResultSheet.value = false }
+
+// 鏃ユ湡閫夋嫨
+const showCheckDatePicker = ref(false)
+const checkDateValue = ref(Date.now())
+const openCheckDatePicker = () => {
+ checkDateValue.value = form.checkTime ? dayjs(form.checkTime, 'YYYY-MM-DD').valueOf() : Date.now()
+ showCheckDatePicker.value = true
+}
+const onCheckDateConfirm = (e) => {
+ form.checkTime = dayjs(e.value).format('YYYY-MM-DD')
+ showCheckDatePicker.value = false
+}
+
+// 浜у搧閫夋嫨
+const showProductPopup = ref(false)
+const productQuery = reactive({ productName: '', model: '' })
+const productList = ref([])
+const productLoading = ref(false)
+const openProductSelector = () => {
+ if (operationType.value === 'edit') return
+ showProductPopup.value = true
+ if (productList.value.length === 0) loadProductList()
+}
+const loadProductList = () => {
+ productLoading.value = true
+ productModelList({ productName: productQuery.productName || '', model: productQuery.model || '', current: 1, size: 20 })
+ .then(res => {
+ const data = res?.records || res?.data?.records || []
+ productList.value = Array.isArray(data) ? data : []
+ })
+ .finally(() => { productLoading.value = false })
+}
+const selectProduct = (row) => {
+ form.productId = row.productId
+ form.productModelId = row.id
+ form.productName = row.productName
+ form.model = row.model
+ form.unit = row.unit
+ showProductPopup.value = false
+}
+
+// 妫�娴嬮」鐩�夋嫨
+const showItemPopup = ref(false)
+const itemQuery = reactive({ name: '' })
+const itemList = ref([])
+const itemLoading = ref(false)
+const selectedItemIds = ref(new Set())
+const isItemSelected = (id) => selectedItemIds.value.has(id)
+const openItemSelector = () => {
+ showItemPopup.value = true
+ selectedItemIds.value = new Set(inspectItems.value.map(i => i.id))
+ if (itemList.value.length === 0) loadItemList()
+}
+const loadItemList = () => {
+ itemLoading.value = true
+ qualityInspectItemListPage({ name: itemQuery.name || null, current: 1, size: 50 })
+ .then(res => {
+ const records = res?.data?.records || []
+ itemList.value = Array.isArray(records) ? records : []
+ })
+ .finally(() => { itemLoading.value = false })
+}
+const toggleItem = (row) => {
+ const set = selectedItemIds.value
+ if (set.has(row.id)) set.delete(row.id)
+ else set.add(row.id)
+ // 绠�鍗曟彁绀洪�変腑鐘舵�侊紙鍙寜闇�鍋� icon锛�
+}
+const confirmItems = () => {
+ const set = selectedItemIds.value
+ const existingMap = new Map(inspectItems.value.map(i => [i.id, i]))
+ const next = []
+ for (const id of set) {
+ const exist = existingMap.get(id)
+ if (exist) next.push(exist)
+ else {
+ const row = itemList.value.find(r => r.id === id)
+ if (row) next.push({ ...row, testValue: '' })
+ }
+ }
+ inspectItems.value = next
+ showItemPopup.value = false
+}
+const removeItem = (id) => {
+ inspectItems.value = inspectItems.value.filter(i => i.id !== id)
+}
+
+const validate = () => {
+ if (!form.productModelId) return '璇烽�夋嫨浜у搧'
+ if (!form.batchNo) return '璇疯緭鍏ユ壒鍙�'
+ if (form.checkType === '' || form.checkType == null) return '璇烽�夋嫨妫�楠岀被鍨�'
+ if (form.checkResult === '' || form.checkResult == null) return '璇烽�夋嫨妫�娴嬬粨鏋�'
+ if (!form.checkUserName) return '璇疯緭鍏ユ楠屽憳'
+ if (!form.checkTime) return '璇烽�夋嫨妫�娴嬫棩鏈�'
+ if (!inspectItems.value.length) return '璇锋坊鍔犳娴嬮」鐩�'
+ return ''
+}
+
+const handleSubmit = () => {
+ if (submitLoading.value) return
+ const msg = validate()
+ if (msg) {
+ uni.showToast({ title: msg, icon: 'none' })
+ return
+ }
+ submitLoading.value = true
+ const payload = { ...form, qualityInspectItem: inspectItems.value }
+ const api = operationType.value === 'add' ? createRawMaterial : updateRawMaterial
+ api(payload)
+ .then(() => {
+ uni.showToast({ title: '淇濆瓨鎴愬姛', icon: 'success' })
+ setTimeout(() => uni.navigateBack(), 400)
+ })
+ .catch(() => {
+ uni.showToast({ title: '淇濆瓨澶辫触', icon: 'none' })
+ })
+ .finally(() => { submitLoading.value = false })
+}
+
+onLoad((options) => {
+ operationType.value = options?.type || 'add'
+ const id = options?.id
+ if (operationType.value === 'edit' && id) {
+ findRawMaterialDetail(id).then(res => {
+ const d = res?.data || {}
+ form.id = d.id
+ form.productId = d.productId
+ form.productModelId = d.productModelId
+ form.productName = d.productName
+ form.model = d.model
+ form.unit = d.unit
+ form.batchNo = d.batchNo
+ form.checkType = d.checkType
+ form.checkResult = d.checkResult
+ form.checkUserName = d.checkUserName || form.checkUserName
+ form.checkTime = d.checkTime
+ inspectItems.value = Array.isArray(d.qualityInspectItem) ? d.qualityInspectItem : []
+ })
+ }
+})
+
+const goBack = () => uni.navigateBack()
+</script>
+
+<style lang="scss" scoped>
+.rm-form-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 110rpx; }
+.content-scroll { height: calc(100vh - 110rpx); }
+.section-card { background: #fff; margin: 24rpx; padding: 24rpx; border-radius: 16rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
+.section-title { font-size: 28rpx; font-weight: 500; color: #333; margin-bottom: 12rpx; display: flex; align-items: center; }
+.row-between { justify-content: space-between; }
+.btn-inline { font-size: 26rpx; color: #2979ff; }
+.form-row { margin-bottom: 22rpx; }
+.form-label { display: block; font-size: 26rpx; color: #666; margin-bottom: 12rpx; }
+.form-label.required:before { content: "*"; color: #f56c6c; margin-right: 6rpx; }
+.selector-trigger { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 24rpx; background: #f5f5f5; border-radius: 12rpx; }
+.selector-trigger.disabled { opacity: 0.6; }
+.selector-text { font-size: 28rpx; color: #333; }
+.selector-text.placeholder { color: #999; }
+.no-data { text-align: center; padding: 40rpx 0; color: #999; font-size: 26rpx; }
+.item-card { padding: 18rpx 0; border-top: 1rpx solid #eee; }
+.item-head { display: flex; justify-content: space-between; align-items: center; padding: 6rpx 0 10rpx; }
+.item-name { font-size: 28rpx; font-weight: 500; color: #333; }
+.item-del { font-size: 26rpx; color: #f56c6c; }
+.item-row { display: flex; justify-content: space-between; align-items: center; padding: 6rpx 0; font-size: 26rpx; }
+.item-row .l { color: #666; }
+.item-row .r { color: #333; }
+.input-row { align-items: center; }
+.test-value-input {
+ flex: 0 0 220rpx;
+}
+:deep(.test-value-input .u-input),
+:deep(.test-value-input .up-input) {
+ width: 100%;
+}
+.bottom-bar { position: fixed; left: 0; right: 0; bottom: 0; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); background: #fff; box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.04); }
+.btn-submit { height: 88rpx; border-radius: 999rpx; background: #2979ff; color: #fff; font-size: 30rpx; display: flex; align-items: center; justify-content: center; }
+.btn-submit.disabled { opacity: 0.6; }
+.popup-wrap { background: #fff; border-radius: 24rpx 24rpx 0 0; padding-bottom: env(safe-area-inset-bottom); }
+.popup-header { padding: 24rpx; border-bottom: 1rpx solid #eee; text-align: center; }
+.popup-title { font-size: 30rpx; font-weight: 500; color: #333; }
+.popup-search { padding: 16rpx 24rpx; display: grid; grid-template-columns: 1fr 1fr 140rpx; gap: 16rpx; align-items: center; }
+.popup-search-btn { height: 72rpx; border-radius: 12rpx; background: #2979ff; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 28rpx; }
+.popup-list { max-height: 60vh; padding: 0 24rpx 24rpx; }
+.popup-item { padding: 18rpx 0; border-bottom: 1rpx solid #f0f0f0; }
+.popup-item.selected { background: rgba(41, 121, 255, 0.06); }
+.popup-item-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6rpx; }
+.right-wrap { display: inline-flex; align-items: center; gap: 12rpx; }
+.popup-item-name { font-size: 28rpx; color: #333; }
+.popup-item-unit { font-size: 24rpx; color: #999; }
+.popup-item-sub { font-size: 24rpx; color: #666; }
+.popup-footer { display: flex; gap: 24rpx; padding: 16rpx 24rpx 24rpx; }
+.btn-cancel { flex: 1; text-align: center; padding: 24rpx; background: #f0f0f0; border-radius: 12rpx; }
+.btn-ok { flex: 1; text-align: center; padding: 24rpx; background: #2979ff; color: #fff; border-radius: 12rpx; }
+</style>
+
diff --git a/src/pages/qualityManagement/rawMaterial/index.vue b/src/pages/qualityManagement/rawMaterial/index.vue
index 8f2d009..99abfeb 100644
--- a/src/pages/qualityManagement/rawMaterial/index.vue
+++ b/src/pages/qualityManagement/rawMaterial/index.vue
@@ -31,7 +31,11 @@
<view v-for="(item, index) in tableData" :key="index" class="list-item">
<view class="item-header">
<text class="product-name">{{ item.productName }}</text>
- <up-tag :text="item.inspectState ? '宸叉彁浜�' : '鏈彁浜�'" :type="item.inspectState ? 'success' : 'warning'" size="mini"></up-tag>
+ <up-tag
+ :text="item.inspectState == 1 ? '宸叉彁浜�' : '鏈彁浜�'"
+ :type="item.inspectState == 1 ? 'success' : 'warning'"
+ size="mini"
+ ></up-tag>
</view>
<view class="item-content">
<view class="item-row">
@@ -56,8 +60,9 @@
</view>
</view>
<view class="item-actions">
- <up-button v-if="!item.inspectState" type="primary" size="mini" @click.stop="openForm('edit', item)">缂栬緫</up-button>
- <up-button v-if="!item.inspectState" type="success" size="mini" @click.stop="handleConfirmSubmit(item)">鎻愪氦</up-button>
+ <up-button v-if="item.inspectState != 1" type="primary" size="mini" @click.stop="openForm('edit', item)">缂栬緫</up-button>
+ <up-button type="info" size="mini" @click.stop="openFiles(item)">闄勪欢</up-button>
+ <up-button v-if="item.inspectState != 1" type="success" size="mini" @click.stop="handleConfirmSubmit(item)">鎻愪氦</up-button>
<up-button type="error" size="mini" @click.stop="handleDelete(item)">鍒犻櫎</up-button>
</view>
</view>
@@ -96,7 +101,8 @@
</template>
<script setup>
-import { ref, reactive, onMounted, computed } from 'vue';
+import { ref, reactive, computed } from 'vue';
+import { onShow } from '@dcloudio/uni-app';
import {
findRawMaterialListPage,
submitRawMaterial,
@@ -195,9 +201,10 @@
};
const openForm = (type, item) => {
- // Mobile usually navigates to a new page for add/edit if complex
- // Here we'll just show a toast for now as the actual form components are many
- toast('鍔熻兘寮�鍙戜腑锛岃鍦≒C绔搷浣�');
+ const id = item?.id
+ uni.navigateTo({
+ url: `/pages/qualityManagement/rawMaterial/form?type=${type}${id ? `&id=${id}` : ''}`
+ })
};
const handleConfirmSubmit = (row) => {
@@ -214,7 +221,8 @@
const handleDelete = (row) => {
showConfirm('纭鍒犻櫎璇ヨ褰曞悧锛�').then(res => {
if (res.confirm) {
- deleteRawMaterial({ id: row.id }).then(() => {
+ // 瀵归綈 PC 绔細鍒犻櫎鎺ュ彛鎺ユ敹 id 鏁扮粍
+ deleteRawMaterial([row.id]).then(() => {
toast('鍒犻櫎鎴愬姛');
handleQuery();
});
@@ -222,11 +230,24 @@
});
};
+const openFiles = (row) => {
+ if (!row?.id) {
+ toast('璇峰厛淇濆瓨鍚庡啀涓婁紶闄勪欢')
+ return
+ }
+ try {
+ uni.setStorageSync('rawMaterialFilesCtx', JSON.stringify({ id: row.id }))
+ } catch (e) {}
+ uni.navigateTo({
+ url: `/pages/qualityManagement/rawMaterial/files?id=${row.id}`
+ })
+}
+
const goBack = () => {
uni.navigateBack();
};
-onMounted(() => {
+onShow(() => {
handleQuery();
});
</script>
@@ -309,10 +330,19 @@
.item-actions {
display: flex;
justify-content: flex-end;
- gap: 20rpx;
+ gap: 16rpx;
border-top: 1rpx solid #ebeef5;
padding-top: 20rpx;
}
+.item-actions :deep(.u-button),
+.item-actions :deep(.up-button) {
+ min-width: 140rpx;
+ height: 64rpx;
+ line-height: 64rpx;
+ font-size: 26rpx;
+ border-radius: 999rpx;
+ padding: 0 24rpx;
+}
.no-data {
padding-top: 200rpx;
--
Gitblit v1.9.3