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/qualityManagement/nonconformingManagement/index.vue | 457 +++++++++++++++++++++++++++++++++++---------------------
1 files changed, 285 insertions(+), 172 deletions(-)
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>
--
Gitblit v1.9.3