Commit 642ac247 by qinjianhui

feat: 生产订单联调完成

parent e91e8d4f
VITE_API_BASE=/api
\ No newline at end of file
VITE_API_BASE=/api
VITE_API_BASE_URL=/
\ No newline at end of file
......@@ -5,5 +5,6 @@
// Generated by unplugin-auto-import
export {}
declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
}
......@@ -8,24 +8,36 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
ElButton: typeof import('element-plus/es')['ElButton']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
Icon: typeof import('./src/components/Icon.vue')['default']
LogList: typeof import('./src/components/LogList.vue')['default']
NavMenu: typeof import('./src/components/NavMenu.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TableView: typeof import('./src/components/TableView.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}
......@@ -6,6 +6,8 @@
<link ref="icon" type="image/x-icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>九猫-工厂端</title>
<link rel="stylesheet" href="/iconfont/iconfont.css">
<script src="/iconfont/iconfont.js"></script>
</head>
<body>
<div id="app"></div>
......
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>iconfont Demo</title>
<link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
<link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
<link rel="stylesheet" href="demo.css">
<link rel="stylesheet" href="iconfont.css">
<script src="iconfont.js"></script>
<!-- jQuery -->
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
<!-- 代码高亮 -->
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
<style>
.main .logo {
margin-top: 0;
height: auto;
}
.main .logo a {
display: flex;
align-items: center;
}
.main .logo .sub-title {
margin-left: 0.5em;
font-size: 22px;
color: #fff;
background: linear-gradient(-45deg, #3967FF, #B500FE);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
</style>
</head>
<body>
<div class="main">
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
<img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
</a></h1>
<div class="nav-tabs">
<ul id="tabs" class="dib-box">
<li class="dib active"><span>Unicode</span></li>
<li class="dib"><span>Font class</span></li>
<li class="dib"><span>Symbol</span></li>
</ul>
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4462827" target="_blank" class="nav-more">查看项目</a>
</div>
<div class="tab-container">
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe622;</span>
<div class="name">喇叭</div>
<div class="code-name">&amp;#xe622;</div>
</li>
</ul>
<div class="article markdown">
<h2 id="unicode-">Unicode 引用</h2>
<hr>
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
<ul>
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
</ul>
<blockquote>
<p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
</blockquote>
<p>Unicode 使用步骤如下:</p>
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1710211601146') format('woff2'),
url('iconfont.woff?t=1710211601146') format('woff'),
url('iconfont.ttf?t=1710211601146') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
<pre><code class="language-css"
>.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
<pre>
<code class="language-html"
>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
</code></pre>
<blockquote>
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
</blockquote>
</div>
</div>
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-a-2labadianji3x"></span>
<div class="name">
喇叭
</div>
<div class="code-name">.icon-a-2labadianji3x
</div>
</li>
</ul>
<div class="article markdown">
<h2 id="font-class-">font-class 引用</h2>
<hr>
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
<p>与 Unicode 使用方式相比,具有如下特点:</p>
<ul>
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
</code></pre>
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;span class="iconfont icon-xxx"&gt;&lt;/span&gt;
</code></pre>
<blockquote>
<p>"
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
</blockquote>
</div>
</div>
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-a-2labadianji3x"></use>
</svg>
<div class="name">喇叭</div>
<div class="code-name">#icon-a-2labadianji3x</div>
</li>
</ul>
<div class="article markdown">
<h2 id="symbol-">Symbol 引用</h2>
<hr>
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
<ul>
<li>支持多色图标了,不再受单色限制。</li>
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
</code></pre>
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
<pre><code class="language-html">&lt;style&gt;
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
&lt;/style&gt;
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
&lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
&lt;/svg&gt;
</code></pre>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.tab-container .content:first').show()
$('#tabs li').click(function (e) {
var tabContent = $('.tab-container .content')
var index = $(this).index()
if ($(this).hasClass('active')) {
return
} else {
$('#tabs li').removeClass('active')
$(this).addClass('active')
tabContent.hide().eq(index).fadeIn()
}
})
})
</script>
</body>
</html>
@font-face {
font-family: "iconfont"; /* Project id 4462827 */
src: url('iconfont.woff2?t=1710211601146') format('woff2'),
url('iconfont.woff?t=1710211601146') format('woff'),
url('iconfont.ttf?t=1710211601146') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-a-2labadianji3x:before {
content: "\e622";
}
window._iconfont_svg_string_4462827='<svg><symbol id="icon-a-2labadianji3x" viewBox="0 0 1024 1024"><path d="M752 416a16 16 0 0 1 16 16v160a16 16 0 1 1-32 0v-160a16 16 0 0 1 16-16z m64-32a16 16 0 0 1 16 16v224a16 16 0 1 1-32 0v-224a16 16 0 0 1 16-16z m64-32a16 16 0 0 1 16 16v288a16 16 0 1 1-32 0v-288a16 16 0 0 1 16-16z m-220.864-134.176A64 64 0 0 1 672 256.32v513.76a64 64 0 0 1-101.728 51.712L321.152 640H192a64 64 0 0 1-64-64v-128a64 64 0 0 1 64-64h140.096l237.408-178.784a64 64 0 0 1 89.6 12.608z" fill="#2875FF" ></path></symbol></svg>',function(n){var t=(t=document.getElementsByTagName("script"))[t.length-1],e=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var i,o,a,d,c,s=function(t,e){e.parentNode.insertBefore(t,e)};if(e&&!n.__iconfont__svg__cssinject__){n.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}i=function(){var t,e=document.createElement("div");e.innerHTML=n._iconfont_svg_string_4462827,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?s(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(i,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),i()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(a=i,d=n.document,c=!1,r(),d.onreadystatechange=function(){"complete"==d.readyState&&(d.onreadystatechange=null,l())})}function l(){c||(c=!0,a())}function r(){try{d.documentElement.doScroll("left")}catch(t){return void setTimeout(r,50)}l()}}(window);
\ No newline at end of file
{
"id": "4462827",
"name": "factory",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "34352187",
"name": "喇叭",
"font_class": "a-2labadianji3x",
"unicode": "e622",
"unicode_decimal": 58914
}
]
}
<template>
<el-config-provider size="default" :zIndex="1000" :locale="locale">
<el-config-provider size="default" :z-index="1000" :locale="locale">
<RouterView />
</el-config-provider>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
// @ts-ignore
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
// @ts-expect-error
import en from 'element-plus/dist/locale/en.mjs'
import zhCn from 'element-plus/es/locale/lang/zh-cn.mjs'
import en from 'element-plus/es/locale/lang/en.mjs'
const language = ref('zh-cn')
const locale = computed(() => (language.value === 'zh-cn' ? zhCn : en))
</script>
......
......@@ -2,5 +2,5 @@ import axios from './axios'
import { LoginReq, LoginResp } from '@/types/api/auth'
export function loginApi(data: LoginReq) {
return axios.post<never, LoginResp>('/login', data)
return axios.post<never, LoginResp>('/factory/login', data)
}
import router from '@/router'
import Axios from 'axios'
const axios = Axios.create({
......@@ -5,13 +6,49 @@ const axios = Axios.create({
timeout: 30000,
})
axios.interceptors.request.use((config) => {
return config
})
axios.interceptors.response.use((resp) => {
const TOKEN_KEY = 'token'
export function setToken(token: string) {
localStorage.setItem(TOKEN_KEY, token)
}
export function getToken() {
return localStorage.getItem(TOKEN_KEY)
}
axios.interceptors.request.use(
(config) => {
const token = getToken()
if (token) {
config.headers['jwt-token'] = token
}
return config
},
(error) => {
Promise.reject(error)
},
)
axios.interceptors.response.use((response) => {
// 1. 判断响应码
const data = response.data
return resp.data
if (data && typeof data === 'object' && typeof data.code === 'number') {
// token 过期
if (data.code === 403) {
router.replace({ path: '/user/login' })
return Promise.reject(data)
}
if (data.code !== 200) return Promise.reject(data)
}
return data
})
export function getFilePath() {
if (!/(http|https):\/\/([^/]+)/i.test(import.meta.env.BASE_URL)) {
return location.origin + import.meta.env.BASE_URL
}
return import.meta.env.BASE_URL
}
export const filePath = getFilePath()
export default axios
import { BaseRespData } from '@/types/api'
import axios from './axios'
import { LogisticsData } from '@/types/api/order'
// 获取物流公司
export function getLogisticsCompanyList(query?: string) {
return axios.get<never, BaseRespData<LogisticsData[]>>(
'factory/customJomallOrder/getLogisticsList',
{
params: {
name: query,
},
},
)
}
import { BasePaginationData, BaseRespData } from '@/types/api'
import axios from './axios'
import {
LogListData,
OrderData,
SearchForm,
SendOrderData,
ShipmentForm,
ShipmentOrderRes,
Tab,
} from '@/types/api/order'
export function getOrderList(
data: SearchForm,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<OrderData>>(
'/factory/customJomallOrder/list_page',
{ ...data, currentPage, pageSize },
)
}
export function getOrderTabData() {
return axios.get<never, BaseRespData<Tab[]>>(
'factory/customJomallOrder/findStateGroupList',
)
}
// 确认生产
export function confirmProductionOrder(ids: number[]) {
return axios.post<never, BaseRespData<never>>(
'factory/customJomallOrder/confirmProduce',
ids,
)
}
// 下载稿件
export function downloadOrder(ids: number[]) {
return axios.post<never, BaseRespData<never>>(
'factory/customJomallOrder/downloadMaterial',
ids,
)
}
// 打印生产单
export function printOrder(ids: number[]) {
return axios.post<never, BaseRespData<never>>(
'factory/customJomallOrder/printProducePdf',
ids,
)
}
// 获取订单详情
export function getOrderBySubOrderNumber(orderNumber: string) {
return axios.get<never, BaseRespData<OrderData>>(
'factory/customJomallOrder/getOrderBySubOrderNumber',
{
params: {
subOrderNumber: orderNumber,
},
},
)
}
// 发货保存
export function saveOrder(
sumbitSendOutList: ShipmentOrderRes[],
from: ShipmentForm,
) {
return axios.post<never, BaseRespData<never>>(
'factory/customJomallOrder/sendOut',
{ sumbitSendOutList, ...from },
)
}
// 添加内部便签
export function addInternalTagApi(idList: number[], memo: string) {
return axios.post<never, BaseRespData<never>>(
'factory/customJomallOrder/batchAddInternalMemo',
{ idList, content: memo },
)
}
// 查看发货单
export function loadSendOutList(id: number) {
return axios.get<never, BaseRespData<SendOrderData[]>>(
'factory/customJomallOrder/getShipmentListByCustomOrderId',
{
params: {
id,
},
},
)
}
// 操作日志
export function getLogList(id: number) {
return axios.get<never, BaseRespData<LogListData[]>>(
'factory/customJomallOrder/getLog',
{
params: {
id,
},
},
)
}
// 根据id获取数据详情
export function getOrderDetail(id: number) {
return axios.get<never, BaseRespData<OrderData>>(
'factory/customJomallOrder/getOrderById',
{
params: {
id,
}
}
)
}
// 取消
export function cancelOrderApi(id: number) {
return axios.post<never, BaseRespData<never>>(
'factory/customJomallOrder/cancel',
{ id },
)
}
<template>
<i
v-if="unicodeIcon"
class="erpIconfont erp unicode-icon"
v-html="unicodeIcon"
></i>
<svg v-else class="svg-icon erp" aria-hidden="true">
<slot name="title"></slot>
<use :xlink:href="svgIcon"></use>
</svg>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
const props = defineProps({
name: {
type: String,
required: true,
}
})
const unicodeIcon = computed(() => {
if (props.name.match(/^x[a-f0-9]{4}$/)) {
return `&#${props.name};`
}
return undefined
})
const svgIcon = computed(() => {
if (unicodeIcon.value) return undefined
return `#icon-${props.name}`
})
</script>
<style>
.unicode-icon {
width: 1em;
height: 1em;
color: inherit;
}
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
\ No newline at end of file
<template>
<div class="log-list">
<div v-for="l in logList" :key="l.id" class="log-item flex flex-align-center">
<div class="log-item-icon">
<Icon name="a-2labadianji3x" />
</div>
<div class="log-item-time">
<span>{{ l.createTime }}</span>
</div>
<div class="log-item-name">
<span>{{ l.employeeName }}</span>
</div>
<div class="log-item-content">
<span>{{ l.description }}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { LogListData } from '@/types/api/order'
import { PropType } from 'vue'
import Icon from './Icon.vue'
defineProps({
logList: {
type: Array as PropType<LogListData[]>,
default: () => [],
},
})
</script>
<style lang="scss" scoped>
.log-list {
height: 500px;
overflow: auto;
}
.log-item {
line-height: 26px;
}
.log-item div:not(:last-child) {
margin-right: 6px;
}
</style>
......@@ -14,32 +14,32 @@
>
<template v-for="item in menuList">
<el-menu-item
v-if="!item.children"
:key="item.id"
:index="item.index"
v-if="!item.children"
>{{ item.label }}</el-menu-item
>
<el-sub-menu :key="item.index" :index="item.index" v-else>
<el-sub-menu v-else :key="item.index" :index="item.index">
<template #title>
<span class="label">{{ item.label }}</span>
</template>
<el-menu-item
v-for="sub in item.children"
:index="sub.index"
:key="sub.id"
:index="sub.index"
>{{ sub.label }}</el-menu-item
>
</el-sub-menu>
</template>
</el-menu>
<div class="user-info">
<div v-if="userInfo" class="user-info">
<span class="user-avatar">
<el-icon><User /></el-icon>
</span>
<el-dropdown style="color: #fff; font-size: 12px">
<el-dropdown style="color: #fff; font-size: 14px">
<span class="el-dropdown-link">
<el-icon style="vertical-align: middle"><User /></el-icon>
用户
{{ userInfo.factory.title }}
<el-icon style="vertical-align: middle" class="el-icon--right">
<arrow-down />
</el-icon>
......@@ -63,10 +63,12 @@ const route = useRoute()
const defaultActive = ref(route.path)
const menuList = reactive(Menu)
const userInfo = userUserStore().user
const userStore = userUserStore()
const userInfo = userStore.user
const logout = () => {
userUserStore().logout()
userStore.logout()
window.location.reload()
}
</script>
<style lang="scss" scoped>
......
......@@ -7,6 +7,7 @@ import Dashboard from '@/views/Dashboard.vue'
import Error from '@/views/error/404.vue'
import OrderList from '@/views/order/index.vue'
import ProductionComplete from '@/views/production/complete.vue'
import { getToken} from '@/api/axios'
const router = createRouter({
history: createWebHistory(),
......@@ -46,7 +47,12 @@ const router = createRouter({
},
],
})
// router.beforeEach((to, from, next) => {
// })
router.beforeEach((to, from, next) => {
const token = getToken()
if (!token && to.path !== '/user/login') {
next({ path: '/user/login' })
return
}
next()
})
export default router
import { defineStore } from 'pinia'
import { User } from '@/types'
import { LoginReq } from '@/types/api/auth'
import { LoginReq, SysUser } from '@/types/api/auth'
import { loginApi } from '@/api/auth'
import { showError } from '@/utils/ui'
import { setToken } from '@/api/axios'
import router from '@/router'
export interface AppStoreState {
user?: User
user?: SysUser
token?: string
}
const useUserStore = defineStore('user', {
state: () => ({} as AppStoreState),
state: () => {
let user: SysUser | undefined
const userJson = localStorage.getItem('user')
if (userJson) {
try {
user = JSON.parse(userJson)
} catch {
// ignore
}
}
return { user } as AppStoreState
},
actions: {
setUser(user?: User) {
setUser(user?: SysUser) {
this.user = user
localStorage.setItem('user', JSON.stringify(user))
},
async login(data: LoginReq) {
const resp = await loginApi(data)
// resp.
try {
const resp = await loginApi(data)
this.setUser(resp.data.sysUser)
setToken(resp.data.token)
router.push({ path: '/dashboard' })
} catch (error) {
showError(error)
}
},
async logout() {
this.setUser(undefined)
setToken('')
},
async logout() {},
},
})
......
img {
max-width: 100%;
}
.card {
background-color: #fff;
padding: 10px;
......@@ -25,10 +29,38 @@
display: flex;
flex-direction: column;
}
.flex-align-center {
align-items: center;
}
.flex-justify-space-between {
justify-content: space-between;
}
.flex-justify-space-around {
justify-content: space-around;
}
.flex-center {
justify-content: center;
align-items: center;
}
.flex-1 {
flex: 1;
}
.margin-top-10 {
margin-top: 10px;
}
.margin-top-20 {
margin-top: 20px;
}
.item {
margin-right: 6px;
}
::-webkit-scrollbar {
width: 5px;
......
......@@ -12,4 +12,10 @@
.el-table__row {
height: 40px;
min-height: 40px;
}
.el-drawer__header {
margin-bottom: 0;
border-bottom: 1px solid #eee;
padding: 10px;
}
\ No newline at end of file
@import './common.scss';
@import './element.scss'
@import './element.scss';
\ No newline at end of file
import { User } from '..'
export interface LoginReq {
factoryNumber: string
iphone: string
factoryCode: string
account: string
password: string
}
export interface LoginResp {
data: Root
message: string
code: number
}
export interface Root {
sysUser: SysUser
token: string
user: User
}
export interface SysUser {
id: number
account: string
password: string
supperMark: number
factoryId: number
factoryCode: string
status: number
factory: Factory
}
export interface Factory {
id: number
bianma: string
title: string
typeId: number
name: string
mobile: string
goodsNumber: number
authorizeNumber: number
status: number
}
\ No newline at end of file
export interface BaseRespData<D> {
code: number
message?: string
data: D
}
export interface PaginationData<D> {
total: number
size: number
current: number
records: D[]
}
export type BasePaginationData<D> = BaseRespData<PaginationData<D>>
......@@ -5,41 +5,65 @@ export interface SearchForm {
endProductId: string
orderOnlineNo: string
shopNumber: string
supplierId: string
internalMemo: string
status?: number
}
export interface Tab {
label: string
quality: number
code: string
status: string
statusCode: number
statusName: string
quantity: number
}
export interface OrderData {
id: number
moreable?: boolean
shopNumber?: string
namespace?: string
erpId?: number
orderNumber?: string
erpOrderNumber?: string
facotoryNo?: string
addressId?: number
erpSubOrderNumber?: string
orderOnlineId?: number
orderOnlineNo?: number
status?: number
weight?: number
outCurrencyCode?: number
productAmount?: number
carriageAmount?: number
totalAmount?: number
productNum?: number
trackStatus?: string
finishTime?: string
paymentTime?: string
remark?: string
startStockingTime?: string
source?: string
createTime?: string
substrateAreaCode?: string
deliveryType?: string
lanshouName?: string
lanshouPhone?: string
lanshouRegion?: string
lanshouAddress?: string
lanshouPost?: string
updateTime?: string
createTime?: string
internalMemoList?: MemoList[]
substrateAreaName?: string
productList?: ProductList[]
address?: Address
delayMinDay?: number
internalMemoList?: MemoList[]
shipmentList?: string
sourceType?: string
moreable?: boolean
}
export interface ProductList {
id: number
customOrderId?: number
subOrderNumber?: string
shopNumber?: string
count?: number
baseSku?: string
erpSubOrderNumber?: string
variantSku?: string
variantImage?: string
erpProductItemId?: number
......@@ -55,39 +79,6 @@ export interface ProductList {
updateTime?: string
}
export interface Address {
id: number
name?: string
swDefault?: 1
deliveryType?: string
lanshouName?: string
lanshouPhone?: string
lanshouRegion?: string
lanshouAddress?: string
lanshouPost?: string
jijianCompanyName?: string
jijianMobile?: string
jijianName?: string
jijianTel?: string
jijianCountryCode?: string
jijianCountryName?: string
jijianProvince?: string
jijianProvinceCode?: string
jijianCity?: string
jijianCityCode?: string
jijianCounty?: string
jijianPostCode?: string
jijianStreet?: string
jijianEmail?: string
tuihuoName?: string
tuihuoPhone?: string
tuihuoRegion?: string
tuihuoAddress?: string
tuihuoPost?: string
remark?: string
createTime?: string
}
export interface MemoList {
id: number
type?: number
......@@ -103,3 +94,70 @@ export interface SpanMethodProps {
rowIndex: number
columnIndex: number
}
export interface ShipmentForm {
shippingWay: string
carriageName: string
logisticsTracking: string
carriageAmount: string
namespace?: string
}
export interface ShipmentOrderRes {
subOrderNumber?: string
erpSubOrderNumber?: string
sendOutQuantity?: number
}
export interface LogisticsData {
id: number
code: string
enName: number
name: string
typeCode: number
}
export interface SendOrderData {
id: number
billNumber?: string
shippingWay?: number
addressId?: number
carriageAmount?: number
carriageName?: string
logisticsTracking?: string
shippingStatus?: number
lanshouName?: string
lanshouPhone?: string
lanshouRegion?: string
lanshouAddress?: string
lanshouPost?: string
updateTime?: string
createTime?: string
detailList?: DetailList[]
}
export interface DetailList {
id: number
shipmentId?: number
customOrderId?: number
subOrderNumber?: string
baseSku?: string
variantSku?: string
variantImage?: string
shipmentNum?: number
updateTime?: string
createTime?: string
productionNum?: number
notShipmentNum?: number
productName?: string
facotoryNo?: string
}
export interface LogListData {
id: number
bizId?: number
userId?: number
employeeName?: string
description?: string
createTime?: string
}
export interface User {
id: number
name: string
}
\ No newline at end of file
export function getFilePath(fileName?: string) {
return import.meta.env.BASE_URL + fileName
}
\ No newline at end of file
......@@ -9,18 +9,14 @@ export default function useElTableColumnWidth(
let cleanupResizeEventListener: (() => void) | undefined
const width = ref(0)
const onThOrderDetailResize = debounce(
() => {
const el = tableWrapperRef.value
if (!el) return
const th = el.querySelector(selector) as HTMLElement | null
if (th) {
width.value = th.offsetWidth
}
},
50,
{ leading: true },
)
const onThOrderDetailResize = debounce(() => {
const el = tableWrapperRef.value
if (!el) return
const th = el.querySelector(selector) as HTMLElement | null
if (th) {
width.value = th.offsetWidth
}
}, 50)
onMounted(() => {
const el = tableWrapperRef.value
......
import { Ref, onMounted, ref } from 'vue'
import { showError } from '../ui'
import type { BasePaginationData } from '@/types/api'
export interface UsePageListOptions<T> {
query: (
currentPage: number,
pageSize: number,
statusCode?: number,
) => Promise<BasePaginationData<T>>
initPageSize?: number
initLoad?: boolean
}
export default function usePageList<T>(options: UsePageListOptions<T>) {
const loading = ref(true)
const currentPage = ref(1)
const pageSize = ref(options.initPageSize || 100)
const total = ref(0)
const data = ref<T[]>([]) as Ref<T[]>
const loadData = async () => {
const { query } = options
try {
loading.value = true
const res = await query(currentPage.value, pageSize.value)
total.value = res.data.total
currentPage.value = res.data.current
pageSize.value = res.data.size
data.value = res.data.records
} catch (error) {
showError(error)
} finally {
loading.value = false
}
}
const onCurrentPageChange = (page: number) => {
currentPage.value = page
loadData()
}
const refresh = () => {
currentPage.value = 1
loadData()
}
const onPageSizeChange = (newPageSize: number) => {
pageSize.value = newPageSize
refresh()
}
onMounted(() => {
if (options.initLoad !== false) {
refresh()
}
})
return {
loading,
currentPage,
pageSize,
total,
data,
onCurrentPageChange,
onPageSizeChange,
refresh,
}
}
import { Ref, ref } from 'vue'
import { cloneDeep } from 'lodash-es'
export function useValue<T extends object>(
initialValue: T,
): [Ref<T>, () => void] {
const value = ref<T>(cloneDeep(initialValue)) as Ref<T>
const resetToDefault = () => {
value.value = cloneDeep(initialValue)
}
return [value, resetToDefault]
}
import { get } from 'lodash-es'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function val<T>(data: T, key: string | ((data: T) => any)) {
if (typeof key === 'function') return key(data)
return get(data, key)
}
import type { ElMessageBoxOptions } from 'element-plus'
// 错误消息
export function showError(err: unknown, options?: ElMessageBoxOptions) {
let message: string | undefined
if (typeof err === 'string') message = err
else if (
err &&
typeof err === 'object' &&
'message' in err &&
typeof err.message === 'string'
) {
message = err.message
}
if (!message) return
return ElMessageBox.alert(
'<div style="max-height:500px;overflow:auto">' + message + '</div>',
'错误提示',
{
type: 'error',
...options,
dangerouslyUseHTMLString: true,
},
).catch(() => {})
}
// 确认
export function showConfirm(message: string, options?: ElMessageBoxOptions) {
return ElMessageBox.confirm(message, '提示', {
...options,
})
}
......@@ -7,23 +7,23 @@
</div>
<div class="form-container">
<el-form
:model="loginForm"
ref="loginFormRef"
:model="loginForm"
:rules="rules"
size="large"
>
<el-form-item prop="factoryNumber">
<el-form-item prop="factoryCode">
<el-input
placeholder="请输入共厂号"
v-model="loginForm.factoryNumber"
v-model="loginForm.factoryCode"
placeholder="请输入工厂号"
>
<template #prefix>
<el-icon class="el-input__icon"><User /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="iphone">
<el-input v-model="loginForm.iphone" placeholder="请输入手机号">
<el-form-item prop="account">
<el-input v-model="loginForm.account" placeholder="请输入手机号">
<template #prefix>
<el-icon class="el-input__icon"><Iphone /></el-icon>
</template>
......@@ -46,10 +46,10 @@
>
</el-form-item>
<el-form-item>
<div style="width: 100%;text-align: right;">
<div style="width: 100%; text-align: right">
<a
href="/user/reset"
style="text-decoration: none; color: #409eff;"
style="text-decoration: none; color: #409eff"
>忘记密码</a
>
</div>
......@@ -67,19 +67,21 @@ import { User, Iphone, Lock } from '@element-plus/icons-vue'
import { reactive, ref } from 'vue'
import { LoginReq } from '@/types/api/auth'
import type { FormInstance, FormRules } from 'element-plus'
import useUserStore from '@/store/user';
const loginFormRef = ref<FormInstance>()
const loginForm = reactive<LoginReq>({
factoryNumber: '',
iphone: '',
factoryCode: '',
account: '',
password: '',
})
const rules = reactive<FormRules<LoginReq>>({
factoryNumber: [{ required: true, message: '请输入共厂号', trigger: 'blur' }],
iphone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
factoryCode: [{ required: true, message: '请输入工厂号', trigger: 'blur' }],
account: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
})
const userStore = useUserStore()
const handleLogin = async () => {
try {
......@@ -88,6 +90,7 @@ const handleLogin = async () => {
return
}
// 登录逻辑
await userStore.login(loginForm)
}
</script>
<style lang="scss" scoped>
......
<template>
<div class="order-detail-order">
<div class="order-detail_order-info">
<div class="order-detail_order-info--title title">订单详情</div>
<div class="order-detail_order-info--content">
<div class="order-detail_order-info--content-item flex">
<span class="order-detail_order-info__label label">主单号:</span>
<span class="order-detail_order-info__value value flex-1">{{
orderDetailData.orderNumber || '--'
}}</span>
</div>
<div class="order-detail_order-info--content-item flex">
<span class="order-detail_order-info__label label">订单类型:</span>
<span class="order-detail_order-info__value value flex-1">{{
orderDetailData.sourceType || '--'
}}</span>
</div>
<div class="order-detail_order-info--content-item flex">
<span class="order-detail_order-info__label label">工厂:</span>
<span class="order-detail_order-info__value value flex-1">{{
orderDetailData.facotoryNo || '--'
}}</span>
</div>
<div class="order-detail_order-info--content-item flex">
<span class="order-detail_order-info__label label">收货人:</span>
<span class="order-detail_order-info__value value flex-1">{{
orderDetailData.lanshouName || '--'
}}</span>
</div>
<div class="order-detail_order-info--content-item flex">
<span class="order-detail_order-info__label label">收货人电话:</span>
<span class="order-detail_order-info__value value flex-1">{{
orderDetailData.lanshouPhone || '--'
}}</span>
</div>
<div class="order-detail_order-info--content-item flex">
<span class="order-detail_order-info__label label">邮编:</span>
<span class="order-detail_order-info__value value flex-1">{{
orderDetailData.lanshouPost || '--'
}}</span>
</div>
<div class="order-detail_order-info--content-item flex">
<span class="order-detail_order-info__label label">收货地址:</span>
<span
class="order-detail_order-info__value value flex-1"
:title="orderDetailData.lanshouAddress"
>{{ orderDetailData.lanshouAddress || '--' }}</span
>
</div>
</div>
</div>
<div class="order-detail_price-info">
<div class="order-detail_price-info--title title">实付款</div>
<div class="order-detail_price-info--content">
<div class="order-detail_price-info--content-item flex">
<span class="order-detail_price-info__label label">商品价:</span>
<span class="order-detail_price-info__value label flex-1">{{
orderDetailData.productAmount || '--'
}}</span>
</div>
<div class="order-detail_price-info--content-item flex">
<span class="order-detail_price-info__label label">运费¥:</span>
<span class="order-detail_price-info__value value flex-1">{{
orderDetailData.carriageAmount || '--'
}}</span>
</div>
<div class="order-detail_price-info--content-item flex">
<span class="order-detail_price-info__label label">小计¥:</span>
<span class="order-detail_price-info__value value flex-1">{{
orderDetailData.totalAmount || '--'
}}</span>
</div>
</div>
</div>
<div class="order-detail_goods-info">
<div class="order-detail_goods-info--title title">商品详情</div>
<div class="order-detail_goods-info--content">
<ProductInfo
:is-detail="true"
:row="orderDetailData"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { OrderData } from '@/types/api/order'
import { PropType } from 'vue'
import ProductInfo from './ProductInfo.vue'
defineProps({
orderDetailData: {
type: Object as PropType<OrderData>,
default: () => {},
},
})
</script>
<style lang="scss" scoped>
.order-detail-order {
.title {
font-size: 18px;
font-weight: 500;
}
}
.order-detail_order-info--content,
.order-detail_price-info--content {
padding: 10px 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
font-size: 14px;
.label {
color: #606266;
margin-right: 6px;
}
.value {
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.order-detail_goods-info--content {
padding: 10px 0;
font-size: 14px;
:deep(.order-list-expand_item) {
padding: 10px 0;
}
}
</style>
<template>
<div
v-for="item in row.productList?.slice(
0,
row.moreable || isDetail ? row.productList.length : 2,
)"
:key="item.id"
class="order-list-expand_item"
>
<div class="order-list-expand_item_img">
<img :src="item.variantImage" style="width: 100%; height: 100%" />
</div>
<div class="order-list-expand_item_info">
<div class="order-list-expand_item_info_title" style="font-weight: bold">
<span class="order-list-expand_item_label">商品名:</span>
<span class="order-list-expand_item_value">{{
item.productName || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">Base SKU:</span>
<span class="order-list-expand_item_value">{{
item.baseSku || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">Variant SKU:</span>
<span class="order-list-expand_item_value">{{
item.variantSku || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">店铺单号:</span>
<span class="order-list-expand_item_value">{{
item.shopNumber || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">生产单号:</span>
<span class="order-list-expand_item_value">{{
item.subOrderNumber || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">发货状态:</span>
<span
v-if="item.shipmentNum === item.num"
class="order-list-expand_item_value"
>
<el-tag effect="dark" type="success"> 已发货 </el-tag>
</span>
<span
v-if="item.num !== item.shipmentNum && item.shipmentNum !== 0"
class="order-list-expand_item_value"
>
<el-tag effect="dark"> 部分发货 </el-tag>
</span>
<span
v-if="item.shipmentNum === 0"
class="order-list-expand_item_value"
>
<el-tag effect="dark" type="danger"> 未发货 </el-tag>
</span>
</div>
</div>
<div class="order-list-expand_item_price">
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">单价:</span>
<span class="order-list-expand_item_value">{{
item.price || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">购买数:</span>
<span class="order-list-expand_item_value">{{ item.num || '--' }}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">已发数:</span>
<span class="order-list-expand_item_value">{{
item.shipmentNum || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">未发数:</span>
<span class="order-list-expand_item_value">{{
(item.num || 0) - (item.shipmentNum || 0)
}}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { OrderData } from '@/types/api/order'
import { PropType } from 'vue'
defineProps({
row: {
type: Object as PropType<OrderData>,
default: () => {},
},
isDetail: {
type: Boolean,
default: false,
}
})
</script>
<style lang="scss" scoped>
.order-list-expand_item {
display: flex;
border-bottom: 1px solid #eee;
padding: 20px;
}
.order-list-expand_item:last-child {
border-bottom: 0;
}
.order-list-expand_item_img {
width: 100px;
height: 100px;
margin-right: 20px;
border: 1px solid #eee;
}
.order-list-expand_item_info {
flex: 1;
margin-right: 20px;
}
.order-list-expand_item_price {
width: 180px;
}
.order-list-expand_item_info_title {
line-height: 26px;
display: flex;
}
.order-list-expand_item_label {
margin-right: 6px;
}
.order-list-expand_item_value {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
<template>
<div class="send-order-list">
<div class="send-order-column send-order-header">发货单信息</div>
<div class="send-order-column send-order-header">物流信息</div>
<template v-for="o in orderList" :key="o.id">
<div class="send-order-column">
<div
v-for="od in o.detailList"
:key="od.id"
class="send-order-product-item"
>
<div class="send-order-product-image">
<img :src="od.variantImage" :alt="od.productName" />
</div>
<div
v-for="(p, i) in productProps"
:key="i"
class="send-order-prop-list"
>
<div v-for="pi in p" :key="pi.key" class="send-order-prop-item">
{{ pi.label }}{{ val(od, pi.key) }}
</div>
</div>
</div>
</div>
<div class="send-order-column">
<div class="send-order-prop-list">
<div
v-for="pi in logisticsProps"
:key="pi.label"
class="send-order-prop-item"
>
{{ pi.label }}{{ val(o, pi.key) }}
</div>
</div>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
import { val } from '@/utils'
import type { SendOrderData } from '@/types/api/order'
import type { PropType } from 'vue'
defineProps({
orderList: {
type: Array as PropType<SendOrderData[]>,
required: true,
},
})
const productProps = [
[
{ label: '商品名', key: 'productName' },
{ label: 'Base SKU', key: 'baseSku' },
{ label: '变体SKU', key: 'variantSku' },
],
[
{ label: '生产单号', key: 'subOrderNumber' },
{ label: '工厂', key: 'facotoryNo' },
],
[
{ label: '生产数', key: 'productionNum' },
{ label: '发货数', key: 'shipmentNum' },
{ label: '未发数', key: 'notShipmentNum' },
],
]
const logisticsProps = [
{ label: '发货单号', key: 'billNumber' },
{
label: '发货方式',
key: (d: SendOrderData) =>
d.shippingWay && { 1: '送货上门', 2: '快递' }[d.shippingWay],
},
{ label: '创建时间', key: 'createTime' },
{ label: '商户号', key: 'categoryName' },
{ label: '收货人', key: 'lanshouName' },
{ label: '收货人电话', key: 'lanshouPhone' },
{ label: '邮编', key: 'lanshouPost' },
{
label: '收货地址',
key: (d: SendOrderData) =>
`${d.lanshouRegion || ''}${d.lanshouAddress || ''}`,
},
]
</script>
<style lang="scss" scoped>
$border: solid 1px #ddd;
.send-order-list {
display: grid;
grid-template-columns: 2fr 1fr;
border-left: $border;
border-top: $border;
}
.send-order-column {
padding: 10px 16px;
border-right: $border;
border-bottom: $border;
line-height: 1.5;
}
.send-order-header {
font-weight: bold;
text-align: center;
background-color: #f8f8f9;
}
.send-order-product-item {
display: flex;
justify-content: space-between;
gap: 20px;
&:not(:first-child) {
border-top: $border;
padding: 10px 0;
}
.send-order-prop-list {
flex: 1;
}
}
.send-order-product-image {
width: 100px;
}
</style>
<template>
<div v-if="orderList.length" class="shipment-info" >
<div v-for="o in orderList" :key="o.id" class="shipment-info-item">
<div class="shipment-info-item-header">
<div class="shipment-info-item-header--title">
<span class="shipment-info-item-header__label">主单号:</span>
<span class="shipment-info-item-header__value">{{
o.orderNumber || '--'
}}</span>
</div>
<div class="shipment-info-item-header--title">
<span class="shipment-info-item-header__label">订单类型:</span>
<span class="shipment-info-item-header__value">{{
o.sourceType || '--'
}}</span>
</div>
<div class="shipment-info-item-header--title">
<span class="shipment-info-item-header__label">收货人:</span>
<span class="shipment-info-item-header__value">{{
o.lanshouName || '--'
}}</span>
</div>
</div>
<div class="shipment-info-item-content">
<div
v-for="item in o.productList"
:key="item.id"
:class="{ 'active-row': item.subOrderNumber === orderNumber }"
class="shipment-info-item-content-item"
>
<div class="image">
<img :src="item.variantImage" />
</div>
<div class="shipment-info-item-content-item--info">
<span class="label">SKU:</span>
<span class="value">{{ item.baseSku || '--' }}</span>
</div>
<div class="shipment-info-item-content-item--info">
<span class="label">生产单号:</span>
<span class="value">{{ item.subOrderNumber || '--' }}</span>
</div>
<div
class="shipment-info-item-content-item--info"
style="width: 90px; flex: none"
>
<span class="label">购买数:</span>
<span class="value">{{ item.num || '--' }}</span>
</div>
<div
class="shipment-info-item-content-item--info"
style="width: 90px; flex: none"
>
<span class="label">已发数:</span>
<span class="value">{{ item.shipmentNum || '--' }}</span>
</div>
<div
class="shipment-info-item-content-item--info"
style="width: 90px; flex: none"
>
<span class="label">未发数:</span>
<span class="value">{{ (item.num || 0) - (item.shipmentNum || 0) }}</span>
</div>
<div
class="shipment-info-item-content-item--info"
style="width: 90px; flex: none"
>
<span class="label">拣货数:</span>
<span class="value">{{ item.count }}</span>
</div>
</div>
</div>
</div>
</div>
<ElEmpty v-else class="shipment-info"></ElEmpty>
</template>
<script setup lang="ts">
import { OrderData } from '@/types/api/order';
import { PropType } from 'vue';
defineProps({
orderList: {
type: Array as PropType<OrderData[]>,
default: () => [],
},
orderNumber: {
type: String,
default: () => '',
},
})
</script>
<style lang="scss" scoped>
.shipment-info {
height: 600px;
margin-top: 20px;
border: 1px solid #eee;
}
.image {
height: 100%;
width: 50px;
border-right: 1px solid #eee;
img {
width: 100%;
height: 100%;
}
}
.shipment-info-item-header {
display: flex;
justify-content: space-between;
background-color: #e6e6e6;
padding: 10px;
}
.shipment-info-item-content-item {
display: flex;
justify-content: space-between;
height: 42px;
border-bottom: 1px solid #eee;
}
.active-row {
background-color: #529b2e;
color: #fff;
}
.shipment-info-item-header__label {
margin-right: 6px;
}
.shipment-info-item-content-item--info {
border-right: 1px solid #eee;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
.label {
margin-right: 6px;
}
}
.shipment-info-item-content-item--info:last-child {
border-right: 0;
}
</style>
......@@ -35,14 +35,6 @@
style="width: 160px"
></ElInput>
</ElFormItem>
<ElFormItem label="供应商">
<ElInput
v-model="searchForm.supplierId"
placeholder="供应商"
clearable
style="width: 160px"
></ElInput>
</ElFormItem>
<ElFormItem label="内部便签">
<ElInput
v-model="searchForm.internalMemo"
......@@ -52,10 +44,10 @@
></ElInput>
</ElFormItem>
<ElFormItem>
<ElButton type="primary">查询</ElButton>
<ElButton type="primary" @click="search">查询</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton>重置</ElButton>
<ElButton @click="resetSearchForm">重置</ElButton>
</ElFormItem>
</ElForm>
</div>
......@@ -63,27 +55,55 @@
<div class="tabs">
<div
v-for="item in tabsNav"
:key="item.code"
:key="item.statusCode"
class="tabs-node"
:class="item.code === activeTab ? 'tabs-node_active' : ''"
:class="item.statusCode === statusCode ? 'tabs-node_active' : ''"
@click="changeTab(item)"
>
<span class="tabs-node_label">{{ item.label }}</span>
<span class="tabs-node_count">{{ `(${item.quality})` }}</span>
<span class="tabs-node_label">{{ item.statusName }}</span>
<span class="tabs-node_count">{{ `(${item.quantity})` }}</span>
</div>
</div>
</div>
</div>
<div class="order-content flex-1 flex-column overflow-hidden">
<!-- 操作按钮 -->
<div class="order-operate">操作按钮</div>
<div ref="tableWrapperRef" class="order-list flex-1 overflow-hidden">
<div class="order-operate-btn">
<span v-if="statusCode === 2" class="item">
<ElButton type="success" @click="confirmProduce">确认生产</ElButton>
</span>
<span v-if="statusCode === 3 || statusCode === 4" class="item">
<ElButton type="success" @click="confirmDelivery">发货</ElButton>
</span>
<span class="item">
<ElButton type="warning" is-dark @click="downloadManuscript"
>下载稿件</ElButton
>
</span>
<span class="item">
<ElButton type="primary" dark @click="printManuscript"
>打印生产单</ElButton
>
</span>
<span class="item">
<ElButton type="success" @click="addInternalTag"
>添加内部标签</ElButton
>
</span>
</div>
<div
ref="tableWrapperRef"
v-loading="loading"
element-loading-text="加载中..."
class="order-list flex-1 overflow-hidden"
>
<ElTable
:data="data"
:data="tableData"
:span-method="arraySpanMethod"
default-expand-all
border
style="width: 100%; height: 100%"
@selection-change="handleSelectionChange"
>
<ElTableColumn type="selection" width="50"></ElTableColumn>
<ElTableColumn type="expand" width="1">
......@@ -93,130 +113,7 @@
class="order-list-expand"
:style="{ width: `${thOrderDetailWidth + 50}px` }"
>
<div
v-for="item in row.productList.slice(
0,
row.moreable ? row.productList.length : 2,
)"
:key="item.id"
class="order-list-expand_item"
>
<div class="order-list-expand_item_img">
<img
:src="item.variantImage"
style="width: 100px; height: 100%"
/>
</div>
<div class="order-list-expand_item_info">
<div
class="order-list-expand_item_info_title"
style="font-weight: bold"
>
<span class="order-list-expand_item_label"
>商品名:</span
>
<span class="order-list-expand_item_value">{{
item.productName || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label"
>Base SKU:</span
>
<span class="order-list-expand_item_value">{{
item.baseSku || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label"
>Variant SKU:</span
>
<span class="order-list-expand_item_value">{{
item.variantSku || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label"
>店铺单号:</span
>
<span class="order-list-expand_item_value">{{
item.shopNumber || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label"
>生产单号:</span
>
<span class="order-list-expand_item_value">{{
item.subOrderNumber || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label"
>发货状态:</span
>
<span
v-if="item.shipmentNum === item.num"
class="order-list-expand_item_value"
>
<el-tag effect="dark" type="success"> 已发货 </el-tag>
</span>
<span
v-if="
item.num !== item.shipmentNum &&
item.shipmentNum !== 0
"
class="order-list-expand_item_value"
>
<el-tag effect="dark"> 部分发货 </el-tag>
</span>
<span
v-if="item.shipmentNum === 0"
class="order-list-expand_item_value"
>
<el-tag effect="dark" type="danger"> 未发货 </el-tag>
</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">工厂:</span>
<span class="order-list-expand_item_value">{{
item.facotoryNo || '--'
}}</span>
</div>
</div>
<div class="order-list-expand_item_price">
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">单价:</span>
<span class="order-list-expand_item_value">{{
item.price || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label"
>购买数:</span
>
<span class="order-list-expand_item_value">{{
item.num || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label"
>已发数:</span
>
<span class="order-list-expand_item_value">{{
item.shipmentNum || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label"
>未发数:</span
>
<span class="order-list-expand_item_value">{{
item.num - item.shipmentNum
}}</span>
</div>
</div>
</div>
<ProductInfo :row="row" />
<template v-if="row.productList.length > 2">
<div class="order-list-expand_more">
<span @click="openAll(row)">
......@@ -260,6 +157,31 @@
</div>
</div>
</div>
<div class="order-memo">
<div class="order-memo-info">
<div
v-for="memo in row.internalMemoList || []"
:key="memo.id"
class="order-memo-item"
>
<div
v-if="memo.operatorEmployeeName"
class="order-memo-item__name"
>
<span>{{ memo.operatorEmployeeName || '' }}</span>
</div>
<div
v-if="memo.operatorTime"
class="order-memo-item__time"
>
<span>{{ memo.operatorTime || '' }}</span>
</div>
<div v-if="memo.content" class="order-memo-item__content">
<span>{{ memo.content || '' }}</span>
</div>
</div>
</div>
</div>
<div class="order-time">
<div class="order-time_info">
<div class="order-list-expand_item_info_title">
......@@ -296,19 +218,35 @@
</div>
</div>
</div>
<div class="order-operate">
<div class="order-operate_info">
<div class="order-list-expand_item_info_title">
<ElButton text type="primary">查看详情</ElButton>
<ElButton text type="primary" @click="openDetail(row.id)"
>查看详情</ElButton
>
</div>
<div class="order-list-expand_item_info_title">
<ElButton text type="primary">修改订单</ElButton>
<div
v-if="statusCode == 4 || statusCode == 5"
class="order-list-expand_item_info_title"
>
<ElButton
text
type="primary"
@click="openShipment(row.id)"
>查看发货单</ElButton
>
</div>
<div class="order-list-expand_item_info_title">
<ElButton text type="primary">查看发货单</ElButton>
<ElButton text type="primary" @click="openLog(row.id)"
>操作日志</ElButton
>
</div>
<div class="order-list-expand_item_info_title">
<ElButton text type="primary">操作日志</ElButton>
<ElButton text type="danger" @click="cancelOrder(row.id)"
>取消</ElButton
>
</div>
</div>
</div>
......@@ -326,37 +264,35 @@
<span class="label">订单号:</span>
<span class="value">{{ scope.row.orderNumber }}</span>
</div>
<!-- <div class="order-detail_item">
<span class="label">第三方单号:</span>
<span class="value">{{ scope.row.orderOnlineNo }}</span>
</div> -->
<div class="order-detail_item">
<span class="label">店铺单号:</span>
<span class="value" :title="scope.row.shopNumber">{{
scope.row.shopNumber
scope.row.shopNumber || '--'
}}</span>
</div>
<div class="order-detail_item">
<span class="label">工厂:</span>
<span class="value" :title="scope.row.facotoryNo">{{
scope.row.facotoryNo || '--'
}}</span>
</div>
<div class="order-detail_item">
<span class="label">收货人:</span>
<span class="value">{{ scope.row.address.lanshouName }}</span>
<span class="value">{{ scope.row.lanshouName }}</span>
</div>
<div class="order-detail_item">
<span class="label">电话:</span>
<span class="value">{{
scope.row.address.lanshouPhone
}}</span>
<span class="value">{{ scope.row.lanshouPhone }}</span>
</div>
<div class="order-detail_item">
<span class="label">收货地址:</span>
<span
class="value"
:title="scope.row.address.lanshouAddress"
>{{ scope.row.address.lanshouAddress }}</span
>
<span class="value" :title="scope.row.lanshouAddress">{{
scope.row.lanshouAddress
}}</span>
</div>
<div class="order-detail_item">
<span class="label">邮编:</span>
<span class="value">{{ scope.row.address.lanshouPost }}</span>
<span class="value">{{ scope.row.lanshouPost }}</span>
</div>
</div>
</template>
......@@ -367,6 +303,11 @@
header-align="center"
></ElTableColumn>
<ElTableColumn
label="内部便签"
width="350"
header-align="center"
></ElTableColumn>
<ElTableColumn
label="时间"
width="350"
header-align="center"
......@@ -388,329 +329,592 @@
/>
</div>
</div>
<el-dialog
v-model="shipmentVisible"
title="发货"
:close-on-click-modal="false"
width="1300px"
@opened="onShipmentDialogOpened"
>
<div class="header-search">
<el-input
ref="inputRef"
v-model="productionOrderNumber"
clearable
size="large"
placeholder="这里是扫码输入框"
@keyup.enter="searchShipmentByOrderNumber"
/>
<el-button
type="primary"
size="large"
@click="searchShipmentByOrderNumber"
>查询</el-button
>
</div>
<Shipment
v-loading="shipmentLoading"
:order-list="orderList"
:order-number="orderNumber"
/>
<div class="shipment-logistics-info margin-top-20">
<ElForm ref="shipmentFormRef" :model="shipmentForm" :rules="rules">
<ElRow :gutter="20">
<ElCol :span="6">
<ElFormItem label="发货方式" prop="shippingWay">
<ElSelect
v-model="shipmentForm.shippingWay"
clearable
placeholder="请选择"
style="width: 100%"
>
<ElOption label="送货上门" value="1"></ElOption>
<ElOption label="快递" value="2"></ElOption>
</ElSelect>
</ElFormItem>
</ElCol>
<ElCol v-if="shipmentForm.shippingWay === '2'" :span="6">
<ElFormItem label="物流名称" prop="carriageName">
<ElSelect
v-model="shipmentForm.carriageName"
filterable
remote
reserve-keyword
placeholder="请输入关键字"
clearable
remote-show-suffix
style="width: 100%"
:remote-method="getLogisticsList"
>
<ElOption
v-for="item in logisticsCompanyList"
:key="item.id"
:value="item.name"
></ElOption>
</ElSelect>
</ElFormItem>
</ElCol>
<ElCol v-if="shipmentForm.shippingWay === '2'" :span="6">
<ElFormItem label="物流跟踪号" prop="logisticsTracking">
<ElInput
v-model="shipmentForm.logisticsTracking"
placeholder="请输入物流跟踪号"
clearable
style="width: 100%"
/>
</ElFormItem>
</ElCol>
<ElCol v-if="shipmentForm.shippingWay === '2'" :span="6">
<ElFormItem label="物流费用" prop="carriageAmount">
<ElInput
v-model="shipmentForm.carriageAmount"
clearable
placeholder="请输入物流费用"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
</ElRow>
</ElForm>
</div>
<template #footer>
<div class="dialog-footer">
<el-button size="large" @click="shipmentVisible = false"
>取消</el-button
>
<el-button size="large" type="primary" @click="saveShipment"
>发货</el-button
>
</div>
</template>
</el-dialog>
<el-dialog
v-model="sendOrderVisible"
title="查看发货单"
width="1300px"
class="send-order-dialog"
:close-on-click-modal="false"
>
<SendOrder :order-list="sendOrderList" />
</el-dialog>
<el-dialog
v-model="logVisible"
title="操作日志"
width="800px"
:close-on-click-modal="false"
>
<LogList :log-list="logList" />
</el-dialog>
<ElDrawer
v-model="orderDetailDialogVisible"
title="生产订单详情"
direction="rtl"
size="40%"
:close-on-click-modal="false"
>
<OrderDetail :order-detail-data="orderDetailData" />
</ElDrawer>
</template>
<script setup lang="ts">
import type { SearchForm, SpanMethodProps, Tab, OrderData } from './types'
import { reactive, ref } from 'vue'
import Shipment from './Shipment.vue'
import type {
SearchForm,
SpanMethodProps,
Tab,
OrderData,
ShipmentForm,
LogisticsData,
ShipmentOrderRes,
SendOrderData,
LogListData,
} from '@/types/api/order'
import { onMounted, reactive, ref } from 'vue'
import useElTableColumnWidth from '@/utils/hooks/useElTableColumnWidth'
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue'
import { ElButton } from 'element-plus'
const currentPage = ref(1)
const pageSize = ref(100)
const total = ref(400)
const searchForm = reactive<SearchForm>({
import usePageList from '@/utils/hooks/usePageList'
import {
getOrderList,
getOrderTabData,
confirmProductionOrder,
downloadOrder,
printOrder,
getOrderBySubOrderNumber,
saveOrder,
addInternalTagApi,
loadSendOutList,
getLogList,
getOrderDetail,
cancelOrderApi,
} from '@/api/order'
import { showError, showConfirm } from '@/utils/ui'
import { useValue } from '@/utils/hooks/useValue'
import { filePath } from '@/api/axios'
import { getLogisticsCompanyList } from '@/api/common'
import type { FormRules } from 'element-plus'
import ProductInfo from './ProductInfo.vue'
import SendOrder from './SendOrder.vue'
import LogList from '@/components/LogList.vue'
import OrderDetail from './OrderDetail.vue'
import { debounce } from 'lodash-es'
const [searchForm, resetSearchForm] = useValue<SearchForm>({
mainSku: '',
endProductId: '',
orderOnlineNo: '',
shopNumber: '',
supplierId: '',
internalMemo: '',
})
const tabsNav = ref<Tab[]>([])
const statusCode = ref(2)
const selection = ref<OrderData[]>([])
const shipmentVisible = ref(false)
const productionOrderNumber = ref('')
const orderNumber = ref('')
const shipmentLoading = ref(false)
const inputRef = ref()
const [shipmentForm, resetShipmentForm] = useValue<ShipmentForm>({
shippingWay: '',
carriageName: '',
logisticsTracking: '',
carriageAmount: '',
namespace: '',
})
const [orderList, resetOrderList] = useValue<OrderData[]>([])
const isLock = ref(false)
const logisticsCompanyList = ref<LogisticsData[]>([])
const shipmentFormRef = ref()
const rules = reactive<FormRules<ShipmentForm>>({
shippingWay: [
{
required: true,
message: '请选择物流方式',
},
],
carriageName: [
{
required: true,
message: '请选择物流公司',
},
],
carriageAmount: [
{
required: true,
message: '请输入物流费用',
},
],
logisticsTracking: [
{
required: true,
message: '请输入物流跟踪号',
},
],
})
const {
loading,
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
getOrderList(
{ ...searchForm.value, status: statusCode.value },
page,
pageSize,
),
})
const tabsNav = reactive<Tab[]>([
{
label: '待确认',
quality: 0,
code: 'TO_BE_CONFIRMED',
},
{
label: '生产中',
quality: 0,
code: 'IN_PRODUCTION',
},
{
label: '待发货',
quality: 0,
code: 'WAITING_FOR_DELIVERY',
},
{
label: '已发货',
quality: 0,
code: 'DELIVERED',
},
{
label: '待质检',
quality: 0,
code: 'WAITING_FOR_QUALITY',
},
{
label: '已完成',
quality: 0,
code: 'COMPLETED',
},
{
label: '已取消',
quality: 0,
code: 'CANCELED',
},
{
label: '所有订单',
quality: 0,
code: 'ALL',
},
])
const activeTab = ref('TO_BE_CONFIRMED')
const [tableWrapperRef, thOrderDetailWidth] = useElTableColumnWidth(
'table th.th-order-detail',
)
const data: OrderData[] = reactive([
{
id: 18,
orderNumber: 'JMDZ-240221-006',
addressId: 1,
status: 4,
productAmount: 119.97,
totalAmount: 119.97,
productNum: 33,
paymentTime: '2024-02-21 16:53:25',
startStockingTime: '2024-02-23 17:42:26',
source: 'MANUALLY_ADD_ORDERS',
createTime: '2024-02-21 16:52:43',
updateTime: '2024-03-05 14:43:08',
productList: [
{
id: 28,
customOrderId: 18,
shopNumber: '',
subOrderNumber: 'JMSC240221008',
baseSku: '24020019_WHI_S',
variantSku: '240200043_WHI_S',
variantImage:
'https://custom.jomalls.com/shengchengtu/xiaoguotu/20240221/1708485398058E1MJ8TNiZf8dZVzy.jpg',
erpProductItemId: 6557,
endProductId: '24',
chimaId: '176',
diyId: '43',
facotoryNo: '8825',
num: 11,
shipmentNum: 2,
price: 39.99,
productName: 'ceshi0221',
createTime: '2024-02-21 16:52:43',
updateTime: '2024-03-05 14:47:53',
},
{
id: 29,
customOrderId: 18,
subOrderNumber: 'JMSC240221009',
baseSku: '24020019_WHI_L',
variantSku: '240200043_WHI_L',
variantImage:
'https://custom.jomalls.com/shengchengtu/xiaoguotu/20240221/1708485398058E1MJ8TNiZf8dZVzy.jpg',
erpProductItemId: 6559,
endProductId: '24',
chimaId: '178',
diyId: '43',
facotoryNo: '8825',
num: 11,
shipmentNum: 0,
price: 39.99,
productName: 'ceshi0221',
createTime: '2024-02-21 16:52:43',
updateTime: '2024-02-21 16:52:43',
},
{
id: 30,
customOrderId: 18,
subOrderNumber: 'JMSC240221010',
baseSku: '24020019_WHI_M',
variantSku: '240200043_WHI_M',
variantImage:
'https://custom.jomalls.com/shengchengtu/xiaoguotu/20240221/1708485398058E1MJ8TNiZf8dZVzy.jpg',
erpProductItemId: 6558,
endProductId: '24',
chimaId: '177',
diyId: '43',
facotoryNo: '8825',
num: 11,
shipmentNum: 0,
price: 39.99,
productName: 'ceshi0221',
createTime: '2024-02-21 16:52:43',
updateTime: '2024-02-21 16:52:43',
},
],
address: {
id: 1,
name: '九猫科技',
swDefault: 1,
deliveryType: 'lanshou',
lanshouName: '李四',
lanshouPhone: '15296328521',
lanshouRegion: '雁塔区',
lanshouAddress: '陕西省西安市雁塔区都市之门C座',
lanshouPost: '710000',
jijianCompanyName: 'Jomalls Network Technology Co.',
jijianMobile: '15211111111',
jijianName: 'Sun Yang',
jijianCountryCode: 'CN',
jijianCountryName: '中国',
jijianProvince: 'Guangdong',
jijianProvinceCode: '44',
jijianCity: 'guangzhou',
jijianCityCode: '4401',
jijianCounty: 'Panyu District',
jijianPostCode: '725000',
jijianStreet: 'No. 25-2, Shunyifang Avenue East, Nancun Town',
jijianEmail: '1950087500@qq.com',
tuihuoName: 'Li Si',
tuihuoPhone: '15296328521',
tuihuoRegion: "Shaanxi Xi'an",
tuihuoAddress: 'Yanta District City Gate',
tuihuoPost: '710000',
createTime: '2023-01-10 10:23:23',
},
delayMinDay: 5,
internalMemoList: [
{
id: 77,
type: 7,
outId: 18,
operatorEmployeeName: '常洲振',
content: '1212',
operatorTime: '2024-02-28 11:30:53',
},
{
id: 75,
type: 7,
outId: 18,
operatorEmployeeName: '徐变变',
content: '777',
operatorTime: '2024-02-23 16:03:25',
},
],
sourceType: '手动添单',
},
{
id: 19,
orderNumber: 'JMDZ-240221-006',
addressId: 1,
status: 4,
productAmount: 119.97,
totalAmount: 119.97,
productNum: 33,
paymentTime: '2024-02-21 16:53:25',
startStockingTime: '2024-02-23 17:42:26',
source: 'MANUALLY_ADD_ORDERS',
createTime: '2024-02-21 16:52:43',
updateTime: '2024-03-05 14:43:08',
productList: [
{
id: 28,
customOrderId: 18,
shopNumber: '',
subOrderNumber: 'JMSC240221008',
baseSku: '24020019_WHI_S',
variantSku: '240200043_WHI_S',
variantImage:
'https://custom.jomalls.com/shengchengtu/xiaoguotu/20240221/1708485398058E1MJ8TNiZf8dZVzy.jpg',
erpProductItemId: 6557,
endProductId: '24',
chimaId: '176',
diyId: '43',
facotoryNo: '8825',
num: 11,
shipmentNum: 2,
price: 39.99,
productName: 'ceshi0221',
createTime: '2024-02-21 16:52:43',
updateTime: '2024-03-05 14:47:53',
},
{
id: 29,
customOrderId: 18,
subOrderNumber: 'JMSC240221009',
baseSku: '24020019_WHI_L',
variantSku: '240200043_WHI_L',
variantImage:
'https://custom.jomalls.com/shengchengtu/xiaoguotu/20240221/1708485398058E1MJ8TNiZf8dZVzy.jpg',
erpProductItemId: 6559,
endProductId: '24',
chimaId: '178',
diyId: '43',
facotoryNo: '8825',
num: 11,
shipmentNum: 0,
price: 39.99,
productName: 'ceshi0221',
createTime: '2024-02-21 16:52:43',
updateTime: '2024-02-21 16:52:43',
},
],
address: {
id: 1,
name: '九猫科技',
swDefault: 1,
deliveryType: 'lanshou',
lanshouName: '李四',
lanshouPhone: '15296328521',
lanshouRegion: '雁塔区',
lanshouAddress: '陕西省西安市雁塔区都市之门C座',
lanshouPost: '710000',
jijianCompanyName: 'Jomalls Network Technology Co.',
jijianMobile: '15211111111',
jijianName: 'Sun Yang',
jijianCountryCode: 'CN',
jijianCountryName: '中国',
jijianProvince: 'Guangdong',
jijianProvinceCode: '44',
jijianCity: 'guangzhou',
jijianCityCode: '4401',
jijianCounty: 'Panyu District',
jijianPostCode: '725000',
jijianStreet: 'No. 25-2, Shunyifang Avenue East, Nancun Town',
jijianEmail: '1950087500@qq.com',
tuihuoName: 'Li Si',
tuihuoPhone: '15296328521',
tuihuoRegion: "Shaanxi Xi'an",
tuihuoAddress: 'Yanta District City Gate',
tuihuoPost: '710000',
createTime: '2023-01-10 10:23:23',
},
delayMinDay: 5,
internalMemoList: [
{
id: 77,
type: 7,
outId: 18,
operatorEmployeeName: '常洲振',
content: '1212',
operatorTime: '2024-02-28 11:30:53',
},
{
id: 75,
type: 7,
outId: 18,
operatorEmployeeName: '徐变变',
content: '777',
operatorTime: '2024-02-23 16:03:25',
},
],
sourceType: '手动添单',
},
])
const onShipmentDialogOpened = () => {
inputRef.value?.focus()
shipmentFormRef.value?.clearValidate()
}
onMounted(() => {
loadTabData()
})
// 获取物流公司
const getLogisticsList = debounce(async (query?: string) => {
if (query) {
try {
const res = await getLogisticsCompanyList(query)
logisticsCompanyList.value = res.data
} catch (error) {
showError(error)
}
}
}, 500)
const sendOrderList = ref<SendOrderData[]>([])
const sendOrderVisible = ref(false)
// 获取发货单列表
const openShipment = async (id: number) => {
try {
const res = await loadSendOutList(id)
sendOrderList.value = res.data
sendOrderVisible.value = true
} catch (error) {
showError(error)
}
}
const arraySpanMethod = ({ columnIndex }: SpanMethodProps) => {
if (columnIndex === 0 || columnIndex === 1) {
return [1, 1]
} else {
return [1, 5]
return [1, 6]
}
}
const changeTab = (item: Tab) => {
activeTab.value = item.code
statusCode.value = item.statusCode
search()
}
const openAll = (row: OrderData) => {
row.moreable = !row.moreable
}
const loadTabData = async () => {
try {
const res = await getOrderTabData()
tabsNav.value = res.data
} catch (error) {
showError(error)
}
}
const handleSelectionChange = (s: OrderData[]) => {
selection.value = s
}
// 确认生产
const confirmProduce = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
try {
await showConfirm('是否确认生产', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
const ids = selection.value.map((item) => item.id)
try {
const res = await confirmProductionOrder(ids)
search()
loadTabData()
ElMessage.success(res.message)
} catch (e) {
showError(e)
}
}
// 发货
const confirmDelivery = async () => {
shipmentVisible.value = true
productionOrderNumber.value = ''
resetOrderList()
resetShipmentForm()
}
const searchShipmentByOrderNumber = async () => {
const code = productionOrderNumber.value
shipmentVisible.value = true
if (isLock.value) {
productionOrderNumber.value = ''
return
}
productionOrderNumber.value = ''
isLock.value = true
if (!code) {
isLock.value = false
inputRef.value.focus()
return ElMessage({
message: '请录入生产单号',
type: 'warning',
offset: window.innerHeight / 2,
})
}
let rowData
for (const item of orderList.value) {
rowData = item.productList?.find((jj) => jj.subOrderNumber === code)
if (rowData) break
}
if (rowData) {
orderNumber.value = code
const unShipmentNum = (rowData.num || 0) - (rowData.shipmentNum || 0)
if (unShipmentNum > (rowData.count || 0)) {
rowData.count || rowData.count === 0
? (rowData.count += 1)
: (rowData.count = 0)
} else {
ElMessage({
message: '拣货数不能大于未发数',
type: 'warning',
offset: window.innerHeight / 2,
})
}
productionOrderNumber.value = ''
isLock.value = false
inputRef.value.focus()
} else {
getPackingData(code)
}
}
const getPackingData = async (code: string) => {
try {
shipmentLoading.value = true
const res = await getOrderBySubOrderNumber(code)
if (res.data) {
for (const item of res.data.productList || []) {
if (item.num) {
item.count = 0
}
if (item.subOrderNumber === code) {
item.count || item.count === 0 ? (item.count += 1) : (item.count = 0)
}
}
orderList.value.unshift(res.data)
}
orderNumber.value = code
productionOrderNumber.value = ''
isLock.value = false
inputRef.value.focus()
} catch (e) {
productionOrderNumber.value = ''
isLock.value = false
inputRef.value.focus()
showError(e)
} finally {
shipmentLoading.value = false
}
}
const saveShipment = async () => {
if (orderList.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
try {
await shipmentFormRef.value?.validate()
} catch {
return
}
const handleSizeChange = (val: number) => {
pageSize.value = val
const isEqual = orderList.value.some(
(item) => item.namespace === orderList.value[0].namespace,
)
if (!isEqual) {
ElMessage({
message: '请选择同一工厂的订单',
type: 'warning',
offset: window.innerHeight / 2,
})
return
}
const data: ShipmentOrderRes[] = []
orderList.value.forEach((item) => {
shipmentForm.value.namespace = item.namespace
const subOrder = item.productList?.map((jj) => {
return {
erpSubOrderNumber: jj.erpSubOrderNumber,
subOrderNumber: jj.subOrderNumber,
sendOutQuantity: jj.count,
}
})
if (subOrder) {
data.push(...subOrder)
}
})
try {
const res = await saveOrder(data, shipmentForm.value)
ElMessage.success(res.message)
shipmentVisible.value = false
loadTabData()
search()
} catch (e) {
showError(e)
}
}
// 下载稿件
const downloadManuscript = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
const ids = selection.value.map((item) => item.id)
try {
await showConfirm('是否确认下载', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const res = await downloadOrder(ids)
window.open(filePath + res.message)
} catch (e) {
showError(e)
}
}
// 打印生产单
const printManuscript = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
const ids = selection.value.map((item) => item.id)
try {
await showConfirm('是否打印生产单', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const res = await printOrder(ids)
window.open(filePath + res.message)
} catch (e) {
showError(e)
}
}
const handleCurrentChange = (val: number) => {
currentPage.value = val
// 添加内部便签
const addInternalTag = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
ElMessageBox.prompt('', '添加内部便签', {
confirmButtonText: '确认',
cancelButtonText: '取消',
inputType: 'textarea',
inputPlaceholder: '请输入内部便签',
inputPattern: /.+/,
inputErrorMessage: '内部便签不能为空',
}).then(async ({ value }) => {
try {
const res = await addInternalTagApi(
selection.value.map((item) => item.id),
value,
)
ElMessage.success(res.message)
loadTabData()
search()
} catch (e) {
showError(e)
}
})
}
const orderDetailDialogVisible = ref(false)
const orderDetailData = ref<OrderData>({} as OrderData)
// 查看详情
const openDetail = async (id: number) => {
try {
const res = await getOrderDetail(id)
orderDetailData.value = res.data
orderDetailDialogVisible.value = true
} catch (e) {
showError(e)
}
}
const logVisible = ref(false)
const logList = ref<LogListData[]>([])
// 操作日志
const openLog = async (id: number) => {
try {
const res = await getLogList(id)
logList.value = res.data
logVisible.value = true
} catch (e) {
showError(e)
}
}
// 取消
const cancelOrder = async (id: number) => {
try {
await showConfirm('是否确认取消', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const res = await cancelOrderApi(id)
ElMessage({
message: res.message,
type: 'success',
offset: window.innerHeight / 2,
})
loadTabData()
search()
} catch (e) {
showError(e)
}
}
</script>
<style lang="scss" scoped>
......@@ -802,62 +1006,42 @@ const handleCurrentChange = (val: number) => {
border-right: 1px solid #eee;
}
.order-time_info {
padding: 20px;
font-size: 14px;
}
.order-actual-payment {
.order-memo {
width: 350px;
border-right: 1px solid #eee;
}
.order-list-expand {
border-right: 1px solid #eee;
.order-time_info {
padding: 20px;
font-size: 14px;
color: #606266;
overflow: auto;
max-height: 600px;
}
.order-list-expand_item {
display: flex;
border-bottom: 1px solid #eee;
.order-memo-info {
padding: 20px;
font-size: 14px;
}
.order-list-expand_item:last-child {
border-bottom: 0;
}
.order-list-expand_item_img {
width: 100px;
height: 100px;
margin-right: 20px;
border: 1px solid #eee;
.order-memo-item {
display: flex;
align-items: center;
line-height: 26px;
}
.order-list-expand_item_info {
flex: 1;
margin-right: 20px;
}
.order-list-expand_item_price {
width: 180px;
.order-memo-item div:not(:last-child) {
margin-right: 6px;
}
.order-list-expand_item_info_title {
line-height: 26px;
display: flex;
.order-actual-payment {
width: 350px;
border-right: 1px solid #eee;
}
.order-list-expand_item_label {
margin-right: 6px;
}
.order-list-expand_item_value {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.order-list-expand {
border-right: 1px solid #eee;
font-size: 14px;
color: #606266;
overflow: auto;
max-height: 600px;
}
.order-list-expand_more {
......@@ -880,4 +1064,23 @@ const handleCurrentChange = (val: number) => {
font-size: 14px;
}
}
.header-search {
display: flex;
gap: 10px;
}
.dialog-footer {
text-align: center;
}
.order-list-expand_item_info_title {
line-height: 26px;
display: flex;
}
</style>
<style>
.send-order-dialog .el-dialog__body {
height: 600px;
}
</style>
......@@ -24,6 +24,11 @@
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"auto-imports.d.ts"
],
"references": [{ "path": "./tsconfig.node.json" }]
}
......@@ -6,6 +6,16 @@ import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
base: './',
server: {
port: 9803,
host: true,
proxy: {
'/api': {
target: 'http://192.168.31.40:8060',
},
},
},
plugins: [
vue(),
AutoImport({
......
// vite.config.ts
import { defineConfig } from "file:///C:/work/factory_front/node_modules/vite/dist/node/index.js";
import AutoImport from "file:///C:/work/factory_front/node_modules/unplugin-auto-import/dist/vite.js";
import Components from "file:///C:/work/factory_front/node_modules/unplugin-vue-components/dist/vite.js";
import { ElementPlusResolver } from "file:///C:/work/factory_front/node_modules/unplugin-vue-components/dist/resolvers.js";
import vue from "file:///C:/work/factory_front/node_modules/@vitejs/plugin-vue/dist/index.mjs";
var vite_config_default = defineConfig({
base: "./",
server: {
port: 9803,
host: true,
proxy: {
"/api": {
target: "http://192.168.31.40:8060"
}
}
},
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
})
],
resolve: {
alias: { "@": "/src" }
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFx3b3JrXFxcXGZhY3RvcnlfZnJvbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkM6XFxcXHdvcmtcXFxcZmFjdG9yeV9mcm9udFxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vQzovd29yay9mYWN0b3J5X2Zyb250L3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcbmltcG9ydCBBdXRvSW1wb3J0IGZyb20gJ3VucGx1Z2luLWF1dG8taW1wb3J0L3ZpdGUnXG5pbXBvcnQgQ29tcG9uZW50cyBmcm9tICd1bnBsdWdpbi12dWUtY29tcG9uZW50cy92aXRlJ1xuaW1wb3J0IHsgRWxlbWVudFBsdXNSZXNvbHZlciB9IGZyb20gJ3VucGx1Z2luLXZ1ZS1jb21wb25lbnRzL3Jlc29sdmVycydcbmltcG9ydCB2dWUgZnJvbSAnQHZpdGVqcy9wbHVnaW4tdnVlJ1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgYmFzZTogJy4vJyxcbiAgc2VydmVyOiB7XG4gICAgcG9ydDogOTgwMyxcbiAgICBob3N0OiB0cnVlLFxuICAgIHByb3h5OiB7XG4gICAgICAnL2FwaSc6IHtcbiAgICAgICAgdGFyZ2V0OiAnaHR0cDovLzE5Mi4xNjguMzEuNDA6ODA2MCcsXG4gICAgICB9LFxuICAgIH0sXG4gIH0sXG4gIHBsdWdpbnM6IFtcbiAgICB2dWUoKSxcbiAgICBBdXRvSW1wb3J0KHtcbiAgICAgIHJlc29sdmVyczogW0VsZW1lbnRQbHVzUmVzb2x2ZXIoKV0sXG4gICAgfSksXG4gICAgQ29tcG9uZW50cyh7XG4gICAgICByZXNvbHZlcnM6IFtFbGVtZW50UGx1c1Jlc29sdmVyKCldLFxuICAgIH0pLFxuICBdLFxuICByZXNvbHZlOiB7XG4gICAgYWxpYXM6IHsgJ0AnOiAnL3NyYycgfSxcbiAgfSxcbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQXVQLFNBQVMsb0JBQW9CO0FBQ3BSLE9BQU8sZ0JBQWdCO0FBQ3ZCLE9BQU8sZ0JBQWdCO0FBQ3ZCLFNBQVMsMkJBQTJCO0FBQ3BDLE9BQU8sU0FBUztBQUdoQixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixNQUFNO0FBQUEsRUFDTixRQUFRO0FBQUEsSUFDTixNQUFNO0FBQUEsSUFDTixNQUFNO0FBQUEsSUFDTixPQUFPO0FBQUEsTUFDTCxRQUFRO0FBQUEsUUFDTixRQUFRO0FBQUEsTUFDVjtBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUCxJQUFJO0FBQUEsSUFDSixXQUFXO0FBQUEsTUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxJQUNuQyxDQUFDO0FBQUEsSUFDRCxXQUFXO0FBQUEsTUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxJQUNuQyxDQUFDO0FBQUEsRUFDSDtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1AsT0FBTyxFQUFFLEtBQUssT0FBTztBQUFBLEVBQ3ZCO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment