云
昨天 346a988f537228ae44731cd31007f1412b482e83
src/views/mobile/quality.vue
@@ -2,7 +2,7 @@
import { computed, onMounted, ref } from 'vue';
import { onBeforeRouteLeave } from 'vue-router';
import { useUserStore } from '@vben/stores';
import { resolveSeedQuality, submitSeedQualityGroup, type SeedQualityScan } from '#/api/mes/qc/seedQuality';
import { getSeedIndicatorRanges, resolveSeedQuality, submitSeedQualityGroup, type SeedQualityScan } from '#/api/mes/qc/seedQuality';
import { getWarehouseSimpleList, type MesWmWarehouseApi } from '#/api/mes/wm/warehouse';
import { allowedMobilePath, extractCode } from '#/mobile/access';
import { errorText } from '#/mobile/api';
@@ -18,10 +18,10 @@
const pendingType = ref<ScanType>('BAG'), pendingIds = ref<number[]>([]);
const storageKey = 'mom-mobile-quality-' + useUserStore().userInfo?.id;
const indicators = ref([
  { code: 'purity', name: '纯度', value: '', minValue: '0', maxValue: '100' },
  { code: 'cleanliness', name: '净度', value: '', minValue: '0', maxValue: '100' },
  { code: 'germination_rate', name: '发芽率', value: '', minValue: '0', maxValue: '100' },
  { code: 'moisture', name: '水分', value: '', minValue: '0', maxValue: '100' },
  { code: 'purity', name: '纯度', value: '', minValue: '', maxValue: '' },
  { code: 'cleanliness', name: '净度', value: '', minValue: '', maxValue: '' },
  { code: 'germination_rate', name: '发芽率', value: '', minValue: '', maxValue: '' },
  { code: 'moisture', name: '水分', value: '', minValue: '', maxValue: '' },
]);
const complete = computed(() => indicators.value.every(i => [i.value, i.minValue, i.maxValue].every(v => String(v).trim() !== '' && Number.isFinite(Number(v))) && Number(i.minValue) >= 0 && Number(i.maxValue) <= 100 && Number(i.value) >= 0 && Number(i.value) <= 100 && Number(i.minValue) <= Number(i.maxValue)));
const passed = computed(() => complete.value && indicators.value.every(i => Number(i.value) >= Number(i.minValue) && Number(i.value) <= Number(i.maxValue)));
@@ -33,20 +33,24 @@
    const data = await resolveSeedQuality(extractCode(raw), scanType.value);
    if (data.canceled || !data.inbound) throw new Error('该对象已作废或有效袋码尚未全部入库,不能质检');
    if (!data.pendingBagIds?.length) throw new Error('该对象没有待质检袋码,请在质检记录中查看已有结果');
    bag.value = data; indicators.value.forEach(i => i.value = ''); reason.value = '';
    bag.value = data; indicators.value.forEach(i => { i.value = ''; i.minValue = ''; i.maxValue = ''; }); reason.value = '';
    try {
      const ranges = await getSeedIndicatorRanges();
      indicators.value.forEach(i => { const range = ranges.find(r => r.indicatorCode === i.code); if (range) { i.minValue = String(range.minValue); i.maxValue = String(range.maxValue); } });
    } catch { /* 模板不可用时仍允许手动填写本次范围 */ }
  } catch (e) { error.value = errorText(e); } finally { busy.value = false; }
}
async function submit() {
  if (busy.value || pendingCode.value || !bag.value || !complete.value || !warehouseId.value) return;
  if (!passed.value && !reason.value.trim()) { error.value = '请填写不合格原因'; return; }
  const count = bag.value.pendingBagIds!.length;
  if (!window.confirm(passed.value ? `确认对本次 ${count} 袋统一判定合格?全部将转入所选合格库。` : `确认对本次 ${count} 袋统一判定不合格?全部将从暂存库存移除并标记报废。`)) return;
  if (!window.confirm(passed.value ? `确认对本次 ${count} 袋统一提交初检合格?审核通过后进入复检。` : `确认对本次 ${count} 袋统一提交初检不合格?将进入不合格处理。`)) return;
  error.value = ''; notice.value = '';
  pendingCode.value = bag.value.code; pendingType.value = scanType.value; pendingIds.value = [...bag.value.pendingBagIds!];
  sessionStorage.setItem(storageKey, JSON.stringify({ code: pendingCode.value, scanType: pendingType.value, bagIds: pendingIds.value })); busy.value = true;
  try {
    const result = await submitSeedQualityGroup({ code: bag.value.code, scanType: scanType.value, bagIds: pendingIds.value, qualifiedWarehouseId: warehouseId.value, reason: reason.value.trim() || undefined, indicators: indicators.value.map(i => ({ code: i.code, value: Number(i.value), minValue: Number(i.minValue), maxValue: Number(i.maxValue) })) });
    clearPending(); notice.value = `本次 ${result.processedCount} 袋` + (passed.value ? '质检合格,已转入合格库。' : '质检不合格,已记录原因并移除暂存库存。');
    clearPending(); notice.value = `本次 ${result.processedCount} 袋` + (passed.value ? '初检合格,等待审核。' : '初检不合格,等待不合格处理。');
    bag.value = undefined; reason.value = ''; indicators.value.forEach(i => i.value = '');
  } catch (e) { error.value = '提交结果待确认:' + errorText(e) + '。请核实后再操作下一袋。'; } finally { busy.value = false; }
}
@@ -87,8 +91,8 @@
        <label class="m-field"><span>实测值</span><input v-model="i.value" type="number" inputmode="decimal" min="0" max="100" step="any" required placeholder="输入实测百分比" /></label>
        <div class="m-grid"><label class="m-field"><span>合格下限</span><input v-model="i.minValue" type="number" inputmode="decimal" min="0" max="100" step="any" required /></label><label class="m-field"><span>合格上限</span><input v-model="i.maxValue" type="number" inputmode="decimal" min="0" max="100" step="any" required /></label></div>
      </fieldset>
      <div class="m-panel"><p class="m-muted">上下限沿用现有质检规则录入方式,请按该品种检验标准核对。</p><Picker v-model="warehouseId" label="目标合格库" :options="warehouses" :disabled="busy" /><p v-if="!warehouses.length && !busy" class="m-warning">暂无可用合格库,请联系管理员配置。</p><label class="m-field"><span>{{complete && !passed ? '不合格原因(必填)' : '检验备注'}}</span><textarea v-model="reason" :disabled="busy" :required="complete && !passed" rows="3" maxlength="255" /></label></div>
      <div class="m-action-bar"><p v-if="complete" :class="passed ? 'm-success' : 'm-warning'">预判:{{passed ? '合格,提交后转入合格库' : '不合格,提交后移除暂存库存'}}。最终以服务端结果为准。</p><button class="m-primary" :disabled="busy || !complete || !warehouseId">{{busy ? '正在提交…' : '确认提交质检'}}</button></div>
      <div class="m-panel"><p class="m-muted">合格范围从模板带入,可在本次质检中修改;请按该品种标准核对。</p><Picker v-model="warehouseId" label="目标合格库" :options="warehouses" :disabled="busy" /><p v-if="!warehouses.length && !busy" class="m-warning">暂无可用合格库,请联系管理员配置。</p><label class="m-field"><span>{{complete && !passed ? '不合格原因(必填)' : '检验备注'}}</span><textarea v-model="reason" :disabled="busy" :required="complete && !passed" rows="3" maxlength="255" /></label></div>
      <div class="m-action-bar"><p v-if="complete" :class="passed ? 'm-success' : 'm-warning'">预判:{{passed ? '初检合格,等待初检审核' : '初检不合格,进入不合格处理'}}。最终以服务端结果为准。</p><button class="m-primary" :disabled="busy || !complete || !warehouseId">{{busy ? '正在提交…' : '确认提交质检'}}</button></div>
    </form>
  </template>
  <RouterLink v-if="allowedMobilePath('/mobile/quality-records')" to="/mobile/quality-records" class="m-link">查看质检记录</RouterLink>