调整部分前端布局和后端增删改逻辑

This commit is contained in:
焦龙言
2026-09-26 23:11:05 +08:00
parent 977e7f996e
commit 17d991a74a
117 changed files with 311994 additions and 3192 deletions
@@ -1,24 +0,0 @@
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# API代理配置(PostgreSQL版本)
location /docker-api/ {
proxy_pass http://shierfeng-backend-pg:9099/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
+3 -2
View File
@@ -6,7 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="/favicon.ico">
<link rel="icon" type="image/svg+xml" href="/sylvan-mark.svg">
<link rel="alternate icon" href="/favicon.ico">
<title>%VITE_APP_TITLE%</title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style>
@@ -212,4 +213,4 @@
<script type="module" src="/src/main.js"></script>
</body>
</html>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 140">
<defs>
<style>
.cls-1 {
fill: #008596;
}
.cls-2 {
fill: #fff;
}
.cls-3 {
fill: #91cdb9;
}
.cls-4 {
fill: #43b69e;
}
.cls-5 {
fill: #554d8a;
}
.cls-6 {
fill: #50bcba;
}
.cls-7, .cls-8, .cls-9 {
fill: #28225c;
}
.cls-10 {
fill: #009a91;
opacity: .8;
}
.cls-10, .cls-11 {
fill-rule: evenodd;
}
.cls-11 {
fill: #a5d7d7;
}
.cls-8 {
opacity: .5;
}
.cls-9 {
opacity: .25;
}
</style>
</defs>
<g id="tekening">
<g>
<g>
<path class="cls-2" d="M302.33,61.89l-18.13,63.56c-3.35,11.65-7.77,17.59-23.63,17.59-3.99,0-10.36-.86-13.06-1.73-1.19-.43-2.27-.76-2.27-1.94v-8.74c0-1.19,.97-2.05,2.16-2.05h.22c3.56,.21,9.93,.86,13.49,.86,3.88,0,5.83-1.29,7.12-5.61l.97-3.35h-1.62c-3.99,0-7.34-.65-10.04-10.04l-14.24-48.56c-.11-.32-.11-.54-.11-.76,0-1.08,.65-1.73,2.16-1.73h14.35c1.08,0,1.83,.97,2.05,2.05l9.71,40.58c.32,1.51,.65,2.05,1.29,2.05h.97l10.04-42.63c.21-1.08,1.08-2.05,2.27-2.05h14.35c1.08,0,2.05,.86,2.05,1.94,0,.22-.11,.33-.11,.54Z"/>
<path class="cls-2" d="M326.72,121.44h-13.17c-1.19,0-2.27-1.08-2.27-2.27V40.83c0-1.19,1.08-2.16,2.27-2.16h13.17c1.19,0,2.05,.97,2.05,2.16V119.17c0,1.19-.86,2.27-2.05,2.27Z"/>
<path class="cls-2" d="M396,61.89l-13.6,48.99c-2.7,9.6-9.28,11.33-15.65,11.33s-12.84-1.73-15.54-11.33l-13.49-48.99c-.11-.21-.11-.43-.11-.65,0-1.08,.76-1.84,1.73-1.84h14.24c1.08,0,1.94,.97,2.27,2.27l9.28,42.3c.33,1.62,.86,2.27,1.62,2.27s1.4-.65,1.73-2.27l9.28-42.3c.32-1.3,1.19-2.27,2.27-2.27h14.24c.97,0,1.84,.76,1.84,1.84,0,.21,0,.43-.11,.65Z"/>
<path class="cls-2" d="M418.66,121.3c-9.39,0-18.45-4.53-18.45-18.56v-.97c0-12.63,8.09-18.24,26.55-18.24h5.83v-3.89c0-6.15-6.78-7.47-11.85-7.47,0,0-17.96,1.95-17.96-12.69,0,0,16.18-.15,23.3-.15,15.54,0,23.99,4.55,23.99,20.31v39.49c0,1.19-.97,2.16-2.16,2.16h-29.24Zm13.89-27.41h-6.15c-7.56,0-9.5,2.59-9.5,7.88v.97c0,4.53,2.27,5.39,6.8,5.39,3.24,0,8.68,0,8.68,0l.17-14.25Z"/>
<path class="cls-2" d="M497.01,58.61c15.65,0,19.1,10.9,19.1,24.5v35.93c0,1.19-1.08,2.27-2.27,2.27h-13.06c-1.19,0-2.16-1.08-2.16-2.27v-35.93c-.1-6.03,.45-8.65-6.13-8.65-3.22,0-8.33,.12-11.99,2.06v42.52c0,1.19-.97,2.27-2.16,2.27h-13.06c-1.19,0-2.27-1.08-2.27-2.27V61.63c0-1.19,.97-2.16,2.16-2.16l31.83-.86Z"/>
<path class="cls-2" d="M184.25,121.47c0-17.9,16.39-18.14,16.39-18.14,8.24,0,17.54-.5,17.98-6.18,.24-3.16-.41-4.44-6.02-7.82l-15.23-8.96c-10.04-6.01-12.73-12.89-12.73-20.87,0-12.02,6.69-21.3,27.63-21.3,1.66,0,23.24,.05,23.26,.07-.13,.14,.12,2.04,.12,3.24,0,14.93-20.16,13.71-20.16,13.71-6.15,0-9.85,2.38-9.85,6.31,0,3.06,1.26,4.7,7.09,7.87l13.81,6.38c11.22,6.12,12.5,12.79,12.5,21.64,0,11.58-6.73,24.27-24.96,24.11l-29.81-.05Z"/>
<g>
<path class="cls-2" d="M365.04,163.65h-1.67c-.32,0-.58-.29-.58-.64v-22.95c0-.32,.23-.61,.58-.61h1.67c.32,0,.58,.26,.58,.61v22.95c0,.32-.23,.64-.58,.64Z"/>
<path class="cls-2" d="M373.88,143.11h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M393.77,146.72l-4.35,15.32c-.42,1.45-1.51,1.84-2.9,1.84s-2.48-.39-2.9-1.84l-4.34-15.32c-.03-.1-.03-.16-.03-.23,0-.32,.19-.55,.51-.55h1.77c.26,0,.48,.19,.61,.61l3.7,14.03c.19,.77,.35,.93,.68,.93,.29,0,.48-.16,.68-.93l3.7-14.03c.13-.42,.35-.61,.61-.61h1.77c.32,0,.51,.23,.51,.55,0,.06,0,.13-.03,.23Z"/>
<path class="cls-2" d="M400.81,143.11h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M420.03,163.65h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M440.07,148.1l-1.16,.22c.58,.61,1.09,1.58,1.09,3.32v.26c0,3.54-2.19,5.5-7.15,5.5-1.71,0-2.8-.26-3.73-.64-.55,.26-.8,.61-.8,1.09,0,.55,.26,.84,1,1.03l4.8,1.22c3.15,.8,6.18,1.71,6.18,4.73v.26c0,3.09-2.06,4.99-7.34,4.99s-7.4-1.96-7.4-5.12v-.29c0-1.8,.97-3.28,2.03-4.09l-.23-.1c-1.03-.45-1.48-1.51-1.48-2.51v-.03c0-.87,.42-1.8,1.32-2.32-1-.93-1.48-2.09-1.48-3.73v-.26c0-2.86,1.61-5.7,7.14-5.7h7.05c.39,0,.74,.32,.74,.71v.84c0,.42-.23,.55-.58,.61Zm-2.61,16.74c0-1.26-1.03-1.77-3.93-2.51l-3.64-.93c-.93,.8-1.58,1.61-1.58,3.28v.29c0,1.8,1.06,2.93,4.63,2.93s4.51-1.03,4.51-2.8v-.26Zm-.26-13.2c0-1.93-.87-3.44-4.35-3.44s-4.34,1.64-4.34,3.44v.26c0,1.42,.64,3.28,4.34,3.28,3.48,0,4.35-1.35,4.35-3.28v-.26Z"/>
<path class="cls-2" d="M365.07,173.76h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M384.28,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M403.17,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M415.71,194.52c-4.73,0-7.14-3.12-7.14-7.85v-2.45c0-4.73,2.41-7.85,7.14-7.85s7.18,3.12,7.18,7.85v2.45c0,4.73-2.45,7.85-7.18,7.85Zm4.35-10.3c0-3.28-1.32-5.38-4.35-5.38s-4.31,2.06-4.31,5.38v2.45c0,3.28,1.29,5.38,4.31,5.38s4.35-2.06,4.35-5.38v-2.45Z"/>
<path class="cls-2" d="M440.52,177.36l-4.35,15.32c-.42,1.45-1.51,1.84-2.9,1.84s-2.48-.39-2.9-1.84l-4.35-15.32c-.03-.1-.03-.16-.03-.23,0-.32,.19-.55,.51-.55h1.77c.26,0,.48,.19,.61,.61l3.7,14.03c.19,.77,.35,.93,.68,.93,.29,0,.48-.16,.68-.93l3.7-14.03c.13-.42,.35-.61,.61-.61h1.77c.32,0,.51,.23,.51,.55,0,.06,0,.13-.03,.23Z"/>
<path class="cls-2" d="M455.97,194.29h-1.48c-.35,0-.64-.29-.64-.64v-.71c-1.54,.9-3.15,1.58-4.96,1.58-2.74,0-5.25-1.19-5.25-5.21v-.39c0-3.48,2.03-5.21,7.76-5.21h2.38v-1.29c0-2.7-.93-3.64-3.44-3.64-2.09,0-3.64,.13-4.92,.26h-.16c-.29,0-.58-.1-.58-.51v-1.13c0-.35,.23-.55,.68-.61,1.32-.23,2.8-.42,4.99-.42,4.47,0,6.28,2.25,6.28,6.05v11.23c0,.35-.29,.64-.64,.64Zm-2.19-8.56h-2.38c-3.9,0-4.93,.87-4.93,3.15v.39c0,2.32,1.09,2.93,2.96,2.93,1.48,0,2.8-.45,4.35-1.29v-5.18Z"/>
<path class="cls-2" d="M469.99,194.36c-.64,.1-1.48,.16-2.09,.16-2.96,0-4.38-.77-4.38-4.31v-11.23l-3.12-.35c-.35-.03-.61-.22-.61-.58v-.87c0-.35,.26-.58,.61-.58h3.12v-3.15c0-.32,.22-.58,.58-.64l1.67-.26h.1c.26,0,.48,.22,.48,.55v3.51h3.48c.35,0,.64,.29,.64,.64v1.09c0,.35-.29,.64-.64,.64h-3.48v11.23c0,1.77,.45,2.03,1.74,2.03h1.8c.45,0,.68,.13,.68,.45v1.13c0,.29-.16,.48-.58,.55Z"/>
<path class="cls-2" d="M477.14,173.76h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M490.01,194.52c-4.73,0-7.14-3.12-7.14-7.85v-2.45c0-4.73,2.41-7.85,7.14-7.85s7.18,3.12,7.18,7.85v2.45c0,4.73-2.45,7.85-7.18,7.85Zm4.35-10.3c0-3.28-1.32-5.38-4.35-5.38s-4.31,2.06-4.31,5.38v2.45c0,3.28,1.29,5.38,4.31,5.38s4.35-2.06,4.35-5.38v-2.45Z"/>
<path class="cls-2" d="M514.85,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
</g>
</g>
<g>
<path class="cls-11" d="M57.78,67.44c-.03,5.02-.86,9.86-2.35,14.39,0,0,.01,.01,.02,.02,0,0,0,0,.01,0,10.82-31.09,38.25-54.41,71.67-59.3C130.83,15.86,132.95,8.18,132.95,0h0s-46.67,0-46.67,0H46.67C30.78,0,16.76,7.94,8.33,20.07c.88-.04,1.76-.07,2.65-.06,26.02,.17,46.98,21.41,46.8,47.43Z"/>
<path class="cls-10" d="M0,46.67c0,5.62,1,11,2.82,15.99,5.22-.78,10.62-.87,16.11-.17,14.7,1.88,27.41,9.05,36.5,19.35,1.49-4.53,2.31-9.36,2.35-14.39,.17-26.02-20.78-47.26-46.8-47.43-.89,0-1.77,.02-2.65,.06C3.08,27.61,0,36.78,0,46.67H0Z"/>
<path class="cls-6" d="M0,46.67c0,5.62,1,11,2.82,15.99,5.22-.78,10.62-.87,16.11-.17,14.7,1.88,27.41,9.05,36.5,19.35,1.49-4.53,2.31-9.36,2.35-14.39,.17-26.02-20.78-47.26-46.8-47.43-.89,0-1.77,.02-2.65,.06C3.08,27.61,0,36.78,0,46.67H0Z"/>
<path class="cls-7" d="M55.46,81.85s-.01-.01-.02-.02c0,0,0,0,0,.01,0,0,.02,0,.02,0Z"/>
<path class="cls-10" d="M55.47,81.85s0,0-.01,0c0,0,0,0,.01,.01,0,0,0,0,0,0Z"/>
<path class="cls-8" d="M55.47,81.85s0,0-.01,0c0,0,0,0,.01,.01,0,0,0,0,0,0Z"/>
<path class="cls-7" d="M55.42,81.88s.03,0,.05-.01c0,0,0,0,0,0,0,0,0,0-.01-.01,0,0-.02,0-.02,0,0,.01,0,.02-.01,.03Z"/>
<path class="cls-8" d="M55.42,81.88s.03,0,.05-.01c0,0,0,0,0,0,0,0,0,0-.01-.01,0,0-.02,0-.02,0,0,.01,0,.02-.01,.03Z"/>
<path class="cls-10" d="M55.47,81.85s0,0,.01,0c5.58-1.58,11.47-2.44,17.56-2.44,24,0,44.92,13.13,56.01,32.6,2.5-5.72,3.9-12.04,3.9-18.68h0c0-24.43-18.78-44.48-42.69-46.5-.22-.02-.22-.31,0-.33,15.91-1.34,29.53-10.67,36.88-23.95-33.42,4.9-60.85,28.21-71.67,59.3Z"/>
<path class="cls-3" d="M55.47,81.85s0,0,.01,0c5.58-1.58,11.47-2.44,17.56-2.44,24,0,44.92,13.13,56.01,32.6,2.5-5.72,3.9-12.04,3.9-18.68h0c0-24.43-18.78-44.48-42.69-46.5-.22-.02-.22-.31,0-.33,15.91-1.34,29.53-10.67,36.88-23.95-33.42,4.9-60.85,28.21-71.67,59.3Z"/>
<path class="cls-10" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<path class="cls-8" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<path class="cls-9" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<polygon class="cls-7" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<polygon class="cls-8" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<polygon class="cls-9" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<path class="cls-10" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-9" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-4" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-10" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-8" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-9" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-1" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-5" d="M55.47,81.86s0,0,0,0c-.02,0-.03,0-.05,.01,0-.01,0-.02,.01-.03,0,0,0,0,0-.01-9.09-10.29-21.81-17.47-36.5-19.35-5.49-.7-10.89-.6-16.11,.17,2.93,8.02,7.98,15.01,14.5,20.29,5.11,4.14,11.12,7.22,17.69,8.91,2.45,.63,4.96,1.08,7.55,1.3,.24,.02,.24,.34,0,.36-4.74,.41-9.26,1.54-13.49,3.27-8.71,3.55-16.1,9.65-21.25,17.38-4.93,7.4-7.82,16.28-7.82,25.84H0s8.74,0,8.74,0h57.85c1.51-3.77,2.58-7.74,3.11-11.86,2.23-17.44-3.45-34.06-14.23-46.28Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

@@ -11,6 +11,25 @@ import * as testBatchContext from '../src/utils/testBatchContext.js'
const frontendRoot = fileURLToPath(new URL('..', import.meta.url))
const execFileAsync = promisify(execFile)
test('Web 端统一使用 Sylvan 横版标识、折叠图标和浏览器图标', async () => {
const [logoComponent, toolBuilder, indexHtml, fullLogo, markLogo] = await Promise.all([
readFile(path.join(frontendRoot, 'src/layout/components/Sidebar/Logo.vue'), 'utf8'),
readFile(path.join(frontendRoot, 'src/views/tool/build/index.vue'), 'utf8'),
readFile(path.join(frontendRoot, 'index.html'), 'utf8'),
readFile(path.join(frontendRoot, 'src/assets/logo/sylvan-logo.svg'), 'utf8'),
readFile(path.join(frontendRoot, 'src/assets/logo/sylvan-mark.svg'), 'utf8')
])
assert.match(logoComponent, /@\/assets\/logo\/sylvan-logo\.svg/)
assert.match(logoComponent, /@\/assets\/logo\/sylvan-mark\.svg/)
assert.match(toolBuilder, /@\/assets\/logo\/sylvan-mark\.svg/)
assert.match(indexHtml, /href="\/sylvan-mark\.svg"/)
assert.match(fullLogo, /viewBox="0 0 516\.1 194\.52"/)
assert.match(markLogo, /viewBox="0 0 140 140"/)
assert.doesNotMatch(`${logoComponent}\n${toolBuilder}`, /assets\/logo\/logo\.png/)
assert.doesNotMatch(`${fullLogo}\n${markLogo}`, /<script\b|<foreignObject\b|javascript:|\b(?:xlink:)?href=|\bonload=|\bonerror=/i)
})
const expectedRequests = {
'src/api/master/strain.js': [
{ url: "'/master/strain/list'", method: 'get' },
@@ -885,6 +904,23 @@ function extractButtonBlocks(source, clickHandler) {
.filter((button) => button.includes(`@click="${clickHandler}`))
}
function assertButtonInSingleToolbar(source, relativePath, toolbarClass, clickHandler) {
const buttonIndex = source.indexOf(`@click="${clickHandler}`)
assert.ok(buttonIndex >= 0, `${relativePath} 缺少 ${clickHandler} 按钮`)
const beforeButton = source.slice(0, buttonIndex)
const rowStart = beforeButton.lastIndexOf('<el-row')
const rowEnd = beforeButton.lastIndexOf('</el-row>')
assert.ok(rowStart > rowEnd, `${relativePath} 的 ${clickHandler} 按钮必须位于 el-row 工具栏内`)
const rowTagEnd = source.indexOf('>', rowStart)
const rowTag = source.slice(rowStart, rowTagEnd + 1)
assert.match(rowTag, new RegExp(`class="[^"]*\\b${toolbarClass}\\b`), `${relativePath} 必须使用 ${toolbarClass}`)
const toolbarRows = [...source.matchAll(new RegExp(`<el-row\\b[^>]*class="[^"]*\\b${toolbarClass}\\b[^"]*"`, 'g'))]
assert.equal(toolbarRows.length, 1, `${relativePath} 的操作按钮必须合并到同一个 ${toolbarClass}`)
}
function assertButtonsUsePermission(source, clickHandler, permission) {
const buttons = extractButtonBlocks(source, clickHandler)
assert.ok(buttons.length > 0, `缺少 ${clickHandler} 按钮`)
@@ -1176,7 +1212,7 @@ test('真实权限门禁解析拒绝注释常量、hasPermiOr 和动态数组假
const missing = expectedPermissions.filter((permission) => permission !== 'master:strain:query')
assert.throws(
() => assertExactPermissionLayers({ mysql: missing, postgresql: missing, controller: missing, vue: missing }),
() => assertExactPermissionLayers({ mysql: missing, controller: missing, vue: missing }),
/权限集合不精确/
)
})
@@ -1487,6 +1523,38 @@ test('六个报表 API 只提供 GET /list,不提供导出接口', async () =>
}
})
test('托盘追溯报表将来源和流转状态显示为中文', async () => {
const source = await readFile(path.join(frontendRoot, 'src/views/report/palletTrace/index.vue'), 'utf8')
const sourceLabels = [
['INBOUND', '入库单'],
['FILLING', '装填记录'],
['SHIPMENT', '发货单'],
['USAGE', '使用记录'],
['RETURN', '退回单']
]
const flowStateLabels = [
['UNSTOCKED', '未入库'],
['EMPTY_IN_WAREHOUSE', '仓库空托盘'],
['FILLED_IN_WAREHOUSE', '仓库有料'],
['AT_SITE_UNUSED', '现场未使用'],
['AT_SITE_PARTIAL', '现场部分使用'],
['AT_SITE_EMPTY', '现场空托盘'],
['RETURNING', '退回中']
]
for (const [value, label] of sourceLabels) {
assert.match(source, new RegExp(`value: '${value}', label: '${label}'`))
}
for (const [value, label] of flowStateLabels) {
assert.match(source, new RegExp(`value: '${value}', label: '${label}'`))
}
assert.match(source, /sourceLabel\(scope\.row\.sourceType,\s*scope\.row\.sourceId\)/)
assert.match(source, /flowStateLabel\(scope\.row\.beforeState\)/)
assert.match(source, /flowStateLabel\(scope\.row\.afterState\)/)
assert.match(source, /function sourceLabel\([\s\S]*?sourceTypeOptions\.find\(/)
assert.match(source, /function flowStateLabel\([\s\S]*?flowStateOptions\.find\(/)
})
test('六个只读报表页面逐页满足 API、权限、筛选、列和异步分页契约', async (t) => {
for (const [resource, contract] of Object.entries(reportPageContracts)) {
await t.test(contract.relativePath, async () => {
@@ -1506,6 +1574,53 @@ test('六个只读报表页面逐页满足 API、权限、筛选、列和异步
assert.doesNotMatch(source, /report:[a-z]+:(?:add|edit|remove|export|print)/)
assert.doesNotMatch(source, /\bhandle(?:Add|Edit|Delete|Remove|Export|Print|Download)\b|proxy\.download\s*\(/)
const queryFormTag = source.match(/<el-form\b[^>]*ref="queryRef"[^>]*>/)?.[0] || ''
assert.match(queryFormTag, /v-show="showSearch"/, `${resource} 搜索区必须支持显隐`)
assert.match(queryFormTag, /label-width="88px"/, `${resource} 筛选标签宽度必须统一`)
assert.match(source, /const showSearch = ref\(true\);?/, `${resource} 搜索区必须默认展开`)
const reportFilterFooter = source.match(/<div\s+class="report-filter__footer">[\s\S]*?<\/div>/)?.[0] || ''
assert.ok(reportFilterFooter, `${resource} 日期和搜索操作必须位于统一底部操作栏`)
assert.match(reportFilterFooter, /<el-form-item\b[^>]*class="report-filter__date"/, `${resource} 底部操作栏必须包含日期范围`)
assert.match(reportFilterFooter, /<el-form-item\b[^>]*class="report-filter__actions"/, `${resource} 底部操作栏必须包含搜索操作`)
assert.ok(
reportFilterFooter.indexOf('report-filter__date') < reportFilterFooter.indexOf('report-filter__actions'),
`${resource} 日期范围必须位于搜索操作之前`
)
const reportToolbar = source.match(/<el-row\b[^>]*class="[^"]*\breport-toolbar\b[^"]*"[^>]*>[\s\S]*?<\/el-row>/)?.[0] || ''
assert.ok(reportToolbar, `${resource} 缺少报表操作栏`)
assert.match(reportToolbar, /<right-toolbar\s+v-model:showSearch="showSearch"\s+@queryTable="getList"\s*\/>/, `${resource} 必须提供搜索显隐和刷新入口`)
assert.match(source, /\.report-filter\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*repeat\(auto-fit,\s*minmax\(260px,\s*1fr\)\)/, `${resource} 筛选区必须使用响应式等宽网格`)
assert.match(
source,
/\.report-filter__footer\s*\{[^}]*grid-column:\s*1\s*\/\s*-1;[^}]*display:\s*flex;[^}]*align-items:\s*flex-end;[^}]*gap:\s*16px;/,
`${resource} 日期和操作项必须位于独占整行的底部操作栏`
)
assert.doesNotMatch(source, /\.report-filter__footer\s*\{[^}]*display:\s*contents;/, `${resource} 底部操作栏不得使用 contents 分散到筛选网格`)
assert.doesNotMatch(source, /\.report-filter__date\s*\{[^}]*grid-column:\s*-3\s*\/\s*-1;/, `${resource} 日期范围不得单独跨最后两列`)
assert.match(
source,
/\.report-filter\s+\.report-filter__date\s*\{[^}]*flex:\s*0\s+1\s+420px;[^}]*width:\s*420px;[^}]*min-width:\s*0;[^}]*max-width:\s*100%;/,
`${resource} 桌面日期范围必须固定基础宽度并覆盖通用表单项宽度`
)
assert.match(source, /\.report-filter\s+\.report-filter__actions\s*\{[^}]*flex:\s*0\s+0\s+auto;[^}]*width:\s*auto;/, `${resource} 操作按钮必须按自身宽度紧邻日期范围`)
assert.match(source, /\.report-filter :deep\(\.el-form-item__content\)\s*\{[^}]*min-width:\s*0;/, `${resource} 筛选内容必须允许收缩`)
assert.match(
source,
/\.report-filter :deep\(\.el-input\),\s*\.report-filter :deep\(\.el-input-number\),\s*\.report-filter :deep\(\.el-select\),\s*\.report-filter :deep\(\.el-date-editor\)\s*\{[^}]*width:\s*100%\s*!important;[^}]*min-width:\s*0;/,
`${resource} 筛选控件必须等宽且不得越界`
)
assert.match(
source,
/@media\s*\(max-width:\s*900px\)[\s\S]*?\.report-filter__footer\s*\{[^}]*grid-column:\s*1;[^}]*flex-direction:\s*column;[^}]*align-items:\s*stretch;/,
`${resource} 窄屏底部操作栏必须恢复纵向布局`
)
assert.match(
source,
/@media\s*\(max-width:\s*900px\)[\s\S]*?\.report-filter\s+\.report-filter__date\s*\{[^}]*flex:\s*0\s+0\s+auto;[^}]*width:\s*100%;[^}]*min-width:\s*0;[^}]*max-width:\s*none;/,
`${resource} 窄屏日期范围必须恢复全宽`
)
for (const queryKey of contract.queryKeys) {
assert.match(source, new RegExp(`\\b${queryKey}\\b`), `${resource} 缺少筛选键 ${queryKey}`)
}
@@ -1646,6 +1761,20 @@ test('六个基础资料页面逐页满足 API、权限、分页和安全删除
assert.match(source, /const detail\s*=\s*response\.data/)
assert.match(source, /function handleQuery\(\)\s*{[\s\S]*?queryParams\.value\.pageNum\s*=\s*1[\s\S]*?getList\(\)/)
assert.match(source, /function resetQuery\(\)\s*{[\s\S]*?resetForm\(['"]queryRef['"]\)[\s\S]*?handleQuery\(\)/)
const queryFormTag = source.match(/<el-form\b[^>]*ref="queryRef"[^>]*>/)?.[0] || ''
assert.match(queryFormTag, /v-show="showSearch"/, `${resource} 搜索区必须支持显隐`)
assert.match(source, /const showSearch = ref\(true\);/, `${resource} 搜索区必须默认展开`)
const masterToolbar = source.match(/<el-row\b[^>]*class="[^"]*\bmaster-toolbar\b[^"]*"[^>]*>[\s\S]*?<\/el-row>/)?.[0] || ''
assert.ok(masterToolbar, `${resource} 缺少基础资料操作栏`)
assert.doesNotMatch(masterToolbar.match(/^<el-row\b[^>]*>/)?.[0] || '', /v-if="isMasterWriteAllowed"/, `${resource} 搜索与刷新入口不得受写权限限制`)
assert.match(masterToolbar, /<template v-if="isMasterWriteAllowed">/, `${resource} 写操作仍需受测试模式限制`)
assert.match(masterToolbar, /<right-toolbar\s+v-model:showSearch="showSearch"\s+@queryTable="getList"\s*\/>/, `${resource} 必须复用右侧搜索与刷新工具栏`)
assert.match(
source,
/\.master-query :deep\(\.el-input\),\s*\.master-query :deep\(\.el-input-number\),\s*\.master-query :deep\(\.el-select\)\s*\{[^}]*min-width:\s*0;/,
`${resource} 查询控件必须允许收缩到网格内容宽度`
)
assert.match(source, /<pagination\b[\s\S]*?v-model:page="queryParams\.pageNum"[\s\S]*?v-model:limit="queryParams\.pageSize"[\s\S]*?@pagination="getList"/)
assert.match(source, /<el-table\b[^>]*v-loading="loading"[^>]*empty-text="暂无数据"/)
assert.match(source, /<el-alert\b[^>]*v-if="loadError"/)
@@ -2495,6 +2624,12 @@ test('测试批次管理与旧数据纳入遵守 root、权限和预览提交契
assert.match(source, /v-hasPermi="\['system:testbatch:adopt'\]"/, `${rootType} 纳入入口必须校验批次纳入权限`)
assert.match(source, new RegExp(`<test-batch-adoption-dialog\\b[\\s\\S]*?:root-type="['\"]${rootType}['\"][\\s\\S]*?:resource-ids="selectedIds"`))
assert.match(source, new RegExp(`selectedIds\\.value\\s*=\\s*selection\\.map\\(\\(item\\)\\s*=>\\s*item\\.${contract.id}\\)`), `${rootType} 必须传递正整数根 ID`)
assertButtonInSingleToolbar(
source,
contract.path,
contract.path.includes('/master/') ? 'master-toolbar' : 'page-toolbar',
'openAdoptionDialog'
)
}
for (const rootType of ['strain', 'customer', 'site', 'line', 'warehouse', 'pallet']) {
@@ -2720,3 +2855,21 @@ test('测试批次管理页真实 hydration 拒绝旧版本回写并保留当前
assert.equal(failedPage.state.batchList.value[0].participantCount, undefined)
assert.equal(failedPage.state.loadError.value, '', '当前详情失败必须是单项状态,不得升级为列表失败')
})
test('三个业务菜单公共样式统一使用 Element Plus 主题变量', async () => {
const styles = await source('src/assets/styles/data-pages.scss')
assert.match(styles, /background:\s*var\(--el-bg-color-page,\s*#f5f7fb\)/)
assert.match(styles, /background:\s*var\(--el-bg-color,\s*#fff\)/)
assert.match(styles, /border:\s*1px solid var\(--el-border-color-lighter,\s*#e4e7ed\)/)
assert.match(styles, /background:\s*var\(--el-fill-color-light,\s*#f5f7fa\)/)
assert.match(styles, /color:\s*var\(--el-text-color-regular,\s*#606266\)/)
assert.match(styles, /background:\s*var\(--el-fill-color-lighter,\s*#fafafa\)/)
assert.match(styles, /box-shadow:\s*var\(--el-box-shadow-lighter,/)
assert.doesNotMatch(styles, /background:\s*#f5f7fb\s*;/)
assert.doesNotMatch(styles, /background:\s*#fff\s*;/)
assert.doesNotMatch(styles, /border:\s*1px solid #e7ebf2\s*;/)
assert.doesNotMatch(styles, /background:\s*#f7f9fc\s*!important/)
assert.doesNotMatch(styles, /background:\s*#fbfcfe\s*;/)
})
@@ -0,0 +1,47 @@
import assert from 'node:assert/strict'
import { test } from 'node:test'
import { buildTrendData, getTrendRangeQuery, TREND_RANGE_OPTIONS } from '../src/utils/dashboardTrend.js'
const referenceDate = new Date(2026, 8, 26, 12)
test('趋势下拉提供 7 天、14 天和 1 个月三个选项', () => {
assert.deepEqual(TREND_RANGE_OPTIONS, [
{ value: 7, label: '近 7 天' },
{ value: 14, label: '近 14 天' },
{ value: 30, label: '近 1 个月' }
])
})
test('趋势范围为近 14 天时,查询从 13 天前开始并包含今天', () => {
assert.deepEqual(getTrendRangeQuery(14, referenceDate), {
beginTime: '2026-09-13',
endTime: '2026-09-26'
})
})
test('趋势范围为近 1 个月时最多查询 30 天', () => {
assert.deepEqual(getTrendRangeQuery(30, referenceDate), {
beginTime: '2026-08-28',
endTime: '2026-09-26'
})
})
test('趋势数据按选择的天数生成日期轴并聚合入库单与出库包数', () => {
const result = buildTrendData(
[
{ createTime: '2026-09-20T08:00:00+08:00' },
{ createTime: '2026-09-24T09:00:00+08:00' },
{ createTime: '2026-09-24T10:00:00+08:00' }
],
[
{ confirmedAt: '2026-09-20T11:00:00+08:00', packageQty: 4 },
{ confirmedAt: '2026-09-24T13:00:00+08:00', packageQty: 7 }
],
7,
referenceDate
)
assert.deepEqual(result.labels, ['9/20', '9/21', '9/22', '9/23', '9/24', '9/25', '9/26'])
assert.deepEqual(result.inbound, [1, 0, 0, 0, 2, 0, 0])
assert.deepEqual(result.outbound, [4, 0, 0, 0, 7, 0, 0])
})
Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 KiB

After

Width:  |  Height:  |  Size: 68 KiB

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 516.1 194.52">
<defs>
<style>
.cls-1 {
fill: #008596;
}
.cls-2 {
fill: #fff;
}
.cls-3 {
fill: #91cdb9;
}
.cls-4 {
fill: #43b69e;
}
.cls-5 {
fill: #554d8a;
}
.cls-6 {
fill: #50bcba;
}
.cls-7, .cls-8, .cls-9 {
fill: #28225c;
}
.cls-10 {
fill: #009a91;
opacity: .8;
}
.cls-10, .cls-11 {
fill-rule: evenodd;
}
.cls-11 {
fill: #a5d7d7;
}
.cls-8 {
opacity: .5;
}
.cls-9 {
opacity: .25;
}
</style>
</defs>
<g id="tekening">
<g>
<g>
<path class="cls-2" d="M302.33,61.89l-18.13,63.56c-3.35,11.65-7.77,17.59-23.63,17.59-3.99,0-10.36-.86-13.06-1.73-1.19-.43-2.27-.76-2.27-1.94v-8.74c0-1.19,.97-2.05,2.16-2.05h.22c3.56,.21,9.93,.86,13.49,.86,3.88,0,5.83-1.29,7.12-5.61l.97-3.35h-1.62c-3.99,0-7.34-.65-10.04-10.04l-14.24-48.56c-.11-.32-.11-.54-.11-.76,0-1.08,.65-1.73,2.16-1.73h14.35c1.08,0,1.83,.97,2.05,2.05l9.71,40.58c.32,1.51,.65,2.05,1.29,2.05h.97l10.04-42.63c.21-1.08,1.08-2.05,2.27-2.05h14.35c1.08,0,2.05,.86,2.05,1.94,0,.22-.11,.33-.11,.54Z"/>
<path class="cls-2" d="M326.72,121.44h-13.17c-1.19,0-2.27-1.08-2.27-2.27V40.83c0-1.19,1.08-2.16,2.27-2.16h13.17c1.19,0,2.05,.97,2.05,2.16V119.17c0,1.19-.86,2.27-2.05,2.27Z"/>
<path class="cls-2" d="M396,61.89l-13.6,48.99c-2.7,9.6-9.28,11.33-15.65,11.33s-12.84-1.73-15.54-11.33l-13.49-48.99c-.11-.21-.11-.43-.11-.65,0-1.08,.76-1.84,1.73-1.84h14.24c1.08,0,1.94,.97,2.27,2.27l9.28,42.3c.33,1.62,.86,2.27,1.62,2.27s1.4-.65,1.73-2.27l9.28-42.3c.32-1.3,1.19-2.27,2.27-2.27h14.24c.97,0,1.84,.76,1.84,1.84,0,.21,0,.43-.11,.65Z"/>
<path class="cls-2" d="M418.66,121.3c-9.39,0-18.45-4.53-18.45-18.56v-.97c0-12.63,8.09-18.24,26.55-18.24h5.83v-3.89c0-6.15-6.78-7.47-11.85-7.47,0,0-17.96,1.95-17.96-12.69,0,0,16.18-.15,23.3-.15,15.54,0,23.99,4.55,23.99,20.31v39.49c0,1.19-.97,2.16-2.16,2.16h-29.24Zm13.89-27.41h-6.15c-7.56,0-9.5,2.59-9.5,7.88v.97c0,4.53,2.27,5.39,6.8,5.39,3.24,0,8.68,0,8.68,0l.17-14.25Z"/>
<path class="cls-2" d="M497.01,58.61c15.65,0,19.1,10.9,19.1,24.5v35.93c0,1.19-1.08,2.27-2.27,2.27h-13.06c-1.19,0-2.16-1.08-2.16-2.27v-35.93c-.1-6.03,.45-8.65-6.13-8.65-3.22,0-8.33,.12-11.99,2.06v42.52c0,1.19-.97,2.27-2.16,2.27h-13.06c-1.19,0-2.27-1.08-2.27-2.27V61.63c0-1.19,.97-2.16,2.16-2.16l31.83-.86Z"/>
<path class="cls-2" d="M184.25,121.47c0-17.9,16.39-18.14,16.39-18.14,8.24,0,17.54-.5,17.98-6.18,.24-3.16-.41-4.44-6.02-7.82l-15.23-8.96c-10.04-6.01-12.73-12.89-12.73-20.87,0-12.02,6.69-21.3,27.63-21.3,1.66,0,23.24,.05,23.26,.07-.13,.14,.12,2.04,.12,3.24,0,14.93-20.16,13.71-20.16,13.71-6.15,0-9.85,2.38-9.85,6.31,0,3.06,1.26,4.7,7.09,7.87l13.81,6.38c11.22,6.12,12.5,12.79,12.5,21.64,0,11.58-6.73,24.27-24.96,24.11l-29.81-.05Z"/>
<g>
<path class="cls-2" d="M365.04,163.65h-1.67c-.32,0-.58-.29-.58-.64v-22.95c0-.32,.23-.61,.58-.61h1.67c.32,0,.58,.26,.58,.61v22.95c0,.32-.23,.64-.58,.64Z"/>
<path class="cls-2" d="M373.88,143.11h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M393.77,146.72l-4.35,15.32c-.42,1.45-1.51,1.84-2.9,1.84s-2.48-.39-2.9-1.84l-4.34-15.32c-.03-.1-.03-.16-.03-.23,0-.32,.19-.55,.51-.55h1.77c.26,0,.48,.19,.61,.61l3.7,14.03c.19,.77,.35,.93,.68,.93,.29,0,.48-.16,.68-.93l3.7-14.03c.13-.42,.35-.61,.61-.61h1.77c.32,0,.51,.23,.51,.55,0,.06,0,.13-.03,.23Z"/>
<path class="cls-2" d="M400.81,143.11h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M420.03,163.65h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M440.07,148.1l-1.16,.22c.58,.61,1.09,1.58,1.09,3.32v.26c0,3.54-2.19,5.5-7.15,5.5-1.71,0-2.8-.26-3.73-.64-.55,.26-.8,.61-.8,1.09,0,.55,.26,.84,1,1.03l4.8,1.22c3.15,.8,6.18,1.71,6.18,4.73v.26c0,3.09-2.06,4.99-7.34,4.99s-7.4-1.96-7.4-5.12v-.29c0-1.8,.97-3.28,2.03-4.09l-.23-.1c-1.03-.45-1.48-1.51-1.48-2.51v-.03c0-.87,.42-1.8,1.32-2.32-1-.93-1.48-2.09-1.48-3.73v-.26c0-2.86,1.61-5.7,7.14-5.7h7.05c.39,0,.74,.32,.74,.71v.84c0,.42-.23,.55-.58,.61Zm-2.61,16.74c0-1.26-1.03-1.77-3.93-2.51l-3.64-.93c-.93,.8-1.58,1.61-1.58,3.28v.29c0,1.8,1.06,2.93,4.63,2.93s4.51-1.03,4.51-2.8v-.26Zm-.26-13.2c0-1.93-.87-3.44-4.35-3.44s-4.34,1.64-4.34,3.44v.26c0,1.42,.64,3.28,4.34,3.28,3.48,0,4.35-1.35,4.35-3.28v-.26Z"/>
<path class="cls-2" d="M365.07,173.76h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M384.28,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M403.17,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M415.71,194.52c-4.73,0-7.14-3.12-7.14-7.85v-2.45c0-4.73,2.41-7.85,7.14-7.85s7.18,3.12,7.18,7.85v2.45c0,4.73-2.45,7.85-7.18,7.85Zm4.35-10.3c0-3.28-1.32-5.38-4.35-5.38s-4.31,2.06-4.31,5.38v2.45c0,3.28,1.29,5.38,4.31,5.38s4.35-2.06,4.35-5.38v-2.45Z"/>
<path class="cls-2" d="M440.52,177.36l-4.35,15.32c-.42,1.45-1.51,1.84-2.9,1.84s-2.48-.39-2.9-1.84l-4.35-15.32c-.03-.1-.03-.16-.03-.23,0-.32,.19-.55,.51-.55h1.77c.26,0,.48,.19,.61,.61l3.7,14.03c.19,.77,.35,.93,.68,.93,.29,0,.48-.16,.68-.93l3.7-14.03c.13-.42,.35-.61,.61-.61h1.77c.32,0,.51,.23,.51,.55,0,.06,0,.13-.03,.23Z"/>
<path class="cls-2" d="M455.97,194.29h-1.48c-.35,0-.64-.29-.64-.64v-.71c-1.54,.9-3.15,1.58-4.96,1.58-2.74,0-5.25-1.19-5.25-5.21v-.39c0-3.48,2.03-5.21,7.76-5.21h2.38v-1.29c0-2.7-.93-3.64-3.44-3.64-2.09,0-3.64,.13-4.92,.26h-.16c-.29,0-.58-.1-.58-.51v-1.13c0-.35,.23-.55,.68-.61,1.32-.23,2.8-.42,4.99-.42,4.47,0,6.28,2.25,6.28,6.05v11.23c0,.35-.29,.64-.64,.64Zm-2.19-8.56h-2.38c-3.9,0-4.93,.87-4.93,3.15v.39c0,2.32,1.09,2.93,2.96,2.93,1.48,0,2.8-.45,4.35-1.29v-5.18Z"/>
<path class="cls-2" d="M469.99,194.36c-.64,.1-1.48,.16-2.09,.16-2.96,0-4.38-.77-4.38-4.31v-11.23l-3.12-.35c-.35-.03-.61-.22-.61-.58v-.87c0-.35,.26-.58,.61-.58h3.12v-3.15c0-.32,.22-.58,.58-.64l1.67-.26h.1c.26,0,.48,.22,.48,.55v3.51h3.48c.35,0,.64,.29,.64,.64v1.09c0,.35-.29,.64-.64,.64h-3.48v11.23c0,1.77,.45,2.03,1.74,2.03h1.8c.45,0,.68,.13,.68,.45v1.13c0,.29-.16,.48-.58,.55Z"/>
<path class="cls-2" d="M477.14,173.76h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M490.01,194.52c-4.73,0-7.14-3.12-7.14-7.85v-2.45c0-4.73,2.41-7.85,7.14-7.85s7.18,3.12,7.18,7.85v2.45c0,4.73-2.45,7.85-7.18,7.85Zm4.35-10.3c0-3.28-1.32-5.38-4.35-5.38s-4.31,2.06-4.31,5.38v2.45c0,3.28,1.29,5.38,4.31,5.38s4.35-2.06,4.35-5.38v-2.45Z"/>
<path class="cls-2" d="M514.85,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
</g>
</g>
<g>
<path class="cls-11" d="M57.78,67.44c-.03,5.02-.86,9.86-2.35,14.39,0,0,.01,.01,.02,.02,0,0,0,0,.01,0,10.82-31.09,38.25-54.41,71.67-59.3C130.83,15.86,132.95,8.18,132.95,0h0s-46.67,0-46.67,0H46.67C30.78,0,16.76,7.94,8.33,20.07c.88-.04,1.76-.07,2.65-.06,26.02,.17,46.98,21.41,46.8,47.43Z"/>
<path class="cls-10" d="M0,46.67c0,5.62,1,11,2.82,15.99,5.22-.78,10.62-.87,16.11-.17,14.7,1.88,27.41,9.05,36.5,19.35,1.49-4.53,2.31-9.36,2.35-14.39,.17-26.02-20.78-47.26-46.8-47.43-.89,0-1.77,.02-2.65,.06C3.08,27.61,0,36.78,0,46.67H0Z"/>
<path class="cls-6" d="M0,46.67c0,5.62,1,11,2.82,15.99,5.22-.78,10.62-.87,16.11-.17,14.7,1.88,27.41,9.05,36.5,19.35,1.49-4.53,2.31-9.36,2.35-14.39,.17-26.02-20.78-47.26-46.8-47.43-.89,0-1.77,.02-2.65,.06C3.08,27.61,0,36.78,0,46.67H0Z"/>
<path class="cls-7" d="M55.46,81.85s-.01-.01-.02-.02c0,0,0,0,0,.01,0,0,.02,0,.02,0Z"/>
<path class="cls-10" d="M55.47,81.85s0,0-.01,0c0,0,0,0,.01,.01,0,0,0,0,0,0Z"/>
<path class="cls-8" d="M55.47,81.85s0,0-.01,0c0,0,0,0,.01,.01,0,0,0,0,0,0Z"/>
<path class="cls-7" d="M55.42,81.88s.03,0,.05-.01c0,0,0,0,0,0,0,0,0,0-.01-.01,0,0-.02,0-.02,0,0,.01,0,.02-.01,.03Z"/>
<path class="cls-8" d="M55.42,81.88s.03,0,.05-.01c0,0,0,0,0,0,0,0,0,0-.01-.01,0,0-.02,0-.02,0,0,.01,0,.02-.01,.03Z"/>
<path class="cls-10" d="M55.47,81.85s0,0,.01,0c5.58-1.58,11.47-2.44,17.56-2.44,24,0,44.92,13.13,56.01,32.6,2.5-5.72,3.9-12.04,3.9-18.68h0c0-24.43-18.78-44.48-42.69-46.5-.22-.02-.22-.31,0-.33,15.91-1.34,29.53-10.67,36.88-23.95-33.42,4.9-60.85,28.21-71.67,59.3Z"/>
<path class="cls-3" d="M55.47,81.85s0,0,.01,0c5.58-1.58,11.47-2.44,17.56-2.44,24,0,44.92,13.13,56.01,32.6,2.5-5.72,3.9-12.04,3.9-18.68h0c0-24.43-18.78-44.48-42.69-46.5-.22-.02-.22-.31,0-.33,15.91-1.34,29.53-10.67,36.88-23.95-33.42,4.9-60.85,28.21-71.67,59.3Z"/>
<path class="cls-10" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<path class="cls-8" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<path class="cls-9" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<polygon class="cls-7" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<polygon class="cls-8" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<polygon class="cls-9" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<path class="cls-10" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-9" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-4" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-10" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-8" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-9" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-1" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-5" d="M55.47,81.86s0,0,0,0c-.02,0-.03,0-.05,.01,0-.01,0-.02,.01-.03,0,0,0,0,0-.01-9.09-10.29-21.81-17.47-36.5-19.35-5.49-.7-10.89-.6-16.11,.17,2.93,8.02,7.98,15.01,14.5,20.29,5.11,4.14,11.12,7.22,17.69,8.91,2.45,.63,4.96,1.08,7.55,1.3,.24,.02,.24,.34,0,.36-4.74,.41-9.26,1.54-13.49,3.27-8.71,3.55-16.1,9.65-21.25,17.38-4.93,7.4-7.82,16.28-7.82,25.84H0s8.74,0,8.74,0h57.85c1.51-3.77,2.58-7.74,3.11-11.86,2.23-17.44-3.45-34.06-14.23-46.28Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 140">
<defs>
<style>
.cls-1 {
fill: #008596;
}
.cls-2 {
fill: #fff;
}
.cls-3 {
fill: #91cdb9;
}
.cls-4 {
fill: #43b69e;
}
.cls-5 {
fill: #554d8a;
}
.cls-6 {
fill: #50bcba;
}
.cls-7, .cls-8, .cls-9 {
fill: #28225c;
}
.cls-10 {
fill: #009a91;
opacity: .8;
}
.cls-10, .cls-11 {
fill-rule: evenodd;
}
.cls-11 {
fill: #a5d7d7;
}
.cls-8 {
opacity: .5;
}
.cls-9 {
opacity: .25;
}
</style>
</defs>
<g id="tekening">
<g>
<g>
<path class="cls-2" d="M302.33,61.89l-18.13,63.56c-3.35,11.65-7.77,17.59-23.63,17.59-3.99,0-10.36-.86-13.06-1.73-1.19-.43-2.27-.76-2.27-1.94v-8.74c0-1.19,.97-2.05,2.16-2.05h.22c3.56,.21,9.93,.86,13.49,.86,3.88,0,5.83-1.29,7.12-5.61l.97-3.35h-1.62c-3.99,0-7.34-.65-10.04-10.04l-14.24-48.56c-.11-.32-.11-.54-.11-.76,0-1.08,.65-1.73,2.16-1.73h14.35c1.08,0,1.83,.97,2.05,2.05l9.71,40.58c.32,1.51,.65,2.05,1.29,2.05h.97l10.04-42.63c.21-1.08,1.08-2.05,2.27-2.05h14.35c1.08,0,2.05,.86,2.05,1.94,0,.22-.11,.33-.11,.54Z"/>
<path class="cls-2" d="M326.72,121.44h-13.17c-1.19,0-2.27-1.08-2.27-2.27V40.83c0-1.19,1.08-2.16,2.27-2.16h13.17c1.19,0,2.05,.97,2.05,2.16V119.17c0,1.19-.86,2.27-2.05,2.27Z"/>
<path class="cls-2" d="M396,61.89l-13.6,48.99c-2.7,9.6-9.28,11.33-15.65,11.33s-12.84-1.73-15.54-11.33l-13.49-48.99c-.11-.21-.11-.43-.11-.65,0-1.08,.76-1.84,1.73-1.84h14.24c1.08,0,1.94,.97,2.27,2.27l9.28,42.3c.33,1.62,.86,2.27,1.62,2.27s1.4-.65,1.73-2.27l9.28-42.3c.32-1.3,1.19-2.27,2.27-2.27h14.24c.97,0,1.84,.76,1.84,1.84,0,.21,0,.43-.11,.65Z"/>
<path class="cls-2" d="M418.66,121.3c-9.39,0-18.45-4.53-18.45-18.56v-.97c0-12.63,8.09-18.24,26.55-18.24h5.83v-3.89c0-6.15-6.78-7.47-11.85-7.47,0,0-17.96,1.95-17.96-12.69,0,0,16.18-.15,23.3-.15,15.54,0,23.99,4.55,23.99,20.31v39.49c0,1.19-.97,2.16-2.16,2.16h-29.24Zm13.89-27.41h-6.15c-7.56,0-9.5,2.59-9.5,7.88v.97c0,4.53,2.27,5.39,6.8,5.39,3.24,0,8.68,0,8.68,0l.17-14.25Z"/>
<path class="cls-2" d="M497.01,58.61c15.65,0,19.1,10.9,19.1,24.5v35.93c0,1.19-1.08,2.27-2.27,2.27h-13.06c-1.19,0-2.16-1.08-2.16-2.27v-35.93c-.1-6.03,.45-8.65-6.13-8.65-3.22,0-8.33,.12-11.99,2.06v42.52c0,1.19-.97,2.27-2.16,2.27h-13.06c-1.19,0-2.27-1.08-2.27-2.27V61.63c0-1.19,.97-2.16,2.16-2.16l31.83-.86Z"/>
<path class="cls-2" d="M184.25,121.47c0-17.9,16.39-18.14,16.39-18.14,8.24,0,17.54-.5,17.98-6.18,.24-3.16-.41-4.44-6.02-7.82l-15.23-8.96c-10.04-6.01-12.73-12.89-12.73-20.87,0-12.02,6.69-21.3,27.63-21.3,1.66,0,23.24,.05,23.26,.07-.13,.14,.12,2.04,.12,3.24,0,14.93-20.16,13.71-20.16,13.71-6.15,0-9.85,2.38-9.85,6.31,0,3.06,1.26,4.7,7.09,7.87l13.81,6.38c11.22,6.12,12.5,12.79,12.5,21.64,0,11.58-6.73,24.27-24.96,24.11l-29.81-.05Z"/>
<g>
<path class="cls-2" d="M365.04,163.65h-1.67c-.32,0-.58-.29-.58-.64v-22.95c0-.32,.23-.61,.58-.61h1.67c.32,0,.58,.26,.58,.61v22.95c0,.32-.23,.64-.58,.64Z"/>
<path class="cls-2" d="M373.88,143.11h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M393.77,146.72l-4.35,15.32c-.42,1.45-1.51,1.84-2.9,1.84s-2.48-.39-2.9-1.84l-4.34-15.32c-.03-.1-.03-.16-.03-.23,0-.32,.19-.55,.51-.55h1.77c.26,0,.48,.19,.61,.61l3.7,14.03c.19,.77,.35,.93,.68,.93,.29,0,.48-.16,.68-.93l3.7-14.03c.13-.42,.35-.61,.61-.61h1.77c.32,0,.51,.23,.51,.55,0,.06,0,.13-.03,.23Z"/>
<path class="cls-2" d="M400.81,143.11h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M420.03,163.65h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M440.07,148.1l-1.16,.22c.58,.61,1.09,1.58,1.09,3.32v.26c0,3.54-2.19,5.5-7.15,5.5-1.71,0-2.8-.26-3.73-.64-.55,.26-.8,.61-.8,1.09,0,.55,.26,.84,1,1.03l4.8,1.22c3.15,.8,6.18,1.71,6.18,4.73v.26c0,3.09-2.06,4.99-7.34,4.99s-7.4-1.96-7.4-5.12v-.29c0-1.8,.97-3.28,2.03-4.09l-.23-.1c-1.03-.45-1.48-1.51-1.48-2.51v-.03c0-.87,.42-1.8,1.32-2.32-1-.93-1.48-2.09-1.48-3.73v-.26c0-2.86,1.61-5.7,7.14-5.7h7.05c.39,0,.74,.32,.74,.71v.84c0,.42-.23,.55-.58,.61Zm-2.61,16.74c0-1.26-1.03-1.77-3.93-2.51l-3.64-.93c-.93,.8-1.58,1.61-1.58,3.28v.29c0,1.8,1.06,2.93,4.63,2.93s4.51-1.03,4.51-2.8v-.26Zm-.26-13.2c0-1.93-.87-3.44-4.35-3.44s-4.34,1.64-4.34,3.44v.26c0,1.42,.64,3.28,4.34,3.28,3.48,0,4.35-1.35,4.35-3.28v-.26Z"/>
<path class="cls-2" d="M365.07,173.76h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M384.28,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M403.17,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M415.71,194.52c-4.73,0-7.14-3.12-7.14-7.85v-2.45c0-4.73,2.41-7.85,7.14-7.85s7.18,3.12,7.18,7.85v2.45c0,4.73-2.45,7.85-7.18,7.85Zm4.35-10.3c0-3.28-1.32-5.38-4.35-5.38s-4.31,2.06-4.31,5.38v2.45c0,3.28,1.29,5.38,4.31,5.38s4.35-2.06,4.35-5.38v-2.45Z"/>
<path class="cls-2" d="M440.52,177.36l-4.35,15.32c-.42,1.45-1.51,1.84-2.9,1.84s-2.48-.39-2.9-1.84l-4.35-15.32c-.03-.1-.03-.16-.03-.23,0-.32,.19-.55,.51-.55h1.77c.26,0,.48,.19,.61,.61l3.7,14.03c.19,.77,.35,.93,.68,.93,.29,0,.48-.16,.68-.93l3.7-14.03c.13-.42,.35-.61,.61-.61h1.77c.32,0,.51,.23,.51,.55,0,.06,0,.13-.03,.23Z"/>
<path class="cls-2" d="M455.97,194.29h-1.48c-.35,0-.64-.29-.64-.64v-.71c-1.54,.9-3.15,1.58-4.96,1.58-2.74,0-5.25-1.19-5.25-5.21v-.39c0-3.48,2.03-5.21,7.76-5.21h2.38v-1.29c0-2.7-.93-3.64-3.44-3.64-2.09,0-3.64,.13-4.92,.26h-.16c-.29,0-.58-.1-.58-.51v-1.13c0-.35,.23-.55,.68-.61,1.32-.23,2.8-.42,4.99-.42,4.47,0,6.28,2.25,6.28,6.05v11.23c0,.35-.29,.64-.64,.64Zm-2.19-8.56h-2.38c-3.9,0-4.93,.87-4.93,3.15v.39c0,2.32,1.09,2.93,2.96,2.93,1.48,0,2.8-.45,4.35-1.29v-5.18Z"/>
<path class="cls-2" d="M469.99,194.36c-.64,.1-1.48,.16-2.09,.16-2.96,0-4.38-.77-4.38-4.31v-11.23l-3.12-.35c-.35-.03-.61-.22-.61-.58v-.87c0-.35,.26-.58,.61-.58h3.12v-3.15c0-.32,.22-.58,.58-.64l1.67-.26h.1c.26,0,.48,.22,.48,.55v3.51h3.48c.35,0,.64,.29,.64,.64v1.09c0,.35-.29,.64-.64,.64h-3.48v11.23c0,1.77,.45,2.03,1.74,2.03h1.8c.45,0,.68,.13,.68,.45v1.13c0,.29-.16,.48-.58,.55Z"/>
<path class="cls-2" d="M477.14,173.76h-1.64c-.35,0-.64-.29-.64-.64v-2.57c0-.35,.29-.64,.64-.64h1.64c.35,0,.64,.29,.64,.64v2.57c0,.35-.29,.64-.64,.64Zm-.06,20.53h-1.54c-.35,0-.64-.29-.64-.64v-16.42c0-.35,.29-.64,.64-.64h1.54c.35,0,.64,.29,.64,.64v16.42c0,.35-.29,.64-.64,.64Z"/>
<path class="cls-2" d="M490.01,194.52c-4.73,0-7.14-3.12-7.14-7.85v-2.45c0-4.73,2.41-7.85,7.14-7.85s7.18,3.12,7.18,7.85v2.45c0,4.73-2.45,7.85-7.18,7.85Zm4.35-10.3c0-3.28-1.32-5.38-4.35-5.38s-4.31,2.06-4.31,5.38v2.45c0,3.28,1.29,5.38,4.31,5.38s4.35-2.06,4.35-5.38v-2.45Z"/>
<path class="cls-2" d="M514.85,194.29h-1.54c-.35,0-.64-.29-.64-.64v-10.59c0-3.25-1.09-4.25-3.54-4.25-1.35,0-2.86,.51-4.34,1.26v13.58c0,.32-.29,.64-.64,.64h-1.58c-.32,0-.61-.29-.61-.64v-16.42c0-.35,.26-.64,.61-.64h1.61c.35,0,.61,.29,.61,.64v.61c1.77-1,3.22-1.48,5.12-1.48,3.86,0,5.6,2.41,5.6,6.69v10.59c0,.35-.29,.64-.64,.64Z"/>
</g>
</g>
<g>
<path class="cls-11" d="M57.78,67.44c-.03,5.02-.86,9.86-2.35,14.39,0,0,.01,.01,.02,.02,0,0,0,0,.01,0,10.82-31.09,38.25-54.41,71.67-59.3C130.83,15.86,132.95,8.18,132.95,0h0s-46.67,0-46.67,0H46.67C30.78,0,16.76,7.94,8.33,20.07c.88-.04,1.76-.07,2.65-.06,26.02,.17,46.98,21.41,46.8,47.43Z"/>
<path class="cls-10" d="M0,46.67c0,5.62,1,11,2.82,15.99,5.22-.78,10.62-.87,16.11-.17,14.7,1.88,27.41,9.05,36.5,19.35,1.49-4.53,2.31-9.36,2.35-14.39,.17-26.02-20.78-47.26-46.8-47.43-.89,0-1.77,.02-2.65,.06C3.08,27.61,0,36.78,0,46.67H0Z"/>
<path class="cls-6" d="M0,46.67c0,5.62,1,11,2.82,15.99,5.22-.78,10.62-.87,16.11-.17,14.7,1.88,27.41,9.05,36.5,19.35,1.49-4.53,2.31-9.36,2.35-14.39,.17-26.02-20.78-47.26-46.8-47.43-.89,0-1.77,.02-2.65,.06C3.08,27.61,0,36.78,0,46.67H0Z"/>
<path class="cls-7" d="M55.46,81.85s-.01-.01-.02-.02c0,0,0,0,0,.01,0,0,.02,0,.02,0Z"/>
<path class="cls-10" d="M55.47,81.85s0,0-.01,0c0,0,0,0,.01,.01,0,0,0,0,0,0Z"/>
<path class="cls-8" d="M55.47,81.85s0,0-.01,0c0,0,0,0,.01,.01,0,0,0,0,0,0Z"/>
<path class="cls-7" d="M55.42,81.88s.03,0,.05-.01c0,0,0,0,0,0,0,0,0,0-.01-.01,0,0-.02,0-.02,0,0,.01,0,.02-.01,.03Z"/>
<path class="cls-8" d="M55.42,81.88s.03,0,.05-.01c0,0,0,0,0,0,0,0,0,0-.01-.01,0,0-.02,0-.02,0,0,.01,0,.02-.01,.03Z"/>
<path class="cls-10" d="M55.47,81.85s0,0,.01,0c5.58-1.58,11.47-2.44,17.56-2.44,24,0,44.92,13.13,56.01,32.6,2.5-5.72,3.9-12.04,3.9-18.68h0c0-24.43-18.78-44.48-42.69-46.5-.22-.02-.22-.31,0-.33,15.91-1.34,29.53-10.67,36.88-23.95-33.42,4.9-60.85,28.21-71.67,59.3Z"/>
<path class="cls-3" d="M55.47,81.85s0,0,.01,0c5.58-1.58,11.47-2.44,17.56-2.44,24,0,44.92,13.13,56.01,32.6,2.5-5.72,3.9-12.04,3.9-18.68h0c0-24.43-18.78-44.48-42.69-46.5-.22-.02-.22-.31,0-.33,15.91-1.34,29.53-10.67,36.88-23.95-33.42,4.9-60.85,28.21-71.67,59.3Z"/>
<path class="cls-10" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<path class="cls-8" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<path class="cls-9" d="M55.47,81.86s.01,0,.02,0c0,0,0,0-.01,0,0,0,0,0,0,0,0,0,0,0,0,0Z"/>
<polygon class="cls-7" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<polygon class="cls-8" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<polygon class="cls-9" points="55.47 81.86 55.47 81.86 55.47 81.86 55.47 81.86"/>
<path class="cls-10" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-9" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-4" d="M73.04,79.42c-6.09,0-11.97,.86-17.56,2.44,24.27,7.47,41.91,30.06,41.91,56.78,0,0,0,.01,0,.02,14.23-3.48,25.89-13.47,31.65-26.64-11.08-19.46-32-32.6-56.01-32.6Z"/>
<path class="cls-10" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-8" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-9" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-1" d="M55.48,81.86s-.01,0-.02,0c10.78,12.22,16.46,28.84,14.23,46.28-.53,4.12-1.59,8.08-3.11,11.86h19.69c3.83,0,7.55-.47,11.11-1.34,0,0,0-.01,0-.02,0-26.72-17.64-49.31-41.91-56.78Z"/>
<path class="cls-5" d="M55.47,81.86s0,0,0,0c-.02,0-.03,0-.05,.01,0-.01,0-.02,.01-.03,0,0,0,0,0-.01-9.09-10.29-21.81-17.47-36.5-19.35-5.49-.7-10.89-.6-16.11,.17,2.93,8.02,7.98,15.01,14.5,20.29,5.11,4.14,11.12,7.22,17.69,8.91,2.45,.63,4.96,1.08,7.55,1.3,.24,.02,.24,.34,0,.36-4.74,.41-9.26,1.54-13.49,3.27-8.71,3.55-16.1,9.65-21.25,17.38-4.93,7.4-7.82,16.28-7.82,25.84H0s8.74,0,8.74,0h57.85c1.51-3.77,2.58-7.74,3.11-11.86,2.23-17.44-3.45-34.06-14.23-46.28Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

@@ -4,7 +4,8 @@
.app-container.report-page {
min-height: 100%;
padding: 18px 20px 24px;
background: #f5f7fb;
background: var(--el-bg-color-page, #f5f7fb);
color: var(--el-text-color-primary, #303133);
}
.business-page .page-filter,
@@ -12,10 +13,10 @@
.report-page .page-filter {
margin: 0 0 14px;
padding: 14px 16px 2px;
border: 1px solid #e7ebf2;
border: 1px solid var(--el-border-color-lighter, #e4e7ed);
border-radius: 12px;
background: #fff;
box-shadow: 0 3px 14px rgba(31, 55, 88, 0.05);
background: var(--el-bg-color, #fff);
box-shadow: var(--el-box-shadow-lighter, 0 3px 14px rgba(31, 55, 88, 0.05));
}
.business-page .page-filter .el-form-item,
@@ -58,19 +59,19 @@
.master-page .data-table,
.report-page .data-table {
width: 100%;
border: 1px solid #e7ebf2;
border: 1px solid var(--el-border-color-lighter, #e4e7ed);
border-radius: 12px;
overflow: hidden;
background: #fff;
box-shadow: 0 3px 14px rgba(31, 55, 88, 0.04);
background: var(--el-bg-color, #fff);
box-shadow: var(--el-box-shadow-lighter, 0 3px 14px rgba(31, 55, 88, 0.04));
}
.business-page .data-table .el-table__header th,
.master-page .data-table .el-table__header th,
.report-page .data-table .el-table__header th {
height: 46px !important;
background: #f7f9fc !important;
color: #344054;
background: var(--el-fill-color-light, #f5f7fa) !important;
color: var(--el-text-color-regular, #606266);
font-weight: 650;
}
@@ -83,7 +84,7 @@
.business-page .data-table .el-table__body tr:nth-child(even) > td,
.master-page .data-table .el-table__body tr:nth-child(even) > td,
.report-page .data-table .el-table__body tr:nth-child(even) > td {
background: #fbfcfe;
background: var(--el-fill-color-lighter, #fafafa);
}
.business-page .data-table .el-table__inner-wrapper::before,
@@ -107,7 +108,7 @@
.business-page .operation-card {
border-radius: 12px;
box-shadow: 0 3px 14px rgba(31, 55, 88, 0.05);
box-shadow: var(--el-box-shadow-lighter, 0 3px 14px rgba(31, 55, 88, 0.05));
}
@media (max-width: 767px) {
@@ -1,21 +1,19 @@
<template>
<div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 v-else class="sidebar-title">{{ title }}</h1>
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" :aria-label="title" to="/">
<img :src="logoMark" :alt="title" class="sidebar-logo sidebar-logo-mark" />
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 class="sidebar-title">{{ title }}</h1>
<router-link v-else key="expand" class="sidebar-logo-link" :aria-label="title" to="/">
<img :src="logo" :alt="title" class="sidebar-logo sidebar-logo-full" />
</router-link>
</transition>
</div>
</template>
<script setup>
import logo from '@/assets/logo/logo.png'
import useSettingsStore from '@/store/modules/settings'
import logo from '@/assets/logo/sylvan-logo.svg'
import logoMark from '@/assets/logo/sylvan-mark.svg'
import variables from '@/assets/styles/variables.module.scss'
defineProps({
@@ -26,30 +24,7 @@ defineProps({
})
const title = import.meta.env.VITE_APP_TITLE;
const settingsStore = useSettingsStore();
const sideTheme = computed(() => settingsStore.sideTheme);
// 获取Logo背景色
const getLogoBackground = computed(() => {
if (settingsStore.isDark) {
return 'var(--sidebar-bg)';
}
if (settingsStore.navType == 3) {
return variables.menuLightBg
}
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg;
});
// 获取Logo文字颜色
const getLogoTextColor = computed(() => {
if (settingsStore.isDark) {
return 'var(--sidebar-text)';
}
if (settingsStore.navType == 3) {
return variables.menuLightText
}
return sideTheme.value === 'theme-dark' ? '#fff' : variables.menuLightText;
});
const logoBackground = variables.menuBg;
</script>
<style lang="scss" scoped>
@@ -66,37 +41,39 @@ const getLogoTextColor = computed(() => {
position: relative;
height: 50px;
line-height: 50px;
background: v-bind(getLogoBackground);
background: v-bind(logoBackground);
text-align: center;
overflow: hidden;
& .sidebar-logo-link {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 4px 12px;
& .sidebar-logo {
width: 32px;
height: 32px;
vertical-align: middle;
margin-right: 12px;
display: block;
object-fit: contain;
}
& .sidebar-title {
display: inline-block;
margin: 0;
color: v-bind(getLogoTextColor);
font-weight: 600;
line-height: 50px;
font-size: 14px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
& .sidebar-logo-full {
width: 132px;
height: 42px;
}
& .sidebar-logo-mark {
width: 32px;
height: 32px;
}
}
&.collapse {
.sidebar-logo {
margin-right: 0px;
.sidebar-logo-link {
padding: 4px;
}
}
}
</style>
</style>
@@ -0,0 +1,55 @@
export const TREND_RANGE_OPTIONS = [
{ value: 7, label: '近 7 天' },
{ value: 14, label: '近 14 天' },
{ value: 30, label: '近 1 个月' }
]
function formatDate(date) {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
function dateKey(value) {
if (value instanceof Date) return formatDate(value)
return typeof value === 'string' ? value.slice(0, 10) : ''
}
export function getTrendRangeQuery(days, today = new Date()) {
const end = new Date(today)
const begin = new Date(end)
begin.setDate(end.getDate() - Number(days) + 1)
return { beginTime: formatDate(begin), endTime: formatDate(end) }
}
export function buildTrendData(inboundRows = [], shipmentRows = [], days = 7, today = new Date()) {
const end = new Date(today)
const dates = []
const inboundMap = new Map()
const shipmentMap = new Map()
for (let offset = Number(days) - 1; offset >= 0; offset -= 1) {
const date = new Date(end)
date.setDate(end.getDate() - offset)
const key = formatDate(date)
dates.push({ key, label: `${date.getMonth() + 1}/${date.getDate()}` })
inboundMap.set(key, 0)
shipmentMap.set(key, 0)
}
inboundRows.forEach((row) => {
const key = dateKey(row.createTime)
if (inboundMap.has(key)) inboundMap.set(key, inboundMap.get(key) + 1)
})
shipmentRows.forEach((row) => {
const key = dateKey(row.confirmedAt)
if (shipmentMap.has(key)) shipmentMap.set(key, shipmentMap.get(key) + (Number(row.packageQty) || 0))
})
return {
labels: dates.map((item) => item.label),
inbound: dates.map((item) => inboundMap.get(item.key) || 0),
outbound: dates.map((item) => shipmentMap.get(item.key) || 0)
}
}
@@ -29,10 +29,12 @@
v-hasPermi="['business:filling:add']"
>托盘装填</el-button>
</el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<div v-if="userStore.admin === true && !testBatchStore.isTestMode" class="mb8"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></div>
<el-table v-loading="loading" empty-text="暂无数据" :data="fillingList" class="data-table" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="装填单号" prop="fillingNo" min-width="170" :show-overflow-tooltip="true" />
@@ -38,10 +38,12 @@
v-hasPermi="['business:inbound:add']"
>新增入库草稿</el-button>
</el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<div v-if="userStore.admin === true && !testBatchStore.isTestMode" class="mb8"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></div>
<el-table v-loading="loading" empty-text="暂无数据" :data="inboundList" class="data-table" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="入库单号" prop="inboundNo" min-width="170" :show-overflow-tooltip="true" />
@@ -172,8 +172,13 @@
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8 page-toolbar">
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<div v-if="userStore.admin === true && !testBatchStore.isTestMode" class="mb8"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></div>
<el-table v-loading="loading" empty-text="暂无数据" :data="returnList" class="data-table" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="退回单号" prop="returnNo" min-width="170" :show-overflow-tooltip="true" />
@@ -35,10 +35,12 @@
v-hasPermi="['business:shipment:add']"
>新增发货草稿</el-button>
</el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<div v-if="userStore.admin === true && !testBatchStore.isTestMode" class="mb8"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></div>
<el-table v-loading="loading" empty-text="暂无数据" :data="shipmentList" class="data-table" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="发货单号" prop="shipmentNo" min-width="170" :show-overflow-tooltip="true" />
@@ -111,8 +111,13 @@
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8 page-toolbar">
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<div v-if="userStore.admin === true && !testBatchStore.isTestMode" class="mb8"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length || operationPending" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></div>
<el-table v-loading="loading" empty-text="暂无数据" :data="usageList" class="data-table" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="使用记录号" prop="usageNo" min-width="170" :show-overflow-tooltip="true" />
@@ -1,22 +1,5 @@
<template>
<div class="dashboard-page">
<section class="dashboard-header">
<div>
<div class="dashboard-kicker">运营驾驶舱</div>
<h1>仓储运营总览</h1>
<p>聚焦托盘库存、入库出库和待处理业务,快速掌握当前运营状态。</p>
</div>
<div class="dashboard-header-actions">
<div class="dashboard-meta">
<el-tag type="info" effect="plain">数据范围:全部仓库</el-tag>
<span>更新于 {{ updatedAtText }}</span>
</div>
<el-button type="primary" plain :icon="Refresh" :loading="state.loading" @click="loadDashboardData">
刷新数据
</el-button>
</div>
</section>
<div v-if="state.partialError" class="dashboard-alert">
<el-icon><Warning /></el-icon>
<span>部分运营数据暂时无法加载,已保留可用模块。</span>
@@ -35,13 +18,6 @@
</section>
<section class="dashboard-section quick-section">
<div class="section-heading">
<div>
<div class="section-kicker">常用流程</div>
<h2>快捷操作</h2>
</div>
<span>从这里直接进入日常仓储操作</span>
</div>
<div class="quick-action-grid">
<button v-for="item in quickActions" :key="item.label" type="button" class="quick-action" @click="navigateTo(item.path)">
<span class="quick-action-icon"><el-icon><component :is="item.icon" /></el-icon></span>
@@ -59,16 +35,25 @@
<template #header>
<div class="card-heading">
<div>
<h2>近 7 天入库 / 出库趋势</h2>
<h2>{{ trendRangeLabel }}入库 / 出库趋势</h2>
<span>入库以单据数统计,出库以确认包数统计</span>
</div>
<el-tag type="primary" effect="plain">近 7 天</el-tag>
<el-select
v-model="selectedTrendDays"
class="trend-range-select"
size="small"
aria-label="趋势时间范围"
:disabled="state.loading || trendLoading"
@change="handleTrendRangeChange"
>
<el-option v-for="option in TREND_RANGE_OPTIONS" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</div>
</template>
<div class="chart-wrapper">
<div ref="trendChartRef" class="chart-canvas" />
<el-skeleton v-if="state.loading" class="chart-loading" animated :rows="5" />
<el-empty v-else-if="!trendHasData" class="chart-empty" description="暂无近 7 天业务数据" :image-size="72" />
<el-skeleton v-if="state.loading || trendLoading" class="chart-loading" animated :rows="5" />
<el-empty v-else-if="!trendHasData" class="chart-empty" :description="`暂无${trendRangeLabel}业务数据`" :image-size="72" />
</div>
</el-card>
@@ -184,13 +169,14 @@
<script setup name="DashBoard">
import * as echarts from 'echarts'
import { ArrowRight, Box, CircleCheck, Coin, Download, DocumentAdd, Operation, Refresh, Upload, Van, Warning, WarningFilled } from '@element-plus/icons-vue'
import { ArrowRight, Box, CircleCheck, Coin, Download, DocumentAdd, Operation, Upload, Van, Warning, WarningFilled } from '@element-plus/icons-vue'
import { listInbound } from '@/api/business/inbound'
import { listReturn } from '@/api/business/return'
import { listShipment } from '@/api/business/shipment'
import { listPalletInventory } from '@/api/report/palletInventory'
import { listShipmentReport } from '@/api/report/shipment'
import useSettingsStore from '@/store/modules/settings'
import { buildTrendData, getTrendRangeQuery, TREND_RANGE_OPTIONS } from '@/utils/dashboardTrend'
defineOptions({ name: 'DashBoard' })
@@ -198,10 +184,11 @@ const router = useRouter()
const settingsStore = useSettingsStore()
const trendChartRef = ref(null)
const distributionChartRef = ref(null)
const selectedTrendDays = ref(7)
const trendLoading = ref(false)
const state = reactive({
loading: false,
partialError: false,
lastUpdated: null,
inventoryRows: [],
inventoryTotal: null,
remainingPackages: null,
@@ -236,9 +223,9 @@ const pendingItems = computed(() => state.pendingItems)
const pendingTotal = computed(() => state.pendingTotal)
const recentInbound = computed(() => state.recentInbound)
const recentShipments = computed(() => state.recentShipments)
const updatedAtText = computed(() => state.lastUpdated ? formatTime(state.lastUpdated) : '尚未更新')
const inventoryHasData = computed(() => state.inventoryRows.length > 0)
const trendHasData = computed(() => state.trendInboundRows.length > 0 || state.trendShipmentRows.length > 0)
const trendRangeLabel = computed(() => TREND_RANGE_OPTIONS.find((option) => option.value === selectedTrendDays.value)?.label || '近 7 天')
const inventoryStateLabels = {
UNSTOCKED: '未入库',
@@ -273,33 +260,7 @@ const warehouseRankings = computed(() => {
return rows.slice(0, 5).map((item) => ({ ...item, percentage: Math.max(4, Math.round(item.packages / maxPackages * 100)) }))
})
const trendData = computed(() => {
const dates = []
const inboundMap = new Map()
const shipmentMap = new Map()
const today = new Date()
for (let offset = 6; offset >= 0; offset -= 1) {
const date = new Date(today)
date.setDate(today.getDate() - offset)
const key = formatDate(date)
dates.push({ key, label: `${date.getMonth() + 1}/${date.getDate()}` })
inboundMap.set(key, 0)
shipmentMap.set(key, 0)
}
state.trendInboundRows.forEach((row) => {
const key = dateKey(row.createTime)
if (inboundMap.has(key)) inboundMap.set(key, inboundMap.get(key) + 1)
})
state.trendShipmentRows.forEach((row) => {
const key = dateKey(row.confirmedAt)
if (shipmentMap.has(key)) shipmentMap.set(key, shipmentMap.get(key) + (Number(row.packageQty) || 0))
})
return {
labels: dates.map((item) => item.label),
inbound: dates.map((item) => inboundMap.get(item.key) || 0),
outbound: dates.map((item) => shipmentMap.get(item.key) || 0)
}
})
const trendData = computed(() => buildTrendData(state.trendInboundRows, state.trendShipmentRows, selectedTrendDays.value))
function formatNumber(value) {
return Number(value || 0).toLocaleString('zh-CN')
@@ -317,10 +278,6 @@ function formatDate(date) {
return `${year}-${month}-${day}`
}
function dateKey(value) {
return typeof value === 'string' ? value.slice(0, 10) : ''
}
function formatTime(value) {
if (!value) return '—'
const date = new Date(value)
@@ -366,26 +323,44 @@ function recentQuery() {
return { pageNum: 1, pageSize: 5 }
}
function sevenDayQuery() {
const begin = new Date()
begin.setDate(begin.getDate() - 6)
return { beginTime: formatDate(begin), endTime: formatDate(new Date()) }
async function fetchTrendData() {
const query = getTrendRangeQuery(selectedTrendDays.value)
const [inboundTrend, shipmentTrend] = await Promise.all([
requestSafely('入库趋势', () => loadAllPages(listInbound, query), { rows: [], total: 0 }),
requestSafely('出库趋势', () => loadAllPages(listShipmentReport, query), { rows: [], total: 0 })
])
return { inboundRows: inboundTrend.rows, shipmentRows: shipmentTrend.rows }
}
async function loadTrendData() {
trendLoading.value = true
try {
const trend = await fetchTrendData()
state.trendInboundRows = trend.inboundRows
state.trendShipmentRows = trend.shipmentRows
} finally {
trendLoading.value = false
await nextTick()
renderCharts()
}
}
async function handleTrendRangeChange() {
await loadTrendData()
}
async function loadDashboardData() {
state.loading = true
state.partialError = false
const today = todayQuery()
const sevenDays = sevenDayQuery()
try {
const [inventory, todayInbound, todayShipment, recentInboundResponse, recentShipmentResponse, inboundTrend, shipmentTrend, pendingInbound, pendingShipment, pendingReturn] = await Promise.all([
const [inventory, todayInbound, todayShipment, recentInboundResponse, recentShipmentResponse, trend, pendingInbound, pendingShipment, pendingReturn] = await Promise.all([
requestSafely('托盘库存', () => loadAllPages(listPalletInventory), { rows: [], total: 0 }),
requestSafely('今日入库', () => listInbound({ ...today, pageNum: 1, pageSize: 1 }), {}),
requestSafely('今日出库', () => listShipment({ ...today, pageNum: 1, pageSize: 1 }), {}),
requestSafely('最近入库', () => listInbound(recentQuery()), {}),
requestSafely('最近出库', () => listShipmentReport(recentQuery()), {}),
requestSafely('入库趋势', () => loadAllPages(listInbound, sevenDays), { rows: [], total: 0 }),
requestSafely('出库趋势', () => loadAllPages(listShipmentReport, sevenDays), { rows: [], total: 0 }),
fetchTrendData(),
requestSafely('待确认入库', () => listInbound({ status: 'DRAFT', pageNum: 1, pageSize: 5 }), {}),
requestSafely('待确认出库', () => listShipment({ status: 'DRAFT', pageNum: 1, pageSize: 5 }), {}),
requestSafely('待确认退回', () => listReturn({ status: 'REQUESTED', pageNum: 1, pageSize: 5 }), {})
@@ -398,15 +373,14 @@ async function loadDashboardData() {
state.todayShipmentTotal = getTotal(todayShipment)
state.recentInbound = getRows(recentInboundResponse)
state.recentShipments = getRows(recentShipmentResponse)
state.trendInboundRows = inboundTrend.rows
state.trendShipmentRows = shipmentTrend.rows
state.trendInboundRows = trend.inboundRows
state.trendShipmentRows = trend.shipmentRows
state.pendingItems = [
...getRows(pendingInbound).map((row) => ({ key: `inbound-${row.inboundId}`, title: row.inboundNo, type: '入库单', status: row.status, time: row.createTime, path: '/businessOperation/inbound', tone: 'orange' })),
...getRows(pendingShipment).map((row) => ({ key: `shipment-${row.shipmentId}`, title: row.shipmentNo, type: '出库单', status: row.status, time: row.createTime, path: '/businessOperation/shipment', tone: 'blue' })),
...getRows(pendingReturn).map((row) => ({ key: `return-${row.returnId}`, title: row.returnNo, type: '退回申请', status: row.status, time: row.requestedAt, path: '/businessOperation/return', tone: 'green' }))
].slice(0, 6)
state.pendingTotal = getTotal(pendingInbound) + getTotal(pendingShipment) + getTotal(pendingReturn)
state.lastUpdated = new Date()
} finally {
state.loading = false
await nextTick()
@@ -446,7 +420,7 @@ function renderCharts() {
tooltip: { trigger: 'axis' },
legend: { bottom: 0, textStyle: { color: chartTextColor() } },
grid: { top: 18, right: 20, bottom: 42, left: 42 },
xAxis: { type: 'category', boundaryGap: false, data: trendData.value.labels, axisLine: { lineStyle: { color: chartGridColor() } }, axisLabel: { color: chartTextColor() } },
xAxis: { type: 'category', boundaryGap: false, data: trendData.value.labels, axisLine: { lineStyle: { color: chartGridColor() } }, axisLabel: { color: chartTextColor(), interval: selectedTrendDays.value > 14 ? 4 : 0, hideOverlap: true } },
yAxis: { type: 'value', minInterval: 1, splitLine: { lineStyle: { color: chartGridColor() } }, axisLabel: { color: chartTextColor() } },
series: [
{ name: '入库单', type: 'line', smooth: true, symbol: 'circle', symbolSize: 7, data: trendData.value.inbound, areaStyle: { opacity: 0.08 } },
@@ -499,23 +473,14 @@ onBeforeUnmount(() => {
color: var(--el-text-color-primary, #1f2937);
}
.dashboard-header,
.section-heading,
.card-heading,
.dashboard-header-actions,
.dashboard-meta,
.warehouse-title {
display: flex;
align-items: center;
}
.dashboard-header { justify-content: space-between; gap: 24px; margin-bottom: 20px; }
.dashboard-kicker, .section-kicker { margin-bottom: 6px; color: var(--el-color-primary); font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; }
.dashboard-header h1, .section-heading h2, .card-heading h2 { margin: 0; color: var(--el-text-color-primary); font-weight: 700; }
.dashboard-header h1 { font-size: 26px; line-height: 1.25; }
.dashboard-header p, .section-heading > span, .card-heading span { margin: 8px 0 0; color: var(--el-text-color-secondary); font-size: 13px; }
.dashboard-header-actions { flex-direction: column; align-items: flex-end; gap: 12px; }
.dashboard-meta { gap: 12px; color: var(--el-text-color-secondary); font-size: 12px; }
.card-heading h2 { margin: 0; color: var(--el-text-color-primary); font-weight: 700; }
.card-heading span { margin: 8px 0 0; color: var(--el-text-color-secondary); font-size: 13px; }
.dashboard-alert { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; padding: 10px 14px; border: 1px solid var(--el-color-warning-light-5); border-radius: 10px; background: var(--el-color-warning-light-9); color: var(--el-color-warning-dark-2); font-size: 13px; }
.dashboard-alert .el-button { margin-left: auto; }
@@ -527,17 +492,17 @@ onBeforeUnmount(() => {
.kpi-content { min-width: 0; }.kpi-label { display: block; color: var(--el-text-color-secondary); font-size: 13px; }.kpi-content strong { display: block; margin: 7px 0 2px; font-size: 28px; line-height: 1; }.kpi-caption { color: var(--el-text-color-placeholder); font-size: 12px; }
.dashboard-section, .dashboard-card { border: 1px solid var(--el-border-color-lighter); border-radius: 14px; background: var(--el-bg-color, #fff); box-shadow: 0 5px 18px rgba(31, 55, 88, .04); }
.quick-section { margin-bottom: 20px; padding: 20px; }.section-heading { justify-content: space-between; gap: 16px; margin-bottom: 16px; }.section-heading > span { margin: 0; }.section-heading h2, .card-heading h2 { font-size: 17px; }
.quick-section { margin-bottom: 20px; padding: 20px; }.card-heading h2 { font-size: 17px; }
.quick-action-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 10px; }.quick-action { display: flex; align-items: center; gap: 10px; min-width: 0; padding: 12px; border: 1px solid var(--el-border-color-lighter); border-radius: 10px; background: transparent; color: inherit; text-align: left; cursor: pointer; transition: border-color .2s ease, background-color .2s ease, transform .2s ease; }.quick-action:hover { border-color: var(--el-color-primary-light-5); background: var(--el-color-primary-light-9); transform: translateY(-1px); }.quick-action-icon { width: 34px; height: 34px; background: var(--el-color-primary-light-9); color: var(--el-color-primary); font-size: 18px; }.quick-action-copy { display: flex; flex-direction: column; min-width: 0; gap: 3px; }.quick-action-copy strong { font-size: 13px; white-space: nowrap; }.quick-action-copy small { overflow: hidden; color: var(--el-text-color-secondary); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }.quick-action-arrow { margin-left: auto; color: var(--el-text-color-placeholder); }
.dashboard-grid { display: grid; gap: 20px; margin-bottom: 20px; }.chart-grid { grid-template-columns: minmax(0, 1.55fr) minmax(340px, 1fr); }.operational-grid { grid-template-columns: minmax(0, 1.1fr) minmax(360px, .9fr); }.activity-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 0; }.dashboard-card :deep(.el-card__header) { padding: 18px 20px; border-bottom-color: var(--el-border-color-lighter); }.dashboard-card :deep(.el-card__body) { padding: 18px 20px; }.dashboard-card { overflow: hidden; }.card-heading { justify-content: space-between; gap: 12px; }.card-heading > div { min-width: 0; }.card-heading span { display: block; margin-top: 5px; font-size: 12px; }
.chart-wrapper { position: relative; min-height: 290px; }.chart-wrapper--distribution { min-height: 290px; }.chart-canvas { width: 100%; height: 290px; }.chart-loading, .chart-empty { position: absolute; inset: 20px; }
.trend-range-select { width: 112px; flex: 0 0 auto; }.chart-wrapper { position: relative; min-height: 290px; }.chart-wrapper--distribution { min-height: 290px; }.chart-canvas { width: 100%; height: 290px; }.chart-loading, .chart-empty { position: absolute; inset: 20px; }
.warehouse-ranking, .pending-list, .activity-list { min-height: 250px; }.warehouse-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; }.warehouse-row + .warehouse-row { border-top: 1px solid var(--el-border-color-lighter); }.warehouse-rank { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 7px; background: var(--el-fill-color-light); color: var(--el-text-color-secondary); font-size: 12px; font-weight: 700; }.warehouse-rank--top { background: var(--el-color-primary-light-9); color: var(--el-color-primary); }.warehouse-main { flex: 1; min-width: 0; }.warehouse-title { justify-content: space-between; gap: 10px; margin-bottom: 8px; font-size: 13px; }.warehouse-title span { color: var(--el-text-color-placeholder); font-size: 12px; }.warehouse-value { flex: 0 0 84px; color: var(--el-text-color-primary); font-size: 13px; text-align: right; }
.pending-item, .activity-item { display: flex; align-items: center; width: 100%; gap: 10px; padding: 12px 0; border: 0; border-bottom: 1px solid var(--el-border-color-lighter); background: transparent; color: inherit; text-align: left; cursor: pointer; }.pending-item:last-child, .activity-item:last-child { border-bottom: 0; }.pending-item:hover .pending-copy strong, .activity-item:hover .activity-copy strong { color: var(--el-color-primary); }.pending-dot { width: 8px; height: 8px; border-radius: 50%; }.pending-dot--orange { background: #f59e0b; }.pending-dot--blue { background: #1677ff; }.pending-dot--green { background: #16a085; }.pending-copy, .activity-copy { display: flex; flex: 1; flex-direction: column; min-width: 0; gap: 4px; }.pending-copy strong, .activity-copy strong { overflow: hidden; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }.pending-copy small, .activity-copy small { color: var(--el-text-color-secondary); font-size: 12px; }.activity-icon { width: 34px; height: 34px; font-size: 16px; }.activity-icon--inbound { background: #eaf3ff; color: #1677ff; }.activity-icon--outbound { background: #fff1e8; color: #d97706; }.activity-quantity { color: var(--el-color-primary); font-size: 12px; font-weight: 700; white-space: nowrap; }
.exception-empty { display: flex; min-height: 250px; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: var(--el-text-color-secondary); text-align: center; }.exception-icon { width: 52px; height: 52px; margin-bottom: 5px; background: var(--el-color-success-light-9); color: var(--el-color-success); font-size: 26px; }.exception-empty strong { color: var(--el-text-color-primary); font-size: 14px; }.exception-empty span { max-width: 230px; font-size: 12px; line-height: 1.6; }
@media (max-width: 1280px) { .kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }.quick-action-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }.activity-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }.activity-card:last-child { grid-column: span 2; } }
@media (max-width: 900px) { .dashboard-page { padding: 16px; }.dashboard-header { align-items: flex-start; flex-direction: column; }.dashboard-header-actions { align-items: flex-start; flex-direction: row; flex-wrap: wrap; }.chart-grid, .operational-grid { grid-template-columns: 1fr; } }
@media (max-width: 640px) { .dashboard-header h1 { font-size: 22px; }.dashboard-header-actions, .dashboard-meta { align-items: flex-start; flex-direction: column; }.dashboard-meta { gap: 6px; }.kpi-grid, .quick-action-grid, .activity-grid { grid-template-columns: 1fr; }.activity-card:last-child { grid-column: auto; }.quick-action { min-height: 54px; }.dashboard-alert { align-items: flex-start; flex-wrap: wrap; }.dashboard-alert .el-button { margin-left: 0; } }
@media (max-width: 900px) { .dashboard-page { padding: 16px; }.chart-grid, .operational-grid { grid-template-columns: 1fr; } }
@media (max-width: 640px) { .kpi-grid, .quick-action-grid, .activity-grid { grid-template-columns: 1fr; }.activity-card:last-child { grid-column: auto; }.quick-action { min-height: 54px; }.trend-range-select { width: 104px; }.dashboard-alert { align-items: flex-start; flex-wrap: wrap; }.dashboard-alert .el-button { margin-left: 0; } }
</style>
@@ -1,6 +1,6 @@
<template>
<div class="app-container master-page">
<el-form ref="queryRef" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form ref="queryRef" v-show="showSearch" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form-item label="客户编码" prop="customerCode">
<el-input v-model="queryParams.customerCode" placeholder="请输入客户编码" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
@@ -18,18 +18,21 @@
</el-form-item>
</el-form>
<el-row v-if="isMasterWriteAllowed" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:customer:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:customer:query', 'master:customer:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:customer:edit']"
>修改</el-button>
</el-col>
</el-row>
<el-row v-if="userStore.admin === true && !testBatchStore.isTestMode" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></el-col>
<el-row :gutter="10" class="mb8 master-toolbar">
<template v-if="isMasterWriteAllowed">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:customer:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:customer:query', 'master:customer:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:customer:edit']"
>修改</el-button>
</el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</template>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
@@ -101,7 +104,8 @@
<style scoped>
.master-query { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:0 14px; padding:16px 18px 4px; margin-bottom:14px; background:var(--el-bg-color); border:1px solid var(--el-border-color-lighter); border-radius:8px; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; }
.master-query :deep(.el-input), .master-query :deep(.el-select) { width:100% !important; }
.master-query :deep(.el-form-item__content) { flex:1; min-width:0; }
.master-query :deep(.el-input), .master-query :deep(.el-input-number), .master-query :deep(.el-select) { width:100% !important; min-width:0; }
.master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-toolbar { display:flex; flex-wrap:wrap; gap:8px; }
.master-toolbar :deep(.el-col) { width:auto; }
@@ -124,6 +128,7 @@ const isMasterWriteAllowed = computed(() => userStore.admin === true || !testBat
const adoptionDialogRef = ref()
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const customerList = ref([]);
const showSearch = ref(true);
const selectedIds = ref([]);
const loading = ref(false);
const detailLoading = ref(false);
@@ -1,6 +1,6 @@
<template>
<div class="app-container master-page">
<el-form ref="queryRef" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form ref="queryRef" v-show="showSearch" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form-item label="现场ID" prop="siteId">
<el-input-number v-model="queryParams.siteId" :min="1" :step="1" step-strictly controls-position="right" placeholder="请输入现场ID" />
</el-form-item>
@@ -21,18 +21,21 @@
</el-form-item>
</el-form>
<el-row v-if="isMasterWriteAllowed" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:line:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:line:query', 'master:line:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:line:edit']"
>修改</el-button>
</el-col>
</el-row>
<el-row v-if="userStore.admin === true && !testBatchStore.isTestMode" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></el-col>
<el-row :gutter="10" class="mb8 master-toolbar">
<template v-if="isMasterWriteAllowed">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:line:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:line:query', 'master:line:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:line:edit']"
>修改</el-button>
</el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</template>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
@@ -107,7 +110,7 @@
<style scoped>
.master-query { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:0 14px; padding:16px 18px 4px; margin-bottom:14px; background:var(--el-bg-color); border:1px solid var(--el-border-color-lighter); border-radius:8px; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-input), .master-query :deep(.el-select) { width:100% !important; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-form-item__content) { flex:1; min-width:0; } .master-query :deep(.el-input), .master-query :deep(.el-input-number), .master-query :deep(.el-select) { width:100% !important; min-width:0; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-toolbar { display:flex; flex-wrap:wrap; gap:8px; } .master-toolbar :deep(.el-col) { width:auto; }
.master-table :deep(.el-table__header th) { background:var(--el-fill-color-light); font-weight:600; } .master-table :deep(.el-table__cell) { padding:10px 0; } .master-table :deep(.fixed-width) { position:sticky; right:0; z-index:2; background:var(--el-bg-color); box-shadow:-4px 0 8px rgba(0,0,0,.04); }
@media (max-width:768px) { .master-query { grid-template-columns:1fr; padding:14px 14px 2px; } .master-query :deep(.el-form-item:last-child) { margin-bottom:12px; } .master-toolbar :deep(.el-button) { margin-left:0; } }
@@ -126,6 +129,7 @@ const isMasterWriteAllowed = computed(() => userStore.admin === true || !testBat
const adoptionDialogRef = ref()
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const lineList = ref([]);
const showSearch = ref(true);
const selectedIds = ref([]);
const loading = ref(false);
const detailLoading = ref(false);
@@ -1,6 +1,6 @@
<template>
<div class="app-container master-page">
<el-form ref="queryRef" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form ref="queryRef" v-show="showSearch" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form-item label="托盘编码" prop="palletCode">
<el-input v-model="queryParams.palletCode" placeholder="请输入托盘编码" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
@@ -15,18 +15,21 @@
</el-form-item>
</el-form>
<el-row v-if="isMasterWriteAllowed" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:pallet:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:pallet:query', 'master:pallet:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:pallet:edit']"
>修改</el-button>
</el-col>
</el-row>
<el-row v-if="userStore.admin === true && !testBatchStore.isTestMode" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></el-col>
<el-row :gutter="10" class="mb8 master-toolbar">
<template v-if="isMasterWriteAllowed">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:pallet:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:pallet:query', 'master:pallet:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:pallet:edit']"
>修改</el-button>
</el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</template>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
@@ -107,7 +110,7 @@
<style scoped>
.master-query { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:0 14px; padding:16px 18px 4px; margin-bottom:14px; background:var(--el-bg-color); border:1px solid var(--el-border-color-lighter); border-radius:8px; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-input), .master-query :deep(.el-select) { width:100% !important; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-form-item__content) { flex:1; min-width:0; } .master-query :deep(.el-input), .master-query :deep(.el-input-number), .master-query :deep(.el-select) { width:100% !important; min-width:0; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-toolbar { display:flex; flex-wrap:wrap; gap:8px; } .master-toolbar :deep(.el-col) { width:auto; }
.master-table :deep(.el-table__header th) { background:var(--el-fill-color-light); font-weight:600; } .master-table :deep(.el-table__cell) { padding:10px 0; } .master-table :deep(.fixed-width) { position:sticky; right:0; z-index:2; background:var(--el-bg-color); box-shadow:-4px 0 8px rgba(0,0,0,.04); }
@media (max-width:768px) { .master-query { grid-template-columns:1fr; padding:14px 14px 2px; } .master-query :deep(.el-form-item:last-child) { margin-bottom:12px; } .master-toolbar :deep(.el-button) { margin-left:0; } }
@@ -126,6 +129,7 @@ const isMasterWriteAllowed = computed(() => userStore.admin === true || !testBat
const adoptionDialogRef = ref()
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const palletList = ref([]);
const showSearch = ref(true);
const selectedIds = ref([]);
const loading = ref(false);
const detailLoading = ref(false);
@@ -1,6 +1,6 @@
<template>
<div class="app-container master-page">
<el-form ref="queryRef" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form ref="queryRef" v-show="showSearch" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form-item label="客户ID" prop="customerId">
<el-input-number v-model="queryParams.customerId" :min="1" :step="1" step-strictly controls-position="right" placeholder="请输入客户ID" />
</el-form-item>
@@ -21,19 +21,22 @@
</el-form-item>
</el-form>
<el-row v-if="isMasterWriteAllowed" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:site:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:site:query', 'master:site:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:site:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5"><el-button type="warning" plain icon="User" :disabled="single" @click="handleAssign()" v-hasPermi="['master:site:assign']">分配用户</el-button></el-col>
</el-row>
<el-row v-if="userStore.admin === true && !testBatchStore.isTestMode" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></el-col>
<el-row :gutter="10" class="mb8 master-toolbar">
<template v-if="isMasterWriteAllowed">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:site:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:site:query', 'master:site:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:site:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5"><el-button type="warning" plain icon="User" :disabled="single" @click="handleAssign()" v-hasPermi="['master:site:assign']">分配用户</el-button></el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</template>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
@@ -115,7 +118,7 @@
<style scoped>
.master-query { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:0 14px; padding:16px 18px 4px; margin-bottom:14px; background:var(--el-bg-color); border:1px solid var(--el-border-color-lighter); border-radius:8px; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-input), .master-query :deep(.el-select) { width:100% !important; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-form-item__content) { flex:1; min-width:0; } .master-query :deep(.el-input), .master-query :deep(.el-input-number), .master-query :deep(.el-select) { width:100% !important; min-width:0; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-toolbar { display:flex; flex-wrap:wrap; gap:8px; } .master-toolbar :deep(.el-col) { width:auto; }
.master-table :deep(.el-table__header th) { background:var(--el-fill-color-light); font-weight:600; } .master-table :deep(.el-table__cell) { padding:10px 0; } .master-table :deep(.fixed-width) { position:sticky; right:0; z-index:2; background:var(--el-bg-color); box-shadow:-4px 0 8px rgba(0,0,0,.04); }
@media (max-width:768px) { .master-query { grid-template-columns:1fr; padding:14px 14px 2px; } .master-query :deep(.el-form-item:last-child) { margin-bottom:12px; } .master-toolbar :deep(.el-button) { margin-left:0; } }
@@ -135,6 +138,7 @@ const isMasterWriteAllowed = computed(() => userStore.admin === true || !testBat
const adoptionDialogRef = ref()
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const siteList = ref([]);
const showSearch = ref(true);
const selectedIds = ref([]);
const selectedRows = ref([]);
const assignDialogRef = ref();
@@ -1,6 +1,6 @@
<template>
<div class="app-container master-page">
<el-form ref="queryRef" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form ref="queryRef" v-show="showSearch" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form-item label="菌种编码" prop="strainCode">
<el-input v-model="queryParams.strainCode" placeholder="请输入菌种编码" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
@@ -18,25 +18,28 @@
</el-form-item>
</el-form>
<el-row v-if="isMasterWriteAllowed" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:strain:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:strain:query', 'master:strain:edit'])"
type="success"
plain
icon="Edit"
:disabled="single || detailLoading"
:loading="detailLoading"
@click="handleUpdate()"
v-hasPermi="['master:strain:edit']"
>修改</el-button>
</el-col>
</el-row>
<el-row v-if="userStore.admin === true && !testBatchStore.isTestMode" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></el-col>
<el-row :gutter="10" class="mb8 master-toolbar">
<template v-if="isMasterWriteAllowed">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:strain:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:strain:query', 'master:strain:edit'])"
type="success"
plain
icon="Edit"
:disabled="single || detailLoading"
:loading="detailLoading"
@click="handleUpdate()"
v-hasPermi="['master:strain:edit']"
>修改</el-button>
</el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</template>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
@@ -118,7 +121,7 @@
<style scoped>
.master-query { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:0 14px; padding:16px 18px 4px; margin-bottom:14px; background:var(--el-bg-color); border:1px solid var(--el-border-color-lighter); border-radius:8px; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-input), .master-query :deep(.el-select) { width:100% !important; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-form-item__content) { flex:1; min-width:0; } .master-query :deep(.el-input), .master-query :deep(.el-input-number), .master-query :deep(.el-select) { width:100% !important; min-width:0; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-toolbar { display:flex; flex-wrap:wrap; gap:8px; } .master-toolbar :deep(.el-col) { width:auto; }
.master-table :deep(.el-table__header th) { background:var(--el-fill-color-light); font-weight:600; } .master-table :deep(.el-table__cell) { padding:10px 0; } .master-table :deep(.fixed-width) { position:sticky; right:0; z-index:2; background:var(--el-bg-color); box-shadow:-4px 0 8px rgba(0,0,0,.04); }
@media (max-width:768px) { .master-query { grid-template-columns:1fr; padding:14px 14px 2px; } .master-query :deep(.el-form-item:last-child) { margin-bottom:12px; } .master-toolbar :deep(.el-button) { margin-left:0; } }
@@ -138,6 +141,7 @@ const adoptionDialogRef = ref()
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const strainList = ref([]);
const showSearch = ref(true);
const selectedIds = ref([]);
const loading = ref(false);
const detailLoading = ref(false);
@@ -1,6 +1,6 @@
<template>
<div class="app-container master-page">
<el-form ref="queryRef" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form ref="queryRef" v-show="showSearch" class="master-query page-filter" :model="queryParams" :inline="true">
<el-form-item label="仓库编码" prop="warehouseCode">
<el-input v-model="queryParams.warehouseCode" placeholder="请输入仓库编码" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
@@ -18,19 +18,22 @@
</el-form-item>
</el-form>
<el-row v-if="isMasterWriteAllowed" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:warehouse:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:warehouse:query', 'master:warehouse:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:warehouse:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5"><el-button type="warning" plain icon="User" :disabled="single" @click="handleAssign()" v-hasPermi="['master:warehouse:assign']">分配用户</el-button></el-col>
</el-row>
<el-row v-if="userStore.admin === true && !testBatchStore.isTestMode" :gutter="10" class="mb8 master-toolbar">
<el-col :span="1.5"><el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button></el-col>
<el-row :gutter="10" class="mb8 master-toolbar">
<template v-if="isMasterWriteAllowed">
<el-col :span="1.5"><el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['master:warehouse:add']">新增</el-button></el-col>
<el-col :span="1.5">
<el-button
v-if="proxy.$auth.hasPermiAnd(['master:warehouse:query', 'master:warehouse:edit'])"
type="success" plain icon="Edit" :disabled="single || detailLoading" :loading="detailLoading"
@click="handleUpdate()" v-hasPermi="['master:warehouse:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5"><el-button type="warning" plain icon="User" :disabled="single" @click="handleAssign()" v-hasPermi="['master:warehouse:assign']">分配用户</el-button></el-col>
<el-col v-if="userStore.admin === true && !testBatchStore.isTestMode" :span="1.5">
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
</el-col>
</template>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
@@ -108,7 +111,7 @@
<style scoped>
.master-query { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:0 14px; padding:16px 18px 4px; margin-bottom:14px; background:var(--el-bg-color); border:1px solid var(--el-border-color-lighter); border-radius:8px; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-input), .master-query :deep(.el-select) { width:100% !important; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-query :deep(.el-form-item) { margin-right:0; width:100%; } .master-query :deep(.el-form-item__content) { flex:1; min-width:0; } .master-query :deep(.el-input), .master-query :deep(.el-input-number), .master-query :deep(.el-select) { width:100% !important; min-width:0; } .master-query :deep(.el-form-item:last-child) { align-self:end; }
.master-toolbar { display:flex; flex-wrap:wrap; gap:8px; } .master-toolbar :deep(.el-col) { width:auto; }
.master-table :deep(.el-table__header th) { background:var(--el-fill-color-light); font-weight:600; } .master-table :deep(.el-table__cell) { padding:10px 0; } .master-table :deep(.fixed-width) { position:sticky; right:0; z-index:2; background:var(--el-bg-color); box-shadow:-4px 0 8px rgba(0,0,0,.04); }
@media (max-width:768px) { .master-query { grid-template-columns:1fr; padding:14px 14px 2px; } .master-query :deep(.el-form-item:last-child) { margin-bottom:12px; } .master-toolbar :deep(.el-button) { margin-left:0; } }
@@ -128,6 +131,7 @@ const isMasterWriteAllowed = computed(() => userStore.admin === true || !testBat
const adoptionDialogRef = ref()
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const warehouseList = ref([]);
const showSearch = ref(true);
const selectedIds = ref([]);
const selectedRows = ref([]);
const assignDialogRef = ref();
@@ -1,9 +1,6 @@
<template>
<div class="app-container report-page">
<el-form ref="queryRef" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="76px">
<el-form-item label="使用时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form ref="queryRef" v-show="showSearch" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="88px">
<el-form-item label="客户ID" prop="customerId">
<el-input-number v-model="queryParams.customerId" :min="1" :controls="false" placeholder="请输入客户ID" />
</el-form-item>
@@ -22,12 +19,21 @@
<el-form-item label="生产批次" prop="batchNo">
<el-input v-model="queryParams.batchNo" placeholder="请输入生产批次" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:consumption:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:consumption:list']">重置</el-button>
</el-form-item>
<div class="report-filter__footer">
<el-form-item class="report-filter__date" label="使用时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item class="report-filter__actions">
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:consumption:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:consumption:list']">重置</el-button>
</el-form-item>
</div>
</el-form>
<el-row :gutter="10" class="mb8 report-toolbar">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<el-table v-loading="loading" class="report-table data-table" stripe border height="calc(100vh - 330px)" :data="reportList">
<template #empty><el-empty description="暂无产线消耗数据" /></template>
@@ -67,11 +73,65 @@
<style scoped>
.report-page { min-width: 0; }
.report-filter { padding: 16px 16px 4px; margin-bottom: 12px; background: var(--el-bg-color); border: 1px solid var(--el-border-color-lighter); border-radius: 6px; }
.report-filter :deep(.el-form-item) { margin-bottom: 12px; }
.report-filter {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 0 14px;
padding: 16px 16px 4px;
margin-bottom: 12px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
}
.report-filter :deep(.el-form-item) { width: 100%; margin-right: 0; margin-bottom: 12px; }
.report-filter :deep(.el-form-item__content) { flex: 1; min-width: 0; }
.report-filter :deep(.el-input),
.report-filter :deep(.el-input-number),
.report-filter :deep(.el-select),
.report-filter :deep(.el-date-editor) { width: 100% !important; min-width: 0; }
.report-filter__footer {
grid-column: 1 / -1;
display: flex;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
width: 100%;
min-width: 0;
}
.report-filter__footer :deep(.el-form-item) {
margin-right: 0;
margin-bottom: 12px;
}
.report-filter .report-filter__date {
flex: 0 1 420px;
width: 420px;
min-width: 0;
max-width: 100%;
}
.report-filter .report-filter__actions {
flex: 0 0 auto;
width: auto;
}
.report-toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
.report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; }
.report-table :deep(.el-table__cell) { padding: 10px 0; }
@media (max-width: 900px) { .report-filter :deep(.el-form-item) { width: 100%; } .report-filter :deep(.el-input), .report-filter :deep(.el-input-number), .report-filter :deep(.el-select), .report-filter :deep(.el-date-editor) { width: 100%; } }
@media (max-width: 900px) {
.report-filter { grid-template-columns: 1fr; padding: 14px 14px 2px; }
.report-filter__footer {
grid-column: 1;
flex-direction: column;
align-items: stretch;
gap: 0;
}
.report-filter .report-filter__date {
flex: 0 0 auto;
width: 100%;
min-width: 0;
max-width: none;
}
.report-filter .report-filter__actions { align-self: flex-end; margin-bottom: 12px; }
.report-toolbar :deep(.el-button) { margin-left: 0; }
}
</style>
<script setup name="ReportConsumption">
@@ -79,6 +139,7 @@ import { listConsumptionReport } from '@/api/report/consumption'
const { proxy } = getCurrentInstance()
const reportList = ref([])
const showSearch = ref(true)
const loading = ref(false)
const loadError = ref('')
const total = ref(0)
@@ -1,9 +1,6 @@
<template>
<div class="app-container report-page">
<el-form ref="queryRef" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="76px">
<el-form-item label="装填时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form ref="queryRef" v-show="showSearch" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="88px">
<el-form-item label="仓库ID" prop="warehouseId">
<el-input-number v-model="queryParams.warehouseId" :min="1" :controls="false" placeholder="请输入仓库ID" />
</el-form-item>
@@ -16,12 +13,21 @@
<el-form-item label="生产批次" prop="batchNo">
<el-input v-model="queryParams.batchNo" placeholder="请输入生产批次" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:filling:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:filling:list']">重置</el-button>
</el-form-item>
<div class="report-filter__footer">
<el-form-item class="report-filter__date" label="装填时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item class="report-filter__actions">
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:filling:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:filling:list']">重置</el-button>
</el-form-item>
</div>
</el-form>
<el-row :gutter="10" class="mb8 report-toolbar">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<el-table v-loading="loading" class="report-table data-table" stripe border height="calc(100vh - 330px)" :data="reportList">
<template #empty><el-empty description="暂无装填记录" /></template>
@@ -56,11 +62,65 @@
<style scoped>
.report-page { min-width: 0; }
.report-filter { padding: 16px 16px 4px; margin-bottom: 12px; background: var(--el-bg-color); border: 1px solid var(--el-border-color-lighter); border-radius: 6px; }
.report-filter :deep(.el-form-item) { margin-bottom: 12px; }
.report-filter {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 0 14px;
padding: 16px 16px 4px;
margin-bottom: 12px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
}
.report-filter :deep(.el-form-item) { width: 100%; margin-right: 0; margin-bottom: 12px; }
.report-filter :deep(.el-form-item__content) { flex: 1; min-width: 0; }
.report-filter :deep(.el-input),
.report-filter :deep(.el-input-number),
.report-filter :deep(.el-select),
.report-filter :deep(.el-date-editor) { width: 100% !important; min-width: 0; }
.report-filter__footer {
grid-column: 1 / -1;
display: flex;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
width: 100%;
min-width: 0;
}
.report-filter__footer :deep(.el-form-item) {
margin-right: 0;
margin-bottom: 12px;
}
.report-filter .report-filter__date {
flex: 0 1 420px;
width: 420px;
min-width: 0;
max-width: 100%;
}
.report-filter .report-filter__actions {
flex: 0 0 auto;
width: auto;
}
.report-toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
.report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; }
.report-table :deep(.el-table__cell) { padding: 10px 0; }
@media (max-width: 900px) { .report-filter :deep(.el-form-item) { width: 100%; } .report-filter :deep(.el-input), .report-filter :deep(.el-input-number), .report-filter :deep(.el-select), .report-filter :deep(.el-date-editor) { width: 100%; } }
@media (max-width: 900px) {
.report-filter { grid-template-columns: 1fr; padding: 14px 14px 2px; }
.report-filter__footer {
grid-column: 1;
flex-direction: column;
align-items: stretch;
gap: 0;
}
.report-filter .report-filter__date {
flex: 0 0 auto;
width: 100%;
min-width: 0;
max-width: none;
}
.report-filter .report-filter__actions { align-self: flex-end; margin-bottom: 12px; }
.report-toolbar :deep(.el-button) { margin-left: 0; }
}
</style>
<script setup name="ReportFilling">
@@ -68,6 +128,7 @@ import { listFillingReport } from '@/api/report/filling'
const { proxy } = getCurrentInstance()
const reportList = ref([])
const showSearch = ref(true)
const loading = ref(false)
const loadError = ref('')
const total = ref(0)
@@ -1,21 +1,11 @@
<template>
<div class="app-container report-page">
<el-form ref="queryRef" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="76px">
<el-form-item label="更新时间">
<el-date-picker
v-model="dateRange"
type="daterange"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form ref="queryRef" v-show="showSearch" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="88px">
<el-form-item label="托盘编号" prop="palletCode">
<el-input v-model="queryParams.palletCode" placeholder="请输入托盘编号" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流转状态" prop="flowState">
<el-select v-model="queryParams.flowState" placeholder="请选择状态" clearable style="width: 190px">
<el-select v-model="queryParams.flowState" placeholder="请选择状态" clearable>
<el-option v-for="option in flowStateOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
@@ -34,12 +24,28 @@
<el-form-item label="生产批次" prop="batchNo">
<el-input v-model="queryParams.batchNo" placeholder="请输入生产批次" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:palletinventory:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:palletinventory:list']">重置</el-button>
</el-form-item>
<div class="report-filter__footer">
<el-form-item class="report-filter__date" label="更新时间">
<el-date-picker
v-model="dateRange"
type="daterange"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item class="report-filter__actions">
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:palletinventory:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:palletinventory:list']">重置</el-button>
</el-form-item>
</div>
</el-form>
<el-row :gutter="10" class="mb8 report-toolbar">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<el-table v-loading="loading" class="report-table data-table" stripe border height="calc(100vh - 330px)" :data="reportList">
<template #empty><el-empty description="暂无托盘库存数据" /></template>
@@ -74,7 +80,66 @@
</template>
<style scoped>
.report-page { min-width: 0; } .report-filter { padding: 16px 16px 4px; margin-bottom: 12px; background: var(--el-bg-color); border: 1px solid var(--el-border-color-lighter); border-radius: 6px; } .report-filter :deep(.el-form-item) { margin-bottom: 12px; } .report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; } .report-table :deep(.el-table__cell) { padding: 10px 0; } @media (max-width: 900px) { .report-filter :deep(.el-form-item) { width: 100%; } .report-filter :deep(.el-input), .report-filter :deep(.el-input-number), .report-filter :deep(.el-select), .report-filter :deep(.el-date-editor) { width: 100%; } }
.report-page { min-width: 0; }
.report-filter {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 0 14px;
padding: 16px 16px 4px;
margin-bottom: 12px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
}
.report-filter :deep(.el-form-item) { width: 100%; margin-right: 0; margin-bottom: 12px; }
.report-filter :deep(.el-form-item__content) { flex: 1; min-width: 0; }
.report-filter :deep(.el-input),
.report-filter :deep(.el-input-number),
.report-filter :deep(.el-select),
.report-filter :deep(.el-date-editor) { width: 100% !important; min-width: 0; }
.report-filter__footer {
grid-column: 1 / -1;
display: flex;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
width: 100%;
min-width: 0;
}
.report-filter__footer :deep(.el-form-item) {
margin-right: 0;
margin-bottom: 12px;
}
.report-filter .report-filter__date {
flex: 0 1 420px;
width: 420px;
min-width: 0;
max-width: 100%;
}
.report-filter .report-filter__actions {
flex: 0 0 auto;
width: auto;
}
.report-toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
.report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; }
.report-table :deep(.el-table__cell) { padding: 10px 0; }
@media (max-width: 900px) {
.report-filter { grid-template-columns: 1fr; padding: 14px 14px 2px; }
.report-filter__footer {
grid-column: 1;
flex-direction: column;
align-items: stretch;
gap: 0;
}
.report-filter .report-filter__date {
flex: 0 0 auto;
width: 100%;
min-width: 0;
max-width: none;
}
.report-filter .report-filter__actions { align-self: flex-end; margin-bottom: 12px; }
.report-toolbar :deep(.el-button) { margin-left: 0; }
}
</style>
<script setup name="ReportPalletInventory">
@@ -82,6 +147,7 @@ import { listPalletInventory } from '@/api/report/palletInventory'
const { proxy } = getCurrentInstance()
const reportList = ref([])
const showSearch = ref(true)
const loading = ref(false)
const loadError = ref('')
const total = ref(0)
@@ -1,23 +1,29 @@
<template>
<div class="app-container report-page">
<el-form ref="queryRef" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="76px">
<el-form-item label="发生时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form ref="queryRef" v-show="showSearch" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="88px">
<el-form-item label="托盘编号" prop="palletCode">
<el-input v-model="queryParams.palletCode" placeholder="请输入托盘编号" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="事件类型" prop="eventType">
<el-select v-model="queryParams.eventType" placeholder="请选择事件类型" clearable style="width: 190px">
<el-select v-model="queryParams.eventType" placeholder="请选择事件类型" clearable>
<el-option v-for="option in eventTypeOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:pallettrace:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:pallettrace:list']">重置</el-button>
</el-form-item>
<div class="report-filter__footer">
<el-form-item class="report-filter__date" label="发生时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item class="report-filter__actions">
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:pallettrace:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:pallettrace:list']">重置</el-button>
</el-form-item>
</div>
</el-form>
<el-row :gutter="10" class="mb8 report-toolbar">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<el-table v-loading="loading" class="report-table data-table" stripe border height="calc(100vh - 330px)" :data="reportList">
<template #empty><el-empty description="暂无托盘追溯数据" /></template>
@@ -31,8 +37,12 @@
<template #default="scope">{{ sourceLabel(scope.row.sourceType, scope.row.sourceId) }}</template>
</el-table-column>
<el-table-column label="托盘" prop="palletCode" min-width="130" :show-overflow-tooltip="true" />
<el-table-column label="变更前状态" prop="beforeState" min-width="140" />
<el-table-column label="变更后状态" prop="afterState" min-width="140" />
<el-table-column label="变更前状态" prop="beforeState" min-width="140">
<template #default="scope">{{ flowStateLabel(scope.row.beforeState) }}</template>
</el-table-column>
<el-table-column label="变更后状态" prop="afterState" min-width="140">
<template #default="scope">{{ flowStateLabel(scope.row.afterState) }}</template>
</el-table-column>
<el-table-column label="变更前位置" min-width="180" :show-overflow-tooltip="true">
<template #default="scope">
{{ formatLocation(scope.row.beforeWarehouseName, scope.row.beforeWarehouseId, scope.row.beforeSiteName, scope.row.beforeSiteId, scope.row.beforeLineName, scope.row.beforeLineId) }}
@@ -63,7 +73,66 @@
</template>
<style scoped>
.report-page { min-width: 0; } .report-filter { padding: 16px 16px 4px; margin-bottom: 12px; background: var(--el-bg-color); border: 1px solid var(--el-border-color-lighter); border-radius: 6px; } .report-filter :deep(.el-form-item) { margin-bottom: 12px; } .report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; } .report-table :deep(.el-table__cell) { padding: 10px 0; } @media (max-width: 900px) { .report-filter :deep(.el-form-item) { width: 100%; } .report-filter :deep(.el-input), .report-filter :deep(.el-input-number), .report-filter :deep(.el-select), .report-filter :deep(.el-date-editor) { width: 100%; } }
.report-page { min-width: 0; }
.report-filter {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 0 14px;
padding: 16px 16px 4px;
margin-bottom: 12px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
}
.report-filter :deep(.el-form-item) { width: 100%; margin-right: 0; margin-bottom: 12px; }
.report-filter :deep(.el-form-item__content) { flex: 1; min-width: 0; }
.report-filter :deep(.el-input),
.report-filter :deep(.el-input-number),
.report-filter :deep(.el-select),
.report-filter :deep(.el-date-editor) { width: 100% !important; min-width: 0; }
.report-filter__footer {
grid-column: 1 / -1;
display: flex;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
width: 100%;
min-width: 0;
}
.report-filter__footer :deep(.el-form-item) {
margin-right: 0;
margin-bottom: 12px;
}
.report-filter .report-filter__date {
flex: 0 1 420px;
width: 420px;
min-width: 0;
max-width: 100%;
}
.report-filter .report-filter__actions {
flex: 0 0 auto;
width: auto;
}
.report-toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
.report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; }
.report-table :deep(.el-table__cell) { padding: 10px 0; }
@media (max-width: 900px) {
.report-filter { grid-template-columns: 1fr; padding: 14px 14px 2px; }
.report-filter__footer {
grid-column: 1;
flex-direction: column;
align-items: stretch;
gap: 0;
}
.report-filter .report-filter__date {
flex: 0 0 auto;
width: 100%;
min-width: 0;
max-width: none;
}
.report-filter .report-filter__actions { align-self: flex-end; margin-bottom: 12px; }
.report-toolbar :deep(.el-button) { margin-left: 0; }
}
</style>
<script setup name="ReportPalletTrace">
@@ -71,6 +140,7 @@ import { listPalletTrace } from '@/api/report/palletTrace'
const { proxy } = getCurrentInstance()
const reportList = ref([])
const showSearch = ref(true)
const loading = ref(false)
const loadError = ref('')
const total = ref(0)
@@ -91,6 +161,22 @@ const eventTypeOptions = [
{ value: 'RETURN_REQUESTED', label: '退回申请' },
{ value: 'RETURN_CONFIRMED', label: '退回确认' }
]
const sourceTypeOptions = [
{ value: 'INBOUND', label: '入库单' },
{ value: 'FILLING', label: '装填记录' },
{ value: 'SHIPMENT', label: '发货单' },
{ value: 'USAGE', label: '使用记录' },
{ value: 'RETURN', label: '退回单' }
]
const flowStateOptions = [
{ value: 'UNSTOCKED', label: '未入库' },
{ value: 'EMPTY_IN_WAREHOUSE', label: '仓库空托盘' },
{ value: 'FILLED_IN_WAREHOUSE', label: '仓库有料' },
{ value: 'AT_SITE_UNUSED', label: '现场未使用' },
{ value: 'AT_SITE_PARTIAL', label: '现场部分使用' },
{ value: 'AT_SITE_EMPTY', label: '现场空托盘' },
{ value: 'RETURNING', label: '退回中' }
]
let listGeneration = 0
function eventTypeLabel(value) {
@@ -98,7 +184,12 @@ function eventTypeLabel(value) {
}
function sourceLabel(sourceType, sourceId) {
return sourceType ? `${sourceType} #${sourceId}` : '—'
const label = sourceTypeOptions.find((option) => option.value === sourceType)?.label || sourceType
return label ? `${label} #${sourceId}` : '—'
}
function flowStateLabel(value) {
return flowStateOptions.find((option) => option.value === value)?.label || value || '—'
}
function formatLocation(warehouseName, warehouseId, siteName, siteId, lineName, lineId) {
@@ -1,9 +1,6 @@
<template>
<div class="app-container report-page">
<el-form ref="queryRef" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="88px">
<el-form-item label="申请时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form ref="queryRef" v-show="showSearch" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="88px">
<el-form-item label="退回单号" prop="returnNo">
<el-input v-model="queryParams.returnNo" placeholder="请输入退回单号" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
@@ -17,17 +14,26 @@
<el-input v-model="queryParams.palletCode" placeholder="请输入托盘编号" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="退回状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 160px">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="已申请" value="REQUESTED" />
<el-option label="已确认" value="CONFIRMED" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:return:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:return:list']">重置</el-button>
</el-form-item>
<div class="report-filter__footer">
<el-form-item class="report-filter__date" label="申请时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item class="report-filter__actions">
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:return:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:return:list']">重置</el-button>
</el-form-item>
</div>
</el-form>
<el-row :gutter="10" class="mb8 report-toolbar">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<el-table v-loading="loading" class="report-table data-table" stripe border height="calc(100vh - 330px)" :data="reportList">
<template #empty><el-empty description="暂无退回仓库数据" /></template>
@@ -64,7 +70,66 @@
</template>
<style scoped>
.report-page { min-width: 0; } .report-filter { padding: 16px 16px 4px; margin-bottom: 12px; background: var(--el-bg-color); border: 1px solid var(--el-border-color-lighter); border-radius: 6px; } .report-filter :deep(.el-form-item) { margin-bottom: 12px; } .report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; } .report-table :deep(.el-table__cell) { padding: 10px 0; } @media (max-width: 900px) { .report-filter :deep(.el-form-item) { width: 100%; } .report-filter :deep(.el-input), .report-filter :deep(.el-input-number), .report-filter :deep(.el-select), .report-filter :deep(.el-date-editor) { width: 100%; } }
.report-page { min-width: 0; }
.report-filter {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 0 14px;
padding: 16px 16px 4px;
margin-bottom: 12px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
}
.report-filter :deep(.el-form-item) { width: 100%; margin-right: 0; margin-bottom: 12px; }
.report-filter :deep(.el-form-item__content) { flex: 1; min-width: 0; }
.report-filter :deep(.el-input),
.report-filter :deep(.el-input-number),
.report-filter :deep(.el-select),
.report-filter :deep(.el-date-editor) { width: 100% !important; min-width: 0; }
.report-filter__footer {
grid-column: 1 / -1;
display: flex;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
width: 100%;
min-width: 0;
}
.report-filter__footer :deep(.el-form-item) {
margin-right: 0;
margin-bottom: 12px;
}
.report-filter .report-filter__date {
flex: 0 1 420px;
width: 420px;
min-width: 0;
max-width: 100%;
}
.report-filter .report-filter__actions {
flex: 0 0 auto;
width: auto;
}
.report-toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
.report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; }
.report-table :deep(.el-table__cell) { padding: 10px 0; }
@media (max-width: 900px) {
.report-filter { grid-template-columns: 1fr; padding: 14px 14px 2px; }
.report-filter__footer {
grid-column: 1;
flex-direction: column;
align-items: stretch;
gap: 0;
}
.report-filter .report-filter__date {
flex: 0 0 auto;
width: 100%;
min-width: 0;
max-width: none;
}
.report-filter .report-filter__actions { align-self: flex-end; margin-bottom: 12px; }
.report-toolbar :deep(.el-button) { margin-left: 0; }
}
</style>
<script setup name="ReportReturn">
@@ -72,6 +137,7 @@ import { listReturnReport } from '@/api/report/return'
const { proxy } = getCurrentInstance()
const reportList = ref([])
const showSearch = ref(true)
const loading = ref(false)
const loadError = ref('')
const total = ref(0)
@@ -1,9 +1,6 @@
<template>
<div class="app-container report-page">
<el-form ref="queryRef" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="88px">
<el-form-item label="确认时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form ref="queryRef" v-show="showSearch" class="report-filter page-filter" :model="queryParams" :inline="true" label-width="88px">
<el-form-item label="发货单号" prop="shipmentNo">
<el-input v-model="queryParams.shipmentNo" placeholder="请输入发货单号" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
@@ -16,12 +13,21 @@
<el-form-item label="托盘编号" prop="palletCode">
<el-input v-model="queryParams.palletCode" placeholder="请输入托盘编号" clearable maxlength="64" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:shipment:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:shipment:list']">重置</el-button>
</el-form-item>
<div class="report-filter__footer">
<el-form-item class="report-filter__date" label="确认时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item class="report-filter__actions">
<el-button type="primary" icon="Search" @click="handleQuery" v-hasPermi="['report:shipment:list']">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery" v-hasPermi="['report:shipment:list']">重置</el-button>
</el-form-item>
</div>
</el-form>
<el-row :gutter="10" class="mb8 report-toolbar">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon class="mb8" />
<el-table v-loading="loading" class="report-table data-table" stripe border height="calc(100vh - 330px)" :data="reportList">
<template #empty><el-empty description="暂无发货出库数据" /></template>
@@ -55,7 +61,66 @@
</template>
<style scoped>
.report-page { min-width: 0; } .report-filter { padding: 16px 16px 4px; margin-bottom: 12px; background: var(--el-bg-color); border: 1px solid var(--el-border-color-lighter); border-radius: 6px; } .report-filter :deep(.el-form-item) { margin-bottom: 12px; } .report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; } .report-table :deep(.el-table__cell) { padding: 10px 0; } @media (max-width: 900px) { .report-filter :deep(.el-form-item) { width: 100%; } .report-filter :deep(.el-input), .report-filter :deep(.el-input-number), .report-filter :deep(.el-select), .report-filter :deep(.el-date-editor) { width: 100%; } }
.report-page { min-width: 0; }
.report-filter {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 0 14px;
padding: 16px 16px 4px;
margin-bottom: 12px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
}
.report-filter :deep(.el-form-item) { width: 100%; margin-right: 0; margin-bottom: 12px; }
.report-filter :deep(.el-form-item__content) { flex: 1; min-width: 0; }
.report-filter :deep(.el-input),
.report-filter :deep(.el-input-number),
.report-filter :deep(.el-select),
.report-filter :deep(.el-date-editor) { width: 100% !important; min-width: 0; }
.report-filter__footer {
grid-column: 1 / -1;
display: flex;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
width: 100%;
min-width: 0;
}
.report-filter__footer :deep(.el-form-item) {
margin-right: 0;
margin-bottom: 12px;
}
.report-filter .report-filter__date {
flex: 0 1 420px;
width: 420px;
min-width: 0;
max-width: 100%;
}
.report-filter .report-filter__actions {
flex: 0 0 auto;
width: auto;
}
.report-toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
.report-table :deep(.el-table__header th) { background: var(--el-fill-color-light); font-weight: 600; }
.report-table :deep(.el-table__cell) { padding: 10px 0; }
@media (max-width: 900px) {
.report-filter { grid-template-columns: 1fr; padding: 14px 14px 2px; }
.report-filter__footer {
grid-column: 1;
flex-direction: column;
align-items: stretch;
gap: 0;
}
.report-filter .report-filter__date {
flex: 0 0 auto;
width: 100%;
min-width: 0;
max-width: none;
}
.report-filter .report-filter__actions { align-self: flex-end; margin-bottom: 12px; }
.report-toolbar :deep(.el-button) { margin-left: 0; }
}
</style>
<script setup name="ReportShipment">
@@ -63,6 +128,7 @@ import { listShipmentReport } from '@/api/report/shipment'
const { proxy } = getCurrentInstance()
const reportList = ref([])
const showSearch = ref(true)
const loading = ref(false)
const loadError = ref('')
const total = ref(0)
@@ -99,7 +99,7 @@
import draggable from "vuedraggable/dist/vuedraggable.common"
import ClipboardJS from 'clipboard'
import beautifier from 'js-beautify'
import logo from '@/assets/logo/logo.png'
import logo from '@/assets/logo/sylvan-mark.svg'
import { inputComponents, selectComponents, layoutComponents, formConf as formConfData } from '@/utils/generator/config'
import { beautifierConf } from '@/utils/index'
import { drawingDefaultValue, initDrawingDefaultValue, cleanDrawingDefaultValue } from '@/utils/generator/drawingDefault'