一、前言

工厂订单及出入库业务的趋势是客户要求越来越高,客户需要快捷、方便、简单、一站式的出入库手续。因此,货物出入库管理信息系统要简化出入库手续,减轻人员作业量,提高工作效率,助力企业数字信息化转型

二、整体架构设计

1、开发语言:


  • Golang:Go 极其地快。其性能与 Java 或 C++相似。在我们的使用中,Go 一般比 Python 要快 30 倍。
  • Vue: 轻量级框架, 大小只有几十kb, 国人开发,中文文档,不存在语言障碍,易于理解和学习。运行速度更快,相比较与react而言,同样都是操作虚拟dom,就性能而言,vue存在很大的优势。

2、部署方式:  


  • 服务器系统:基于免安装可执行程序:支持Windows、Linux,Centos,Ubuntu操作系统
  • 数据库类型:目前已支持PostgreSQL、MySQL、Oracle、Microsoft SQL Server、SQLite等,还可以定制其它类型数据库
  • 热数据缓存服务:基于Key-Value 的Redis 数据库,关键热活数据存储在Redis服务中,提高响应速率。
  • 主备双活服务器:确保稳定性,如果主服务器故障,自动切换到备服务器。热数据
  • 数据库备份:定时增量备份,定期全量备份。

三、编码实现 (基于篇幅及可读性考虑,此处展示部分关键代码)

1、模块截图

《工厂订单出入库信息管理系统》模块1 -- 订单管理_vue.js

2、Go 关键代码:

package models

import (
"fmt"
"github.com/astaxie/beego/orm"
"productManage/common"
"productManage/log"
"strconv"
cron "github.com/robfig/cron"
"time"
)

type Order struct {
OrderID string `orm:"column(OrderID);size(255);pk" description:"订单号"`
BoxTotal int `orm:"column(BoxTotal)" description:"总箱数"`
CreateTime int64 `orm:"column(CreateTime)" description:"创建时间"`
NumberPerBox int `orm:"column(NumberPerBox)" description:"每箱产品数"`
ProductID int `orm:"column(ProductID)" description:"产品id"`
ProductTotal int `orm:"column(ProductTotal)" description:"产品总数"`
SnTotal int `orm:"column(SnTotal)" description:"sn总数"`
RecipientTotal int `orm:"column(RecipientTotal)" description:"发货信息(收件人总数)"`
StatusBox int8 `orm:"column(StatusBox)" description:"装箱状态:0 未确认;1正确;2异常;"`
StatusPrepare int8 `orm:"column(StatusPrepare)" description:"备货状态:0 未确认;1正确;2异常;"`
StatusQc int8 `orm:"column(StatusQc)" description:"品管状态:0 未确认;1正确;2异常;"`
OriOrderID string `orm:"column(OriOrderID);size(255);" description:"源订单号"`
}

func (t *Order) TableName() string {
return "tb_order"
}

func init() {
orm.RegisterModel(new(Order))
go func() {
crontab := cron.New()
task := func() {
go CronDelOrder()
}
//crontab.AddFunc("1 1 5 * *", task) //每天5点1分1秒执行一次
crontab.AddFunc("1 * * * *", task)//每分钟
crontab.Start()
select {}
}()
}

//为了防止数据库一直增长,超过180天的已经出货的订单每天自动删除一次。如果需要查询老数据,需要手工把备份数据导入回来。
func CronDelOrder() {
o := orm.NewOrm()
i := 0
for 1==1 {
var orders []Order
num, err := o.QueryTable("tb_order").Filter("CreateTime__lt", time.Now().Unix()-int64(180*24*60*60)).Filter("StatusBox", 1).Filter("StatusPrepare", 1).Filter("StatusQc", 1).All(&orders)
if err != nil {
writelog.WriteDebug("CronDelOrder error %s", err.Error())
return
}

if num == 0 {
break
}

for _,v := range orders {
writelog.WriteDebug("Auto delete order: %s", v.OrderID)
DelOrder(v.OrderID)
// 删除Distribute
DelDistributeByOrderID(v.OrderID)
// 删除Distribute_file
DelDistributeFileByOrderID(v.OrderID)
// 删除recipient
DelRecipientByOrderID(v.OrderID)
// 删除box
DelBoxByOrderID(v.OrderID)
// 删除sn
DelSnByOrderID(v.OrderID)
}

i = i + 1
if i > 1000 {//一个循环最多跑1000次,防止因未删除数据,一直循环下去
break
}
}
}

