删除根目录的node_modules

This commit is contained in:
砂糖
2025-08-28 13:07:28 +08:00
parent 69e967325b
commit 93b5100f9b
21 changed files with 1540 additions and 5 deletions

View File

@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询BOM 头,关联产品或原材料列表
export function listBom(query) {
return request({
url: '/klp/bom/list',
method: 'get',
params: query
})
}
// 查询BOM 头,关联产品或原材料详细
export function getBom(bomId) {
return request({
url: '/klp/bom/' + bomId,
method: 'get'
})
}
// 新增BOM 头,关联产品或原材料
export function addBom(data) {
return request({
url: '/klp/bom',
method: 'post',
data: data
})
}
// 修改BOM 头,关联产品或原材料
export function updateBom(data) {
return request({
url: '/klp/bom',
method: 'put',
data: data
})
}
// 删除BOM 头,关联产品或原材料
export function delBom(bomId) {
return request({
url: '/klp/bom/' + bomId,
method: 'delete'
})
}

View File

@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询BOM 明细,存放属性–值列表
export function listBomItem(query) {
return request({
url: '/wms/bomItem/list',
method: 'get',
params: query
})
}
// 查询BOM 明细,存放属性–值详细
export function getBomItem(itemId) {
return request({
url: '/wms/bomItem/' + itemId,
method: 'get'
})
}
// 新增BOM 明细,存放属性–值
export function addBomItem(data) {
return request({
url: '/wms/bomItem',
method: 'post',
data: data
})
}
// 修改BOM 明细,存放属性–值
export function updateBomItem(data) {
return request({
url: '/wms/bomItem',
method: 'put',
data: data
})
}
// 删除BOM 明细,存放属性–值
export function delBomItem(itemId) {
return request({
url: '/wms/bomItem/' + itemId,
method: 'delete'
})
}

View File

@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询产品列表
export function listProduct(query) {
return request({
url: '/wms/product/list',
method: 'get',
params: query
})
}
// 查询产品详细
export function getProduct(productId) {
return request({
url: '/wms/product/' + productId,
method: 'get'
})
}
// 新增产品
export function addProduct(data) {
return request({
url: '/wms/product',
method: 'post',
data: data
})
}
// 修改产品
export function updateProduct(data) {
return request({
url: '/wms/product',
method: 'put',
data: data
})
}
// 删除产品
export function delProduct(productId) {
return request({
url: '/wms/product/' + productId,
method: 'delete'
})
}

View File

@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询产品分类树列表
export function listProductCategory(query) {
return request({
url: '/oa/productCategory/list',
method: 'get',
params: query
})
}
// 查询产品分类树详细
export function getProductCategory(categoryId) {
return request({
url: '/oa/productCategory/' + categoryId,
method: 'get'
})
}
// 新增产品分类树
export function addProductCategory(data) {
return request({
url: '/oa/productCategory',
method: 'post',
data: data
})
}
// 修改产品分类树
export function updateProductCategory(data) {
return request({
url: '/oa/productCategory',
method: 'put',
data: data
})
}
// 删除产品分类树
export function delProductCategory(categoryId) {
return request({
url: '/oa/productCategory/' + categoryId,
method: 'delete'
})
}

View File

