优化采购需求
This commit is contained in:
parent
50f8a07b1b
commit
23cf2ed453
@ -861,11 +861,64 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover{
|
|||||||
.toast.show{opacity:1}
|
.toast.show{opacity:1}
|
||||||
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center}
|
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center}
|
||||||
.overlay.hidden{display:none}
|
.overlay.hidden{display:none}
|
||||||
.loader{display:flex;gap:8px}
|
|
||||||
.loader .dot{width:10px;height:10px;border-radius:999px;background:var(--primary);animation:bounce .9s ease infinite}
|
/* 芯片电路板加载动画 */
|
||||||
.loader .dot:nth-child(2){animation-delay:.15s}
|
.main-container {
|
||||||
.loader .dot:nth-child(3){animation-delay:.3s}
|
display: flex;
|
||||||
@keyframes bounce{0%,100%{transform:translateY(0);opacity:.7}50%{transform:translateY(-8px);opacity:1}}
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
height: 100%;
|
||||||
|
max-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-bg {
|
||||||
|
stroke: #333;
|
||||||
|
stroke-width: 1.8;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-flow {
|
||||||
|
stroke-width: 1.8;
|
||||||
|
fill: none;
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-dashoffset: 438;
|
||||||
|
filter: drop-shadow(0 0 6px currentColor);
|
||||||
|
animation: flow 3s cubic-bezier(0.5, 0, 0.9, 1) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yellow {
|
||||||
|
stroke: #ffea00;
|
||||||
|
color: #ffea00;
|
||||||
|
}
|
||||||
|
.blue {
|
||||||
|
stroke: #00ccff;
|
||||||
|
color: #00ccff;
|
||||||
|
}
|
||||||
|
.green {
|
||||||
|
stroke: #00ff15;
|
||||||
|
color: #00ff15;
|
||||||
|
}
|
||||||
|
.purple {
|
||||||
|
stroke: #9900ff;
|
||||||
|
color: #9900ff;
|
||||||
|
}
|
||||||
|
.red {
|
||||||
|
stroke: #ff3300;
|
||||||
|
color: #ff3300;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes flow {
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.three-body {
|
.three-body {
|
||||||
--uib-size: 35px;
|
--uib-size: 35px;
|
||||||
@ -970,64 +1023,6 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
position: relative;
|
|
||||||
perspective: 800px;
|
|
||||||
animation: spinner-y0fdc1 2s infinite ease;
|
|
||||||
transform-style: preserve-3d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner > div {
|
|
||||||
background-color: rgba(0,77,255,0.2);
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
border: 2px solid #004dff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner div:nth-of-type(1) {
|
|
||||||
transform: translateZ(-22px) rotateY(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner div:nth-of-type(2) {
|
|
||||||
transform: rotateY(-270deg) translateX(50%);
|
|
||||||
transform-origin: top right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner div:nth-of-type(3) {
|
|
||||||
transform: rotateY(270deg) translateX(-50%);
|
|
||||||
transform-origin: center left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner div:nth-of-type(4) {
|
|
||||||
transform: rotateX(90deg) translateY(-50%);
|
|
||||||
transform-origin: top center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner div:nth-of-type(5) {
|
|
||||||
transform: rotateX(-90deg) translateY(50%);
|
|
||||||
transform-origin: bottom center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner div:nth-of-type(6) {
|
|
||||||
transform: translateZ(22px);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spinner-y0fdc1 {
|
|
||||||
0% {
|
|
||||||
transform: rotate(45deg) rotateX(-25deg) rotateY(25deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
transform: rotate(45deg) rotateX(-385deg) rotateY(25deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: rotate(45deg) rotateX(-385deg) rotateY(385deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fade-enter{opacity:0;transform:translateY(8px)}
|
.fade-enter{opacity:0;transform:translateY(8px)}
|
||||||
.fade-enter-active{transition:opacity .25s ease-out,transform .25s ease-out;opacity:1;transform:translateY(0)}
|
.fade-enter-active{transition:opacity .25s ease-out,transform .25s ease-out;opacity:1;transform:translateY(0)}
|
||||||
.error{color:#ffb4b4}
|
.error{color:#ffb4b4}
|
||||||
|
|||||||
@ -279,13 +279,159 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="toast" class="toast"></div>
|
<div id="toast" class="toast"></div>
|
||||||
<div id="overlay" class="overlay hidden">
|
<div id="overlay" class="overlay hidden">
|
||||||
<div class="spinner">
|
<div class="main-container">
|
||||||
<div></div>
|
<div class="loader">
|
||||||
<div></div>
|
<svg viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
<div></div>
|
<defs>
|
||||||
<div></div>
|
<linearGradient id="chipGradient" x1="0" y1="0" x2="0" y2="1">
|
||||||
<div></div>
|
<stop offset="0%" stop-color="#2d2d2d"></stop>
|
||||||
<div></div>
|
<stop offset="100%" stop-color="#0f0f0f"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient id="textGradient" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0%" stop-color="#eeeeee"></stop>
|
||||||
|
<stop offset="100%" stop-color="#888888"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient id="pinGradient" x1="1" y1="0" x2="0" y2="0">
|
||||||
|
<stop offset="0%" stop-color="#bbbbbb"></stop>
|
||||||
|
<stop offset="50%" stop-color="#888888"></stop>
|
||||||
|
<stop offset="100%" stop-color="#555555"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<g id="traces">
|
||||||
|
<path d="M100 100 H200 V210 H326" class="trace-bg"></path>
|
||||||
|
<path d="M100 100 H200 V210 H326" class="trace-flow purple"></path>
|
||||||
|
|
||||||
|
<path d="M80 180 H180 V230 H326" class="trace-bg"></path>
|
||||||
|
<path d="M80 180 H180 V230 H326" class="trace-flow blue"></path>
|
||||||
|
|
||||||
|
<path d="M60 260 H150 V250 H326" class="trace-bg"></path>
|
||||||
|
<path d="M60 260 H150 V250 H326" class="trace-flow yellow"></path>
|
||||||
|
|
||||||
|
<path d="M100 350 H200 V270 H326" class="trace-bg"></path>
|
||||||
|
<path d="M100 350 H200 V270 H326" class="trace-flow green"></path>
|
||||||
|
|
||||||
|
<path d="M700 90 H560 V210 H474" class="trace-bg"></path>
|
||||||
|
<path d="M700 90 H560 V210 H474" class="trace-flow blue"></path>
|
||||||
|
|
||||||
|
<path d="M740 160 H580 V230 H474" class="trace-bg"></path>
|
||||||
|
<path d="M740 160 H580 V230 H474" class="trace-flow green"></path>
|
||||||
|
|
||||||
|
<path d="M720 250 H590 V250 H474" class="trace-bg"></path>
|
||||||
|
<path d="M720 250 H590 V250 H474" class="trace-flow red"></path>
|
||||||
|
|
||||||
|
<path d="M680 340 H570 V270 H474" class="trace-bg"></path>
|
||||||
|
<path d="M680 340 H570 V270 H474" class="trace-flow yellow"></path>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<rect
|
||||||
|
x="330"
|
||||||
|
y="190"
|
||||||
|
width="140"
|
||||||
|
height="100"
|
||||||
|
rx="20"
|
||||||
|
ry="20"
|
||||||
|
fill="url(#chipGradient)"
|
||||||
|
stroke="#222"
|
||||||
|
stroke-width="3"
|
||||||
|
filter="drop-shadow(0 0 6px rgba(0,0,0,0.8))"
|
||||||
|
></rect>
|
||||||
|
|
||||||
|
<g>
|
||||||
|
<rect
|
||||||
|
x="322"
|
||||||
|
y="205"
|
||||||
|
width="8"
|
||||||
|
height="10"
|
||||||
|
fill="url(#pinGradient)"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="322"
|
||||||
|
y="225"
|
||||||
|
width="8"
|
||||||
|
height="10"
|
||||||
|
fill="url(#pinGradient)"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="322"
|
||||||
|
y="245"
|
||||||
|
width="8"
|
||||||
|
height="10"
|
||||||
|
fill="url(#pinGradient)"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="322"
|
||||||
|
y="265"
|
||||||
|
width="8"
|
||||||
|
height="10"
|
||||||
|
fill="url(#pinGradient)"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g>
|
||||||
|
<rect
|
||||||
|
x="470"
|
||||||
|
y="205"
|
||||||
|
width="8"
|
||||||
|
height="10"
|
||||||
|
fill="url(#pinGradient)"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="470"
|
||||||
|
y="225"
|
||||||
|
width="8"
|
||||||
|
height="10"
|
||||||
|
fill="url(#pinGradient)"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="470"
|
||||||
|
y="245"
|
||||||
|
width="8"
|
||||||
|
height="10"
|
||||||
|
fill="url(#pinGradient)"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
x="470"
|
||||||
|
y="265"
|
||||||
|
width="8"
|
||||||
|
height="10"
|
||||||
|
fill="url(#pinGradient)"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<text
|
||||||
|
x="400"
|
||||||
|
y="240"
|
||||||
|
font-family="Arial, sans-serif"
|
||||||
|
font-size="22"
|
||||||
|
fill="url(#textGradient)"
|
||||||
|
text-anchor="middle"
|
||||||
|
alignment-baseline="middle"
|
||||||
|
>
|
||||||
|
Loading
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<circle cx="100" cy="100" r="5" fill="black"></circle>
|
||||||
|
<circle cx="80" cy="180" r="5" fill="black"></circle>
|
||||||
|
<circle cx="60" cy="260" r="5" fill="black"></circle>
|
||||||
|
<circle cx="100" cy="350" r="5" fill="black"></circle>
|
||||||
|
|
||||||
|
<circle cx="700" cy="90" r="5" fill="black"></circle>
|
||||||
|
<circle cx="740" cy="160" r="5" fill="black"></circle>
|
||||||
|
<circle cx="720" cy="250" r="5" fill="black"></circle>
|
||||||
|
<circle cx="680" cy="340" r="5" fill="black"></circle>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -127,15 +127,67 @@
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#purchase-demand-page .product-tabs-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
#purchase-demand-page .product-tab {
|
#purchase-demand-page .product-tab {
|
||||||
border-bottom: 2px solid transparent;
|
min-width: 100px;
|
||||||
border-radius: 4px 4px 0 0;
|
height: 51px;
|
||||||
|
border-radius: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.3s ease;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom right,
|
||||||
|
#2e8eff 0%,
|
||||||
|
rgba(46, 142, 255, 0) 30%
|
||||||
|
);
|
||||||
|
background-color: rgba(46, 142, 255, 0.2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#purchase-demand-page .product-tab:hover,
|
||||||
|
#purchase-demand-page .product-tab:focus {
|
||||||
|
background-color: rgba(46, 142, 255, 0.7);
|
||||||
|
box-shadow: 0 0 10px rgba(46, 142, 255, 0.5);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#purchase-demand-page .product-tab.active {
|
#purchase-demand-page .product-tab.active {
|
||||||
background: var(--primary);
|
background-color: rgba(46, 142, 255, 0.9);
|
||||||
color: white;
|
box-shadow: 0 0 15px rgba(46, 142, 255, 0.7);
|
||||||
border-bottom-color: var(--primary);
|
}
|
||||||
|
|
||||||
|
#purchase-demand-page .product-tab-inner {
|
||||||
|
width: 100%;
|
||||||
|
height: 47px;
|
||||||
|
border-radius: 13px;
|
||||||
|
background-color: var(--bg);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
color: #66b3ff;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
text-shadow: 0 0 2px rgba(102, 179, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] #purchase-demand-page .product-tab-inner {
|
||||||
|
color: #0066cc;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#purchase-demand-page .product-tab.active .product-tab-inner {
|
||||||
|
color: #003d7a;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@ -153,16 +205,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-area">
|
<div class="content-area">
|
||||||
<!-- 计算说明提示 -->
|
|
||||||
<div style="margin: 0 20px 16px 20px; padding: 8px 16px; background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%); border-left: 4px solid #2196f3; border-radius: 4px; font-size: 13px; color: #424242;">
|
|
||||||
<strong>采购需求计算公式:</strong>
|
|
||||||
客户订单数量 × BOM单机用量 - 期初库存 = 净需求 → 按最小包装向上取整 = 实际采购数量
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 产品选择标签页 -->
|
<!-- 产品选择标签页 -->
|
||||||
<div style="padding: 0 20px;">
|
<div style="padding: 0 20px;">
|
||||||
<div style="display: flex; gap: 10px; margin-bottom: 16px; border-bottom: 2px solid var(--border); padding-bottom: 10px;">
|
<div class="product-tabs-wrapper" id="product-tabs-container">
|
||||||
<button class="product-tab btn btn-secondary" data-product="" onclick="PurchaseDemand.switchProduct('')">全部</button>
|
<button class="product-tab active" data-product="" onclick="PurchaseDemand.switchProduct('')">
|
||||||
|
<div class="product-tab-inner">全部</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -466,6 +515,25 @@
|
|||||||
<option value="cancelled">已取消</option>
|
<option value="cancelled">已取消</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>应用范围</label>
|
||||||
|
<div style="display: flex; gap: 20px; margin-top: 8px;">
|
||||||
|
<label for="batch-scope-selected" style="display: flex; align-items: center; cursor: pointer;">
|
||||||
|
<input type="radio" id="batch-scope-selected" name="batch-scope" value="selected" checked style="margin-right: 8px;">
|
||||||
|
仅当前选中的记录
|
||||||
|
</label>
|
||||||
|
<label for="batch-scope-all" style="display: flex; align-items: center; cursor: pointer;">
|
||||||
|
<input type="radio" id="batch-scope-all" name="batch-scope" value="all" style="margin-right: 8px;">
|
||||||
|
指定产品的所有记录
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="batch-product-select-container" style="margin-top: 12px; display: none;">
|
||||||
|
<label style="font-size: 14px; margin-bottom: 4px; display: block;">选择产品</label>
|
||||||
|
<select id="batch-product-select" class="input" style="width: 100%;">
|
||||||
|
<option value="">请选择产品</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>备注(可选)</label>
|
<label>备注(可选)</label>
|
||||||
<textarea id="batch-remark" class="input" rows="3" placeholder="可以为所有选中的项目添加统一的备注"></textarea>
|
<textarea id="batch-remark" class="input" rows="3" placeholder="可以为所有选中的项目添加统一的备注"></textarea>
|
||||||
@ -537,14 +605,20 @@
|
|||||||
console.log('检查数据是否有product_code字段:', demandList.some(item => 'product_code' in item)); // 检查字段是否存在
|
console.log('检查数据是否有product_code字段:', demandList.some(item => 'product_code' in item)); // 检查字段是否存在
|
||||||
|
|
||||||
// 更新标签页
|
// 更新标签页
|
||||||
const tabsContainer = document.querySelector('.product-tab').parentElement;
|
const tabsContainer = document.getElementById('product-tabs-container');
|
||||||
if (!tabsContainer) return;
|
if (!tabsContainer) return;
|
||||||
|
|
||||||
let html = '<button class="product-tab btn btn-secondary" data-product="" onclick="PurchaseDemand.switchProduct(\'\')">全部</button>';
|
const productArray = Array.from(products).sort();
|
||||||
|
|
||||||
Array.from(products).sort().forEach(product => {
|
let html = `<button class="product-tab ${!currentProduct ? 'active' : ''}" data-product="" onclick="PurchaseDemand.switchProduct('')">
|
||||||
|
<div class="product-tab-inner">全部</div>
|
||||||
|
</button>`;
|
||||||
|
|
||||||
|
productArray.forEach(product => {
|
||||||
const isActive = product === currentProduct ? 'active' : '';
|
const isActive = product === currentProduct ? 'active' : '';
|
||||||
html += `<button class="product-tab btn btn-secondary ${isActive}" data-product="${product}" onclick="PurchaseDemand.switchProduct('${product}')">${product}</button>`;
|
html += `<button class="product-tab ${isActive}" data-product="${product}" onclick="PurchaseDemand.switchProduct('${product}')">
|
||||||
|
<div class="product-tab-inner">${product}</div>
|
||||||
|
</button>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
tabsContainer.innerHTML = html;
|
tabsContainer.innerHTML = html;
|
||||||
@ -1144,22 +1218,58 @@
|
|||||||
const checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
const checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
||||||
const selectedCount = checkboxes.length;
|
const selectedCount = checkboxes.length;
|
||||||
|
|
||||||
if (selectedCount === 0) {
|
|
||||||
alert('请先选择要编辑的记录');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新选中数量显示
|
// 更新选中数量显示
|
||||||
document.getElementById('selected-count').textContent = selectedCount;
|
document.getElementById('selected-count').textContent = selectedCount;
|
||||||
|
|
||||||
|
// 加载产品列表
|
||||||
|
loadProductOptions();
|
||||||
|
|
||||||
// 重置表单
|
// 重置表单
|
||||||
document.getElementById('batch-status').value = '';
|
document.getElementById('batch-status').value = '';
|
||||||
document.getElementById('batch-remark').value = '';
|
document.getElementById('batch-remark').value = '';
|
||||||
|
document.querySelector('input[name="batch-scope"][value="selected"]').checked = true;
|
||||||
|
document.getElementById('batch-product-select-container').style.display = 'none';
|
||||||
|
|
||||||
|
// 添加事件监听
|
||||||
|
document.getElementById('batch-scope-all').onchange = function() {
|
||||||
|
document.getElementById('batch-product-select-container').style.display =
|
||||||
|
this.checked ? 'block' : 'none';
|
||||||
|
};
|
||||||
|
document.getElementById('batch-scope-selected').onchange = function() {
|
||||||
|
document.getElementById('batch-product-select-container').style.display =
|
||||||
|
this.checked ? 'none' : 'block';
|
||||||
|
};
|
||||||
|
|
||||||
// 显示弹窗
|
// 显示弹窗
|
||||||
document.getElementById('batch-edit-status-modal').style.display = 'flex';
|
document.getElementById('batch-edit-status-modal').style.display = 'flex';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadProductOptions() {
|
||||||
|
// 获取所有产品
|
||||||
|
const products = new Set();
|
||||||
|
demandList.forEach(item => {
|
||||||
|
if (item.product_code) {
|
||||||
|
products.add(item.product_code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const productSelect = document.getElementById('batch-product-select');
|
||||||
|
productSelect.innerHTML = '<option value="">请选择产品</option>';
|
||||||
|
|
||||||
|
// 添加产品选项
|
||||||
|
Array.from(products).sort().forEach(product => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = product;
|
||||||
|
option.textContent = product;
|
||||||
|
productSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果当前有选中的产品,设为默认值
|
||||||
|
if (currentProduct) {
|
||||||
|
productSelect.value = currentProduct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function closeBatchEditStatusModal() {
|
function closeBatchEditStatusModal() {
|
||||||
document.getElementById('batch-edit-status-modal').style.display = 'none';
|
document.getElementById('batch-edit-status-modal').style.display = 'none';
|
||||||
}
|
}
|
||||||
@ -1167,26 +1277,49 @@
|
|||||||
async function saveBatchEditStatus() {
|
async function saveBatchEditStatus() {
|
||||||
const status = document.getElementById('batch-status').value;
|
const status = document.getElementById('batch-status').value;
|
||||||
const remark = document.getElementById('batch-remark').value.trim();
|
const remark = document.getElementById('batch-remark').value.trim();
|
||||||
|
const scope = document.querySelector('input[name="batch-scope"]:checked').value;
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
alert('请选择状态');
|
alert('请选择状态');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取选中的ID
|
let requestData = {
|
||||||
|
status: status,
|
||||||
|
remark: remark
|
||||||
|
};
|
||||||
|
|
||||||
|
let confirmMessage = '';
|
||||||
|
|
||||||
|
if (scope === 'all') {
|
||||||
|
// 更新指定产品的所有记录
|
||||||
|
const selectedProduct = document.getElementById('batch-product-select').value;
|
||||||
|
if (!selectedProduct) {
|
||||||
|
alert('请选择要更新的产品');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requestData.product_code = selectedProduct;
|
||||||
|
confirmMessage = `确定要将"${selectedProduct}"的所有记录状态更新为"${getStatusText(status)}"吗?`;
|
||||||
|
} else {
|
||||||
|
// 仅更新选中的记录
|
||||||
const checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
const checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
||||||
const ids = Array.from(checkboxes).map(cb => parseInt(cb.dataset.id));
|
const ids = Array.from(checkboxes).map(cb => parseInt(cb.dataset.id));
|
||||||
|
|
||||||
if (!confirm(`确定要将选中的 ${ids.length} 条记录状态更新为"${getStatusText(status)}"吗?`)) {
|
if (ids.length === 0) {
|
||||||
|
alert('请选择要更新的记录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestData.ids = ids;
|
||||||
|
confirmMessage = `确定要将选中的 ${ids.length} 条记录状态更新为"${getStatusText(status)}"吗?`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!confirm(confirmMessage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await API.post('/api/purchase-demand/batch-update-status', {
|
const res = await API.post('/api/purchase-demand/batch-update-status', requestData);
|
||||||
ids: ids,
|
|
||||||
status: status,
|
|
||||||
remark: remark
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
alert(`成功更新 ${res.count} 条记录`);
|
alert(`成功更新 ${res.count} 条记录`);
|
||||||
|
|||||||
@ -6507,16 +6507,17 @@ def delete_purchase_demand(demand_id):
|
|||||||
|
|
||||||
@app.post('/api/purchase-demand/batch-update-status')
|
@app.post('/api/purchase-demand/batch-update-status')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('superadmin')
|
@require_any_role('superadmin', 'admin')
|
||||||
def batch_update_purchase_demand_status():
|
def batch_update_purchase_demand_status():
|
||||||
"""批量更新采购需求状态"""
|
"""批量更新采购需求状态"""
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
ids = data.get('ids', [])
|
ids = data.get('ids', [])
|
||||||
status = data.get('status', '').strip()
|
status = data.get('status')
|
||||||
remark = data.get('remark', '').strip()
|
remark = data.get('remark', '')
|
||||||
|
product_code = data.get('product_code', '').strip()
|
||||||
|
|
||||||
if not ids:
|
if not ids and not product_code:
|
||||||
return jsonify({'error': '请选择要更新的记录'}), 400
|
return jsonify({'error': '请选择要更新的记录或指定产品'}), 400
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
return jsonify({'error': '请选择状态'}), 400
|
return jsonify({'error': '请选择状态'}), 400
|
||||||
@ -6528,9 +6529,15 @@ def batch_update_purchase_demand_status():
|
|||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
now = get_beijing_time()
|
now = get_beijing_time()
|
||||||
|
|
||||||
# 验证ID是否存在并获取原始状态
|
# 根据条件查询记录
|
||||||
|
if product_code:
|
||||||
|
# 按产品代码查询
|
||||||
|
c.execute('SELECT id, status, material_code, material_name, actual_purchase_qty, net_demand, total_demand, initial_stock FROM purchase_demand WHERE product_code=?', (product_code,))
|
||||||
|
else:
|
||||||
|
# 按ID列表查询
|
||||||
placeholders = ','.join(['?' for _ in ids])
|
placeholders = ','.join(['?' for _ in ids])
|
||||||
c.execute(f'SELECT id, status, material_code, material_name, actual_purchase_qty, net_demand FROM purchase_demand WHERE id IN ({placeholders})', ids)
|
c.execute(f'SELECT id, status, material_code, material_name, actual_purchase_qty, net_demand, total_demand, initial_stock FROM purchase_demand WHERE id IN ({placeholders})', ids)
|
||||||
|
|
||||||
existing_records = c.fetchall()
|
existing_records = c.fetchall()
|
||||||
|
|
||||||
if not existing_records:
|
if not existing_records:
|
||||||
@ -6547,11 +6554,18 @@ def batch_update_purchase_demand_status():
|
|||||||
material_name = record['material_name']
|
material_name = record['material_name']
|
||||||
actual_purchase_qty = record['actual_purchase_qty']
|
actual_purchase_qty = record['actual_purchase_qty']
|
||||||
net_demand = record['net_demand']
|
net_demand = record['net_demand']
|
||||||
|
total_demand = record['total_demand']
|
||||||
|
initial_stock = record['initial_stock']
|
||||||
|
|
||||||
# 计算之前增加的库存量:实际采购 - 净需求
|
# 根据实际采购数量计算之前的变化量
|
||||||
|
if actual_purchase_qty > 0:
|
||||||
|
# 如果实际采购不等于零,之前增加的库存量 = 实际采购 - 净需求
|
||||||
added_stock = actual_purchase_qty - net_demand
|
added_stock = actual_purchase_qty - net_demand
|
||||||
|
else:
|
||||||
|
# 如果实际采购等于零,之前减少的库存量 = 期初库存 - 总需求
|
||||||
|
added_stock = -(initial_stock - total_demand)
|
||||||
|
|
||||||
# 从期初库存表中减去之前增加的库存(恢复原值)
|
# 从期初库存表中减去之前的变化量(恢复原值)
|
||||||
c.execute('SELECT stock_qty FROM initial_stock WHERE material_code=?', (material_code,))
|
c.execute('SELECT stock_qty FROM initial_stock WHERE material_code=?', (material_code,))
|
||||||
stock_record = c.fetchone()
|
stock_record = c.fetchone()
|
||||||
|
|
||||||
@ -6576,18 +6590,24 @@ def batch_update_purchase_demand_status():
|
|||||||
params.extend(existing_ids)
|
params.extend(existing_ids)
|
||||||
c.execute(f'UPDATE purchase_demand SET {", ".join(updates)} WHERE id IN ({placeholders})', params)
|
c.execute(f'UPDATE purchase_demand SET {", ".join(updates)} WHERE id IN ({placeholders})', params)
|
||||||
|
|
||||||
# 如果状态更新为已下单,更新期初库存
|
# 如果状态更新为已下单,更新期初库存(包括已经是已下单状态的情况)
|
||||||
if status == 'ordered':
|
if status == 'ordered':
|
||||||
for record in existing_records:
|
for record in existing_records:
|
||||||
# 只处理之前不是"已下单"状态的记录
|
# 处理所有记录(包括已经是"已下单"状态的记录)
|
||||||
if record['status'] != 'ordered':
|
|
||||||
material_code = record['material_code']
|
material_code = record['material_code']
|
||||||
material_name = record['material_name']
|
material_name = record['material_name']
|
||||||
actual_purchase_qty = record['actual_purchase_qty']
|
actual_purchase_qty = record['actual_purchase_qty']
|
||||||
net_demand = record['net_demand']
|
net_demand = record['net_demand']
|
||||||
|
total_demand = record['total_demand']
|
||||||
|
initial_stock = record['initial_stock']
|
||||||
|
|
||||||
# 计算新的期初库存:实际采购 - 净需求
|
# 根据实际采购数量计算新的期初库存
|
||||||
|
if actual_purchase_qty > 0:
|
||||||
|
# 如果实际采购不等于零,期初库存 = 实际采购 - 净需求
|
||||||
new_initial_stock = actual_purchase_qty - net_demand
|
new_initial_stock = actual_purchase_qty - net_demand
|
||||||
|
else:
|
||||||
|
# 如果实际采购等于零,期初库存 = 期初库存 - 总需求
|
||||||
|
new_initial_stock = initial_stock - total_demand
|
||||||
|
|
||||||
# 只更新期初库存表
|
# 只更新期初库存表
|
||||||
c.execute('''INSERT OR REPLACE INTO initial_stock
|
c.execute('''INSERT OR REPLACE INTO initial_stock
|
||||||
@ -6881,11 +6901,18 @@ def update_purchase_demand(id):
|
|||||||
material_name = existing['material_name']
|
material_name = existing['material_name']
|
||||||
actual_purchase_qty = existing['actual_purchase_qty']
|
actual_purchase_qty = existing['actual_purchase_qty']
|
||||||
net_demand = existing['net_demand']
|
net_demand = existing['net_demand']
|
||||||
|
total_demand = existing['total_demand']
|
||||||
|
initial_stock = existing['initial_stock']
|
||||||
|
|
||||||
# 计算之前增加的库存量:实际采购 - 净需求
|
# 根据实际采购数量计算之前的变化量
|
||||||
|
if actual_purchase_qty > 0:
|
||||||
|
# 如果实际采购不等于零,之前增加的库存量 = 实际采购 - 净需求
|
||||||
added_stock = actual_purchase_qty - net_demand
|
added_stock = actual_purchase_qty - net_demand
|
||||||
|
else:
|
||||||
|
# 如果实际采购等于零,之前减少的库存量 = 期初库存 - 总需求
|
||||||
|
added_stock = -(initial_stock - total_demand)
|
||||||
|
|
||||||
# 从期初库存表中减去之前增加的库存(恢复原值)
|
# 从期初库存表中减去之前的变化量(恢复原值)
|
||||||
c.execute('SELECT stock_qty FROM initial_stock WHERE material_code=?', (material_code,))
|
c.execute('SELECT stock_qty FROM initial_stock WHERE material_code=?', (material_code,))
|
||||||
stock_record = c.fetchone()
|
stock_record = c.fetchone()
|
||||||
|
|
||||||
@ -6903,16 +6930,22 @@ def update_purchase_demand(id):
|
|||||||
update_values.append(id)
|
update_values.append(id)
|
||||||
c.execute(f'UPDATE purchase_demand SET {", ".join(update_fields)} WHERE id=?', update_values)
|
c.execute(f'UPDATE purchase_demand SET {", ".join(update_fields)} WHERE id=?', update_values)
|
||||||
|
|
||||||
# 如果状态更新为已下单,更新期初库存
|
# 如果状态更新为已下单,更新期初库存(包括已经是已下单状态的情况)
|
||||||
if 'status' in data and new_status == 'ordered' and old_status != 'ordered':
|
if 'status' in data and new_status == 'ordered':
|
||||||
# 获取更新后的记录信息
|
# 获取更新后的记录信息
|
||||||
c.execute('SELECT material_code, material_name, actual_purchase_qty, net_demand FROM purchase_demand WHERE id=?', (id,))
|
c.execute('SELECT material_code, material_name, actual_purchase_qty, net_demand, total_demand, initial_stock FROM purchase_demand WHERE id=?', (id,))
|
||||||
record = c.fetchone()
|
record = c.fetchone()
|
||||||
|
|
||||||
if record:
|
if record:
|
||||||
material_code, material_name, actual_purchase_qty, net_demand = record
|
material_code, material_name, actual_purchase_qty, net_demand, total_demand, initial_stock = record
|
||||||
# 计算新的期初库存:实际采购 - 净需求
|
|
||||||
|
# 根据实际采购数量计算新的期初库存
|
||||||
|
if actual_purchase_qty > 0:
|
||||||
|
# 如果实际采购不等于零,期初库存 = 实际采购 - 净需求
|
||||||
new_initial_stock = actual_purchase_qty - net_demand
|
new_initial_stock = actual_purchase_qty - net_demand
|
||||||
|
else:
|
||||||
|
# 如果实际采购等于零,期初库存 = 期初库存 - 总需求
|
||||||
|
new_initial_stock = initial_stock - total_demand
|
||||||
|
|
||||||
# 只更新期初库存表
|
# 只更新期初库存表
|
||||||
c.execute('''INSERT OR REPLACE INTO initial_stock
|
c.execute('''INSERT OR REPLACE INTO initial_stock
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user