// AddOrder insert a new Order into database and returns
// last inserted Id on success.
func AddOrder(m *Order) (id int64, err error) {
o := orm.NewOrm()
id, err = o.Insert(m)
return
}

// GetOrderById retrieves Order by Id. Returns error if
// Id doesn't exist
func GetOrderById(OrderID string) (v *Order, err error) {

o := orm.NewOrm()
var order []Order
num, err := o.QueryTable("tb_order").Filter("OrderID", OrderID).All(&order)
if err == nil {
if num <= 0 {
writelog.WriteDebug("没找到%s", OrderID)
return nil, orm.ErrNoRows
} else if num > 1 {
writelog.WriteDebug("多条报错%s", OrderID)
return nil, orm.ErrMultiRows
} else { //num==1
return &order[0], nil
}
}
return nil, err
}

func EditOrder(OrderID string, ProductID int, ProductTotal int, NumberPerBox int, BoxTotal int) (num int64, err error) {
writelog.WriteDebug("%+v", OrderID)
o := orm.NewOrm()
sql := fmt.Sprintf(`Update tb_order set ProductID=%d, ProductTotal=%d, NumberPerBox=%d, BoxTotal=%d WHERE OrderID="%s"`, ProductID, ProductTotal, NumberPerBox, BoxTotal, OrderID)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("EditOrder ok, rows=%d", num)
} else {
writelog.WriteDebug("EditOrder err:%s", err.Error())
}
return num, err
}

func DelOrder(OrderID string) (num int64, err error) {
o := orm.NewOrm()
num, err = o.Delete(&Order{OrderID: OrderID})
return num, err
}

func GetOrderList(StartTime int64, EndTime int64, OrderID string, ProductID int64, StatusBox int, StatusPrepare int, StatusQc int, Page int, RowNum int) (recordcount int64, num int64, r []Order, err error, sql string) {
if RowNum <= 0 {
RowNum = common.Row_Num_Default
}

sql = fmt.Sprintf(`SELECT * FROM tb_order WHERE 1=1`)
if StartTime > 0 {
sql = fmt.Sprintf(` %s AND CreateTime>%d`, sql, StartTime)
}
if EndTime > 0 {
sql = fmt.Sprintf(` %s AND CreateTime<%d`, sql, EndTime)
}
if len(OrderID) > 0 {
sql = fmt.Sprintf(` %s AND OrderID='%s'`, sql, OrderID)
}
if ProductID > 0 {
sql = fmt.Sprintf(` %s AND ProductID=%d`, sql, ProductID)
}

if StatusBox > common.StatusAll {
sql = fmt.Sprintf(` %s AND StatusBox=%d`, sql, StatusBox)
}
if StatusPrepare > common.StatusAll {
sql = fmt.Sprintf(` %s AND StatusPrepare=%d`, sql, StatusPrepare)
}
if StatusQc > common.StatusAll {
sql = fmt.Sprintf(` %s AND StatusQc=%d`, sql, StatusQc)
}
o := orm.NewOrm()
sql_num := "SELECT COUNT(*) FROM ( " + sql + " ) AS a"

err = o.Raw(sql_num).QueryRow(&recordcount)
if err != nil {
return 0, 0, r, err, sql
}
if Page >= 0 {
sql = sql + " limit " + strconv.Itoa(Page*RowNum) + "," + strconv.Itoa(RowNum)
}
writelog.WriteDebug(sql)
num, err = o.Raw(sql).QueryRows(&r)
return recordcount, num, r, err, sql
}

func EditOrderSnTotal(OrderID string, SnTotal int) (num int64, err error) {
writelog.WriteDebug("%+v", OrderID)
o := orm.NewOrm()
sql := fmt.Sprintf(`Update tb_order set SnTotal=%d WHERE OrderID="%s"`, SnTotal, OrderID)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("EditOrder ok, rows=%d", num)
} else {
writelog.WriteDebug("EditOrder err:%s", err.Error())
}
return num, err
}

