管理运营

This commit is contained in:
DESKTOP-H2PAFLR\Administrator
2023-10-11 16:56:25 +08:00
parent 30a7502701
commit 39b86ccdf0
13 changed files with 908 additions and 2 deletions

View File

@@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ZR.Admin.WebApi.Extensions;
using ZR.Model.mes.md;
using ZR.Model.MES.op.DTO;
using ZR.Model.MES.op.ZR.Model.mes.md;
using ZR.Service.mes.md;
using ZR.Service.MES.md;
using ZR.Service.MES.md.IService;
using ZR.Service.MES.op;
using ZR.Service.MES.op.IService;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace ZR.Admin.WebApi.Controllers.MES.md
{
[Route("mes/op/operation")]
public class OperationController : BaseController
{
IOperationService operationService;
public OperationController(IOperationService operationService) {
this.operationService= operationService;
}
/// <summary>
/// 获取所有统计信息
/// </summary>
/// <param name="workshopid">车间id</param>
/// <returns></returns>
[HttpGet("getOperation")]
public IActionResult GetActionResult(string WorkerID = "1")
{
List<OpStatisticsDTO> OpStatisticsList= operationService.GetAllData(WorkerID);
return ToResponse(new ApiResult(200, "success", OpStatisticsList));
}
}
}

View File

@@ -7,6 +7,8 @@ namespace ZR.Common
{
public class Tools
{
/// <summary>
/// 要分割的字符串 eg: 1,3,10,00
/// </summary>

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ZR.Model.MES.op.DTO
{
public class OpStatisticsDTO
{
/// <summary>
/// 工作车间
///</summary>
public string WorkshopId { get; set; }
/// <summary>
/// 工作车间
///</summary>
public string WorkshopName { get; set; }
/// <summary>
/// 生产日期
///</summary>
[SugarColumn(ColumnName = "productDate")]
public DateTime? ProductDate { get; set; }
/// <summary>
/// 计划数量
///</summary>
public int? PlanNum { get; set; }
/// <summary>
/// 已经生产数量
///</summary>
public int? ProductedNum { get; set; }
/// <summary>
/// 生产进度 ProductedNum/PlanNum*100
/// </summary>
public string ProductProgressRate { get; set; }
/// <summary>
/// 良品数量
///</summary>
public int? GoodproductsNum { get; set; }
/// <summary>
/// 不良品数量
///</summary>
public int? DefectiveProductsNum { get; set; }
/// <summary>
/// 品质率: 良品数量/不良品数量*100
/// </summary>
public string QualityRate { get; set; }
/// <summary>
/// 生产开始时间
///</summary>
public DateTime? ProductStarttime { get; set; }
/// <summary>
/// 生产结束时间
///</summary>
public DateTime? ProductEndtime { get; set; }
/// <summary>
/// 是否正在生产
/// </summary>
public bool isProducting { get; set; }
/// <summary>
/// 线体id
///</summary>
public string LineId { get; set; }
/// <summary>
/// 排序序号
/// </summary>
public int numSort { get; set; }
/// <summary>
/// 线体名称
///</summary>
public string LineName { get; set; }
/// <summary>
///
///</summary>
public string CreatedBy { get; set; }
/// <summary>
///
///</summary>
public string CreatedTime { get; set; }
/// <summary>
///
///</summary>
public string UpdatedBy { get; set; }
/// <summary>
///
///</summary>
public string UpdatedTime { get; set; }
/// <summary>
///
///</summary>
public int Id { get; set; }
}
}

View File

@@ -0,0 +1,100 @@

using System;
using System.Collections.Generic;
using System.Linq;
using SqlSugar;
namespace ZR.Model.MES.op
{
namespace ZR.Model.mes.md
{
/// <summary>
/// 操作统计表
///</summary>
[SugarTable("op_statistics")]
public class OpStatistics
{
/// <summary>
/// 工作车间
///</summary>
[SugarColumn(ColumnName = "workshop_id")]
public string WorkshopId { get; set; }
/// <summary>
/// 工作车间
///</summary>
[SugarColumn(ColumnName = "workshop_name")]
public string WorkshopName { get; set; }
/// <summary>
/// 生产日期
///</summary>
[SugarColumn(ColumnName = "productDate")]
public DateTime? ProductDate { get; set; }
/// <summary>
/// 计划数量
///</summary>
[SugarColumn(ColumnName = "planNum")]
public int? PlanNum { get; set; }
/// <summary>
/// 已经生产数量
///</summary>
[SugarColumn(ColumnName = "productedNum")]
public int? ProductedNum { get; set; }
/// <summary>
/// 良品数量
///</summary>
[SugarColumn(ColumnName = "goodproductsNum")]
public int? GoodproductsNum { get; set; }
/// <summary>
/// 不良品数量
///</summary>
[SugarColumn(ColumnName = "defectiveProductsNum")]
public int? DefectiveProductsNum { get; set; }
/// <summary>
/// 生产开始时间
///</summary>
[SugarColumn(ColumnName = "product_starttime")]
public DateTime? ProductStarttime { get; set; }
/// <summary>
/// 生产结束时间
///</summary>
[SugarColumn(ColumnName = "product_endtime")]
public DateTime? ProductEndtime { get; set; }
/// <summary>
/// 线体id
///</summary>
[SugarColumn(ColumnName = "line_id")]
public string LineId { get; set; }
/// <summary>
/// 线体名称
///</summary>
[SugarColumn(ColumnName = "line_name")]
public string LineName { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "CREATED_BY")]
public string CreatedBy { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "CREATED_TIME")]
public string CreatedTime { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "UPDATED_BY")]
public string UpdatedBy { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "UPDATED_TIME")]
public string UpdatedTime { get; set; }
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
}
}
}

