对库存管理视图进行重构,以支持动态产品类别和型号,并提升库存检索功能
已修改4个文件
380 ■■■■ 文件已修改
src/views/inventoryManagement/stockManagement/New.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Qualified.vue 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Unqualified.vue 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/index.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/New.vue
@@ -7,17 +7,7 @@
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item
            label="产品名称"
            prop="productModelId"
            :rules="[
                {
                required: true,
                message: '请选择产品',
                trigger: 'change',
              }
            ]"
        >
        <el-form-item label="产品名称" prop="productName">
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ formState.productName ? formState.productName : '选择产品' }}
          </el-button>
@@ -38,21 +28,40 @@
        </el-form-item>
        <el-form-item
            label="厚度(mm)"
            prop="thickness"
            label="库存类型"
            prop="inventoryType"
            :rules="[
              {
                required: true,
                message: '请选择库存类型',
                trigger: 'change',
              }
            ]"
        >
          <el-input v-model="formState.thickness" disabled />
          <el-select
            v-model="formState.inventoryType"
            placeholder="请选择库存类型"
            clearable
            filterable
            style="width: 100%"
          >
            <el-option
              label="合格库存"
              value="qualified"
            />
            <el-option
              label="不合格库存"
              value="unqualified"
            />
          </el-select>
        </el-form-item>
        <el-form-item
            label="库存数量"
            prop="qualitity"
        >
        <el-form-item label="库存数量" prop="qualitity">
          <el-input-number v-model="formState.qualitity" :step="1" :min="1" style="width: 100%" />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified'"
            v-if="formState.inventoryType === 'qualified'"
            label="库存预警数量"
            prop="warnNum"
        >