func EditOrderRecipientTotal(OrderID string, RecipientTotal int) (num int64, err error) {
writelog.WriteDebug("%+v", OrderID)
o := orm.NewOrm()
sql := fmt.Sprintf(`Update tb_order set RecipientTotal=%d WHERE OrderID="%s"`, RecipientTotal, OrderID)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("EditOrder ok, rows=%d", num)
} else {
writelog.WriteDebug("EditOrder err:%s", err.Error())
}
return num, err
}

func UpdateRecipientTotalByOrderID(OrderID string, increment int) (num int64, err error) {
o, err := GetOrderById(OrderID)
if err != nil {
return -1, err
}

RecipientTotal := o.RecipientTotal + increment
num, err = EditOrderRecipientTotal(OrderID, RecipientTotal)
return
}

func UpdateOrderStatusPrepare(OrderID string, StatusPrepare int) (num int64, err error) {

o := orm.NewOrm()
sql := fmt.Sprintf(`Update tb_order set StatusPrepare=%d WHERE OrderID="%s"`, StatusPrepare, OrderID)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("UpdateOrderStatusPrepare ok, rows=%d", num)
} else {
writelog.WriteDebug("UpdateOrderStatusPrepare err:%s", err.Error())
}
return num, err

}

func UpdateOrderStatusQc(OrderID string, StatusQc int) (num int64, err error) {

o := orm.NewOrm()
sql := fmt.Sprintf(`Update tb_order set StatusQc=%d WHERE OrderID="%s"`, StatusQc, OrderID)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("UpdateOrderStatusQc ok, rows=%d", num)
} else {
writelog.WriteDebug("UpdateOrderStatusQc err:%s", err.Error())
}
writelog.WriteDebug(sql)
return num, err

}

func UpdateOrderStatusBox(OrderID string, StatusBox int) (num int64, err error) {
o := orm.NewOrm()
sql := fmt.Sprintf(`Update tb_order set StatusBox=%d WHERE OrderID="%s"`, StatusBox, OrderID)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("UpdateOrderStatusBox ok, rows=%d", num)
} else {
writelog.WriteDebug("UpdateOrderStatusBox err:%s", err.Error())
}
return num, err
}

func GetProductBriefNameByOrderID(OrderID string) string {
o := orm.NewOrm()
var order []Order
num, err := o.QueryTable("tb_order").Filter("OrderID", OrderID).All(&order)
if err == nil {
if num <= 0 {
writelog.WriteDebug("GetProductBriefNameByOrderID 没找到%s", OrderID)
return ""
} else if num > 1 {
writelog.WriteDebug("GetProductBriefNameByOrderID 多条报错%s", OrderID)
return ""
} else { //num==1
var product []Product
num, err := o.QueryTable("product").Filter("ProductID", order[0].ProductID).All(&product)
if err == nil {
if num <= 0 {
writelog.WriteDebug("GetProductBriefNameByOrderID ProductID 没找到%s", order[0].ProductID)
return ""
} else if num > 1 {
writelog.WriteDebug("GetProductBriefNameByOrderID ProductID 多条报错%s", order[0].ProductID)
return ""
} else { //num==1
return product[0].ProductBriefName
}
}
}
}
return ""
}

func GetNumberPerBoxByOrderID(OrderID string) (NumberPerBox int, err error) {
var order Order
sql := fmt.Sprintf(`select NumberPerBox from tb_order where OrderID='%s'`, OrderID)
o := orm.NewOrm()
err = o.Raw(sql).QueryRow(&order)
if err!= nil{
writelog.WriteDebug("GetNumberPerBoxByOrderID OrderID:%s err:%s" ,OrderID, err.Error())
}
return order.NumberPerBox, err
}


func EditOrderBoxTotal(OrderID string, BoxTotal int) (num int64, err error) {
writelog.WriteDebug("%+v", OrderID)
o := orm.NewOrm()
sql := fmt.Sprintf(`Update tb_order set BoxTotal=%d WHERE OrderID="%s"`, BoxTotal, OrderID)
writelog.WriteDebug(sql)
res, err := o.Raw(sql).Exec()
if err == nil {
num, _ = res.RowsAffected()
writelog.WriteDebug("EditOrderBoxTotal ok, rows=%d", num)
} else {
writelog.WriteDebug("EditOrderBoxTotal err:%s", err.Error())
}
return num, err
}

