feat: 新增物料管理看板功能及多项优化
新增物料管理看板功能,包含统计卡片和图表展示 优化物料选择器组件,支持分页和搜索功能 重构物料详情展示组件,支持动态加载数据 添加多个ECharts图表组件用于数据可视化 完善出入库和采购单相关功能,增加在途数量显示 修复若干界面显示问题和交互逻辑
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
"@vueuse/core": "13.3.0",
|
"@vueuse/core": "13.3.0",
|
||||||
"axios": "1.9.0",
|
"axios": "1.9.0",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
|
"dayjs": "^1.11.19",
|
||||||
"echarts": "5.6.0",
|
"echarts": "5.6.0",
|
||||||
"element-plus": "2.9.9",
|
"element-plus": "2.9.9",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
|
|||||||
23
gear-ui3/pnpm-lock.yaml
generated
23
gear-ui3/pnpm-lock.yaml
generated
@@ -26,6 +26,9 @@ importers:
|
|||||||
clipboard:
|
clipboard:
|
||||||
specifier: 2.0.11
|
specifier: 2.0.11
|
||||||
version: 2.0.11
|
version: 2.0.11
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.19
|
||||||
|
version: 1.11.19
|
||||||
echarts:
|
echarts:
|
||||||
specifier: 5.6.0
|
specifier: 5.6.0
|
||||||
version: 5.6.0
|
version: 5.6.0
|
||||||
@@ -71,6 +74,9 @@ importers:
|
|||||||
vue-router:
|
vue-router:
|
||||||
specifier: 4.5.1
|
specifier: 4.5.1
|
||||||
version: 4.5.1(vue@3.5.16)
|
version: 4.5.1(vue@3.5.16)
|
||||||
|
vue3-treeselect:
|
||||||
|
specifier: ^0.1.10
|
||||||
|
version: 0.1.10(vue@3.5.16)
|
||||||
vuedraggable:
|
vuedraggable:
|
||||||
specifier: 4.1.0
|
specifier: 4.1.0
|
||||||
version: 4.1.0(vue@3.5.16)
|
version: 4.1.0(vue@3.5.16)
|
||||||
@@ -911,8 +917,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
|
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
dayjs@1.11.13:
|
dayjs@1.11.19:
|
||||||
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
|
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||||
@@ -2463,6 +2469,11 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.2.0
|
vue: ^3.2.0
|
||||||
|
|
||||||
|
vue3-treeselect@0.1.10:
|
||||||
|
resolution: {integrity: sha512-QawdAbzmlZ7T3uBdSU4FRnrnmcV0Q9Jrph5hUBQJcXmM9OZ8lULQo7O7YbKxkOyuDX9Yx2rGjs6L5FKcL1FeXA==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.0.0
|
||||||
|
|
||||||
vue@3.5.16:
|
vue@3.5.16:
|
||||||
resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==}
|
resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3245,7 +3256,7 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
is-data-view: 1.0.2
|
is-data-view: 1.0.2
|
||||||
|
|
||||||
dayjs@1.11.13: {}
|
dayjs@1.11.19: {}
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3361,7 +3372,7 @@ snapshots:
|
|||||||
'@types/lodash-es': 4.17.12
|
'@types/lodash-es': 4.17.12
|
||||||
'@vueuse/core': 9.13.0(vue@3.5.16)
|
'@vueuse/core': 9.13.0(vue@3.5.16)
|
||||||
async-validator: 4.2.5
|
async-validator: 4.2.5
|
||||||
dayjs: 1.11.13
|
dayjs: 1.11.19
|
||||||
escape-html: 1.0.3
|
escape-html: 1.0.3
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
lodash-es: 4.17.21
|
lodash-es: 4.17.21
|
||||||
@@ -4972,6 +4983,10 @@ snapshots:
|
|||||||
'@vue/devtools-api': 6.6.4
|
'@vue/devtools-api': 6.6.4
|
||||||
vue: 3.5.16
|
vue: 3.5.16
|
||||||
|
|
||||||
|
vue3-treeselect@0.1.10(vue@3.5.16):
|
||||||
|
dependencies:
|
||||||
|
vue: 3.5.16
|
||||||
|
|
||||||
vue@3.5.16:
|
vue@3.5.16:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-dom': 3.5.16
|
'@vue/compiler-dom': 3.5.16
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div @click="open = true" style="cursor: pointer;">
|
||||||
|
<!-- 触发区插槽:默认展示选择按钮/已选物料信息 -->
|
||||||
|
<slot name="trigger">
|
||||||
|
<el-button v-if="!materialId" type="primary" size="small">选择配料</el-button>
|
||||||
|
<el-tag v-else type="info" closable @close="handleClear">
|
||||||
|
{{ currentMaterial.materialName }}({{ currentMaterial.spec }})[{{ currentMaterial.model }}] - {{
|
||||||
|
currentMaterial.factory }},库存:{{ currentMaterial.currentStock }}{{ currentMaterial.unit }}
|
||||||
|
</el-tag>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选择弹窗:表格+分页+底部按钮 -->
|
||||||
|
<el-dialog title="选择配料" v-model="open" width="800px" destroy-on-close>
|
||||||
|
<el-table :data="list" style="width: 100%" border stripe @row-click="handleRowSelect" highlight-current-row
|
||||||
|
row-key="materialId" :current-row-key="materialId">
|
||||||
|
<el-table-column prop="materialName" label="配料名称" min-width="120" align="center" />
|
||||||
|
<el-table-column prop="spec" label="配料规格" min-width="100" align="center" />
|
||||||
|
<el-table-column prop="model" label="配料型号" min-width="100" align="center" />
|
||||||
|
<el-table-column prop="factory" label="生产厂家" min-width="120" align="center" />
|
||||||
|
<el-table-column prop="currentStock" label="现存库存" min-width="80" align="center" />
|
||||||
|
<el-table-column prop="unit" label="计量单位" width="80" align="center" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页组件 -->
|
||||||
|
<div style="margin: 15px 0; text-align: right; width: 100%">
|
||||||
|
<el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :total="total"
|
||||||
|
:page-sizes="[5, 10, 20, 50]" layout="prev, pager, next, jumper, ->, total, sizes"
|
||||||
|
@size-change="fetchMaterialList" @current-change="fetchMaterialList" small />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="open = false">取消</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="RawSelector">
|
||||||
|
import { ref, computed, watch, onMounted } from 'vue';
|
||||||
|
import { listMaterial, getMaterial } from '@/api/mat/material';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
// 双向绑定物料ID
|
||||||
|
const materialId = defineModel({
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算当前选中的物料信息
|
||||||
|
const currentMaterial = computed(() => {
|
||||||
|
if (!materialId.value) return {};
|
||||||
|
return list.value.find(item => item.materialId === materialId.value) || {};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
// 基础响应式数据
|
||||||
|
const list = ref([]); // 物料列表
|
||||||
|
const open = ref(false); // 弹窗显隐
|
||||||
|
|
||||||
|
// 分页响应式数据(核心新增)
|
||||||
|
const pageNum = ref(1); // 当前页码
|
||||||
|
const pageSize = ref(10); // 每页条数
|
||||||
|
const total = ref(0); // 总数据条数
|
||||||
|
|
||||||
|
// 组件挂载加载列表
|
||||||
|
onMounted(async () => {
|
||||||
|
await fetchMaterialList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载物料列表(适配分页参数)
|
||||||
|
async function fetchMaterialList() {
|
||||||
|
try {
|
||||||
|
const res = await listMaterial({ pageNum: pageNum.value, pageSize: pageSize.value });
|
||||||
|
|
||||||
|
list.value = res.rows;
|
||||||
|
total.value = res.total; // 赋值总条数供分页使用
|
||||||
|
// 分页查询后检查是否存在当前选中的物料ID
|
||||||
|
const isExist = res.rows.some(item => item.materialId === materialId.value);
|
||||||
|
if (!isExist) {
|
||||||
|
console.log('获取物料详情:', materialId.value);
|
||||||
|
if (!materialId.value) return;
|
||||||
|
const res = await getMaterial(materialId.value);
|
||||||
|
list.value.push(res.data);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('加载物料列表失败:', err);
|
||||||
|
ElMessage.error('配料列表加载失败,请刷新重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格行选择物料
|
||||||
|
function handleRowSelect(row) {
|
||||||
|
if (row.materialId === materialId.value) return;
|
||||||
|
materialId.value = row.materialId;
|
||||||
|
emit('change', row);
|
||||||
|
open.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空已选物料
|
||||||
|
function handleClear() {
|
||||||
|
materialId.value = '';
|
||||||
|
emit('change', {});
|
||||||
|
ElMessage.info('已清空配料选择');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,25 +1,48 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-popover width="400">
|
<el-popover width="400">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button type="primary" size="mini" link>{{data.materialName}}</el-button>
|
<el-button type="primary" size="mini" link>{{formattedData.materialName}}</el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-descriptions :column="2">
|
<el-descriptions :column="2">
|
||||||
<el-descriptions-item label="配料名称">{{ data.materialName }}</el-descriptions-item>
|
<el-descriptions-item label="配料名称">{{ formattedData.materialName }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="配料规格">{{ data.spec }}</el-descriptions-item>
|
<el-descriptions-item label="配料规格">{{ formattedData.spec }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="配料型号">{{ data.model }}</el-descriptions-item>
|
<el-descriptions-item label="配料型号">{{ formattedData.model }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="厂家">{{ data.factory }}</el-descriptions-item>
|
<el-descriptions-item label="厂家">{{ formattedData.factory }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="现存库存">{{ data.currentStock }}</el-descriptions-item>
|
<el-descriptions-item label="现存库存">{{ formattedData.currentStock }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="计量单位">{{ data.unit }}</el-descriptions-item>
|
<el-descriptions-item label="计量单位">{{ formattedData.unit }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="备注" :span="2">{{ data.remark }}</el-descriptions-item>
|
<el-descriptions-item label="备注" :span="2">{{ formattedData.remark }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { getMaterial } from '@/api/mat/material'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
materialId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const content = ref({});
|
||||||
|
|
||||||
|
watch(() => props.materialId, (newVal, oldVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
getMaterial(newVal).then(response => {
|
||||||
|
content.value = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
const formattedData = computed(() => {
|
||||||
|
if (props.materialId) {
|
||||||
|
return content.value;
|
||||||
|
}
|
||||||
|
return props.data;
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
149
gear-ui3/src/components/StickyDragContainer/index.vue
Normal file
149
gear-ui3/src/components/StickyDragContainer/index.vue
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
<!-- @/components/StickyDragContainer/index.vue -->
|
||||||
|
<template>
|
||||||
|
<div class="sticky-drag-wrapper" :style="wrapperStyle">
|
||||||
|
<!-- 拖拽条 -->
|
||||||
|
<div class="drag-bar" ref="dragBarRef" @mousedown="handleDragStart"></div>
|
||||||
|
<!-- 高度可调节+内部滚动容器 -->
|
||||||
|
<div
|
||||||
|
class="content-scroll-container"
|
||||||
|
ref="scrollContainerRef"
|
||||||
|
:style="{ height: `${containerHeight}px` }"
|
||||||
|
>
|
||||||
|
<!-- 默认插槽:接收外部传入的任意内容 -->
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="StickyDragContainer">
|
||||||
|
import { ref, onMounted, onUnmounted, defineProps, watch, nextTick } from 'vue'
|
||||||
|
|
||||||
|
// 新增:接收父容器Ref(必传,用于获取父容器布局信息)
|
||||||
|
const props = defineProps({
|
||||||
|
parentRef: { // 父容器的Ref对象,外部传入
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
initialHeight: { // 容器初始高度(px)
|
||||||
|
type: Number,
|
||||||
|
default: 300
|
||||||
|
},
|
||||||
|
minHeight: { // 容器最小高度(px)
|
||||||
|
type: Number,
|
||||||
|
default: 200
|
||||||
|
},
|
||||||
|
zIndex: { // 容器层级
|
||||||
|
type: Number,
|
||||||
|
default: 99
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 响应式变量
|
||||||
|
const dragBarRef = ref(null)
|
||||||
|
const scrollContainerRef = ref(null)
|
||||||
|
const startY = ref(0)
|
||||||
|
const startHeight = ref(props.initialHeight)
|
||||||
|
const containerHeight = ref(props.initialHeight)
|
||||||
|
// 新增:组件外层样式(动态绑定宽度、左侧偏移、z-index等)
|
||||||
|
const wrapperStyle = ref({
|
||||||
|
zIndex: props.zIndex,
|
||||||
|
width: '0px',
|
||||||
|
left: '0px'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 核心方法:更新组件布局(宽度/左侧偏移),与父容器保持一致
|
||||||
|
const updateContainerLayout = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
// 校验父容器Ref是否有效
|
||||||
|
console.log(props.parentRef)
|
||||||
|
if (!props.parentRef) {
|
||||||
|
console.warn('StickyDragContainer:传入的parentRef无效,请确保绑定了正确的DOM Ref')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取父容器的实际布局信息(相对于视口的位置、宽度)
|
||||||
|
const parentRect = props.parentRef.getBoundingClientRect()
|
||||||
|
// 动态设置组件宽度(与父容器完全一致)、左侧偏移(与父容器左对齐)
|
||||||
|
wrapperStyle.value = {
|
||||||
|
zIndex: props.zIndex,
|
||||||
|
width: `${parentRect.width}px`, // 继承父容器宽度
|
||||||
|
left: `${parentRect.left}px` // 与父容器左对齐
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽相关方法(保持不变)
|
||||||
|
const handleDragStart = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
startY.value = e.clientY
|
||||||
|
startHeight.value = scrollContainerRef.value?.getBoundingClientRect().height || containerHeight.value
|
||||||
|
document.addEventListener('mousemove', handleDragMove)
|
||||||
|
document.addEventListener('mouseup', handleDragEnd)
|
||||||
|
}
|
||||||
|
const handleDragMove = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const diffY = startY.value - e.clientY
|
||||||
|
const newHeight = Math.max(props.minHeight, startHeight.value + diffY)
|
||||||
|
containerHeight.value = newHeight
|
||||||
|
}
|
||||||
|
const handleDragEnd = () => {
|
||||||
|
document.removeEventListener('mousemove', handleDragMove)
|
||||||
|
document.removeEventListener('mouseup', handleDragEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生命周期:初始化+监听窗口缩放
|
||||||
|
onMounted(() => {
|
||||||
|
containerHeight.value = props.initialHeight
|
||||||
|
updateContainerLayout() // 初始化时适配父容器布局
|
||||||
|
// 新增:监听窗口缩放,父容器宽度变化时同步更新组件布局
|
||||||
|
window.addEventListener('resize', updateContainerLayout)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 解绑所有事件,防止内存泄漏
|
||||||
|
document.removeEventListener('mousemove', handleDragMove)
|
||||||
|
document.removeEventListener('mouseup', handleDragEnd)
|
||||||
|
window.removeEventListener('resize', updateContainerLayout) // 解绑窗口监听
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听props变化,动态更新
|
||||||
|
watch([() => props.initialHeight, () => props.zIndex], () => {
|
||||||
|
containerHeight.value = props.initialHeight
|
||||||
|
wrapperStyle.value.zIndex = props.zIndex
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// 新增:如果父容器Ref变化,重新适配布局
|
||||||
|
watch(() => props.parentRef, () => {
|
||||||
|
updateContainerLayout()
|
||||||
|
}, { deep: true, immediate: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 吸底外层容器:移除left/right:0,改为动态绑定;保留fixed+bottom:0核心吸底 */
|
||||||
|
.sticky-drag-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0; /* 仅保留吸底,宽度/左侧偏移由JS动态设置 */
|
||||||
|
background: #ffffff;
|
||||||
|
border-top: 1px solid #e6e6e6;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: all 0.1s ease; /* 宽度/高度变化平滑过渡 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 拖拽条样式(保持不变) */
|
||||||
|
.drag-bar {
|
||||||
|
height: 6px;
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
cursor: n-resize;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
.drag-bar:hover {
|
||||||
|
background-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内容滚动容器样式(保持不变) */
|
||||||
|
.content-scroll-container {
|
||||||
|
width: 100%; /* 继承外层wrapper的宽度(即父容器宽度) */
|
||||||
|
overflow: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: height 0.1s ease;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -20,7 +20,11 @@
|
|||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<!-- <el-table-column label="关联ID 主键" align="center" prop="relationId" v-if="true"/> -->
|
<!-- <el-table-column label="关联ID 主键" align="center" prop="relationId" v-if="true"/> -->
|
||||||
<!-- <el-table-column label="产品ID 关联t_product.id" align="center" prop="productId" /> -->
|
<!-- <el-table-column label="产品ID 关联t_product.id" align="center" prop="productId" /> -->
|
||||||
<el-table-column label="配料" align="center" prop="materialId" />
|
<el-table-column label="配料" align="center" prop="materialId">
|
||||||
|
<template #default="scope">
|
||||||
|
<raw :data="scope.row" :materialId="scope.row.materialId" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="所需数量" align="center" prop="materialNum" />
|
<el-table-column label="所需数量" align="center" prop="materialNum" />
|
||||||
<!-- <el-table-column label="配料排序 用于前端展示顺序" align="center" prop="sort" /> -->
|
<!-- <el-table-column label="配料排序 用于前端展示顺序" align="center" prop="sort" /> -->
|
||||||
<el-table-column label="备注" align="center" prop="remark" />
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
@@ -42,7 +46,7 @@
|
|||||||
<el-input v-model="form.productId" placeholder="请输入产品ID 关联t_product.id" />
|
<el-input v-model="form.productId" placeholder="请输入产品ID 关联t_product.id" />
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="form.materialId" placeholder="请输入配料" />
|
<raw-selector ref="rawSelector" v-model="form.materialId" placeholder="请选择配料" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所需数量" prop="materialNum">
|
<el-form-item label="所需数量" prop="materialNum">
|
||||||
<el-input v-model="form.materialNum" placeholder="请输入所需数量" />
|
<el-input v-model="form.materialNum" placeholder="请输入所需数量" />
|
||||||
@@ -67,7 +71,8 @@
|
|||||||
|
|
||||||
<script setup name="ProductMaterialRelation">
|
<script setup name="ProductMaterialRelation">
|
||||||
import { listProductMaterialRelation, getProductMaterialRelation, delProductMaterialRelation, addProductMaterialRelation, updateProductMaterialRelation } from "@/api/mat/productMaterialRelation";
|
import { listProductMaterialRelation, getProductMaterialRelation, delProductMaterialRelation, addProductMaterialRelation, updateProductMaterialRelation } from "@/api/mat/productMaterialRelation";
|
||||||
|
import RawSelector from '@/components/RawSelector/index.vue'
|
||||||
|
import Raw from '@/components/Renderer/Raw.vue'
|
||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
||||||
const productMaterialRelationList = ref([]);
|
const productMaterialRelationList = ref([]);
|
||||||
@@ -193,7 +198,7 @@ function submitForm() {
|
|||||||
buttonLoading.value = true;
|
buttonLoading.value = true;
|
||||||
if (form.value.relationId != null) {
|
if (form.value.relationId != null) {
|
||||||
updateProductMaterialRelation(form.value).then(response => {
|
updateProductMaterialRelation(form.value).then(response => {
|
||||||
proxy.$modal.msgSuccess("修改成功");
|
proxy.$modal.msgSuccess("修改成功,刷新后生效");
|
||||||
open.value = false;
|
open.value = false;
|
||||||
getList();
|
getList();
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
@@ -201,7 +206,7 @@ function submitForm() {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
addProductMaterialRelation(form.value).then(response => {
|
addProductMaterialRelation(form.value).then(response => {
|
||||||
proxy.$modal.msgSuccess("新增成功");
|
proxy.$modal.msgSuccess("新增成功,刷新后生效");
|
||||||
open.value = false;
|
open.value = false;
|
||||||
getList();
|
getList();
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
@@ -221,7 +226,7 @@ function handleDelete(row) {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
getList();
|
getList();
|
||||||
proxy.$modal.msgSuccess("删除成功");
|
proxy.$modal.msgSuccess("删除成功,刷新后生效");
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
135
gear-ui3/src/views/mat/components/charts/FactoryBarChart.vue
Normal file
135
gear-ui3/src/views/mat/components/charts/FactoryBarChart.vue
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="chartRef" class="echarts-container"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, onUpdated, onUnmounted, watch } from 'vue'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
|
||||||
|
// 接收父组件传入的厂家数据
|
||||||
|
const props = defineProps({
|
||||||
|
factoryData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartRef = ref(null) // ECharts容器ref
|
||||||
|
let myChart = null // ECharts实例
|
||||||
|
|
||||||
|
// 初始化/更新ECharts
|
||||||
|
const initEcharts = () => {
|
||||||
|
// 容器不存在则返回
|
||||||
|
if (!chartRef.value) return
|
||||||
|
// 初始化实例(单例模式)
|
||||||
|
myChart = echarts.init(chartRef.value)
|
||||||
|
// 设置配置项
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: { type: 'cross' }
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['材料个数', '库存总数'],
|
||||||
|
top: 0
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: props.factoryData.map(item => item.factory),
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 30, // 标签旋转,防止重叠
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 双Y轴:适配材料个数(小数值)和库存总数(大数值)
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '材料个数',
|
||||||
|
min: 0,
|
||||||
|
axisLabel: { formatter: '{value} 种' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '库存总数',
|
||||||
|
min: 0,
|
||||||
|
axisLabel: { formatter: '{value} 件' }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '材料个数',
|
||||||
|
type: 'bar',
|
||||||
|
yAxisIndex: 0, // 对应第一个Y轴
|
||||||
|
data: props.factoryData.map(item => item.materialCount),
|
||||||
|
itemStyle: { color: '#409EFF' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '库存总数',
|
||||||
|
type: 'bar',
|
||||||
|
yAxisIndex: 1, // 对应第二个Y轴
|
||||||
|
data: props.factoryData.map(item => item.stockTotal),
|
||||||
|
itemStyle: { color: '#67C23A' }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 空数据提示
|
||||||
|
noDataLoadingOption: {
|
||||||
|
text: '暂无厂家数据',
|
||||||
|
textStyle: { fontSize: 14 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置配置项并渲染
|
||||||
|
myChart.setOption(option, true)
|
||||||
|
// 自适应窗口大小
|
||||||
|
window.addEventListener('resize', resizeEcharts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图表自适应
|
||||||
|
const resizeEcharts = () => {
|
||||||
|
if (myChart) myChart.resize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销毁ECharts实例
|
||||||
|
const destroyEcharts = () => {
|
||||||
|
if (myChart) {
|
||||||
|
myChart.dispose()
|
||||||
|
myChart = null
|
||||||
|
window.removeEventListener('resize', resizeEcharts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时初始化
|
||||||
|
onMounted(() => {
|
||||||
|
initEcharts()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件更新时(props变化)重绘
|
||||||
|
onUpdated(() => {
|
||||||
|
destroyEcharts()
|
||||||
|
initEcharts()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件卸载时销毁实例
|
||||||
|
onUnmounted(() => {
|
||||||
|
destroyEcharts()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 深度监听数据源变化,重绘图表
|
||||||
|
watch(() => props.factoryData, () => {
|
||||||
|
destroyEcharts()
|
||||||
|
initEcharts()
|
||||||
|
}, { deep: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.echarts-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 280px; /* 固定图表高度,可根据需求调整 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
112
gear-ui3/src/views/mat/components/charts/InOutCompareChart.vue
Normal file
112
gear-ui3/src/views/mat/components/charts/InOutCompareChart.vue
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="chartRef" class="echarts-container"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, onUpdated, onUnmounted, watch } from 'vue'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
|
||||||
|
// 接收父组件传入的出入库对比数据
|
||||||
|
const props = defineProps({
|
||||||
|
inOutData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartRef = ref(null)
|
||||||
|
let myChart = null
|
||||||
|
|
||||||
|
const initEcharts = () => {
|
||||||
|
if (!chartRef.value) return
|
||||||
|
myChart = echarts.init(chartRef.value)
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: '日期:{b}<br/>{a}:{c} 件'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['入库数量', '出库数量'],
|
||||||
|
top: 0
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '15%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: props.inOutData.map(item => item.time),
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45,
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '操作数量(件)',
|
||||||
|
min: 0
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '入库数量',
|
||||||
|
type: 'line',
|
||||||
|
data: props.inOutData.map(item => item.inNum),
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: { width: 2, color: '#67C23A' },
|
||||||
|
itemStyle: { color: '#67C23A' },
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '出库数量',
|
||||||
|
type: 'line',
|
||||||
|
data: props.inOutData.map(item => item.outNum),
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: { width: 2, color: '#E6A23C' },
|
||||||
|
itemStyle: { color: '#E6A23C' },
|
||||||
|
symbol: 'triangle',
|
||||||
|
symbolSize: 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
noDataLoadingOption: {
|
||||||
|
text: '暂无出入库数据',
|
||||||
|
textStyle: { fontSize: 14 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myChart.setOption(option, true)
|
||||||
|
window.addEventListener('resize', resizeEcharts)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resizeEcharts = () => {
|
||||||
|
if (myChart) myChart.resize()
|
||||||
|
}
|
||||||
|
|
||||||
|
const destroyEcharts = () => {
|
||||||
|
if (myChart) {
|
||||||
|
myChart.dispose()
|
||||||
|
myChart = null
|
||||||
|
window.removeEventListener('resize', resizeEcharts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => initEcharts())
|
||||||
|
onUpdated(() => {
|
||||||
|
destroyEcharts()
|
||||||
|
initEcharts()
|
||||||
|
})
|
||||||
|
onUnmounted(() => destroyEcharts())
|
||||||
|
|
||||||
|
watch(() => props.inOutData, () => {
|
||||||
|
destroyEcharts()
|
||||||
|
initEcharts()
|
||||||
|
}, { deep: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.echarts-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 280px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
103
gear-ui3/src/views/mat/components/charts/StockTrendChart.vue
Normal file
103
gear-ui3/src/views/mat/components/charts/StockTrendChart.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="chartRef" class="echarts-container"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, onUpdated, onUnmounted, watch } from 'vue'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
|
||||||
|
// 接收父组件传入的库存趋势数据
|
||||||
|
const props = defineProps({
|
||||||
|
trendData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartRef = ref(null)
|
||||||
|
let myChart = null
|
||||||
|
|
||||||
|
const initEcharts = () => {
|
||||||
|
if (!chartRef.value) return
|
||||||
|
myChart = echarts.init(chartRef.value)
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: '日期:{b}<br/>库存总量:{c} 件'
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '15%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: props.trendData.map(item => item.time),
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45,
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '库存数量(件)',
|
||||||
|
min: 0
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '库存总量',
|
||||||
|
type: 'line',
|
||||||
|
data: props.trendData.map(item => item.stock.toFixed(2)),
|
||||||
|
smooth: true, // 平滑折线
|
||||||
|
lineStyle: { width: 2 },
|
||||||
|
itemStyle: { color: '#F56C6C' },
|
||||||
|
areaStyle: {
|
||||||
|
// 面积渐变背景
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{ offset: 0, color: 'rgba(245,108,108,0.3)' },
|
||||||
|
{ offset: 1, color: 'rgba(245,108,108,0)' }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
noDataLoadingOption: {
|
||||||
|
text: '暂无库存趋势数据',
|
||||||
|
textStyle: { fontSize: 14 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myChart.setOption(option, true)
|
||||||
|
window.addEventListener('resize', resizeEcharts)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resizeEcharts = () => {
|
||||||
|
if (myChart) myChart.resize()
|
||||||
|
}
|
||||||
|
|
||||||
|
const destroyEcharts = () => {
|
||||||
|
if (myChart) {
|
||||||
|
myChart.dispose()
|
||||||
|
myChart = null
|
||||||
|
window.removeEventListener('resize', resizeEcharts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => initEcharts())
|
||||||
|
onUpdated(() => {
|
||||||
|
destroyEcharts()
|
||||||
|
initEcharts()
|
||||||
|
})
|
||||||
|
onUnmounted(() => destroyEcharts())
|
||||||
|
|
||||||
|
watch(() => props.trendData, () => {
|
||||||
|
destroyEcharts()
|
||||||
|
initEcharts()
|
||||||
|
}, { deep: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.echarts-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 280px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,19 +13,6 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</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="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
|
||||||
</el-col>
|
|
||||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
||||||
</el-row> -->
|
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="purchaseInDetailList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="purchaseInDetailList" @selection-change="handleSelectionChange">
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column label="入库明细ID 主键" align="center" prop="detailId" v-if="false" />
|
<el-table-column label="入库明细ID 主键" align="center" prop="detailId" v-if="false" />
|
||||||
@@ -283,4 +270,8 @@ watch(() => props.purchaseId, (newVal, oldVal) => {
|
|||||||
getList();
|
getList();
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getList
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
197
gear-ui3/src/views/mat/components/price.vue
Normal file
197
gear-ui3/src/views/mat/components/price.vue
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
<template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<div v-if="!materialId" style="height: 400px; display: flex; align-items: center; justify-content: center;">
|
||||||
|
<el-empty description="请选择配料"></el-empty>
|
||||||
|
</div>
|
||||||
|
<!-- 图表容器:必须设置宽高,否则ECharts无法渲染 -->
|
||||||
|
<div v-else-if="priceHistoryList.length > 0" ref="chartRef" class="price-chart-container"></div>
|
||||||
|
<div v-else style="height: 400px; display: flex; align-items: center; justify-content: center;">
|
||||||
|
<el-empty description="暂无价格历史记录"></el-empty>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="Price">
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import { ref, watch, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { listMatPriceHistory } from "@/api/mat/matPriceHistory";
|
||||||
|
|
||||||
|
// 接收父组件传参:物料ID
|
||||||
|
const props = defineProps({
|
||||||
|
materialId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 价格历史列表
|
||||||
|
const priceHistoryList = ref([]);
|
||||||
|
// ECharts实例引用(核心:用于实例管理和自适应)
|
||||||
|
const chartRef = ref(null);
|
||||||
|
// 存储ECharts实例,方便后续销毁和更新
|
||||||
|
let myChart = null;
|
||||||
|
// 窗口大小监听标识(用于销毁监听)
|
||||||
|
let resizeObserver = null;
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const getListChart = () => {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
listMatPriceHistory({
|
||||||
|
materialId: props.materialId,
|
||||||
|
pageSize: 1000,
|
||||||
|
pageNum: 1
|
||||||
|
}).then(response => {
|
||||||
|
// 倒序显示,最新数据在顶部
|
||||||
|
priceHistoryList.value = (response.rows || []).reverse();
|
||||||
|
// 关键:等待Vue异步更新DOM,确保图表容器已渲染,再初始化图表
|
||||||
|
nextTick(() => {
|
||||||
|
initECharts();
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 初始化ECharts图表
|
||||||
|
const initECharts = () => {
|
||||||
|
// 容器不存在,直接返回
|
||||||
|
if (!chartRef.value) return;
|
||||||
|
// 先销毁旧实例,避免叠加
|
||||||
|
destroyChart();
|
||||||
|
// 初始化新实例
|
||||||
|
myChart = echarts.init(chartRef.value);
|
||||||
|
// 解析图表数据
|
||||||
|
const chartData = parseChartData();
|
||||||
|
// 设置图表配置项
|
||||||
|
const option = {
|
||||||
|
// 标题:显示物料名称+规格,居中
|
||||||
|
title: {
|
||||||
|
text: `历史平均价格`,
|
||||||
|
left: 'center',
|
||||||
|
textStyle: { fontSize: 16 }
|
||||||
|
},
|
||||||
|
// 提示框:鼠标悬浮显示价格,格式化保留2位小数
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: '价格:<b>{c} 元</b>',
|
||||||
|
axisPointer: { type: 'line', lineStyle: { type: 'dashed' } }
|
||||||
|
},
|
||||||
|
// 图例:单系列可隐藏,多系列需配置
|
||||||
|
legend: { data: ['单价'], left: 'left' },
|
||||||
|
// 网格:防止坐标轴标签超出容器
|
||||||
|
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||||
|
// X轴:类目轴,使用historyId作为标识
|
||||||
|
xAxis: {
|
||||||
|
// 隐藏x轴
|
||||||
|
show: false,
|
||||||
|
type: 'category',
|
||||||
|
data: chartData.xData,
|
||||||
|
// X轴标签旋转,防止重叠
|
||||||
|
axisLabel: { rotate: 30 },
|
||||||
|
// 隐藏X轴刻度线
|
||||||
|
axisTick: { show: false }
|
||||||
|
},
|
||||||
|
// Y轴:数值轴,价格保留2位小数,单位元
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '单价(元)',
|
||||||
|
min: 0,
|
||||||
|
// 格式化Y轴标签,保留2位小数
|
||||||
|
axisLabel: { formatter: '{value} ' },
|
||||||
|
// 保留2位小数的刻度
|
||||||
|
splitNumber: 5,
|
||||||
|
precision: 2
|
||||||
|
},
|
||||||
|
// 系列:折线图,核心价格趋势
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '单价',
|
||||||
|
type: 'line',
|
||||||
|
data: chartData.yData,
|
||||||
|
// 折线平滑
|
||||||
|
smooth: true,
|
||||||
|
// 标记点:显示每个价格节点
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 6,
|
||||||
|
// 标记点悬浮放大
|
||||||
|
emphasis: { symbolSize: 8 },
|
||||||
|
// 区域填充:增加视觉效果
|
||||||
|
areaStyle: {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{ offset: 0, color: 'rgba(66, 165, 245, 0.3)' },
|
||||||
|
{ offset: 1, color: 'rgba(66, 165, 245, 0)' }
|
||||||
|
])
|
||||||
|
},
|
||||||
|
// 折线颜色
|
||||||
|
lineStyle: { color: '#42a5f5', width: 2 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
// 设置配置项并渲染
|
||||||
|
myChart.setOption(option);
|
||||||
|
// 绑定窗口大小自适应事件(仅绑定一次)
|
||||||
|
if (!resizeObserver) {
|
||||||
|
resizeObserver = () => { myChart && myChart.resize(); };
|
||||||
|
window.addEventListener('resize', resizeObserver);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 解析接口数据为ECharts所需格式
|
||||||
|
const parseChartData = () => {
|
||||||
|
const list = priceHistoryList.value || [];
|
||||||
|
// 物料名称和规格(取第一条即可,同物料ID下一致)
|
||||||
|
const materialName = list[0]?.materialName || '物料';
|
||||||
|
const spec = list[0]?.spec || '';
|
||||||
|
// X轴数据:historyId(唯一标识),Y轴数据:价格(转数值)
|
||||||
|
const xData = list.map(item => item.historyId);
|
||||||
|
const yData = list.map(item => Number(item.price) || 0);
|
||||||
|
return { materialName, spec, xData, yData };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 销毁ECharts实例
|
||||||
|
const destroyChart = () => {
|
||||||
|
if (myChart && echarts.getInstanceByDom(chartRef.value)) {
|
||||||
|
myChart.dispose();
|
||||||
|
myChart = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听物料ID变化,重新请求数据
|
||||||
|
watch(() => props.materialId, (newVal, oldVal) => {
|
||||||
|
if (newVal && newVal !== oldVal) {
|
||||||
|
getListChart();
|
||||||
|
} else if (!newVal) {
|
||||||
|
// 清空物料ID时,重置列表和销毁图表
|
||||||
|
priceHistoryList.value = [];
|
||||||
|
destroyChart();
|
||||||
|
}
|
||||||
|
}, { immediate: true }); // 立即执行:初始加载若有materialId则直接请求
|
||||||
|
|
||||||
|
// 初始化:若初始有物料ID,直接请求数据
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.materialId) {
|
||||||
|
getListChart();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 销毁:移除监听、销毁图表实例,防止内存泄漏
|
||||||
|
onUnmounted(() => {
|
||||||
|
// destroyChart();
|
||||||
|
if (resizeObserver) {
|
||||||
|
window.removeEventListener('resize', resizeObserver);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getListChart
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 图表容器样式:设置固定高度,宽度100%自适应父容器 */
|
||||||
|
.price-chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,17 +1,312 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="stock-dashboard-container" style="padding: 20px;">
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<el-loading v-loading="loading" text="数据加载中...">
|
||||||
|
<!-- 第一行:4个指标卡 -->
|
||||||
|
<el-row :gutter="20" mb="20">
|
||||||
|
<el-col :span="6" v-for="item in statCards" :key="item.key">
|
||||||
|
<el-card shadow="hover" class="stat-card">
|
||||||
|
<el-statistic
|
||||||
|
:title="item.title"
|
||||||
|
:value="item.value"
|
||||||
|
:precision="item.precision || 2"
|
||||||
|
:suffix="item.suffix"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 第二行:3个图表组件 -->
|
||||||
|
<el-row :gutter="20" mb="20" style="margin-top: 10px;">
|
||||||
|
<!-- 厂家汇总柱状图 -->
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" title="按厂家汇总材料数/库存数">
|
||||||
|
<FactoryBarChart :factory-data="factoryBarData" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<!-- 库存总量时间趋势折线图 -->
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" title="库存总量随时间变化趋势">
|
||||||
|
<StockTrendChart :trend-data="stockTrendData" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<!-- 出入库数量对比折线图 -->
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" title="出入库数量按时间对比">
|
||||||
|
<InOutCompareChart :in-out-data="inOutCompareData" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 第三行:材料原始数据表格(带分页) -->
|
||||||
|
<el-row style="margin-top: 10px;">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-card shadow="hover" title="材料原始数据">
|
||||||
|
<el-table
|
||||||
|
:data="paginationMaterialList"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
style="width: 100%; margin-bottom: 20px;"
|
||||||
|
empty-text="暂无材料数据"
|
||||||
|
>
|
||||||
|
<el-table-column prop="materialName" label="材料名称" align="center" />
|
||||||
|
<el-table-column prop="spec" label="规格" align="center" />
|
||||||
|
<el-table-column prop="model" label="型号" align="center" />
|
||||||
|
<el-table-column prop="factory" label="生产厂家" align="center" />
|
||||||
|
<el-table-column prop="unit" label="单位" align="center" />
|
||||||
|
<el-table-column
|
||||||
|
prop="currentStock"
|
||||||
|
label="当前库存"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
<el-table-column prop="inTransitNum" label="在途数" align="center" />
|
||||||
|
<!-- <el-table-column prop="planNum" label="计划数" align="center" /> -->
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页组件 -->
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="queryParams.pageNum"
|
||||||
|
v-model:page-size="queryParams.pageSize"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
@size-change="getMaterialList"
|
||||||
|
@current-change="getMaterialList"
|
||||||
|
style="text-align: right;"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-loading>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
// 库存看板
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
// 导入接口
|
||||||
import { listPurchaseInDetail } from "@/api/mat/purchaseInDetail";
|
import { listPurchaseInDetail } from "@/api/mat/purchaseInDetail";
|
||||||
import { listMaterialOut } from "@/api/mat/materialOut";
|
import { listMaterialOut } from "@/api/mat/materialOut";
|
||||||
import { listPurchase } from "@/api/mat/purchase";
|
|
||||||
import { listMaterial } from "@/api/mat/material";
|
import { listMaterial } from "@/api/mat/material";
|
||||||
|
// 导入封装的图表组件
|
||||||
|
import FactoryBarChart from '../components/charts/FactoryBarChart.vue'
|
||||||
|
import StockTrendChart from '../components/charts/StockTrendChart.vue'
|
||||||
|
import InOutCompareChart from '../components/charts/InOutCompareChart.vue'
|
||||||
|
|
||||||
const purchaseList = ref([]);
|
// 响应式数据:接口返回列表
|
||||||
const materialList = ref([]);
|
const materialList = ref([]) // 材料列表
|
||||||
const purchaseInDetailList = ref([]);
|
const purchaseInDetailList = ref([]) // 入库列表
|
||||||
const materialOutList = ref([]);
|
const materialOutList = ref([]) // 出库列表
|
||||||
|
// 加载状态
|
||||||
|
const loading = ref(false)
|
||||||
|
// 分页参数(若依接口标准分页)
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10
|
||||||
|
})
|
||||||
|
const total = ref(0) // 材料总条数
|
||||||
|
|
||||||
|
// 格式化数值:保留2位小数,处理字符串转数字
|
||||||
|
const formatNumber = (row, column) => {
|
||||||
|
return Number(row[column.prop] || 0).toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页后的材料列表(计算属性)
|
||||||
|
const paginationMaterialList = computed(() => {
|
||||||
|
const start = (queryParams.pageNum - 1) * queryParams.pageSize
|
||||||
|
const end = start + queryParams.pageSize
|
||||||
|
return materialList.value.slice(start, end)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 指标卡数据:库存总量、材料类型数、库存操作数、库存变化量
|
||||||
|
const statCards = ref([
|
||||||
|
{ title: '库存总量', value: 0, suffix: '件', precision: 2, key: 'totalStock' },
|
||||||
|
{ title: '材料类型数量', value: 0, key: 'materialTypeCount', precision: 0 },
|
||||||
|
{ title: '库存操作数', value: 0, key: 'stockOperateCount', precision: 0 },
|
||||||
|
{ title: '库存变化量', value: 0, suffix: '件', precision: 2, key: 'stockChange' }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 图表数据源(供子组件使用)
|
||||||
|
const factoryBarData = ref([]) // 厂家汇总柱状图数据
|
||||||
|
const stockTrendData = ref([]) // 库存趋势折线图数据
|
||||||
|
const inOutCompareData = ref([]) // 出入库对比折线图数据
|
||||||
|
|
||||||
|
// 1. 获取材料列表(带分页)
|
||||||
|
const getMaterialList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await listMaterial(queryParams) // 传入分页参数
|
||||||
|
materialList.value = res.rows || []
|
||||||
|
total.value = res.total || 0 // 若依接口返回total总条数
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取材料列表失败:', err)
|
||||||
|
materialList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取入库+出库列表(无需分页,取全部数据做统计)
|
||||||
|
const getInOutList = async () => {
|
||||||
|
try {
|
||||||
|
// 并行请求,提高效率
|
||||||
|
const [inRes, outRes] = await Promise.all([
|
||||||
|
listPurchaseInDetail(),
|
||||||
|
listMaterialOut()
|
||||||
|
])
|
||||||
|
purchaseInDetailList.value = inRes.rows || []
|
||||||
|
materialOutList.value = outRes.rows || []
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取出入库列表失败:', err)
|
||||||
|
purchaseInDetailList.value = []
|
||||||
|
materialOutList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 计算指标卡数值
|
||||||
|
const calcStatCards = () => {
|
||||||
|
// 库存总量:所有材料当前库存求和
|
||||||
|
const totalStock = materialList.value.reduce((sum, item) => {
|
||||||
|
return sum + Number(item.currentStock || 0)
|
||||||
|
}, 0)
|
||||||
|
// 材料类型数量:去重(按materialId)
|
||||||
|
const materialTypeCount = new Set(materialList.value.map(item => item.materialId)).size
|
||||||
|
// 库存操作数:入库次数+出库次数
|
||||||
|
const stockOperateCount = purchaseInDetailList.value.length + materialOutList.value.length
|
||||||
|
// 库存变化量:总入库数 - 总出库数
|
||||||
|
const totalInNum = purchaseInDetailList.value.reduce((sum, item) => sum + Number(item.inNum || 0), 0)
|
||||||
|
const totalOutNum = materialOutList.value.reduce((sum, item) => sum + Number(item.outNum || 0), 0)
|
||||||
|
const stockChange = totalInNum - totalOutNum
|
||||||
|
|
||||||
|
// 更新指标卡
|
||||||
|
statCards.value = [
|
||||||
|
{ title: '库存总量', value: totalStock, suffix: '件', precision: 2, key: 'totalStock' },
|
||||||
|
{ title: '材料类型数量', value: materialTypeCount, key: 'materialTypeCount', precision: 0 },
|
||||||
|
{ title: '库存操作数', value: stockOperateCount, key: 'stockOperateCount', precision: 0 },
|
||||||
|
{ title: '库存变化量', value: stockChange, suffix: '件', precision: 2, key: 'stockChange' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 转换厂家汇总柱状图数据:按厂家分组,统计材料数+库存数
|
||||||
|
const transformFactoryBarData = () => {
|
||||||
|
const factoryMap = new Map()
|
||||||
|
// 按厂家分组
|
||||||
|
materialList.value.forEach(item => {
|
||||||
|
const factory = item.factory || '未知厂家'
|
||||||
|
if (!factoryMap.has(factory)) {
|
||||||
|
factoryMap.set(factory, {
|
||||||
|
materialCount: 0, // 材料个数(去重后)
|
||||||
|
stockTotal: 0, // 库存总数
|
||||||
|
materialIds: new Set() // 去重材料ID
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const factoryItem = factoryMap.get(factory)
|
||||||
|
factoryItem.materialIds.add(item.materialId)
|
||||||
|
factoryItem.materialCount = factoryItem.materialIds.size
|
||||||
|
factoryItem.stockTotal += Number(item.currentStock || 0)
|
||||||
|
})
|
||||||
|
// 转换为数组供图表使用
|
||||||
|
factoryBarData.value = Array.from(factoryMap).map(([factory, data]) => ({
|
||||||
|
factory,
|
||||||
|
materialCount: data.materialCount,
|
||||||
|
stockTotal: data.stockTotal.toFixed(2)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 转换库存趋势+出入库对比图表数据(按时间分组)
|
||||||
|
const transformTimeTrendData = () => {
|
||||||
|
// 整合所有时间操作记录:入库+出库,统一格式
|
||||||
|
const allOperate = [
|
||||||
|
...purchaseInDetailList.value.map(item => ({
|
||||||
|
time: dayjs(item.inTime).format('YYYY-MM-DD'),
|
||||||
|
type: 'in',
|
||||||
|
num: Number(item.inNum || 0)
|
||||||
|
})),
|
||||||
|
...materialOutList.value.map(item => ({
|
||||||
|
time: dayjs(item.outTime).format('YYYY-MM-DD'),
|
||||||
|
type: 'out',
|
||||||
|
num: Number(item.outNum || 0)
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
if (allOperate.length === 0) {
|
||||||
|
stockTrendData.value = []
|
||||||
|
inOutCompareData.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按时间排序,获取所有唯一时间(升序)
|
||||||
|
const timeSet = new Set(allOperate.map(item => item.time))
|
||||||
|
const timeList = Array.from(timeSet).sort((a, b) => dayjs(a) - dayjs(b))
|
||||||
|
|
||||||
|
// 初始化时间分组数据:统计每日入库/出库数
|
||||||
|
const timeDataMap = {}
|
||||||
|
timeList.forEach(time => {
|
||||||
|
timeDataMap[time] = { in: 0, out: 0 }
|
||||||
|
})
|
||||||
|
allOperate.forEach(item => {
|
||||||
|
timeDataMap[item.time][item.type] += item.num
|
||||||
|
})
|
||||||
|
|
||||||
|
// 转换出入库对比数据
|
||||||
|
inOutCompareData.value = timeList.map(time => ({
|
||||||
|
time,
|
||||||
|
inNum: timeDataMap[time].in,
|
||||||
|
outNum: timeDataMap[time].out
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 转换库存趋势数据:累计库存 = 初始库存 + 累计入库 - 累计出库
|
||||||
|
// 初始库存:当前总库存 - (总入库 - 总出库),保证趋势图最后一个点匹配当前库存
|
||||||
|
const totalStockNow = materialList.value.reduce((sum, item) => sum + Number(item.currentStock || 0), 0)
|
||||||
|
const totalInAll = purchaseInDetailList.value.reduce((sum, item) => sum + Number(item.inNum || 0), 0)
|
||||||
|
const totalOutAll = materialOutList.value.reduce((sum, item) => sum + Number(item.outNum || 0), 0)
|
||||||
|
const initStock = totalStockNow - (totalInAll - totalOutAll)
|
||||||
|
|
||||||
|
let cumulativeIn = 0 // 累计入库
|
||||||
|
let cumulativeOut = 0 // 累计出库
|
||||||
|
stockTrendData.value = timeList.map(time => {
|
||||||
|
cumulativeIn += timeDataMap[time].in
|
||||||
|
cumulativeOut += timeDataMap[time].out
|
||||||
|
const currentStock = initStock + cumulativeIn - cumulativeOut
|
||||||
|
return {
|
||||||
|
time,
|
||||||
|
stock: currentStock < 0 ? 0 : currentStock // 库存不能为负
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 核心:加载所有数据并处理转换
|
||||||
|
const loadAllData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 并行请求所有数据
|
||||||
|
await Promise.all([
|
||||||
|
getMaterialList(),
|
||||||
|
getInOutList()
|
||||||
|
])
|
||||||
|
// 数据转换:指标卡 + 所有图表
|
||||||
|
calcStatCards()
|
||||||
|
transformFactoryBarData()
|
||||||
|
transformTimeTrendData()
|
||||||
|
} catch (err) {
|
||||||
|
console.error('数据加载失败:', err)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面挂载时加载数据
|
||||||
|
onMounted(() => {
|
||||||
|
loadAllData()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.stat-card {
|
||||||
|
height: 120px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
/* 图表容器高度由子组件控制,父组件仅做布局 */
|
||||||
|
.el-card {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="queryParams.materialId" placeholder="请输入配料" clearable @keyup.enter="handleQuery" />
|
<raw-selector ref="rawSelector" v-model="queryParams.materialId" placeholder="请选择配料" clearable @keyup.enter="handleQuery" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="操作人" prop="operator">
|
<el-form-item label="操作人" prop="operator">
|
||||||
<el-input v-model="queryParams.operator" placeholder="请输入入库操作人" clearable @keyup.enter="handleQuery" />
|
<el-input v-model="queryParams.operator" placeholder="请输入入库操作人" clearable @keyup.enter="handleQuery" />
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
<el-input v-model="form.purchaseId" placeholder="请输入采购单ID 关联t_purchase.id" />
|
<el-input v-model="form.purchaseId" placeholder="请输入采购单ID 关联t_purchase.id" />
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="form.materialId" placeholder="请输入配料" />
|
<raw-selector ref="rawSelector" v-model="form.materialId" placeholder="请选择配料" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="入库数量" prop="inNum">
|
<el-form-item label="入库数量" prop="inNum">
|
||||||
<el-input v-model="form.inNum" placeholder="请输入入库数量" />
|
<el-input v-model="form.inNum" placeholder="请输入入库数量" />
|
||||||
@@ -109,6 +109,7 @@
|
|||||||
<script setup name="PurchaseInDetail">
|
<script setup name="PurchaseInDetail">
|
||||||
import { listPurchaseInDetail, getPurchaseInDetail, delPurchaseInDetail, addPurchaseInDetail, updatePurchaseInDetail } from "@/api/mat/purchaseInDetail";
|
import { listPurchaseInDetail, getPurchaseInDetail, delPurchaseInDetail, addPurchaseInDetail, updatePurchaseInDetail } from "@/api/mat/purchaseInDetail";
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import RawSelector from '@/components/RawSelector/index.vue'
|
||||||
import Raw from '@/components/Renderer/Raw.vue'
|
import Raw from '@/components/Renderer/Raw.vue'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<el-input v-model="queryParams.outNo" placeholder="请输入出库单号" clearable @keyup.enter="handleQuery" />
|
<el-input v-model="queryParams.outNo" placeholder="请输入出库单号" clearable @keyup.enter="handleQuery" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="queryParams.materialId" placeholder="请输入配料" clearable @keyup.enter="handleQuery" />
|
<raw-selector ref="rawSelector" v-model="queryParams.materialId" placeholder="请选择配料" clearable @keyup.enter="handleQuery" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="操作人" prop="operator">
|
<el-form-item label="操作人" prop="operator">
|
||||||
<el-input v-model="queryParams.operator" placeholder="请输入出库操作人" clearable @keyup.enter="handleQuery" />
|
<el-input v-model="queryParams.operator" placeholder="请输入出库操作人" clearable @keyup.enter="handleQuery" />
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
<el-input v-model="form.outNo" placeholder="请输入出库单号" />
|
<el-input v-model="form.outNo" placeholder="请输入出库单号" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="form.materialId" placeholder="请输入配料" />
|
<raw-selector ref="rawSelector" v-model="form.materialId" placeholder="请选择配料" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="出库数量" prop="outNum">
|
<el-form-item label="出库数量" prop="outNum">
|
||||||
<el-input v-model="form.outNum" placeholder="请输入出库数量" />
|
<el-input v-model="form.outNum" placeholder="请输入出库数量" />
|
||||||
@@ -109,6 +109,7 @@
|
|||||||
import { listMaterialOut, getMaterialOut, delMaterialOut, addMaterialOut, updateMaterialOut } from "@/api/mat/materialOut";
|
import { listMaterialOut, getMaterialOut, delMaterialOut, addMaterialOut, updateMaterialOut } from "@/api/mat/materialOut";
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import RawSelector from '@/components/RawSelector/index.vue'
|
||||||
import Raw from '@/components/Renderer/Raw.vue'
|
import Raw from '@/components/Renderer/Raw.vue'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|||||||
@@ -1,29 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container" ref="appContainer">
|
||||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||||
<el-form-item label="产品名称" prop="productName">
|
<el-form-item label="产品名称" prop="productName">
|
||||||
<el-input
|
<el-input v-model="queryParams.productName" placeholder="请输入产品名称" clearable @keyup.enter="handleQuery" />
|
||||||
v-model="queryParams.productName"
|
|
||||||
placeholder="请输入产品名称"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="产品规格" prop="spec">
|
<el-form-item label="产品规格" prop="spec">
|
||||||
<el-input
|
<el-input v-model="queryParams.spec" placeholder="请输入产品规格" clearable @keyup.enter="handleQuery" />
|
||||||
v-model="queryParams.spec"
|
|
||||||
placeholder="请输入产品规格"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="产品型号" prop="model">
|
<el-form-item label="产品型号" prop="model">
|
||||||
<el-input
|
<el-input v-model="queryParams.model" placeholder="请输入产品型号" clearable @keyup.enter="handleQuery" />
|
||||||
v-model="queryParams.model"
|
|
||||||
placeholder="请输入产品型号"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
@@ -33,66 +18,40 @@
|
|||||||
|
|
||||||
<el-row :gutter="10" class="mb8">
|
<el-row :gutter="10" class="mb8">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button
|
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
icon="Plus"
|
|
||||||
@click="handleAdd"
|
|
||||||
>新增</el-button>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button
|
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate">修改</el-button>
|
||||||
type="success"
|
|
||||||
plain
|
|
||||||
icon="Edit"
|
|
||||||
:disabled="single"
|
|
||||||
@click="handleUpdate"
|
|
||||||
>修改</el-button>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button
|
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
|
||||||
type="danger"
|
|
||||||
plain
|
|
||||||
icon="Delete"
|
|
||||||
:disabled="multiple"
|
|
||||||
@click="handleDelete"
|
|
||||||
>删除</el-button>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button
|
<el-button type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||||
type="warning"
|
|
||||||
plain
|
|
||||||
icon="Download"
|
|
||||||
@click="handleExport"
|
|
||||||
>导出</el-button>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="productList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="productList" @selection-change="handleSelectionChange"
|
||||||
|
@row-click="handleRowClick">
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column label="产品ID 主键" align="center" prop="productId" v-if="false"/>
|
<el-table-column label="产品ID 主键" align="center" prop="productId" v-if="false" />
|
||||||
<el-table-column label="产品名称" align="center" prop="productName" />
|
<el-table-column label="产品名称" align="center" prop="productName" />
|
||||||
<el-table-column label="产品规格" align="center" prop="spec" />
|
<el-table-column label="产品规格" align="center" prop="spec" />
|
||||||
<el-table-column label="产品型号" align="center" prop="model" />
|
<el-table-column label="产品型号" align="center" prop="model" />
|
||||||
<el-table-column label="产品单价" align="center" prop="unitPrice" />
|
<el-table-column label="产品单价" align="center" prop="unitPrice" />
|
||||||
<el-table-column label="备注" align="center" prop="remark" />
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button link type="primary" icon="Plus" @click="handleBom(scope.row)">配方</el-button>
|
<el-button link type="primary" icon="Plus" @click="handleBom(scope.row)">配方</el-button>
|
||||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<pagination
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
||||||
v-show="total>0"
|
v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNum"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 添加或修改产品基础信息对话框 -->
|
<!-- 添加或修改产品基础信息对话框 -->
|
||||||
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||||
@@ -121,15 +80,35 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog title="产品配方" v-model="bomOpen" width="500px" append-to-body>
|
<el-dialog title="产品配方" v-model="bomOpen" width="800px" append-to-body>
|
||||||
<bom :productId="currentProductId" @close="bomOpen = false" />
|
<bom :productId="currentProductId" @close="bomOpen = false" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 将这个表格改为始终吸底,且外层容器可以拖拽调节高度 -->
|
||||||
|
<!-- <StickyDragContainer v-if="currentProduct.productId" :parent-ref="appContainer"> -->
|
||||||
|
<div v-if="currentProduct.productId">
|
||||||
|
<h3>产品配料</h3>
|
||||||
|
<!-- 插槽内容:原有配料表格,无需任何修改 -->
|
||||||
|
<el-table :data="currentProduct.materials">
|
||||||
|
<el-table-column label="配料名称" align="center" prop="materialName" />
|
||||||
|
<el-table-column label="配料规格" align="center" prop="spec" />
|
||||||
|
<el-table-column label="配料型号" align="center" prop="model" />
|
||||||
|
<el-table-column label="厂家" align="center" prop="factory" />
|
||||||
|
<el-table-column label="计量单位" align="center" prop="unit" />
|
||||||
|
<el-table-column label="现存库存" align="center" prop="currentStock" />
|
||||||
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else description="选择产品查看配料信息" />
|
||||||
|
|
||||||
|
<!-- </StickyDragContainer> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="Product">
|
<script setup name="Product">
|
||||||
import { listProduct, getProduct, delProduct, addProduct, updateProduct } from "@/api/mat/product";
|
import { listProduct, getProduct, delProduct, addProduct, updateProduct } from "@/api/mat/product";
|
||||||
import bom from "@/views/mat/components/bom.vue";
|
import bom from "@/views/mat/components/bom.vue";
|
||||||
|
import StickyDragContainer from "@/components/StickyDragContainer/index.vue";
|
||||||
|
|
||||||
const bomOpen = ref(false);
|
const bomOpen = ref(false);
|
||||||
|
|
||||||
@@ -146,6 +125,8 @@ const multiple = ref(true);
|
|||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
const title = ref("");
|
const title = ref("");
|
||||||
const currentProductId = ref(null);
|
const currentProductId = ref(null);
|
||||||
|
const currentProduct = ref({});
|
||||||
|
const appContainer = ref(null);
|
||||||
|
|
||||||
const formatterTime = (time) => {
|
const formatterTime = (time) => {
|
||||||
return proxy.parseTime(time, '{y}-{m}-{d}')
|
return proxy.parseTime(time, '{y}-{m}-{d}')
|
||||||
@@ -268,7 +249,7 @@ function submitForm() {
|
|||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
function handleDelete(row) {
|
function handleDelete(row) {
|
||||||
const _productIds = row.productId || ids.value;
|
const _productIds = row.productId || ids.value;
|
||||||
proxy.$modal.confirm('是否确认删除产品基础信息编号为"' + _productIds + '"的数据项?').then(function() {
|
proxy.$modal.confirm('是否确认删除产品基础信息编号为"' + _productIds + '"的数据项?').then(function () {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
return delProduct(_productIds);
|
return delProduct(_productIds);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -293,5 +274,9 @@ function handleExport() {
|
|||||||
}, `product_${new Date().getTime()}.xlsx`)
|
}, `product_${new Date().getTime()}.xlsx`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleRowClick(row) {
|
||||||
|
currentProduct.value = row;
|
||||||
|
}
|
||||||
|
|
||||||
getList();
|
getList();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<el-input v-model="queryParams.factory" placeholder="请输入供应商" clearable @keyup.enter="handleQuery" />
|
<el-input v-model="queryParams.factory" placeholder="请输入供应商" clearable @keyup.enter="handleQuery" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="queryParams.materialId" placeholder="请输入配料" clearable @keyup.enter="handleQuery" />
|
<raw-selector ref="rawSelector" v-model="queryParams.materialId" placeholder="请选择配料" clearable @keyup.enter="handleQuery" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="采购状态" prop="status">
|
<el-form-item label="采购状态" prop="status">
|
||||||
<el-select style="width: 120px" v-model="queryParams.status" placeholder="请选择采购状态" clearable>
|
<el-select style="width: 120px" v-model="queryParams.status" placeholder="请选择采购状态" clearable>
|
||||||
@@ -51,7 +51,9 @@
|
|||||||
<raw :data="scope.row" />
|
<raw :data="scope.row" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="采购数量" align="center" prop="planNum" />
|
<el-table-column label="采购总数" align="center" prop="planNum" />
|
||||||
|
<el-table-column label="已收数量" align="center" prop="receivedNum" />
|
||||||
|
<el-table-column label="在途数量" align="center" prop="inTransitNum" />
|
||||||
<el-table-column label="采购单价" align="center" prop="purchasePrice" />
|
<el-table-column label="采购单价" align="center" prop="purchasePrice" />
|
||||||
<el-table-column label="截止日期" align="center" prop="deadline" width="180">
|
<el-table-column label="截止日期" align="center" prop="deadline" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -73,8 +75,8 @@
|
|||||||
@click="handleUpdate(scope.row)">修改</el-button>
|
@click="handleUpdate(scope.row)">修改</el-button>
|
||||||
<el-button link type="primary" icon="Delete" v-if="scope.row.status == 1 || scope.row.status == 3"
|
<el-button link type="primary" icon="Delete" v-if="scope.row.status == 1 || scope.row.status == 3"
|
||||||
@click="handleDelete(scope.row)">删除</el-button>
|
@click="handleDelete(scope.row)">删除</el-button>
|
||||||
<el-button link type="primary" icon="Delete" v-if="scope.row.status == 1 || scope.row.status == 3"
|
<el-button link type="primary" icon="Check" v-if="scope.row.status == 1 || scope.row.status == 3"
|
||||||
@click="handleZero(scope.row)">归零</el-button>
|
@click="handleZero(scope.row)">完成</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -92,7 +94,9 @@
|
|||||||
<el-input v-model="form.factory" placeholder="请输入供应商" />
|
<el-input v-model="form.factory" placeholder="请输入供应商" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="form.materialId" placeholder="请输入配料ID 关联material.id" />
|
<raw-selector v-model="form.materialId" />
|
||||||
|
<!-- <el-input v-model="form.materialId" placeholder="请输入配料ID 关联material.id" /> -->
|
||||||
|
<!-- <el-input v-model="form.materialId" placeholder="请输入配料ID 关联material.id" /> -->
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="采购数量" prop="planNum" v-if="!form.purchaseId">
|
<el-form-item label="采购数量" prop="planNum" v-if="!form.purchaseId">
|
||||||
<el-input v-model="form.planNum" placeholder="请输入采购数量" />
|
<el-input v-model="form.planNum" placeholder="请输入采购数量" />
|
||||||
@@ -135,7 +139,7 @@
|
|||||||
<el-input v-model="inForm.materialId" placeholder="请输入配料" />
|
<el-input v-model="inForm.materialId" placeholder="请输入配料" />
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
<el-form-item label="入库数量" prop="inNum">
|
<el-form-item label="入库数量" prop="inNum">
|
||||||
<el-input v-model="inForm.inNum" placeholder="请输入入库数量" />
|
<el-input-number style="width: 100%" v-model="inForm.inNum" :placeholder="'请输入入库数量,最大数量为' + maxInNum" :max="maxInNum" :controls="false" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="入库单价" prop="inPrice">
|
<el-form-item label="入库单价" prop="inPrice">
|
||||||
<el-input v-model="inForm.inPrice" placeholder="请输入入库单价" />
|
<el-input v-model="inForm.inPrice" placeholder="请输入入库单价" />
|
||||||
@@ -161,7 +165,7 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<div style="border: 1px solid #aaa; margin-top: 10px;">
|
<div style="border: 1px solid #aaa; margin-top: 10px;">
|
||||||
<PurchaseInDetail v-if="currentPurchaseId" :purchaseId="currentPurchaseId" />
|
<PurchaseInDetail ref="inDetailRef" v-if="currentPurchaseId" :purchaseId="currentPurchaseId" />
|
||||||
<el-empty v-else description="选择采购单查看入库记录详情" />
|
<el-empty v-else description="选择采购单查看入库记录详情" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -172,6 +176,7 @@
|
|||||||
import { listPurchase, getPurchase, delPurchase, addPurchase, updatePurchase } from "@/api/mat/purchase";
|
import { listPurchase, getPurchase, delPurchase, addPurchase, updatePurchase } from "@/api/mat/purchase";
|
||||||
import { listPurchaseInDetail, addPurchaseInDetail } from "@/api/mat/purchaseInDetail";
|
import { listPurchaseInDetail, addPurchaseInDetail } from "@/api/mat/purchaseInDetail";
|
||||||
import PurchaseInDetail from "@/views/mat/components/in.vue"
|
import PurchaseInDetail from "@/views/mat/components/in.vue"
|
||||||
|
import RawSelector from '@/components/RawSelector/index.vue'; // 引入组件
|
||||||
import Raw from "@/components/Renderer/Raw.vue"
|
import Raw from "@/components/Renderer/Raw.vue"
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
@@ -362,10 +367,13 @@ function handleExport() {
|
|||||||
|
|
||||||
const inOpen = ref(false);
|
const inOpen = ref(false);
|
||||||
const inForm = ref({});
|
const inForm = ref({});
|
||||||
|
const maxInNum = ref(0);
|
||||||
|
const inDetailRef = ref(null);
|
||||||
|
|
||||||
function handleIn(row) {
|
function handleIn(row) {
|
||||||
inOpen.value = true;
|
inOpen.value = true;
|
||||||
const inTime = proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
|
const inTime = proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
|
||||||
|
maxInNum.value = row.inTransitNum;
|
||||||
|
|
||||||
inForm.value = {
|
inForm.value = {
|
||||||
detailId: null,
|
detailId: null,
|
||||||
@@ -388,6 +396,7 @@ function submitFormIn() {
|
|||||||
proxy.$modal.msgSuccess("新增成功");
|
proxy.$modal.msgSuccess("新增成功");
|
||||||
inOpen.value = false;
|
inOpen.value = false;
|
||||||
getList();
|
getList();
|
||||||
|
inDetailRef.value?.getList();
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
buttonLoading.value = false;
|
buttonLoading.value = false;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -400,5 +409,22 @@ function cancelIn() {
|
|||||||
inOpen.value = false;
|
inOpen.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleZero(row) {
|
||||||
|
proxy.$modal.confirm('是否将采购单编号为"' + row.purchaseId + '"的计划数量设为与已收数量相同,且状态设为已完成?').then(function () {
|
||||||
|
loading.value = true;
|
||||||
|
return updatePurchase({
|
||||||
|
purchaseId: row.purchaseId,
|
||||||
|
planNum: row.receivedNum,
|
||||||
|
status: 2,
|
||||||
|
});
|
||||||
|
}).then(() => {
|
||||||
|
loading.value = true;
|
||||||
|
getList();
|
||||||
|
proxy.$modal.msgSuccess("发货计划已归档");
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getList();
|
getList();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="materialList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="materialList" @selection-change="handleSelectionChange" @row-click="handleRowClick">
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<!-- <el-table-column label="配料ID 主键" align="center" prop="materialId" v-if="true" /> -->
|
<!-- <el-table-column label="配料ID 主键" align="center" prop="materialId" v-if="true" /> -->
|
||||||
<el-table-column label="配料名称" align="center" prop="materialName" />
|
<el-table-column label="配料名称" align="center" prop="materialName" />
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
<el-table-column label="厂家" align="center" prop="factory" />
|
<el-table-column label="厂家" align="center" prop="factory" />
|
||||||
<el-table-column label="计量单位" align="center" prop="unit" />
|
<el-table-column label="计量单位" align="center" prop="unit" />
|
||||||
<el-table-column label="现存库存" align="center" prop="currentStock" />
|
<el-table-column label="现存库存" align="center" prop="currentStock" />
|
||||||
|
<el-table-column label="在途数量" align="center" prop="inTransitNum" />
|
||||||
<el-table-column label="备注" align="center" prop="remark" />
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -100,7 +101,7 @@
|
|||||||
<el-dialog title="入库" v-model="inOpen" width="500px" append-to-body>
|
<el-dialog title="入库" v-model="inOpen" width="500px" append-to-body>
|
||||||
<el-form ref="materialInRef" :model="inForm" label-width="80px">
|
<el-form ref="materialInRef" :model="inForm" label-width="80px">
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="inForm.materialId" placeholder="请输入配料" />
|
<raw-selector ref="rawSelector" v-model="inForm.materialId" placeholder="请选择配料" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="入库数量" prop="inNum">
|
<el-form-item label="入库数量" prop="inNum">
|
||||||
<el-input v-model="inForm.inNum" placeholder="请输入入库数量" />
|
<el-input v-model="inForm.inNum" placeholder="请输入入库数量" />
|
||||||
@@ -134,7 +135,7 @@
|
|||||||
<el-input v-model="outForm.outNo" placeholder="请输入出库单号" />
|
<el-input v-model="outForm.outNo" placeholder="请输入出库单号" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="配料" prop="materialId">
|
<el-form-item label="配料" prop="materialId">
|
||||||
<el-input v-model="outForm.materialId" placeholder="请输入配料" />
|
<raw-selector ref="rawSelector" v-model="outForm.materialId" placeholder="请选择配料" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="出库数量" prop="outNum">
|
<el-form-item label="出库数量" prop="outNum">
|
||||||
<el-input v-model="outForm.outNum" placeholder="请输入出库数量" />
|
<el-input v-model="outForm.outNum" placeholder="请输入出库数量" />
|
||||||
@@ -161,6 +162,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
|
||||||
|
<Price :materialId="currentMaterialId" ref="priceRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -168,6 +172,8 @@
|
|||||||
import { listMaterial, getMaterial, delMaterial, addMaterial, updateMaterial } from "@/api/mat/material";
|
import { listMaterial, getMaterial, delMaterial, addMaterial, updateMaterial } from "@/api/mat/material";
|
||||||
import { addPurchaseInDetail } from "@/api/mat/purchaseInDetail";
|
import { addPurchaseInDetail } from "@/api/mat/purchaseInDetail";
|
||||||
import { addMaterialOut } from "@/api/mat/materialOut";
|
import { addMaterialOut } from "@/api/mat/materialOut";
|
||||||
|
import Price from "@/views/mat/components/price.vue";
|
||||||
|
import RawSelector from "@/components/RawSelector/index.vue";
|
||||||
|
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
|
||||||
@@ -191,6 +197,14 @@ const outForm = ref({});
|
|||||||
const inOpen = ref(false);
|
const inOpen = ref(false);
|
||||||
const outOpen = ref(false);
|
const outOpen = ref(false);
|
||||||
|
|
||||||
|
const currentMaterialId = ref(null);
|
||||||
|
|
||||||
|
function handleRowClick(row) {
|
||||||
|
currentMaterialId.value = row.materialId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const priceHistoryList = ref([]);
|
||||||
|
|
||||||
const nickName = computed(() => userStore.nickName)
|
const nickName = computed(() => userStore.nickName)
|
||||||
|
|
||||||
|
|
||||||
@@ -367,6 +381,8 @@ function cancelIn() {
|
|||||||
inOpen.value = false;
|
inOpen.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const priceRef = ref(null);
|
||||||
|
|
||||||
function submitFormIn() {
|
function submitFormIn() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
buttonLoading.value = true;
|
buttonLoading.value = true;
|
||||||
@@ -374,6 +390,7 @@ function submitFormIn() {
|
|||||||
proxy.$modal.msgSuccess("新增成功");
|
proxy.$modal.msgSuccess("新增成功");
|
||||||
inOpen.value = false;
|
inOpen.value = false;
|
||||||
getList();
|
getList();
|
||||||
|
priceRef.value.getListChart();
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
buttonLoading.value = false;
|
buttonLoading.value = false;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user