@@ -81,7 +90,7 @@
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import { ref, computed, getCurrentInstance, watch } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {createStockInventory} from "@/api/inventoryManagement/stockInventory.js";
import {createStockUnInventory} from "@/api/inventoryManagement/stockUninventory.js";
@@ -97,6 +106,10 @@
    required: true,
    default: 'qualified',
  },
  parentId: {
    type: [Number, String],
    default: undefined,
  },
});
const emit = defineEmits(['update:visible', 'completed']);
@@ -105,10 +118,10 @@
const formState = ref({
  productId: undefined,
  productModelId: undefined,
  inventoryType: "qualified",
  productName: "",
  productModelName: "",
  unit: "",
  thickness: "",
  qualitity: 0,
  warnNum: 0,
  remark: '',
@@ -132,10 +145,10 @@
  formState.value = {
    productId: undefined,
    productModelId: undefined,
    inventoryType: props.type || "qualified",
    productName: "",
    productModelName: "",
    unit: "",
    thickness: "",
    qualitity: 0,
    warnNum: 0,
    remark: '',
@@ -152,7 +165,6 @@
    formState.value.productModelName = product.model;
    formState.value.productModelId = product.id;
    formState.value.unit = product.unit;
    formState.value.thickness = product.thickness ?? "";
    showProductSelectDialog.value = false;
    // 触发表单验证更新
    proxy.$refs["formRef"]?.validateField('productModelId');
@@ -171,7 +183,7 @@
        proxy.$modal.msgError("请选择规格");
        return;
      }
      if (props.type === 'qualified') {
      if (formState.value.inventoryType === 'qualified') {
        createStockInventory(formState.value).then(res => {
          // 关闭模态框
          isShow.value = false;
@@ -199,4 +211,14 @@
  handleSubmit,
  isShow,
});
watch(
  () => props.visible,
  (visible) => {
    if (visible) {
      formState.value.inventoryType = props.type || "qualified";
    }
  },
  { immediate: true }
);
</script>
src/views/inventoryManagement/stockManagement/Qualified.vue
@@ -3,10 +3,31 @@
    <div class="search_form">
      <div>
        <span class="search_title ml10">产品大类:</span>
        <el-input v-model="searchForm.productName"
        <el-tree-select
          v-model="searchForm.parentId"
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/>
          placeholder="请选择"
          clearable
          filterable
          check-strictly
          :data="productOptions"
          :render-after-expand="false"
          @change="handleParentChange"
        />
        <span class="search_title ml10">规格型号:</span>
        <el-select
          v-model="searchForm.productModelId"
          style="width: 240px"
          placeholder="请选择"
          clearable
          filterable
          @change="handleModelChange"
        >
          <el-option v-for="item in modelOptions"
                     :key="item.id"
                     :label="item.model"
                     :value="item.id" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
@@ -25,10 +46,19 @@
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="产品大类" prop="productName" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="model" show-overflow-tooltip />
        <el-table-column label="厚度(mm)" prop="thickness" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip />
        <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip />
        <el-table-column label="合格库存数量" prop="qualifiedQuantity" show-overflow-tooltip>
          <template #default="scope">{{ scope.row.qualifiedQuantity ?? scope.row.qualitity ?? scope.row.unLockedQuantity ?? 0 }}</template>
        </el-table-column>
        <el-table-column label="不合格库存数量" prop="unqualifiedQuantity" show-overflow-tooltip>
          <template #default="scope">{{ scope.row.unqualifiedQuantity ?? 0 }}</template>
        </el-table-column>
        <el-table-column label="合格冻结数量" prop="qualifiedLockedQuantity" show-overflow-tooltip>
          <template #default="scope">{{ scope.row.qualifiedLockedQuantity ?? scope.row.lockedQuantity ?? 0 }}</template>
        </el-table-column>
        <el-table-column label="不合格冻结数量" prop="unqualifiedLockedQuantity" show-overflow-tooltip>
          <template #default="scope">{{ scope.row.unqualifiedLockedQuantity ?? 0 }}</template>
        </el-table-column>
        <el-table-column label="库存预警数量" prop="warnNum"  show-overflow-tooltip />
        <el-table-column label="备注" prop="remark"  show-overflow-tooltip />
        <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip />
@@ -46,6 +76,7 @@
    <new-stock-inventory v-if="isShowNewModal"
                 v-model:visible="isShowNewModal"
                 type="qualified"
                 :parent-id="props.parentId"
                 @completed="handleQuery" />
    <subtract-stock-inventory v-if="isShowSubtractModal"
@@ -70,15 +101,24 @@
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance, watch } from 'vue'
import {ElMessage, ElMessageBox} from "element-plus";
import { getStockInventoryListPage } from "@/api/inventoryManagement/stockInventory.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue"));
const SubtractStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Subtract.vue"));
const ImportStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Import.vue"));
const FrozenAndThawStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/FrozenAndThaw.vue"));
const props = defineProps({
  parentId: {
    type: [Number, String],
    default: undefined,
  },
})
const { proxy } = getCurrentInstance()
const tableData = ref([])
const productOptions = ref([])
const modelOptions = ref([])
const selectedRows = ref([])
const record = ref({})
const tableLoading = ref(false)
@@ -100,6 +140,9 @@
const data = reactive({
  searchForm: {
    productName: '',
    parentId: undefined,
    productModelId: undefined,
    model: '',
  }
})
const { searchForm } = toRefs(data)
@@ -117,7 +160,12 @@
}
const getList = () => {
  tableLoading.value = true
  getStockInventoryListPage({ ...searchForm.value, ...page }).then(res => {
  const queryParentId = searchForm.value.parentId || props.parentId
  getStockInventoryListPage({
    ...searchForm.value,
    ...page,
    parentId: queryParentId,
  }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
@@ -126,6 +174,61 @@
  }).catch(() => {
    tableLoading.value = false
  })
}
const convertIdToValue = (data) => {
  return (data || []).map((item) => {
    const children = Array.isArray(item.children) ? item.children : []
    return {
      ...item,
      value: item.id,
      label: item.label || item.productName || '',
      children: convertIdToValue(children),
    }
  })
}
const findNodeById = (nodes, id) => {
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i]
    if (node.value === id) return node
    if (node.children?.length) {
      const found = findNodeById(node.children, id)
      if (found) return found
    }
  }
  return null
}
const loadProductOptions = async () => {
  try {
    const res = await productTreeList()
    productOptions.value = convertIdToValue(res || [])
  } catch (e) {
    productOptions.value = []
  }
}
const loadModelOptions = async (parentId) => {
  if (!parentId) {
    modelOptions.value = []
    return
  }
  const res = await modelList({ id: parentId })
  modelOptions.value = Array.isArray(res) ? res : []
}
const handleParentChange = async (value) => {
  searchForm.value.productModelId = undefined
  searchForm.value.model = ''
  const selected = findNodeById(productOptions.value, value)
  searchForm.value.productName = selected?.label || ''
  await loadModelOptions(value || props.parentId)
}
const handleModelChange = (value) => {
  const selected = modelOptions.value.find((item) => item.id === value)
  searchForm.value.model = selected?.model || ''
}
const handleFileSuccess = (response) => {
@@ -194,8 +297,21 @@
}
onMounted(() => {
  loadProductOptions()
  getList()
})
watch(
  () => props.parentId,
  async () => {
    searchForm.value.parentId = props.parentId
    searchForm.value.productModelId = undefined
    searchForm.value.model = ''
    await loadModelOptions(props.parentId)
    handleQuery()
  },
  { immediate: true }
)
</script>
<style scoped lang="scss">
src/views/inventoryManagement/stockManagement/Unqualified.vue
@@ -3,10 +3,31 @@
    <div class="search_form">
      <div>
        <span class="search_title ml10">产品大类:</span>
        <el-input v-model="searchForm.productName"
        <el-tree-select
          v-model="searchForm.parentId"
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/>
          placeholder="请选择"
          clearable
          filterable
          check-strictly
          :data="productOptions"
          :render-after-expand="false"
          @change="handleParentChange"
        />
        <span class="search_title ml10">规格型号:</span>
        <el-select
          v-model="searchForm.productModelId"
          style="width: 240px"
          placeholder="请选择"
          clearable
          filterable
          @change="handleModelChange"
        >
          <el-option v-for="item in modelOptions"
                     :key="item.id"
                     :label="item.model"
                     :value="item.id" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