3. vue代码 

<template>
<div class="page-container">
<div class='add-wrapper'>
<el-button type="primary" @click="addOrderClick">+添加</el-button>
</div>
<div class="search-wrapper">
<div class="block">
<span class="label">开始时间</span>
<el-date-picker
v-model="searchForm.startTime"
type="date"
:clearable="true"
format="yyyy-MM-dd 00:00:00"
placeholder="选择日期">
</el-date-picker>
</div>
<div class="block">
<span class="label">结束时间</span>
<el-date-picker
v-model="searchForm.endTime"
type="date"
:clearable="true"
format="yyyy-MM-dd 23:59:59"
placeholder="选择日期">
</el-date-picker>
</div>
<div class="block">
<span class="label">产品简称</span>
<el-select v-model="searchForm.productId" placeholder="请选择">
<el-option
v-for="item in productList"
:key="item.ProductID"
:label="item.ProductBriefName"
:value="item.ProductID">
{{item.ProductBriefName}}
</el-option>
</el-select>
</div>
<div class="operates">
<el-button type="primary" @click="searchClick">查询</el-button>
<el-button type="primary" @click="resetClick">重置</el-button>
</div>

</div>

<el-table
:data="orderList"
style="width: 100%">
<el-table-column
prop="OrderID"
label="订单号"
>
</el-table-column>
<el-table-column
prop="ProductBriefName"
label="产品简称"
>
</el-table-column>
<el-table-column
prop="ProductTotal"
label="产品总数"
width="120">
</el-table-column>
<el-table-column
prop="NumberPerBox"
label="产品数/箱"
width="120">
</el-table-column>
<el-table-column
prop="SnTotal"
label="SN总数"
width="100">
<template slot-scope="scope">
<a @click="toSnListClick(scope.row)">{{scope.row.SnTotal}}</a>
</template>
</el-table-column>
<el-table-column
prop="BoxTotal"
label="总箱数"
width="90">
<template slot-scope="scope">
<a @click="toBoxListClick(scope.row)">{{scope.row.BoxTotal}}</a>
</template>

</el-table-column>
<el-table-column
prop="RecipientTotal"
label="收件信息"
width="120">
<template slot-scope="scope">
<a @click="toRecipientListClick(scope.row)">{{scope.row.RecipientTotal}}<span v-if="scope.row.RecipientSnTotal&&scope.row.RecipientSnTotal!=''">({{scope.row.RecipientSnTotal}})</span></a>
</template>
</el-table-column>
<el-table-column
prop="CreateTime"
label="创建时间"
>
</el-table-column>
<el-table-column
prop="role"
label="导入数据"
>
<template slot-scope="scope">
<div>
<a>
导入《SN》
<input type="file" @change="changeFile($event,scope.row,1)">
</a>
</div>
<div>
<a>
导入《发货申请单》
<input type="file" @change="changeFile($event,scope.row,2)">
</a>
</div>
<div>
<a>
导入《五码合一》
<input type="file" @change="changeFile($event,scope.row,3)">
</a>
</div>
<div>
<a>
导入《电子运单》
<input type="file" @change="changeFile($event,scope.row,4)">
</a>
</div>
</template>
</el-table-column>
<el-table-column
prop="role"
label="导出数据"
>
<template slot-scope="scope">
<div>
<a :href="scope.row.href1">
导出《物流信息》
</a>

</div>
<div>
<a :href="scope.row.href2">
导出《出货交接明细表》
</a>

<a :href="scope.row.href3">
导出《出货信息表》
</a>

</div>

</template>
</el-table-column>
<el-table-column

label="操作">
<template slot-scope="scope">

<a @click="delOrderClick(scope)">刪除</a>
</template>
</el-table-column>
</el-table>

<el-pagination

@current-change="handleCurrentChange"
:current-page="pageData.Page"
:page-size ="pageData.RowNum"
layout="total,pager, prev, next"
:total="RowNum"></el-pagination>
<AddOrderDialog ref="addOrderDialogRef" v-on:getOrderList="getOrderList"/>
</div>
</template>

