|
|
|
|
@ -32,52 +32,96 @@
|
|
|
|
|
</el-form>
|
|
|
|
|
</ContentWrap>
|
|
|
|
|
|
|
|
|
|
<ContentWrap>
|
|
|
|
|
<el-tabs v-model="activeName" @tab-change="handleTabChange">
|
|
|
|
|
<el-tab-pane
|
|
|
|
|
v-for="dict in getIntDictOptions(DICT_TYPE.MATERIAL_CLASSIFICATION_TYPE)"
|
|
|
|
|
:key="dict.value"
|
|
|
|
|
:label="dict.label"
|
|
|
|
|
:name="String(dict.value)"
|
|
|
|
|
/>
|
|
|
|
|
</el-tabs>
|
|
|
|
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableBarCodeColumn')" align="center" prop="barCode"
|
|
|
|
|
sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableNameColumn')" align="left" prop="name"
|
|
|
|
|
width="220px" sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStandardColumn')" align="center"
|
|
|
|
|
prop="standard" sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCategoryColumn')" align="center"
|
|
|
|
|
prop="subCategoryName" sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableUnitColumn')" align="center" prop="unitName"
|
|
|
|
|
sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStatusColumn')" align="center" prop="status"
|
|
|
|
|
sortable>
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCreateTimeColumn')" align="center"
|
|
|
|
|
prop="createTime" :formatter="dateFormatter" width="180px" sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableOperateColumn')" align="center" width="150px">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['erp:product:update']">
|
|
|
|
|
{{ t('FactoryModeling.ProductInformation.tableEditAction') }}
|
|
|
|
|
<div class="product-page-layout">
|
|
|
|
|
<!-- 左侧分类树 -->
|
|
|
|
|
<ContentWrap class="product-page-left">
|
|
|
|
|
<div class="tree-header">
|
|
|
|
|
<span class="tree-title">{{ t('FactoryModeling.ProductInformation.categoryTree') }}</span>
|
|
|
|
|
<div class="tree-header-actions">
|
|
|
|
|
<el-button link type="primary" size="small" @click="fetchGroupTree">
|
|
|
|
|
<Icon icon="ep:refresh" /> {{ t('FactoryModeling.ProductInformation.refreshTree') }}
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['erp:product:delete']">
|
|
|
|
|
{{ t('FactoryModeling.ProductInformation.tableDeleteAction') }}
|
|
|
|
|
<el-button link type="primary" size="small" @click="openCategoryForm('create')" v-hasPermi="['erp:product-category:create']">
|
|
|
|
|
<Icon icon="ep:plus" /> {{ t('FactoryModeling.ProductInformation.addCategory') }}
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<el-tree
|
|
|
|
|
ref="treeRef"
|
|
|
|
|
:data="treeData"
|
|
|
|
|
:props="{ children: 'children', label: 'name' }"
|
|
|
|
|
node-key="nodeKey"
|
|
|
|
|
highlight-current
|
|
|
|
|
default-expand-all
|
|
|
|
|
@node-click="handleTreeNodeClick"
|
|
|
|
|
>
|
|
|
|
|
<template #default="{ data }">
|
|
|
|
|
<span class="custom-tree-node">
|
|
|
|
|
<span class="tree-node-label">{{ data.name }}</span>
|
|
|
|
|
<span class="tree-node-actions">
|
|
|
|
|
<el-button link type="primary" size="small" @click.stop="openCategoryForm('create', data.isType ? { type: data.type } : { type: data.type, parentId: data.id })"
|
|
|
|
|
v-hasPermi="['erp:product-category:create']">
|
|
|
|
|
<Icon icon="ep:plus" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<template v-if="!data.isType">
|
|
|
|
|
<el-button link type="primary" size="small" @click.stop="openCategoryForm('update', data)"
|
|
|
|
|
v-hasPermi="['erp:product-category:update']">
|
|
|
|
|
<Icon icon="ep:edit" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button link type="danger" size="small" @click.stop="handleCategoryDelete(data)"
|
|
|
|
|
v-hasPermi="['erp:product-category:delete']">
|
|
|
|
|
<Icon icon="ep:delete" />
|
|
|
|
|
</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</span>
|
|
|
|
|
</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
|
|
|
|
@pagination="getList" />
|
|
|
|
|
</ContentWrap>
|
|
|
|
|
</el-tree>
|
|
|
|
|
</ContentWrap>
|
|
|
|
|
|
|
|
|
|
<!-- 右侧产品表格 -->
|
|
|
|
|
<ContentWrap class="product-page-right">
|
|
|
|
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableBarCodeColumn')" align="center" prop="barCode"
|
|
|
|
|
sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableNameColumn')" align="left" prop="name"
|
|
|
|
|
width="220px" sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStandardColumn')" align="center"
|
|
|
|
|
prop="standard" sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCategoryColumn')" align="center"
|
|
|
|
|
prop="subCategoryName" sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableUnitColumn')" align="center" prop="unitName"
|
|
|
|
|
sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStatusColumn')" align="center" prop="status"
|
|
|
|
|
sortable>
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCreateTimeColumn')" align="center"
|
|
|
|
|
prop="createTime" :formatter="dateFormatter" width="180px" sortable />
|
|
|
|
|
<el-table-column :label="t('FactoryModeling.ProductInformation.tableOperateColumn')" align="center" width="150px">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['erp:product:update']">
|
|
|
|
|
{{ t('FactoryModeling.ProductInformation.tableEditAction') }}
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['erp:product:delete']">
|
|
|
|
|
{{ t('FactoryModeling.ProductInformation.tableDeleteAction') }}
|
|
|
|
|
</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
|
|
|
|
@pagination="getList" />
|
|
|
|
|
</ContentWrap>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<ProductForm v-else-if="formType === 'product'" ref="formRef" @success="getList" @closed="handleFormClosed" />
|
|
|
|
|
<BomForm v-else-if="formType === 'bom'" ref="bomFormRef" @success="getList" @closed="handleFormClosed" />
|
|
|
|
|
|
|
|
|
|
<!-- 分类表单弹窗 -->
|
|
|
|
|
<ProductCategoryForm ref="categoryFormRef" @success="handleCategorySuccess" />
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
@ -85,7 +129,9 @@
|
|
|
|
|
import { dateFormatter } from '@/utils/formatTime'
|
|
|
|
|
import download from '@/utils/download'
|
|
|
|
|
import { ProductApi, ProductVO } from '@/api/erp/product/product'
|
|
|
|
|
import { ProductCategoryApi } from '@/api/erp/product/category'
|
|
|
|
|
import ProductForm from './ProductForm.vue'
|
|
|
|
|
import ProductCategoryForm from '../category/ProductCategoryForm.vue'
|
|
|
|
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|
|
|
|
import BomForm from "@/views/mes/bom/BomForm.vue";
|
|
|
|
|
|
|
|
|
|
@ -111,6 +157,69 @@ const exportLoading = ref(false)
|
|
|
|
|
const formVisible = ref(false)
|
|
|
|
|
const formType = ref('')
|
|
|
|
|
|
|
|
|
|
/** 分类树数据 */
|
|
|
|
|
const groupTreeData = ref<any[]>([])
|
|
|
|
|
const treeRef = ref()
|
|
|
|
|
const typeDict = computed(() => getIntDictOptions(DICT_TYPE.MATERIAL_CLASSIFICATION_TYPE))
|
|
|
|
|
|
|
|
|
|
const treeData = computed(() => {
|
|
|
|
|
return (groupTreeData.value || []).map((group: any) => {
|
|
|
|
|
const dictItem = typeDict.value.find((d: any) => d.value === group.type)
|
|
|
|
|
const name = dictItem?.label || String(group.type)
|
|
|
|
|
return {
|
|
|
|
|
nodeKey: `type_${group.type}`,
|
|
|
|
|
name,
|
|
|
|
|
type: group.type,
|
|
|
|
|
isType: true,
|
|
|
|
|
children: (group.children || []).map((child: any) => ({
|
|
|
|
|
...child,
|
|
|
|
|
nodeKey: `cat_${child.id}`,
|
|
|
|
|
isType: false
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const fetchGroupTree = async () => {
|
|
|
|
|
const res = await ProductCategoryApi.getProductCategoryGroupTree()
|
|
|
|
|
groupTreeData.value = res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 树节点点击 - 筛选产品 */
|
|
|
|
|
const handleTreeNodeClick = (data: any) => {
|
|
|
|
|
if (data.isType) {
|
|
|
|
|
queryParams.categoryType = data.type
|
|
|
|
|
queryParams.categoryId = undefined
|
|
|
|
|
} else {
|
|
|
|
|
queryParams.categoryType = data.type
|
|
|
|
|
queryParams.categoryId = data.id
|
|
|
|
|
}
|
|
|
|
|
handleQuery()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 分类增删改 */
|
|
|
|
|
const categoryFormRef = ref()
|
|
|
|
|
const openCategoryForm = (type: string, data?: any) => {
|
|
|
|
|
const treeData = type === 'update' ? data : undefined
|
|
|
|
|
const defaultData = data && !treeData ? { ...data } : undefined
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
categoryFormRef.value.open(type, treeData?.id, defaultData)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleCategoryDelete = async (data: any) => {
|
|
|
|
|
try {
|
|
|
|
|
await message.delConfirm()
|
|
|
|
|
await ProductCategoryApi.deleteProductCategory(data.id)
|
|
|
|
|
message.success(t('common.delSuccess'))
|
|
|
|
|
await fetchGroupTree()
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleCategorySuccess = () => {
|
|
|
|
|
fetchGroupTree()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getList = async () => {
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
|
|
|
|
@ -163,20 +272,14 @@ const handleExport = async () => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
await fetchGroupTree()
|
|
|
|
|
const typeOptions = getIntDictOptions(DICT_TYPE.MATERIAL_CLASSIFICATION_TYPE)
|
|
|
|
|
if (typeOptions.length > 0) {
|
|
|
|
|
activeName = String(typeOptions[0].value)
|
|
|
|
|
queryParams.categoryType = typeOptions[0].value
|
|
|
|
|
}
|
|
|
|
|
await getList()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
let activeName = ''
|
|
|
|
|
const handleTabChange = (name: string) => {
|
|
|
|
|
queryParams.categoryType = name ? Number(name) : undefined
|
|
|
|
|
handleQuery()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bomFormRef = ref()
|
|
|
|
|
const openBomForm = (type: string, id?: number) => {
|
|
|
|
|
formVisible.value = true
|
|
|
|
|
@ -191,3 +294,66 @@ const handleFormClosed = () => {
|
|
|
|
|
formType.value = ''
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.product-page-layout {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-page-left {
|
|
|
|
|
width: 260px;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-page-right {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
|
|
|
|
.tree-title {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--el-text-color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-header-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-tree-node {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
flex: 1;
|
|
|
|
|
padding-right: 8px;
|
|
|
|
|
|
|
|
|
|
.tree-node-label {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-node-actions {
|
|
|
|
|
display: none;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
margin-left: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:hover .tree-node-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|