@@ -22,10 +43,20 @@
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="产品大类" prop="productName" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="model" show-overflow-tooltip />
        <el-table-column label="厚度(mm)" prop="thickness" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip />
        <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip />
        <el-table-column label="合格库存数量" prop="qualifiedQuantity" show-overflow-tooltip>
          <template #default="scope">{{ scope.row.qualifiedQuantity ?? 0 }}</template>
        </el-table-column>
        <el-table-column label="不合格库存数量" prop="unqualifiedQuantity" show-overflow-tooltip>
          <template #default="scope">{{ scope.row.unqualifiedQuantity ?? scope.row.qualitity ?? scope.row.unLockedQuantity ?? 0 }}</template>
        </el-table-column>
        <el-table-column label="合格冻结数量" prop="qualifiedLockedQuantity" show-overflow-tooltip>
          <template #default="scope">{{ scope.row.qualifiedLockedQuantity ?? 0 }}</template>
        </el-table-column>
        <el-table-column label="不合格冻结数量" prop="unqualifiedLockedQuantity" show-overflow-tooltip>
          <template #default="scope">{{ scope.row.unqualifiedLockedQuantity ?? scope.row.lockedQuantity ?? 0 }}</template>
        </el-table-column>
        <el-table-column label="库存预警数量" prop="warnNum" show-overflow-tooltip />
        <el-table-column label="备注" prop="remark"  show-overflow-tooltip />
        <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="90" align="center">
@@ -42,6 +73,7 @@
    <new-stock-inventory v-if="isShowNewModal"
                 v-model:visible="isShowNewModal"
                 type="unqualified"
                 :parent-id="props.parentId"
                 @completed="handleQuery" />
    <subtract-stock-inventory v-if="isShowSubtractModal"