@@ -0,0 +1,83 @@
<template>
<el-descriptions :column="1" border v-if="bomInfo.length > 0">
<el-descriptions-item v-for="item in bomInfo" :key="item.attrKey" :label="item.attrKey">
{{ item.attrValue }}
</el-descriptions-item>
</el-descriptions>
<div v-else>
<el-empty description="暂无BOM信息" />
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import useProductStore from '@/store/modules/product';
import { ElEmpty } from 'element-plus';
// 定义组件props
const props = defineProps({
bomId: {
type: [String, Number],
required: false
},
itemType: {
type: String,
required: false
},
itemId: {
type: [String, Number],
required: false
}
});
// 状态管理
const productStore = useProductStore();
const bomInfo = ref([]);
// 获取BOM信息的方法
const getBomInfo = async () => {
const bomMap = productStore.bomMap;
if (!props.bomId && !props.itemType && !props.itemId) {
return;
}
let bomId = props.bomId;
if (!bomId) {
// 这里假设productMap是productStore中的属性
bomId = productStore.productMap?.[props.itemId]?.bomId;
}
if (!bomId) {
bomInfo.value = [];
return;
}
const info = await productStore.getBomInfo(bomId);
bomInfo.value = info;
};
// 监听属性变化
watch(
() => props.bomId,
() => getBomInfo(),
{ immediate: true }
);
watch(
() => props.itemId,
() => getBomInfo(),
{ immediate: true }
);
</script>
<style scoped>
.bom-info {
cursor: pointer;
/* 溢出隐藏显示省略号 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100px;
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<div v-if="bomInfo.length > 0">
<el-tooltip :content="bomInfo.map(item => item.attrKey + ':' + item.attrValue).join(',')" class="bom-info"
placement="top">
<span>{{bomInfo.map(item => item.attrKey).join(',')}}</span>
</el-tooltip>
</div>
<div v-else>
<el-empty description="暂无BOM信息" />
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import useProductStore from '@/store/modules/product';
import { ElEmpty } from 'element-plus';
// 定义组件props
const props = defineProps({
bomId: {
type: [String, Number],
required: false
},
itemType: {
type: String,
required: false
},
itemId: {
type: [String, Number],
required: false
}
});
// 状态管理
const productStore = useProductStore();
const bomInfo = ref([]);
// 获取BOM信息的方法
const getBomInfo = async () => {
const bomMap = productStore.bomMap;
if (!props.bomId && !props.itemType && !props.itemId) {
return;
}
let bomId = props.bomId;
if (!bomId) {
// 这里假设productMap是productStore中的属性
bomId = productStore.productMap?.[props.itemId]?.bomId;
}
if (!bomId) {
bomInfo.value = [];
return;
}
const info = await productStore.getBomInfo(bomId);
bomInfo.value = info;
};
// 监听属性变化
watch(
() => props.bomId,
() => getBomInfo(),
{ immediate: true }
);
watch(
() => props.itemId,
() => getBomInfo(),
{ immediate: true }
);
</script>
<style scoped>
.bom-info {
cursor: pointer;
/* 溢出隐藏显示省略号 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100px;
}
</style>

View File

@@ -0,0 +1,88 @@
<template>
<div>
<span class="product-name" @click="clickHandle">
<slot name="default" :product="product">
{{ product && product.productName ? product.productName : '--' }}
</slot>
</span>
<el-dialog
:visible="showDetail"
@close="showDetail = false"
:title="product && product.productName ? product.productName : '--' "
width="500px"
append-to-body
>
<el-descriptions :column="1" border>
<el-descriptions-item label="产品ID">
{{ product.productId || '--' }}
</el-descriptions-item>
<el-descriptions-item label="产品名称">
{{ product.productName || '--' }}
</el-descriptions-item>
<el-descriptions-item label="产品编码">
{{ product.productCode || '--' }}
</el-descriptions-item>
</el-descriptions>
<BomInfo :bomId="product.bomId" />
</el-dialog>
</div>
</template>
<script>
import { mapState } from 'vuex';
import BomInfo from './BomInfo.vue';
export default {
name: 'ProductInfo',
components: {
BomInfo
},
props: {
productId: {
type: [String, Number],
required: true
},
},
data() {
return {
showDetail: false,
product: {},
};
},
computed: {
...mapState({
productMap: state => state.category.productMap
}),
},
methods: {
clickHandle() {
this.showDetail = true;
}
},
watch: {
productId: {
handler(newVal) {
if (!newVal) {
this.product = {};
}
const res = this.productMap[this.productId] ? this.productMap[this.productId] : {};
this.product = res;
},
immediate: true
}
}
};
</script>
<style scoped>
.product-name {
color: #1890ff;
cursor: pointer;
text-decoration: underline;
}
/* 可选:调整描述列表的外边距 */
:deep(.el-descriptions) {
margin-top: -10px;
}
</style>

View File

@@ -0,0 +1,41 @@
import { listProduct } from '@/api/oa/product';
import { listBomItem } from '@/api/oa/bomItem';
const useProductStore = defineStore('product', {
state: () => ({
productMap: {},
bomMap: {},
}),
actions: {
setProductMap(productMap) {
this.productMap = productMap;
},
setBomMap(bomMap) {
this.bomMap = bomMap;
},
async getBomInfo(bomId) {
try {
if (!this.bomMap[bomId]) {
const res = await listBomItem({ bomId });
this.bomMap[bomId] = res.rows;
}
return this.bomMap[bomId];
} catch (error) {
console.error(error);
return [];
}
},
fetchProductMap() {
listProduct().then(res => {
const map = {};
res.data.forEach(item => {
map[item.productId] = item;
});
this.productMap = map;
})
},
}
})
export default useProductStore;

View File

@@ -88,7 +88,7 @@
@click="handleExport"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar :v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="reportDetailList" @selection-change="handleSelectionChange">

View File

@@ -70,7 +70,7 @@
@click="handleExport"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar :v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="reportSummaryList" @selection-change="handleSelectionChange">

View File

@@ -110,7 +110,7 @@
>导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar :v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="expressList" @selection-change="handleSelectionChange">

View File

@@ -78,7 +78,7 @@
@click="handleExport"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar :v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="expressQuestionList" @selection-change="handleSelectionChange">

View File

@@ -82,7 +82,7 @@
>导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar :v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="projectReportList" @selection-change="handleSelectionChange">

View File

@@ -0,0 +1,265 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="节点名称" prop="categoryName">
<el-input
v-model="queryParams.categoryName"
placeholder="请输入节点名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="节点类型" prop="categoryType">
<el-input
v-model="queryParams.categoryType"
placeholder="请输入节点类型"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="Sort"
@click="toggleExpandAll"
>展开/折叠</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-if="refreshTable"
v-loading="loading"
:data="productCategoryList"
row-key="categoryId"
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>
<el-table-column label="父节点tree_id" prop="parentId" />
<el-table-column label="节点名称" align="center" prop="categoryName" />
<el-table-column label="节点类型" align="center" prop="categoryType" />
<el-table-column label="同级排序号" align="center" prop="sortNo" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)">新增</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改产品分类树对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="productCategoryRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="父节点tree_id" prop="parentId">
<el-tree-select
v-model="form.parentId"
:data="productCategoryOptions"
:props="{ value: 'categoryId', label: 'categoryName', children: 'children' }"
value-key="categoryId"
placeholder="请选择父节点tree_id"
check-strictly
/>
</el-form-item>
<el-form-item label="节点名称" prop="categoryName">
<el-input v-model="form.categoryName" placeholder="请输入节点名称" />
</el-form-item>
<el-form-item label="节点类型" prop="categoryType">
<el-input v-model="form.categoryType" placeholder="请输入节点类型" />
</el-form-item>
<el-form-item label="同级排序号" prop="sortNo">
<el-input v-model="form.sortNo" placeholder="请输入同级排序号" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="ProductCategory">
import { listProductCategory, getProductCategory, delProductCategory, addProductCategory, updateProductCategory } from "@/api/oa/productCategory";
const { proxy } = getCurrentInstance();
const productCategoryList = ref([]);
const productCategoryOptions = ref([]);
const open = ref(false);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const title = ref("");
const isExpandAll = ref(true);
const refreshTable = ref(true);
const data = reactive({
form: {},
queryParams: {
categoryName: undefined,
categoryType: undefined,
},
rules: {
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询产品分类树列表 */
function getList() {
loading.value = true;
listProductCategory(queryParams.value).then(response => {
productCategoryList.value = proxy.handleTree(response.data, "categoryId", "parentId");
loading.value = false;
});
}
/** 查询产品分类树下拉树结构 */
function getTreeselect() {
listProductCategory().then(response => {
productCategoryOptions.value = [];
const data = { categoryId: 0, categoryName: '顶级节点', children: [] };
data.children = proxy.handleTree(response.data, "categoryId", "parentId");
productCategoryOptions.value.push(data);
});
}
// 取消按钮
function cancel() {
open.value = false;
reset();
}
// 表单重置
function reset() {
form.value = {
categoryId: null,
parentId: null,
categoryName: null,
categoryType: null,
sortNo: null,
delFlag: null,
createTime: null,
createBy: null,
updateTime: null,
updateBy: null,
remark: null
};
proxy.resetForm("productCategoryRef");
}
/** 搜索按钮操作 */
function handleQuery() {
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
handleQuery();
}
/** 新增按钮操作 */
function handleAdd(row) {
reset();
getTreeselect();
if (row != null && row.categoryId) {
form.value.parentId = row.categoryId;
} else {
form.value.parentId = 0;
}
open.value = true;
title.value = "添加产品分类树";
}
/** 展开/折叠操作 */
function toggleExpandAll() {
refreshTable.value = false;
isExpandAll.value = !isExpandAll.value;
nextTick(() => {
refreshTable.value = true;
});
}
/** 修改按钮操作 */
async function handleUpdate(row) {
loading.value = true;
reset();
await getTreeselect();
if (row != null) {
form.value.parentId = row.categoryId;
}
getProductCategory(row.categoryId).then(response => {
loading.value = false;
form.value = response.data;
open.value = true;
title.value = "修改产品分类树";
});
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["productCategoryRef"].validate(valid => {
if (valid) {
buttonLoading.value = true;
if (form.value.categoryId != null) {
updateProductCategory(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
}).finally(() => {
buttonLoading.value = false;
});
} else {
addProductCategory(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
}).finally(() => {
buttonLoading.value = false;
});
}
}
});
}
/** 删除按钮操作 */
function handleDelete(row) {
proxy.$modal.confirm('是否确认删除产品分类树编号为"' + row.categoryId + '"的数据项?').then(function() {
loading.value = true;
return delProductCategory(row.categoryId);
}).then(() => {
loading.value = false;
getList();
proxy.$modal.msgSuccess("删除成功");
}).catch(() => {
}).finally(() => {
loading.value = false;
});
}
getList();
</script>

View File

@@ -0,0 +1,254 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
>导出</el-button>
</el-col>
<right-toolbar :v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="bomItemList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="属性名称" align="center" prop="attrKey" />
<el-table-column label="属性值" align="center" prop="attrValue" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改BOM 明细存放属性值对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="属性名称" prop="attrKey">
<el-input v-model="form.attrKey" placeholder="请输入属性名称" />
</el-form-item>
<el-form-item label="属性值" prop="attrValue">
<el-input v-model="form.attrValue" placeholder="请输入属性值" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listBomItem, getBomItem, delBomItem, addBomItem, updateBomItem } from "@/api/oa/bomItem";
export default {
name: "BomItem",
props: {
bomId: [String, Number],
},
data() {
return {
// 按钮loading
buttonLoading: false,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// BOM 明细,存放属性–值表格数据
bomItemList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
bomId: this.bomId,
attrKey: undefined,
attrValue: undefined,
isEnabled: undefined,
},
// 表单参数
form: {
bomId: this.bomId,
},
};
},
created() {
this.getList();
},
methods: {
/** 查询BOM 明细,存放属性–值列表 */
getList() {
this.loading = true;
listBomItem(this.queryParams).then(response => {
this.bomItemList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
itemId: undefined,
bomId: this.bomId,
attrKey: undefined,
attrValue: undefined,
remark: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.itemId)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加BOM 明细,存放属性–值";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.loading = true;
this.reset();
const itemId = row.itemId || this.ids
getBomItem(itemId).then(response => {
this.loading = false;
this.form = response.data;
this.open = true;
this.title = "修改BOM 明细,存放属性–值";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.itemId != null) {
updateBomItem(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
} else {
addBomItem(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const itemIds = row.itemId || this.ids;
this.$modal.confirm('是否确认删除BOM 明细,存放属性–值编号为"' + itemIds + '"的数据项?').then(() => {
this.loading = true;
return delBomItem(itemIds);
}).then(() => {
this.loading = false;
this.getList();
this.$modal.msgSuccess("删除成功");
this.$store.dispatch('category/getBomMap');
}).catch(() => {
}).finally(() => {
this.loading = false;
});
},
/** 导出按钮操作 */
handleExport() {
this.download('wms/bomItem/export', {
...this.queryParams
}, `bomItem_${new Date().getTime()}.xlsx`)
}
}
};
</script>

View File

@@ -0,0 +1,230 @@
<template>
<div class="bom-container">
<!-- 当没有传入id时显示创建按钮 -->
<div v-if="!id" class="create-bom">
<el-button type="primary" :loading="createLoading" :disabled="createLoading" @click="handleCreate"
class="create-button">
<template v-if="!createLoading">
<i class="el-icon-plus"></i> 创建BOM
</template>
<template v-else>
创建中...
</template>
</el-button>
</div>
<!-- 当传入id时显示数据区域 -->
<div v-else class="bom-details">
<div v-if="loading" class="loading-indicator">
加载中...
<div class="spinner"></div>
</div>
<div v-if="error" class="error-message">
加载失败: {{ error }}
<button @click="fetchData" class="retry-button">重试</button>
</div>
<div v-if="data && !loading">
<el-form label-width="100px">
<el-form-item label="BOM名称">
<el-input v-model="info.bomName" @blur="handleUpdateBom" />
</el-form-item>
<el-form-item label="BOM编码">
<el-input v-model="info.bomCode" @blur="handleUpdateBom" />
</el-form-item>
</el-form>
<BomItem :bomId="id" />
</div>
</div>
</div>
</template>
<script>
import { addBom, updateBom, getBom } from '@/api/oa/bom';
import { listBomItem } from '@/api/oa/bomItem';
import { updateProduct } from '@/api/oa/product';
import BomItem from './BomItem.vue';
export default {
props: {
id: [String, Number], // 支持字符串或数字类型的ID
itemId: [String, Number],
type: String // 可选类型参数
},
components: {
BomItem
},
data() {
return {
loading: false,
error: null,
data: null,
info: {},
createLoading: false,
};
},
watch: {
id: {
immediate: true, // 组件创建时立即执行
handler(newVal) {
if (newVal) {
console.log('侦听到变化')
this.fetchBomDetails();
} else {
// 没有ID时重置数据
this.data = null;
this.error = null;
}
}
}
},
methods: {
async fetchBomDetails() {
try {
this.loading = true;
this.error = null;
// 实际项目中替换为真实API调用
const response = await listBomItem({
bomId: this.id
})
this.data = response.rows;
const response2 = await getBom(this.id)
this.info = response2.data;
} catch (err) {
this.error = err.message || '请求失败,请重试';
console.error('获取BOM详情失败:', err);
} finally {
this.loading = false;
}
},
async handleCreate() {
// 防止重复点击
if (this.createLoading) return;
try {
this.createLoading = true;
const bomResponse = await addBom({
bomName: (this.type == 'product' ? '产品BOM' : '原材料BOM') + new Date().getTime(),
bomCode: (this.type == 'product' ? 'P' : 'R') + new Date().getTime()
});
this.$message.success('创建BOM成功');
const bomData = bomResponse.data;
// 根据类型更新产品/原材料
await updateProduct({
productId: this.itemId,
bomId: bomData.bomId
});
// 触发事件
this.$emit('addBom', bomData);
} catch (error) {
console.error('创建失败:', error);
this.$message.error(`创建失败: ${error.message || '未知错误'}`);
} finally {
this.createLoading = false;
}
},
handleUpdateBom() {
this.$message.warning('正在更新BOM...');
updateBom({
bomId: this.id,
bomName: this.info.bomName,
bomCode: this.info.bomCode
}).then(_ => {
this.$message.success('更新BOM成功');
this.$store.dispatch('category/getBomMap');
})
},
fetchData() {
// 重试获取数据
if (this.id) this.fetchBomDetails();
}
}
};
</script>
<style scoped>
.create-bom {
text-align: center;
padding: 40px 0;
}
.create-button {
background-color: #4CAF50;
color: white;
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.create-button:hover {
background-color: #388E3C;
}
.loading-indicator {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
padding: 20px;
color: #666;
}
.spinner {
width: 20px;
height: 20px;
border: 3px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: #3498db;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.error-message {
padding: 20px;
background-color: #ffebee;
color: #d32f2f;
border-radius: 4px;
display: flex;
flex-direction: column;
gap: 12px;
}
.retry-button {
padding: 8px 16px;
background-color: #f44336;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
width: 120px;
}
.bom-info {
margin-top: 20px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 4px;
border-left: 4px solid #2196F3;
}
.bom-info p {
margin: 10px 0;
}
</style>

View File

@@ -0,0 +1,314 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="产品编号" prop="productCode">
<el-input v-model="queryParams.productCode" placeholder="请输入产品编号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="产品名称" prop="productName">
<el-input v-model="queryParams.productName" placeholder="请输入产品名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="负责人" prop="salesManager">
<el-input v-model="queryParams.owner" :multiple="false" placeholder="请填写负责人" />
</el-form-item>
<!-- <el-form-item label="是否启用" prop="isEnabled">
<el-select v-model="queryParams.isEnabled" placeholder="请选择是否启用" clearable>
<el-option
v-for="dict in dict.type.common_swicth"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item> -->
<el-form-item>
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" size="mini" :disabled="single"
@click="handleUpdate">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" size="mini" :disabled="multiple"
@click="handleDelete">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" size="mini" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="productList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="产品编号" align="center" prop="productCode" />
<el-table-column label="产品名称" align="center" prop="productName" />
<el-table-column label="负责人" align="center" prop="owner" />
<el-table-column label="BOM" align="center">
<template slot-scope="scope">
<BomInfoMini :bomId="scope.row.bomId" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
<el-button size="mini" type="text" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
<!-- <el-button
size="mini"
type="text"
icon="el-icon-document"
@click="goLedger(scope.row)"
>台账</el-button> -->
<el-button size="mini" type="text" icon="Document"
@click="handleInstallManual(scope.row)">安装说明书</el-button>
<el-button size="mini" type="text" icon="DataAnalysis" @click="handleBom(scope.row)">BOM</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
<!-- 添加或修改产品对话框 -->
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="产品编号" prop="productCode">
<el-input v-model="form.productCode" placeholder="请输入产品编号" />
</el-form-item>
<el-form-item label="产品名称" prop="productName">
<el-input v-model="form.productName" placeholder="请输入产品名称" />
</el-form-item>
<el-form-item label="负责人" prop="owner">
<el-input v-model="form.owner" :multiple="false" placeholder="请填写负责人" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<el-dialog title="BOM" @close="bomDialogVisible = false" v-model="bomDialogVisible" width="900px" append-to-body>
<BomPanel :id="bomId" type="product" @addBom="handleAddBom" :itemId="itemId" />
</el-dialog>
</div>
</template>
<script>
import { listProduct, getProduct, delProduct, addProduct, updateProduct } from "@/api/oa/product";
import UserSelect from '@/components/UserSelect';
import BomPanel from '../components/BomPanel.vue';
import BomInfoMini from '@/components/Renderer/BomInfoMini.vue';
export default {
name: "Product",
components: {
UserSelect,
BomPanel,
BomInfoMini
},
// dicts: ['common_swicth'],
data() {
return {
// 按钮loading
buttonLoading: false,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 产品表格数据
productList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
type: 'product',
productCode: undefined,
productName: undefined,
owner: undefined,
baseMaterialId: undefined,
surfaceTreatmentId: undefined,
customerReqId: undefined,
packagingId: undefined,
thickness: undefined,
width: undefined,
innerDiameter: undefined,
isEnabled: undefined,
},
// 表单参数
form: {},
// 表单校验
rules: {
productCode: [
{ required: true, message: "产品编号不能为空", trigger: "blur" }
],
productName: [
{ required: true, message: "产品名称不能为空", trigger: "blur" }
],
owner: [
{ required: true, message: "负责人不能为空", trigger: "blur" }
],
},
bomDialogVisible: false,
bomId: undefined,
itemId: undefined,
};
},
created() {
this.getList();
},
methods: {
/** 查询产品列表 */
getList() {
this.loading = true;
listProduct(this.queryParams).then(response => {
this.productList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleAddBom(bom) {
this.bomId = bom.bomId;
this.getList();
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
productId: undefined,
productCode: undefined,
productName: undefined,
owner: undefined,
baseMaterialId: undefined,
surfaceTreatmentId: undefined,
customerReqId: undefined,
packagingId: undefined,
thickness: undefined,
width: undefined,
innerDiameter: undefined,
isEnabled: undefined,
unit: undefined,
delFlag: undefined,
createTime: undefined,
createBy: undefined,
updateTime: undefined,
updateBy: undefined,
type: 'product'
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
handleInstallManual(row) {
console.log(row);
},
handleBom(row) {
this.bomDialogVisible = true;
this.bomId = row.bomId;
this.itemId = row.productId;
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.productId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加产品";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.loading = true;
this.reset();
const productId = row.productId || this.ids
getProduct(productId).then(response => {
this.loading = false;
this.form = response.data;
this.open = true;
this.title = "修改产品";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.productId != null) {
updateProduct(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
// this.$store.dispatch('category/getProductMap');
}).finally(() => {
this.buttonLoading = false;
});
} else {
addProduct(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
// this.$store.dispatch('category/getProductMap');
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const productIds = row.productId || this.ids;
this.$modal.confirm('是否确认删除产品编号为"' + productIds + '"的数据项?').then(() => {
this.loading = true;
return delProduct(productIds);
}).then(() => {
this.loading = false;
this.getList();
this.$modal.msgSuccess("删除成功");
// this.$store.dispatch('category/getProductMap');
}).catch(() => {
}).finally(() => {
this.loading = false;
});
},
/** 导出按钮操作 */
handleExport() {
this.download('wms/product/export', {
...this.queryParams
}, `product_${new Date().getTime()}.xlsx`)
}
}
};
</script>