Commit 642ac247 by qinjianhui

feat: 生产订单联调完成

parent e91e8d4f
VITE_API_BASE=/api VITE_API_BASE=/api
VITE_API_BASE_URL=/
\ No newline at end of file
...@@ -5,5 +5,6 @@ ...@@ -5,5 +5,6 @@
// Generated by unplugin-auto-import // Generated by unplugin-auto-import
export {} export {}
declare global { declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
} }
...@@ -8,24 +8,36 @@ export {} ...@@ -8,24 +8,36 @@ export {}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] 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'] ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm'] ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon'] ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination'] 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'] ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElTable: typeof import('element-plus/es')['ElTable'] ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag'] 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'] NavMenu: typeof import('./src/components/NavMenu.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
TableView: typeof import('./src/components/TableView.vue')['default'] TableView: typeof import('./src/components/TableView.vue')['default']
} }
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
} }
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
<link ref="icon" type="image/x-icon" href="/favicon.ico"/> <link ref="icon" type="image/x-icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>九猫-工厂端</title> <title>九猫-工厂端</title>
<link rel="stylesheet" href="/iconfont/iconfont.css">
<script src="/iconfont/iconfont.js"></script>
</head> </head>
<body> <body>
<div id="app"></div> <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> <template>
<el-config-provider size="default" :zIndex="1000" :locale="locale"> <el-config-provider size="default" :z-index="1000" :locale="locale">
<RouterView /> <RouterView />
</el-config-provider> </el-config-provider>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
// @ts-ignore import zhCn from 'element-plus/es/locale/lang/zh-cn.mjs'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs' import en from 'element-plus/es/locale/lang/en.mjs'
// @ts-expect-error
import en from 'element-plus/dist/locale/en.mjs'
const language = ref('zh-cn') const language = ref('zh-cn')
const locale = computed(() => (language.value === 'zh-cn' ? zhCn : en)) const locale = computed(() => (language.value === 'zh-cn' ? zhCn : en))
</script> </script>
......
...@@ -2,5 +2,5 @@ import axios from './axios' ...@@ -2,5 +2,5 @@ import axios from './axios'
import { LoginReq, LoginResp } from '@/types/api/auth' import { LoginReq, LoginResp } from '@/types/api/auth'
export function loginApi(data: LoginReq) { 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' import Axios from 'axios'
const axios = Axios.create({ const axios = Axios.create({
...@@ -5,13 +6,49 @@ const axios = Axios.create({ ...@@ -5,13 +6,49 @@ const axios = Axios.create({
timeout: 30000, timeout: 30000,
}) })
axios.interceptors.request.use((config) => { 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 return config
}) },
axios.interceptors.response.use((resp) => { (error) => {
Promise.reject(error)
},
)
axios.interceptors.response.use((response) => {
// 1. 判断响应码 // 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 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 @@ ...@@ -14,32 +14,32 @@
> >
<template v-for="item in menuList"> <template v-for="item in menuList">
<el-menu-item <el-menu-item
v-if="!item.children"
:key="item.id" :key="item.id"
:index="item.index" :index="item.index"
v-if="!item.children"
>{{ item.label }}</el-menu-item >{{ 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> <template #title>
<span class="label">{{ item.label }}</span> <span class="label">{{ item.label }}</span>
</template> </template>
<el-menu-item <el-menu-item
v-for="sub in item.children" v-for="sub in item.children"
:index="sub.index"
:key="sub.id" :key="sub.id"
:index="sub.index"
>{{ sub.label }}</el-menu-item >{{ sub.label }}</el-menu-item
> >
</el-sub-menu> </el-sub-menu>
</template> </template>
</el-menu> </el-menu>
<div class="user-info"> <div v-if="userInfo" class="user-info">
<span class="user-avatar"> <span class="user-avatar">
<el-icon><User /></el-icon> <el-icon><User /></el-icon>
</span> </span>
<el-dropdown style="color: #fff; font-size: 12px"> <el-dropdown style="color: #fff; font-size: 14px">
<span class="el-dropdown-link"> <span class="el-dropdown-link">
<el-icon style="vertical-align: middle"><User /></el-icon> <el-icon style="vertical-align: middle"><User /></el-icon>
用户 {{ userInfo.factory.title }}
<el-icon style="vertical-align: middle" class="el-icon--right"> <el-icon style="vertical-align: middle" class="el-icon--right">
<arrow-down /> <arrow-down />
</el-icon> </el-icon>
...@@ -63,10 +63,12 @@ const route = useRoute() ...@@ -63,10 +63,12 @@ const route = useRoute()
const defaultActive = ref(route.path) const defaultActive = ref(route.path)
const menuList = reactive(Menu) const menuList = reactive(Menu)
const userInfo = userUserStore().user const userStore = userUserStore()
const userInfo = userStore.user
const logout = () => { const logout = () => {
userUserStore().logout() userStore.logout()
window.location.reload()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
......
...@@ -7,6 +7,7 @@ import Dashboard from '@/views/Dashboard.vue' ...@@ -7,6 +7,7 @@ import Dashboard from '@/views/Dashboard.vue'
import Error from '@/views/error/404.vue' import Error from '@/views/error/404.vue'
import OrderList from '@/views/order/index.vue' import OrderList from '@/views/order/index.vue'
import ProductionComplete from '@/views/production/complete.vue' import ProductionComplete from '@/views/production/complete.vue'
import { getToken} from '@/api/axios'
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
...@@ -46,7 +47,12 @@ const router = createRouter({ ...@@ -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 export default router
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { User } from '@/types' import { LoginReq, SysUser } from '@/types/api/auth'
import { LoginReq } from '@/types/api/auth'
import { loginApi } from '@/api/auth' import { loginApi } from '@/api/auth'
import { showError } from '@/utils/ui'
import { setToken } from '@/api/axios'
import router from '@/router'
export interface AppStoreState { export interface AppStoreState {
user?: User user?: SysUser
token?: string
} }
const useUserStore = defineStore('user', { 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: { actions: {
setUser(user?: User) { setUser(user?: SysUser) {
this.user = user this.user = user
localStorage.setItem('user', JSON.stringify(user))
}, },
async login(data: LoginReq) { async login(data: LoginReq) {
try {
const resp = await loginApi(data) const resp = await loginApi(data)
// resp. 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 { .card {
background-color: #fff; background-color: #fff;
padding: 10px; padding: 10px;
...@@ -25,10 +29,38 @@ ...@@ -25,10 +29,38 @@
display: flex; display: flex;
flex-direction: column; 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 {
flex: 1; flex: 1;
} }
.margin-top-10 {
margin-top: 10px;
}
.margin-top-20 {
margin-top: 20px;
}
.item {
margin-right: 6px;
}
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 5px; width: 5px;
......
...@@ -13,3 +13,9 @@ ...@@ -13,3 +13,9 @@
height: 40px; height: 40px;
min-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 './common.scss';
@import './element.scss' @import './element.scss';
\ No newline at end of file
import { User } from '..'
export interface LoginReq { export interface LoginReq {
factoryNumber: string factoryCode: string
iphone: string account: string
password: string password: string
} }
export interface LoginResp { export interface LoginResp {
data: Root
message: string
code: number
}
export interface Root {
sysUser: SysUser
token: string 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 { ...@@ -5,41 +5,65 @@ export interface SearchForm {
endProductId: string endProductId: string
orderOnlineNo: string orderOnlineNo: string
shopNumber: string shopNumber: string
supplierId: string
internalMemo: string internalMemo: string
status?: number
} }
export interface Tab { export interface Tab {
label: string status: string
quality: number statusCode: number
code: string statusName: string
quantity: number
} }
export interface OrderData { export interface OrderData {
id: number id: number
moreable?: boolean namespace?: string
shopNumber?: string erpId?: number
orderNumber?: string orderNumber?: string
erpOrderNumber?: string
facotoryNo?: string
addressId?: number addressId?: number
erpSubOrderNumber?: string
orderOnlineId?: number
orderOnlineNo?: number
status?: number status?: number
weight?: number
outCurrencyCode?: number
productAmount?: number productAmount?: number
carriageAmount?: number
totalAmount?: number totalAmount?: number
productNum?: number
trackStatus?: string
finishTime?: string
paymentTime?: string paymentTime?: string
remark?: string
startStockingTime?: string startStockingTime?: string
source?: string source?: string
createTime?: string substrateAreaCode?: string
deliveryType?: string
lanshouName?: string
lanshouPhone?: string
lanshouRegion?: string
lanshouAddress?: string
lanshouPost?: string
updateTime?: string updateTime?: string
createTime?: string
internalMemoList?: MemoList[]
substrateAreaName?: string
productList?: ProductList[] productList?: ProductList[]
address?: Address
delayMinDay?: number delayMinDay?: number
internalMemoList?: MemoList[] shipmentList?: string
sourceType?: string sourceType?: string
moreable?: boolean
} }
export interface ProductList { export interface ProductList {
id: number id: number
customOrderId?: number customOrderId?: number
subOrderNumber?: string subOrderNumber?: string
shopNumber?: string shopNumber?: string
count?: number
baseSku?: string baseSku?: string
erpSubOrderNumber?: string
variantSku?: string variantSku?: string
variantImage?: string variantImage?: string
erpProductItemId?: number erpProductItemId?: number
...@@ -55,39 +79,6 @@ export interface ProductList { ...@@ -55,39 +79,6 @@ export interface ProductList {
updateTime?: string 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 { export interface MemoList {
id: number id: number
type?: number type?: number
...@@ -103,3 +94,70 @@ export interface SpanMethodProps { ...@@ -103,3 +94,70 @@ export interface SpanMethodProps {
rowIndex: number rowIndex: number
columnIndex: 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( ...@@ -9,18 +9,14 @@ export default function useElTableColumnWidth(
let cleanupResizeEventListener: (() => void) | undefined let cleanupResizeEventListener: (() => void) | undefined
const width = ref(0) const width = ref(0)
const onThOrderDetailResize = debounce( const onThOrderDetailResize = debounce(() => {
() => {
const el = tableWrapperRef.value const el = tableWrapperRef.value
if (!el) return if (!el) return
const th = el.querySelector(selector) as HTMLElement | null const th = el.querySelector(selector) as HTMLElement | null
if (th) { if (th) {
width.value = th.offsetWidth width.value = th.offsetWidth
} }
}, }, 50)
50,
{ leading: true },
)
onMounted(() => { onMounted(() => {
const el = tableWrapperRef.value 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 @@ ...@@ -7,23 +7,23 @@
</div> </div>
<div class="form-container"> <div class="form-container">
<el-form <el-form
:model="loginForm"
ref="loginFormRef" ref="loginFormRef"
:model="loginForm"
:rules="rules" :rules="rules"
size="large" size="large"
> >
<el-form-item prop="factoryNumber"> <el-form-item prop="factoryCode">
<el-input <el-input
placeholder="请输入共厂号" v-model="loginForm.factoryCode"
v-model="loginForm.factoryNumber" placeholder="请输入工厂号"
> >
<template #prefix> <template #prefix>
<el-icon class="el-input__icon"><User /></el-icon> <el-icon class="el-input__icon"><User /></el-icon>
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="iphone"> <el-form-item prop="account">
<el-input v-model="loginForm.iphone" placeholder="请输入手机号"> <el-input v-model="loginForm.account" placeholder="请输入手机号">
<template #prefix> <template #prefix>
<el-icon class="el-input__icon"><Iphone /></el-icon> <el-icon class="el-input__icon"><Iphone /></el-icon>
</template> </template>
...@@ -46,10 +46,10 @@ ...@@ -46,10 +46,10 @@
> >
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<div style="width: 100%;text-align: right;"> <div style="width: 100%; text-align: right">
<a <a
href="/user/reset" href="/user/reset"
style="text-decoration: none; color: #409eff;" style="text-decoration: none; color: #409eff"
>忘记密码</a >忘记密码</a
> >
</div> </div>
...@@ -67,19 +67,21 @@ import { User, Iphone, Lock } from '@element-plus/icons-vue' ...@@ -67,19 +67,21 @@ import { User, Iphone, Lock } from '@element-plus/icons-vue'
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { LoginReq } from '@/types/api/auth' import { LoginReq } from '@/types/api/auth'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import useUserStore from '@/store/user';
const loginFormRef = ref<FormInstance>() const loginFormRef = ref<FormInstance>()
const loginForm = reactive<LoginReq>({ const loginForm = reactive<LoginReq>({
factoryNumber: '', factoryCode: '',
iphone: '', account: '',
password: '', password: '',
}) })
const rules = reactive<FormRules<LoginReq>>({ const rules = reactive<FormRules<LoginReq>>({
factoryNumber: [{ required: true, message: '请输入共厂号', trigger: 'blur' }], factoryCode: [{ required: true, message: '请输入工厂号', trigger: 'blur' }],
iphone: [{ required: true, message: '请输入手机号', trigger: 'blur' }], account: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
}) })
const userStore = useUserStore()
const handleLogin = async () => { const handleLogin = async () => {
try { try {
...@@ -88,6 +90,7 @@ const handleLogin = async () => { ...@@ -88,6 +90,7 @@ const handleLogin = async () => {
return return
} }
// 登录逻辑 // 登录逻辑
await userStore.login(loginForm)
} }
</script> </script>
<style lang="scss" scoped> <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>
...@@ -24,6 +24,11 @@ ...@@ -24,6 +24,11 @@
"@/*": ["./src/*"] "@/*": ["./src/*"]
} }
}, },
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], "include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"auto-imports.d.ts"
],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]
} }
...@@ -6,6 +6,16 @@ import vue from '@vitejs/plugin-vue' ...@@ -6,6 +6,16 @@ import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
base: './',
server: {
port: 9803,
host: true,
proxy: {
'/api': {
target: 'http://192.168.31.40:8060',
},
},
},
plugins: [ plugins: [
vue(), vue(),
AutoImport({ 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