@@ -61,15 +93,24 @@
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance, watch } from 'vue'
import { ElMessageBox } from "element-plus";
import { getStockUninventoryListPage } from "@/api/inventoryManagement/stockUninventory.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue"));
const SubtractStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Subtract.vue"));
const FrozenAndThawStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/FrozenAndThaw.vue"));
const props = defineProps({
  parentId: {
    type: [Number, String],
    default: undefined,
  },
})
const { proxy } = getCurrentInstance()
const tableData = ref([])
const productOptions = ref([])
const modelOptions = ref([])
const selectedRows = ref([])
const record = ref({})
const tableLoading = ref(false)
@@ -89,6 +130,9 @@
const data = reactive({
  searchForm: {
    productName: '',
    parentId: undefined,
    productModelId: undefined,
    model: '',
  }
})
const { searchForm } = toRefs(data)
@@ -106,7 +150,12 @@
}
const getList = () => {
  tableLoading.value = true
  getStockUninventoryListPage({ ...searchForm.value, ...page }).then(res => {
  const queryParentId = searchForm.value.parentId || props.parentId
  getStockUninventoryListPage({
    ...searchForm.value,
    ...page,
    parentId: queryParentId,
  }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
@@ -115,6 +164,61 @@
  }).catch(() => {
    tableLoading.value = false
  })
}
const convertIdToValue = (data) => {
  return (data || []).map((item) => {
    const children = Array.isArray(item.children) ? item.children : []
    return {
      ...item,
      value: item.id,
      label: item.label || item.productName || '',
      children: convertIdToValue(children),
    }
  })
}
const findNodeById = (nodes, id) => {
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i]
    if (node.value === id) return node
    if (node.children?.length) {
      const found = findNodeById(node.children, id)
      if (found) return found
    }
  }
  return null
}
const loadProductOptions = async () => {
  try {
    const res = await productTreeList()
    productOptions.value = convertIdToValue(res || [])
  } catch (e) {
    productOptions.value = []
  }
}
const loadModelOptions = async (parentId) => {
  if (!parentId) {
    modelOptions.value = []
    return
  }
  const res = await modelList({ id: parentId })
  modelOptions.value = Array.isArray(res) ? res : []
}
const handleParentChange = async (value) => {
  searchForm.value.productModelId = undefined
  searchForm.value.model = ''
  const selected = findNodeById(productOptions.value, value)
  searchForm.value.productName = selected?.label || ''
  await loadModelOptions(value || props.parentId)
}
const handleModelChange = (value) => {
  const selected = modelOptions.value.find((item) => item.id === value)
  searchForm.value.model = selected?.model || ''
}
// 点击领用
@@ -172,8 +276,21 @@
}
onMounted(() => {
  loadProductOptions()
  getList()
})
watch(
  () => props.parentId,
  async () => {
    searchForm.value.parentId = props.parentId
    searchForm.value.productModelId = undefined
    searchForm.value.model = ''
    await loadModelOptions(props.parentId)
    handleQuery()
  },
  { immediate: true }
)
</script>
<style scoped lang="scss">
src/views/inventoryManagement/stockManagement/index.vue
@@ -4,30 +4,45 @@
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <component :is="tab.name === 'qualified' ? QualifiedRecord : UnqualifiedRecord" />
      </el-tab-pane>
                   :key="tab.name" />
    </el-tabs>
    <qualified-record
      v-if="currentParentId !== undefined"
      :key="activeTab"
      :parent-id="currentParentId"
    />
  </div>
</template>
<script setup>
import { getProductParentNames } from "@/api/basicData/productModel.js";
import QualifiedRecord from "@/views/inventoryManagement/stockManagement/Qualified.vue";
import UnqualifiedRecord from "@/views/inventoryManagement/stockManagement/Unqualified.vue";
const activeTab = ref('qualified')
const tabs = ref([
  {
    label: '合格库存',
    name: 'qualified'
  },
  {
    label: '不合格库存',
    name: 'unqualified'
  }
])
const activeTab = ref('')
const tabs = ref([])
const currentParentId = computed(() => {
  const current = tabs.value.find((item) => item.name === activeTab.value)
  return current?.parentId
})
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
}
const getTabs = async () => {
  const res = await getProductParentNames()
  const list = Array.isArray(res?.data) ? res.data : []
  tabs.value = list.map((item) => ({
    label: item.productName,
    name: String(item.id),
    parentId: item.id,
  }))
  if (tabs.value.length && !activeTab.value) {
    activeTab.value = tabs.value[0].name
  }
}
onMounted(() => {
  getTabs()
})
</script>