<script>
import {ORDER_API_PATH,PRODUCT_API_PATH,UPLOAD_SN_API_PATH,UPLOAD_DISTRIBUTE_API_PATH,UPLOAD_WAYBILL_API_PATH,
UPLOAD_5CODE_API_PATH,DOWNLOAD_DISTRIBUTE_API_PATH,DOWNLOAD_HANDOVER_API_PATH,DOWNLOAD_KUAYUEEXPRESS_API_PATH,
DOWNLOAD_TODAY_DISTRIBUTE_API_PATH,DOWNLOAD_TODAY_KUAYUEEXPRESS_API_PATH,DOWNLOAD_TODAY_HANDOVER_API_PATH} from "../../service/api"
import AddOrderDialog from "./add"
export default{
components:{AddOrderDialog},
data(){
return{
orderList:[],
productList:[],
RowNum:0,
searchForm:{
startTime:new Date(new Date().toLocaleDateString()),
endTime:new Date(new Date().toLocaleDateString()),
productId:0
},
pageData:{
Page:1,
RowNum:20
},
}
},
created(){
this.getProductList()

},

methods:{
downloadKuayueExpress(item){
let formData = new FormData()
formData.append("Token",sessionStorage.getItem("token"))
formData.append("OrderID",item.OrderID)
this.$axios.get(DOWNLOAD_KUAYUEEXPRESS_API_PATH,{
params:{
Token:sessionStorage.getItem("token"),
OrderID:item.OrderID
}
},{
headers: {
"Content-Type": "application/octet-stream"
},
responseType: 'blob'
}).then(res=>{

var blob = this.data;
var reader = new FileReader();
reader.readAsDataURL(blob); // 转换为base64,可以直接放入a表情href
reader.onload = function (e) {
// 转换完成,创建一个a标签用于下载
var a = document.createElement("a");
a.download = name + ".xls";
a.href = e.target.result;
$("body").append(a); // 修复firefox中无法触发click
a.click();
resolve(200)
$(a).remove();
};
})
},
downloadHandover(item){
let formData = new FormData()
formData.append("Token",sessionStorage.getItem("token"))
formData.append("OrderID",item.OrderID)
this.$axios.get(DOWNLOAD_HANDOVER_API_PATH,{
params:{
Token:sessionStorage.getItem("token"),
OrderID:item.OrderID
}
})
},
downloadDistribute(item){
let formData = new FormData()
formData.append("Token",sessionStorage.getItem("token"))
formData.append("OrderID",item.OrderID)
this.$axios.get(DOWNLOAD_DISTRIBUTE_API_PATH,{
params:{
Token:sessionStorage.getItem("token"),
OrderID:item.OrderID
}
})
},
changeFile(e,item,type){
let file = e.target.files[0]
let url = ""
if(type==1){
url = UPLOAD_SN_API_PATH
}else if(type==2){
url = UPLOAD_DISTRIBUTE_API_PATH
}else if(type==3){
url = UPLOAD_5CODE_API_PATH
}else if(type==4){
url = UPLOAD_WAYBILL_API_PATH
}
let formData = new FormData()

formData.append("Token",sessionStorage.getItem("token"))
formData.append("OrderID",item.OrderID)
formData.append("file",file)
this.$axios.post(url,formData).then(res=>{

if(res.data.Ret == 0){


this.$message({
message:"成功导入"+res.data.Data+"条数据",
type:"success",
duration:3000
});
this.getOrderList()
}else{

this.$message({
message:res.data.Msg,
type:"error",
duration:3000
});
}
}).finally(()=>{
e.target.value = ""
})
},
toSnListClick(item){

let startTime = this.moment(this.searchForm.startTime).format("YYYY-MM-DD 00:00:00")
let endTime = this.moment(this.searchForm.endTime).format("YYYY-MM-DD 23:59:59")

this.$router.push({
name:"Sn",
query:{
orderId:item.OrderID,
startTime:startTime,
endTime:endTime
}
})
},
toBoxListClick(item){
let startTime = this.moment(this.searchForm.startTime).format("YYYY-MM-DD 00:00:00")
let endTime = this.moment(this.searchForm.endTime).format("YYYY-MM-DD 23:59:59")
this.$router.push({
name:"Box",
query:{
orderId:item.OrderID,
startTime:startTime,
endTime:endTime
}
})
},
toRecipientListClick(item){
let startTime = this.moment(this.searchForm.startTime).format("YYYY-MM-DD 00:00:00")
let endTime = this.moment(this.searchForm.endTime).format("YYYY-MM-DD 23:59:59")
this.$router.push({
name:"Delivery",
query:{
orderId:item.OrderID,
startTime:startTime,
endTime:endTime
}
})
},
getProductList(){
let formData = new FormData()
formData.append("Act","GetProductList")
formData.append("Token",sessionStorage.getItem("token"))

this.$axios.post(PRODUCT_API_PATH,formData).then(res=>{
if(res.data.Ret==0){

this.productList = [{
ProductID:0,
ProductBriefName:"全部"
}].concat(res.data.Data)
this.RowNum = res.data.Recordcount
}
}).finally(()=>{
this.getOrderList()
})
},
handleCurrentChange(){
this.getOrderList()
},
getOrderList(){
let formData = new FormData()
formData.append("Act","GetOrderList")
formData.append("Token",sessionStorage.getItem("token"))
formData.append("ProductID",this.searchForm.productId)
formData.append("Page",this.pageData.Page-1)
formData.append("RowNum",this.pageData.RowNum)
let startTime = this.moment(this.searchForm.startTime).format("YYYY-MM-DD 00:00:00")
let endTime = this.moment(this.searchForm.endTime).format("YYYY-MM-DD 23:59:59")
formData.append("StartTime",new Date(startTime).getTime()/1000)
formData.append("EndTime",new Date(endTime).getTime()/1000)
this.$axios.post(ORDER_API_PATH,formData).then(res=>{

if(res.data.Ret == 0){
let data = res.data.Data

if(data){
data = data.map(v=>{
let product = this.productList.find(item=>item.ProductID == v.ProductID)
v.CreateTime = this.moment(new Date(v.CreateTime*1000)).format('YYYY-MM-DD HH:mm:ss')
v.ProductBriefName = product?product.ProductBriefName:""
v.href1 = DOWNLOAD_KUAYUEEXPRESS_API_PATH+"?Token="+sessionStorage.getItem("token")+"&OrderID="+v.OrderID
v.href2 = DOWNLOAD_HANDOVER_API_PATH+"?Token="+sessionStorage.getItem("token")+"&OrderID="+v.OrderID
v.href3 = DOWNLOAD_DISTRIBUTE_API_PATH+"?Token="+sessionStorage.getItem("token")+"&OrderID="+v.OrderID

return v
})
}else{
data = []
}

this.orderList = data
this.RowNum = res.data.Recordcount

}
})
},
addOrderClick(){
this.$refs.addOrderDialogRef.show(0)
},
resetClick(){
this.searchForm = {
startTime:new Date(new Date().toLocaleDateString()),
endTime:new Date(new Date().toLocaleDateString()),
productName:""
}
},
searchClick(){
this.getProductList()
},
editOrderClick(scope){
this.$refs.addOrderDialogRef.show(1,scope.row)
},
delOrderClick(scope){
this.$confirm('确认删除订单?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(()=>{
let formData = new FormData()
formData.append("Act","DelOrder")
formData.append("Token",sessionStorage.getItem("token"))
formData.append("OrderID",scope.row.OrderID)
this.$axios.post(ORDER_API_PATH,formData).then(res=>{

if(res.data.Ret == 0){

this.$message({
message:"删除订单成功",
type:"success",
duration:3000
});
this.productList.splice(scope.$index,1)
this.getOrderList()
}
})
})
}
}
}
</script>

<style scoped="scoped">
.search-wrapper{
display:flex;
flex-wrap:wrap;
align-items:center;
border:1px solid #dcdcdc;
padding:15px;
justy-content:flex-start;
}

.label-text{
min-width:120px;
}
.operate{
display:flex;
align-items: center;
}
.add-wrapper{
display:flex;
justy-content:flex-start;
margin-bottom:15px;
}
td a{
position:relative;
display:block;
margin:10px 0;
color: #3a8ee6;
}


input[type=file]{
position:absolute;
left:0px;
top:0px;
width:100%;
height:100%;
opacity:0;
}
</style>

四、结语

本次分享结束,欢迎来撩!

系统演示网址:http://47.113.115.218:81 演示密码:123456