View File

@@ -202,6 +202,10 @@ namespace ZR.Repository
{
return Context.Queryable<T>();
}
public ISugarQueryable<T> Queryable(Expression<Func<T, bool>> expression)
{
return Context.Queryable<T>().Where(expression);
}
public List<T> SqlQueryToList(string sql, object obj = null)
{

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZR.Model.MES.op.DTO;
using ZR.Model.MES.op.ZR.Model.mes.md;
namespace ZR.Service.MES.op.IService
{
public interface IOperationService
{
public List<OpStatisticsDTO> GetAllData(string workshopID);
}
}

View File

@@ -0,0 +1,70 @@
using Infrastructure.Attribute;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZR.Model.mes.md;
using ZR.Model.MES.op.ZR.Model.mes.md;
using ZR.Service.MES.md.IService;
using ZR.Service;
using ZR.Service.MES.op.IService;
using ZR.Model.MES.op.DTO;
using Microsoft.AspNetCore.Http.HttpResults;
using Org.BouncyCastle.Asn1.Esf;
namespace ZR.Service.MES.op
{
[AppService(ServiceType = typeof(IOperationService), ServiceLifetime = LifeTime.Transient)]
public class OperationService : BaseService<OpStatistics>, IOperationService
{
public List<OpStatisticsDTO> GetAllData(string workshopID)
{
decimal myDecimal = 123.456789m;
string formattedDecimal = myDecimal.ToString("0.00"); // 保留两位小数
List<OpStatisticsDTO> OpStatisticsDTOList= new List<OpStatisticsDTO>();
List<OpStatistics> OpStatisticsList =Queryable().Where(it => it.WorkshopId == workshopID ).ToList();
foreach (var OpStatistics in OpStatisticsList)
{
OpStatisticsDTO opStatisticsDTO=new OpStatisticsDTO();
opStatisticsDTO.WorkshopId = OpStatistics.WorkshopId;
opStatisticsDTO.WorkshopName = OpStatistics.WorkshopName;
opStatisticsDTO.ProductDate = OpStatistics.ProductDate;
opStatisticsDTO.PlanNum = OpStatistics.PlanNum;
opStatisticsDTO.ProductedNum = OpStatistics.ProductedNum;
decimal num1 = (decimal) OpStatistics.ProductedNum / (decimal)OpStatistics.PlanNum*100;
opStatisticsDTO.ProductProgressRate = num1.ToString("0.00") ;
opStatisticsDTO.GoodproductsNum = OpStatistics.GoodproductsNum;
opStatisticsDTO.DefectiveProductsNum = OpStatistics.DefectiveProductsNum;
decimal num2 = (decimal)OpStatistics.DefectiveProductsNum / (decimal)(OpStatistics.GoodproductsNum + OpStatistics.DefectiveProductsNum);
opStatisticsDTO.QualityRate = num2.ToString("f2");
opStatisticsDTO.ProductStarttime = OpStatistics.ProductStarttime;
opStatisticsDTO.ProductEndtime = OpStatistics.ProductEndtime;
if(DateTime.Now> opStatisticsDTO.ProductStarttime&& DateTime.Now < opStatisticsDTO.ProductEndtime)
{
opStatisticsDTO.isProducting = true;
}
else
{
opStatisticsDTO.isProducting = false;
}
opStatisticsDTO.numSort = OpStatisticsList.IndexOf(OpStatistics);
opStatisticsDTO.LineId = OpStatistics.LineId;
opStatisticsDTO.LineName = OpStatistics.LineName;
opStatisticsDTO.ProductEndtime = OpStatistics.ProductEndtime;
OpStatisticsDTOList.Add(opStatisticsDTO);
}
return OpStatisticsDTOList;
}
}
}

View File

@@ -0,0 +1,13 @@
import request from '@/utils/request'
/**
* 演示分页查询
* @param {查询条件} data
*/
export function getOperation(query) {
return request({
url: 'mes/op/operation/getOperation',
method: 'get',
params: query,
})
}

View File

@@ -48,5 +48,5 @@ module.exports = {
* If you want to also use it in dev, you can pass ['production', 'development']
*/
errorLog: 'production',
copyRight: 'Copyright ©2023 www.izhaorui.cn All Rights Reserved.'
copyRight: 'Copyright ©2023 www.doan.com All Rights Reserved.'
}

View File

@@ -0,0 +1,239 @@
<template>
<div class="app-container">
<div>
<div class="title">装配车间生产状态统计</div>
</div>
<vxe-table border resizable align="center" ref="xTable" :data="OperationList">
<vxe-column field="lineId" title="线体code" width="120" />
<vxe-column field="lineName" title="线体名称" width="120" />
<vxe-column field="productProgressRate" title="生产进度" width="500" :title-help="{ message: '小于20%:红色;小于60%:黄色;大于60%绿色' }">
<template #default="{ row }">
<div class="progress">
<div style="width: 400px">
<el-progress
:text-inside="true"
:stroke-width="16"
:percentage="row.productProgressRate"
:color="customColor(row.productProgressRate)"
></el-progress>
</div>
<span>{{ row.productedNum }}/{{ row.planNum }}</span>
</div>
</template>
</vxe-column>
<vxe-column field="isProducting" title="生产状态" :title-help="{ message: '生产中:绿色;停止生产:灰色' }" width="100">
<template #default="{ row }">
<template v-if="row.isProducting">
<div class="producting"></div>
</template>
<template v-else>
<div class="productstop"></div>
</template>
</template>
</vxe-column>
<vxe-column field="QualityRate" title="不良占比" :title-help="{ message: '不良品:红色;良品:灰色' }">
<template #default="{ row }">
<div class="progress">
<div style="height: 100px; width: 100px; margin: 0 auto" class="pies"></div>
<div style="margin:auto auto">{{ row.qualityRate }}% {{ row.goodproductsNum }}/{{ row.defectiveProductsNum }}</div>
</div>
</template>
</vxe-column>
</vxe-table>
</div>
</template>
<script>
import XEUtils from 'xe-utils'
import VXETable from 'vxe-table'
import * as echarts from 'echarts'
import { getOperation } from '@/api/operationManagement/operation.js'
export default {
name: 'assemblyWorkshop',
data() {
return {
OperationList: [],
}
},
created() {},
mounted() {
this.getData()
},
methods: {
// 获取运营数据
getData() {
const data = {
//这里填写车间id
WorkerID: '1234',
}
getOperation(data).then((res) => {
if (res.code == 200) {
this.OperationList = res.data
this.drawChart()
}
})
},
//进度条颜色
customColor(row) {
if (row <= 20) {
return 'red'
} else if (row <= 60) {
return 'yellow'
} else {
return 'green'
}
},
// 饼图配置
drawChart() {
this.$nextTick(() => {
var chartDom = document.getElementsByClassName('pies')
console.log('chartDom.length', chartDom.length)
for (let i = 0; i < chartDom.length; i++) {
const data = [
{ value: this.OperationList[i].defectiveProductsNum, name: '不良品' },
{ value: this.OperationList[i].goodproductsNum, name: '良品' },
]
var myChart = echarts.init(chartDom[i])
var option
option = {
color: ['red', '#909399'],
series: [
{
type: 'pie',
radius: '50%',
data: data,
label: {
normal: {
show: false,
},
},
labelLine: {
normal: {
show: false,
},
},
},
],
}
option && myChart.setOption(option)
//监听,根据视口变化动态从新渲染表格
window.addEventListener('resize', function () {
myChart.resize()
})
}
})
},
formatDate(value) {
return XEUtils.toDateString(value, 'yyyy-MM-dd HH:mm:ss.S')
},
changeFilterEvent(event, option, $panel) {
$panel.changeOption(event, !!option.data, option)
},
showDetailEvent(row) {
this.selectRow = row
this.showDetails = true
},
clickFooterItem(items, _columnIndex) {
VXETable.modal.alert(`点击了表尾第${_columnIndex}`)
},
checkboxChangeEvent() {
this.isAllChecked = this.$refs.xTable.isAllCheckboxChecked()
this.isIndeterminate = this.$refs.xTable.isAllCheckboxIndeterminate()
this.selectRecords = this.$refs.xTable.getCheckboxRecords()
},
changeAllEvent() {
this.$refs.xTable.setAllCheckboxRow(this.isAllChecked)
this.selectRecords = this.$refs.xTable.getCheckboxRecords()
},
sumNum(list, field) {
let count = 0
list.forEach((item) => {
count += Number(item[field])
})
return count
},
footerMethod({ columns, data }) {
return [
columns.map((column) => {
if (['num'].includes(column.property)) {
return this.sumNum(data, column.property)
}
return null
}),
]
},
},
}
</script>
<style lang="scss" scoped>
.title {
font-size: larger;
font-weight: bold;
color: #409eff;
margin: 0, auto;
text-align: center;
}
.progress {
display: flex;
justify-content: space-between;
}
.producting {
width: 30px;
height: 30px;
background: green;
border: 1px solid green;
border-radius: 50%;
margin: 0 auto;
}
.productstop {
width: 30px;
height: 30px;
background: gray;
border: 1px solid gray;
border-radius: 50%;
margin: 0 auto;
}
.first-col {
position: relative;
height: 20px;
}
.first-col:before {
content: '';
position: absolute;
left: -15px;
top: 10px;
width: 170px;
height: 1px;
transform: rotate(18deg);
background-color: #e8eaec;
}
.first-col .first-col-top {
position: absolute;
right: 4px;
top: -10px;
}
.first-col .first-col-bottom {
position: absolute;
left: 4px;
bottom: -10px;
}
.my-filter {
margin: 10px;
}
.page-left {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
z-index: 10;
}
</style>

View File

@@ -0,0 +1,15 @@
<template>
<div>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,299 @@
<template>
<div>
<vxe-table
border
resizable
show-footer
ref="xTable"
height="500"
:footer-method="footerMethod"
:data="tableData"
@checkbox-change="checkboxChangeEvent"
@checkbox-all="checkboxChangeEvent"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column type="seq" width="160" :resizable="false" show-overflow>
<template #header>
<div class="first-col">
<div class="first-col-top">名称</div>
<div class="first-col-bottom">序号</div>
</div>
</template>
<template #footer="{ items, _columnIndex }">
<vxe-button status="primary" @click="clickFooterItem(items, _columnIndex)" size="mini">支持</vxe-button>
<vxe-button @click="clickFooterItem(items, _columnIndex)" size="mini">test abc</vxe-button>
</template>
<template #default="{ row }">
<vxe-button @click="showDetailEvent(row)">弹框{{ row.name }}</vxe-button>
</template>
</vxe-column>
<vxe-column field="name" title="app.body.label.name" sortable>
<template #default="{ row }">
<a href="https://github.com/x-extends/vxe-table" target="_black">我是超链接{{ row.name }}</a>
</template>
</vxe-column>
<vxe-column field="role" title="Role">
<template #header>
<span style="color: red">自定义头部</span>
</template>
<template #footer="{ items, _columnIndex }">
<span style="color: red">累计{{ items[_columnIndex] }}</span>
</template>
<template #filter="{ $panel, column }">
<template v-for="(option, index) in column.filters">
<input class="my-filter" type="type" v-model="option.data" :key="index" @input="changeFilterEvent($event, option, $panel)" />
</template>
</template>
<template #default>
<vxe-button type="text">自定义按钮</vxe-button>
</template>
</vxe-column>
<vxe-column field="time" title="Time">
<template #header>
<vxe-input v-model="value1" placeholder="放个输入框" size="mini"></vxe-input>
</template>
<template #default="{ row, rowIndex }">
<template v-if="rowIndex === 2">
<vxe-switch v-model="row.flag"></vxe-switch>
</template>
<template v-else-if="rowIndex === 3">
<vxe-switch v-model="row.flag" open-label="开" close-label="关"></vxe-switch>
</template>
<template v-else>
<span>{{ formatDate(row.time) }}</span>
</template>
</template>
</vxe-column>
<vxe-column field="sex" title="Sex" show-overflow>
<template #default="{ row }">
<vxe-select v-model="row.sex" transfer>
<vxe-option value="Man" label="Man"></vxe-option>
<vxe-option value="Women" label="Women"></vxe-option>
</vxe-select>
</template>
</vxe-column>
<vxe-column field="html1" title="Html片段" width="200" show-overflow>
<template #default="{ row }">
<span v-html="row.html1"></span>
</template>
<template #footer>
<span>
<img src="https://pic2.zhimg.com/50/v2-f7031359103859e1ed38559715ef5f3f_hd.gif" style="width: 36px" />自定义模板<img
src="https://n.sinaimg.cn/sinacn17/w120h120/20180314/89fc-fyscsmv5911424.gif"
style="width: 30px"
/>
</span>
</template>
</vxe-column>
<vxe-column field="img1" title="图片路径" width="120">
<template #default="{ row }">
<img v-if="row.img1" :src="row.img1" style="width: 100px" />
<span v-else></span>
</template>
</vxe-column>
</vxe-table>
</div>
</template>
<script>
import XEUtils from 'xe-utils'
import VXETable from 'vxe-table'
export default {
name: 'paintingWorkhsop',
data() {
return {
value1: '',
value2: '',
showDetails: false,
selectRow: null,
isAllChecked: false,
isIndeterminate: false,
selectRecords: [],
tableData: [
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
flag: false,
time: 1600261774531,
html1: '<span style="color:red">自定义HTML</span>',
img1: 'https://5b0988e595225.cdn.sohucs.com/images/20181014/dce7cdaa130440e8b609fad083877ef3.gif',
},
{
id: 10002,
name: 'Test2',
role: 'Test',
sex: 'Women',
age: 22,
address: 'Guangzhou',
flag: false,
time: 1600261774531,
html1: '',
img1: 'https://5b0988e595225.cdn.sohucs.com/images/20181014/dce7cdaa130440e8b609fad083877ef3.gif',
},
{
id: 10003,
name: 'Test3',
role: 'PM',
sex: 'Man',
age: 32,
address: 'Shanghai',
flag: true,
time: 1600261774531,
html1: '<span style="color:orange">自定义HTML</span>',
img1: 'https://pic2.zhimg.com/50/v2-f7031359103859e1ed38559715ef5f3f_hd.gif',
},
{
id: 10004,
name: 'Test4',
role: 'Designer',
sex: 'Women',
age: 23,
address: 'test abc',
flag: false,
time: 1600261774531,
html1: '',
img1: 'https://pic2.zhimg.com/50/v2-f7031359103859e1ed38559715ef5f3f_hd.gif',
},
{
id: 10005,
name: 'Test5',
role: 'Develop',
sex: 'Women',
age: 30,
address: 'Shanghai',
flag: true,
time: 1600261774531,
html1: '',
img1: 'https://5b0988e595225.cdn.sohucs.com/images/20181014/dce7cdaa130440e8b609fad083877ef3.gif',
},
{
id: 10006,
name: 'Test6',
role: 'Designer',
sex: 'Women',
age: 21,
address: 'test abc',
flag: true,
time: 1600261774531,
html1: '<span style="color:blue">自定义HTML</span>',
img1: 'https://pic2.zhimg.com/50/v2-f7031359103859e1ed38559715ef5f3f_hd.gif',
},
{
id: 10007,
name: 'Test7',
role: 'Test',
sex: 'Man',
age: 29,
address: 'test abc',
flag: false,
time: 1600261774531,
html1: '',
img1: 'https://5b0988e595225.cdn.sohucs.com/images/20181014/dce7cdaa130440e8b609fad083877ef3.gif',
},
{
id: 10008,
name: 'Test8',
role: 'Develop',
sex: 'Man',
age: 35,
address: 'test abc',
flag: false,
time: 1600261774531,
html1: '',
img1: 'https://5b0988e595225.cdn.sohucs.com/images/20181014/dce7cdaa130440e8b609fad083877ef3.gif',
},
],
tablePage: {
total: 0,
currentPage: 1,
pageSize: 10,
},
}
},
methods: {
formatDate(value) {
return XEUtils.toDateString(value, 'yyyy-MM-dd HH:mm:ss.S')
},
changeFilterEvent(event, option, $panel) {
$panel.changeOption(event, !!option.data, option)
},
showDetailEvent(row) {
this.selectRow = row
this.showDetails = true
},
clickFooterItem(items, _columnIndex) {
VXETable.modal.alert(`点击了表尾第${_columnIndex}`)
},
checkboxChangeEvent() {
this.isAllChecked = this.$refs.xTable.isAllCheckboxChecked()
this.isIndeterminate = this.$refs.xTable.isAllCheckboxIndeterminate()
this.selectRecords = this.$refs.xTable.getCheckboxRecords()
},
changeAllEvent() {
this.$refs.xTable.setAllCheckboxRow(this.isAllChecked)
this.selectRecords = this.$refs.xTable.getCheckboxRecords()
},
sumNum(list, field) {
let count = 0
list.forEach((item) => {
count += Number(item[field])
})
return count
},
footerMethod({ columns, data }) {
return [
columns.map((column) => {
if (['num'].includes(column.property)) {
return this.sumNum(data, column.property)
}
return null
}),
]
},
},
}
</script>
<style>
.first-col {
position: relative;
height: 20px;
}
.first-col:before {
content: '';
position: absolute;
left: -15px;
top: 10px;
width: 170px;
height: 1px;
transform: rotate(18deg);
background-color: #e8eaec;
}
.first-col .first-col-top {
position: absolute;
right: 4px;
top: -10px;
}
.first-col .first-col-bottom {
position: absolute;
left: 4px;
bottom: -10px;
}
.my-filter {
margin: 10px;
}
.page-left {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
z-index: 10;
}
</style>

View File

@@ -72,7 +72,7 @@
<panel-group @handleSetLineChartData="handleSetLineChartData" />
<!-- 在这里加入我们的统计 -->
<div class="md-main">
<p>数量统计</p>
<p>生产进度</p>
<div class="flex-box">
<div class="flex-item" v-for="(item, index) in items" :key="index" :style="item.style">
<p style="text-align: center">{{ item.name }}</p>