zhangafei 1 неделя назад
Родитель
Сommit
0701c5c101

+ 44 - 8
src/views/billet/operator/components/heatList.vue

@@ -38,8 +38,8 @@
       <!--引用表格-->
       <BasicTable @register="registerTable">
         <!--操作栏-->
-        <template #action="{ record }">
-          <TableAction class="flex flex-col" style="gap: 4px" :actions="getTableAction(record)" />
+        <template #action="{ record, index }">
+          <TableAction class="flex flex-col" style="gap: 4px; min-height: 88px" :actions="getTableAction(record, index)" />
         </template>
         <!--字段回显插槽-->
         <!-- <template v-slot:bodyCell="{ column, record, index, text }"> </template> -->
@@ -48,7 +48,7 @@
     <BasicTable @register="registerExtraTable" v-if="otherShiftTableData.length > 0" :dataSource="otherShiftTableData">
       <!--操作栏-->
       <template #action="{ record }">
-        <TableAction class="flex flex-col" style="gap: 4px" :actions="getTableAction(record)" />
+        <TableAction class="flex flex-col" style="gap: 4px; min-height: 88px" :actions="getTableAction(record)" />
       </template>
       <!--字段回显插槽-->
       <!-- <template v-slot:bodyCell="{ column, record, index, text }"> </template> -->
@@ -139,6 +139,8 @@
       </div>
     </a-modal>
   </div>
+  <!-- 打印 -->
+  <rollLinePrint @register="registerPrintModal" />
 </template>
 
 <script lang="ts" name="billetLiftingBill" setup>
@@ -156,9 +158,13 @@
   } from '../operator.api';
   import { h, onMounted, onUnmounted, ref, watch } from 'vue';
   import { mapTableTotalSummary } from '/@/utils/common/compUtils';
-  import { useTimeoutFn } from '/@/hooks/core/useTimeout';
   import { useMessage } from '/@/hooks/web/useMessage';
   import JSearchSelect from '/@/components/Form/src/jeecg/components/JSearchSelect.vue';
+  import { useModal } from '/@/components/Modal';
+  import rollLinePrint from './rollLinePrint.vue';
+
+  // 注册打印modal
+  const [registerPrintModal, { openModal: openPrintModal }] = useModal();
 
   const { createMessage, createConfirm } = useMessage();
 
@@ -205,6 +211,7 @@
   });
 
   const otherShiftTableData = ref<any[]>([]);
+  const currentShiftTableData = ref<any[]>([]);
 
   // 流修改定尺
   const openSizeModal = ref(false); // 打开定尺modal
@@ -479,6 +486,8 @@
           };
         });
 
+        currentShiftTableData.value = cunShift;
+
         return cunShift.map((item, index) => {
           return {
             ...item,
@@ -595,9 +604,26 @@
 
   const [registerExtraTable] = extraTableContext;
 
-  const { start, stop } = useTimeoutFn(() => {
+  const useTimeoutFn = (fn: TimerHandler, delay: number) => {
+    let timer: number | undefined = undefined;
+
+    const start = () => {
+      stop();
+      timer = setTimeout(fn, delay);
+    };
+
+    const stop = () => {
+      if (timer) {
+        clearTimeout(timer);
+      }
+    };
+
+    return { start, stop };
+  };
+
+  const { start, stop } = useTimeoutFn(async () => {
     if (!props.changeShiftId) {
-      reload();
+      await reload();
       start();
     }
   }, 10000);
@@ -774,8 +800,17 @@
   /**
    * 操作栏
    */
-  function getTableAction(record): ActionItem[] | undefined {
-    if (!record.operateStatus) return undefined;
+  function getTableAction(record, index?: number): ActionItem[] | undefined {
+    const printAction: ActionItem[] = [
+      {
+        label: '打印',
+        type: 'primary',
+        onClick: () => {
+          openPrintModal(true, { record, list: currentShiftTableData.value, index });
+        },
+      },
+    ];
+    if (!record.operateStatus) return printAction;
 
     const chargeInfo = props.carRef && props.carRef.getCurrentCar ? props.carRef.getCurrentCar() : {};
     return [
@@ -796,6 +831,7 @@
           doStack(record);
         },
       },
+      ...printAction,
     ];
   }
 

+ 176 - 0
src/views/billet/operator/components/rollLinePrint.vue

@@ -0,0 +1,176 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="registerModal"
+    destroyOnClose
+    title="钢坯热送传递单打印"
+    :height="400"
+    :width="800"
+    ok-text="打印"
+    @ok="onPrint"
+  >
+    <section
+      ref="print"
+      style="padding: 10px 10px 0; position: relative; border: 1px solid #c5c5c5; background: #d4ffe6; width: 98%"
+      id="printContent"
+    >
+      <div class="ticket next-ticket">
+        <div style="text-align: center">
+          <p style="font-size: 24px; font-weight: 800; display: inline-block; border-bottom: 2px solid #000; margin-bottom: 16px; line-height: 30px">
+            钢坯热送单炉支数传递单
+          </p>
+        </div>
+        <div class="flex" style="line-height: 24px">
+          <div class="flex-1">序号:{{ rollOneInfo.seriesIndex }}</div>
+          <div class="flex-1" style="text-align: center; font-size: 13px">日期:{{ dayjs(info.createTime).format('MM. DD') }}</div>
+          <div class="flex-1" style="text-align: right; font-size: 12px"> 班次:{{ info.shiftGroupTxt }} </div>
+        </div>
+        <a-descriptions style="margin-top: 6px; margin-bottom: 4px" layout="vertical" bordered :column="8" size="small">
+          <a-descriptions-item style="border: 1px solid #bfbfbf; font-size: 12px; padding: 4px; text-align: center; height: 36px" label="起止时间">
+            <div style="min-height: 80px; display: flex; align-items: center; justify-content: center"></div>
+          </a-descriptions-item>
+          <a-descriptions-item style="border: 1px solid #bfbfbf; font-size: 12px; padding: 4px; text-align: center; height: 36px" label="炉号">
+            {{ info.heatNo }}
+          </a-descriptions-item>
+          <a-descriptions-item style="border: 1px solid #bfbfbf; font-size: 12px; padding: 4px; text-align: center; height: 36px" label="钢种">
+            <component :is="renderDictTag(info.brandNum, 'billet_spec')" />
+          </a-descriptions-item>
+          <a-descriptions-item style="border: 1px solid #bfbfbf; font-size: 12px; padding: 4px; text-align: center; height: 36px" label="规格尺寸">
+            <a-select v-model:value="sizeValue" :options="sizeOptions" @change="getStartEnd" style="width: 100%" :bordered="false"></a-select>
+          </a-descriptions-item>
+          <a-descriptions-item style="border: 1px solid #bfbfbf; font-size: 12px; padding: 4px; text-align: center; height: 36px" label="合格支数">
+            {{ sizeValue && rollOneInfo.size[sizeValue] ? rollOneInfo.size[sizeValue] : '' }}
+          </a-descriptions-item>
+          <a-descriptions-item style="border: 1px solid #bfbfbf; font-size: 12px; padding: 4px; text-align: center; height: 36px" label="本炉起始根">
+            {{ rollOneInfo.startNum }}
+          </a-descriptions-item>
+          <a-descriptions-item style="border: 1px solid #bfbfbf; font-size: 12px; padding: 4px; text-align: center; height: 36px" label="本炉终止根">
+            {{ rollOneInfo.endNum }}
+          </a-descriptions-item>
+        </a-descriptions>
+        <a-descriptions style="padding: 0 30px" size="small" :column="4">
+          <a-descriptions-item label="推钢工"> </a-descriptions-item>
+          <a-descriptions-item label="轧钢"> </a-descriptions-item>
+          <a-descriptions-item label="钢转站"> </a-descriptions-item>
+          <a-descriptions-item label="质检站"> </a-descriptions-item>
+        </a-descriptions>
+      </div>
+    </section>
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import dayjs from 'dayjs';
+  import { render } from '/@/utils/common/renderUtils';
+  import { printJS } from '/@/hooks/web/usePrintJS';
+  import { getDictValue } from '../../Dashboard/dashboard.api';
+
+  const props = defineProps({});
+
+  const info = ref<any>({});
+  const rollOneInfo = ref<any>({
+    count: 0,
+    weight: 0,
+    size: {},
+    startNum: 0,
+    endNum: 0,
+    seriesIndex: 1,
+  });
+
+  const sizeOptions = ref<any[]>([]);
+  const sizeValue = ref<string>('');
+  const curIndex = ref<number>(0);
+  const curList = ref<any[]>([]);
+
+  //表单赋值
+  const [registerModal, { changeLoading, changeOkLoading }] = useModalInner(async (data) => {
+    const { record, list, index } = data;
+    if (record) {
+      info.value = { ...record, shiftGroupTxt: getDictValue('lg_bz', record.shiftGroup) };
+      curIndex.value = index;
+      curList.value = list;
+      // 计算棒一支数
+      const { directRolling } = record;
+      if (directRolling) {
+        const obj = JSON.parse(directRolling);
+        rollOneInfo.value = {
+          count: obj.directRollingTotalCount,
+          weight: obj.directRollingTotalWeight,
+          size: obj.lengthGroupCount,
+          startNum: 0,
+          endNum: 0,
+        };
+
+        sizeOptions.value = (Object.keys(obj.lengthGroupCount || {}) as Array<string>).map((ele) => {
+          return {
+            label: Number(ele) / 1000 + 'm',
+            value: ele,
+          };
+        });
+        sizeValue.value = sizeOptions.value.length ? sizeOptions.value[0].value : '';
+
+        // 计算起始根
+        getStartEnd();
+      }
+    }
+  });
+
+  // 计算起始根
+  const getStartEnd = () => {
+    let startNum = 0;
+    let seriesIndex = 1;
+    try {
+      curList.value.forEach((ele, index) => {
+        if (index > curIndex.value && ele.directRolling) {
+          const obj = JSON.parse(ele.directRolling);
+          if (obj.lengthGroupCount && obj.lengthGroupCount[sizeValue.value]) {
+            startNum += Number(obj.lengthGroupCount[sizeValue.value]);
+            seriesIndex += 1;
+          }
+        }
+      });
+      rollOneInfo.value.startNum = startNum;
+      rollOneInfo.value.endNum = startNum + Number(rollOneInfo.value.size[sizeValue.value]);
+      rollOneInfo.value.seriesIndex = seriesIndex;
+    } catch (error) {}
+  };
+
+  // 渲染字典标签
+  const renderDictTag = (value: string, dictCode: string) => {
+    return render.renderDict(value, dictCode);
+  };
+
+  function onPrint() {
+    changeOkLoading(true);
+    printJS({
+      printable: '#printContent',
+      type: 'html',
+    });
+    setTimeout(() => {
+      changeOkLoading(false);
+    }, 1000);
+  }
+</script>
+<style scoped lang="less">
+  .ticket {
+    :deep(.ant-select-arrow) {
+      display: none;
+    }
+  }
+</style>
+<style lang="less">
+  @media print {
+    header,
+    footer,
+    .noprint {
+      display: none;
+    }
+
+    @page :first {
+      margin-top: 10px; /* 第一页的页眉距离顶部为0 */
+      margin-bottom: 0; /* 第一页的页脚距离底部为0 */
+    }
+  }
+</style>

+ 3 - 5
src/views/billet/shippingBill/index.vue

@@ -166,7 +166,7 @@
   import JSearchSelect from '/@/components/Form/src/jeecg/components/JSearchSelect.vue';
   import { useListPage } from '/@/hooks/system/useListPage';
   import { columns, initDictConfig, queryParam, searchFormSchema, carPosition6, carPosition5 } from './shippingBill.data';
-  import { deleteOne, batchDelete, edit } from './shippingBill.api';
+  import { deleteOne, batchDelete, edit, destinationSwitch } from './shippingBill.api';
   import { defHttp } from '/@/utils/http/axios';
   import { onMounted, ref, reactive, h } from 'vue';
   // import hotDeliveryModal from './components/hotDeliveryModal.vue';
@@ -515,12 +515,10 @@
       okText: '确认',
       cancelText: '取消',
       onOk: () => {
-        return edit({
-          id: record.id,
+        return destinationSwitch({
+          ...record,
           typeConfigId: v,
           destination: destinationInfo.label,
-          btype: record.btype,
-          licensePlate: record.licensePlate,
         }).then(() => {
           handleSuccess();
         });

+ 7 - 0
src/views/billet/shippingBill/shippingBill.api.ts

@@ -39,6 +39,8 @@ enum Api {
   removeBillets = '/storageBill/deleteByAssemblyNumber',
   // 发车
   startCar = '/storageBill/startCar',
+  // 棒线切换目的地
+  destinationSwitch = '/storageBill/destinationSwitch',
 }
 
 /**
@@ -128,3 +130,8 @@ export const edit = (params) => {
 export const startCar = (params) => {
   return defHttp.post({ url: Api.startCar, params }, { joinParamsToUrl: true });
 };
+
+// 棒线切换目的地
+export const destinationSwitch = (params) => {
+  return defHttp.put({ url: Api.destinationSwitch, params }, { joinParamsToUrl: true });
+};

+ 6 - 0
src/views/billet/shippingBill/shippingBill.data.ts

@@ -100,6 +100,12 @@ export const columns: BasicColumn[] = computed(() => [
     dataIndex: 'positionNum',
     // fixed: true,
   },
+  {
+    title: '炉号',
+    align: 'center',
+    width: 80,
+    dataIndex: 'heatNo',
+  },
   {
     title: '组坯号',
     align: 'center',

+ 541 - 0
src/views/billet/storageAndTransportation/index copy.vue

@@ -0,0 +1,541 @@
+<template>
+  <div class="storageAndTransportation flex flex-col">
+    <div class="search-wrapper">
+      <BasicForm class="search-form" @register="registerForm" @submit="handleSubmit" @reset="handleSubmit">
+        <template #ccmNo="{ model, field }">
+          <segmented-select
+            v-model:value="model[field]"
+            @change="
+              () => {
+                handleSubmit();
+              }
+            "
+            dict="lg_zj"
+          />
+        </template>
+        <template #advanceBefore>
+          <a-button type="primary" preIcon="ant-design:export-outlined" style="margin-right: 10px" @click="showExportModal"> 导出 </a-button>
+        </template>
+      </BasicForm>
+    </div>
+    <a-list item-layout="vertical" size="large" :pagination="pagination" :loading="loading" :data-source="listData">
+      <template #renderItem="{ item, index }">
+        <a-list-item key="item.heatNo">
+          <a-list-item-meta>
+            <template #title>
+              <span>炉号:{{ item.heatNo }}</span>
+              <span style="padding: 0 20px"> 总支数:{{ item.heatNoAmount }} </span>
+              <span> 总重量:{{ item.heatNoWeight }} t </span>
+              <span style="padding: 0 20px"> 开始时间:{{ item.createTime }} </span>
+            </template>
+            <template #avatar>
+              <a-tag color="#108ee9">{{ (page - 1) * pageSize + index + 1 }}</a-tag>
+            </template>
+          </a-list-item-meta>
+          <div class="flex heat-wrapper">
+            <div class="items-content flex-1" :style="{ width: 100 / item.content.length + '%' }" v-for="pval in item.content">
+              <a-card>
+                <template #title>
+                  <div class="customer-title flex">
+                    <img src="/@/assets/images/send.png" v-if="pval.type === 'line'" alt="" class="heat-img" />
+                    <img src="/@/assets/images/charge.png" v-if="pval.type === 'car'" alt="" class="heat-img" />
+                    <div class="heat-title">
+                      {{ pval.title }} 【{{ pval.type == 'car' ? pval.content['rollChargeDetails'].length : pval.content['rollSendDetails'].length }}
+                      条记录】
+                    </div>
+                  </div>
+                </template>
+                <template v-if="pval.type == 'car'">
+                  <div class="ticket next-ticket" v-for="(sval, i) in pval.content['rollChargeDetails']">
+                    <a-descriptions size="small">
+                      <a-descriptions-item label="库名"> {{ pval.title }} </a-descriptions-item>
+                      <a-descriptions-item> {{ dayjs(sval.createTime).format('YYYY 年 MM 月 DD 日 HH 时 mm 分') }} </a-descriptions-item>
+                      <a-descriptions-item>
+                        <div style="width: 100%; text-align: right">
+                          <a-tag v-if="sval.btype == 1">冷装</a-tag>
+                          <a-tag color="#f50" v-else>热装</a-tag>
+                        </div>
+                      </a-descriptions-item>
+                    </a-descriptions>
+                    <a-descriptions class="ticket-content" layout="vertical" bordered :column="8" size="small">
+                      <a-descriptions-item label="序号">{{ sval.carNum }}</a-descriptions-item>
+                      <a-descriptions-item label="炉号">{{ sval.heatNo }}</a-descriptions-item>
+                      <a-descriptions-item label="车号">{{ sval.licensePlate }}</a-descriptions-item>
+                      <a-descriptions-item label="类型"><component :is="renderDictTag(sval.btype, 'lg_btype')" /></a-descriptions-item>
+                      <a-descriptions-item label="定尺">{{ sval.size }}</a-descriptions-item>
+                      <a-descriptions-item label="牌号"><component :is="renderDictTag(sval.brandNum, 'billet_spec')" /></a-descriptions-item>
+                      <a-descriptions-item label="支数">{{ sval.amount }}</a-descriptions-item>
+                      <a-descriptions-item label="重量">{{ sval.weight }}</a-descriptions-item>
+                    </a-descriptions>
+                  </div>
+                </template>
+                <a-table
+                  v-else
+                  class="heat-s-table"
+                  size="small"
+                  :scroll="{ y: 300 }"
+                  :columns="columnLine"
+                  :pagination="false"
+                  :data-source="pval.content['rollSendDetails']"
+                />
+              </a-card>
+            </div>
+          </div>
+        </a-list-item>
+      </template>
+    </a-list>
+  </div>
+  <a-modal v-model:open="openExportModal" title="导出" centered width="700px" :footer="null">
+    <a-spin :spinning="spinning">
+      <div class="export-btn-wrapper">
+        <a-button type="primary" preIcon="ant-design:export-outlined" style="margin-right: 10px" @click="onExportXls"> 导出</a-button>
+        <a-divider orientation="left">按班次导出:</a-divider>
+        <div>
+          <div> 当前日期:<a-date-picker v-model:value="shiftDate" @change="(date) => getShiftInfo(3, date)" /></div>
+          <div class="shift-performance-tags flex-1">
+            <a-tag :color="shiftColor[index]" @click="exportShiftXls(item)" v-for="(item, index) in shiftPerformanceColumns">
+              {{ item.createTime ? item.createTime.substring(5, 16) : '' }} ~
+              {{ item.changeShiftTime ? item.changeShiftTime.substring(5, 16) : '' }}
+              【{{ getTeamShift(item.shift, item.shiftGroup) }}】
+            </a-tag>
+          </div>
+        </div>
+      </div>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script lang="ts" name="storageAndTransportation" setup>
+  import { onMounted, ref } from 'vue';
+  //引入依赖
+  import { useForm, BasicForm, FormSchema } from '/@/components/Form';
+  import { getStorageCenterInvoicingInfo, exportExcel } from './storageAndTransportation.api';
+  import SegmentedSelect from '/@/components/SegmentedSelect/index.vue';
+  import { isArray } from '/@/utils/is';
+  import { render } from '/@/utils/common/renderUtils';
+  import { useMethods } from '/@/hooks/system/useMethods';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import dayjs, { Dayjs } from 'dayjs';
+  import { list as ShiftPerformanceList } from '../ShiftPerformance/ShiftPerformance.api';
+  import { getTeamShift } from '../Dashboard/dashboard.api';
+
+  const { createMessage } = useMessage();
+  //导入导出方法
+  const { handleExportXlsx } = useMethods();
+
+  // 渲染字典标签
+  const renderDictTag = (value: string, dictCode: string) => {
+    return render.renderDict(value, dictCode);
+  };
+
+  //自定义表单字段
+  const formSchemas: FormSchema[] = [
+    {
+      field: 'ccmNo',
+      label: '铸机',
+      component: 'Input',
+      defaultValue: '5',
+      componentProps: {
+        dictCode: 'lg_zj',
+      },
+      colProps: { span: 6 },
+      slot: 'ccmNo',
+    },
+    {
+      label: '炉号',
+      field: 'heatsCode',
+      component: 'Input',
+    },
+    {
+      label: '班组',
+      field: 'shiftGroup',
+      component: 'JDictSelectTag',
+      componentProps: {
+        dictCode: 'lg_bz',
+      },
+    },
+    {
+      label: '班别',
+      field: 'shift',
+      component: 'JDictSelectTag',
+      componentProps: {
+        dictCode: 'lg_bb',
+      },
+    },
+    // {
+    //   label: '炉次日期',
+    //   field: 'createTime',
+    //   component: 'RangePicker',
+    //   componentProps: {
+    //     valueFormat: 'YYYY-MM-DD',
+    //   },
+    // },
+    {
+      label: '装运日期',
+      field: 'storageTime',
+      component: 'RangePicker',
+      componentProps: {
+        valueFormat: 'YYYY-MM-DD',
+      },
+    },
+  ];
+  /**
+   * BasicForm绑定注册;
+   */
+  const [registerForm, { getFieldsValue }] = useForm({
+    //注册表单列
+    schemas: formSchemas,
+    //是否显示展开收起按钮,默认false
+    showAdvancedButton: true,
+    //超过指定行数折叠,默认3行
+    autoAdvancedCol: 3,
+    //折叠时默认显示行数,默认1行
+    alwaysShowLines: 3,
+    //将表单内时间区域的值映射成 2个字段, 'YYYY-MM-DD'日期格式化
+    fieldMapToTime: [
+      ['createTime', ['createTime_begin', 'createTime_end'], 'YYYY-MM-DD'],
+      ['storageTime', ['storageTime_begin', 'storageTime_end'], 'YYYY-MM-DD'],
+    ],
+    //每列占比,默认一行为24
+    baseColProps: { span: 6 },
+  });
+
+  /**
+   * 点击提交按钮的value值
+   * @param values
+   */
+  function handleSubmit() {
+    console.log(getFieldsValue());
+    page.value = 1;
+    getList();
+  }
+
+  const listData = ref<Record<string, any>>([]);
+  const loading = ref(false);
+  const columnLine = [
+    { title: '日期', dataIndex: 'createTime' },
+    { title: '定尺', dataIndex: 'size' },
+    { title: '规格', dataIndex: 'spec' },
+    { title: '支数', dataIndex: 'amount' },
+    { title: '重量/t', dataIndex: 'weight' },
+  ];
+
+  // 分页请求
+  const pageSize = ref(10);
+  const page = ref(1);
+
+  const pagination = ref({
+    onChange: (p: number, ps: number) => {
+      page.value = p;
+      pageSize.value = ps;
+      getList();
+    },
+    pageSize: 10,
+    current: 1,
+    total: 0,
+  });
+
+  const getList = async () => {
+    try {
+      loading.value = true;
+      const values = getFieldsValue();
+
+      if (!values.ccmNo) {
+        values.ccmNo = '5';
+      }
+      console.log('values', values);
+
+      const params = Object.assign(
+        {},
+        {
+          pageNo: page.value,
+          pageSize: pageSize.value,
+        },
+        values,
+        {
+          createTimeBegin: values.createTime_begin,
+          createTimeEnd: values.createTime_end,
+          storageTimeBegin: values.storageTime_begin,
+          storageTimeEnd: values.storageTime_end,
+        }
+      );
+
+      const res = await getStorageCenterInvoicingInfo(params);
+
+      const { records, current, total, size } = res;
+
+      pagination.value = {
+        ...pagination.value,
+        current,
+        total,
+        pageSize: size,
+      };
+
+      if (records && isArray(records)) {
+        const list = records.map((item: any) => {
+          let content: any[] = [];
+          const {
+            heatNoDetails,
+            storageCenterHeatNoInvoicing: { rollClubOneDetails, rollClubTwoDetails, rollClubThreeDetails, rollHeightDetails, rollOutShippDetails },
+          } = item;
+
+          if (rollClubOneDetails) {
+            content.push({
+              title: '棒一',
+              type: 'line',
+              content: rollClubOneDetails,
+            });
+          }
+
+          if (rollClubTwoDetails) {
+            content.push({
+              title: '棒二',
+              type: 'car',
+              content: rollClubTwoDetails,
+            });
+          }
+
+          if (rollClubThreeDetails) {
+            content.push({
+              title: '棒三',
+              type: 'car',
+              content: rollClubThreeDetails,
+            });
+          }
+
+          if (rollOutShippDetails) {
+            content.push({
+              title: '上若',
+              type: 'car',
+              content: rollOutShippDetails,
+            });
+          }
+
+          if (rollHeightDetails) {
+            content.push({
+              title: '高线',
+              type: 'line',
+              content: rollHeightDetails,
+            });
+          }
+
+          return {
+            ...heatNoDetails[0],
+            content,
+          };
+        });
+
+        listData.value = list;
+      }
+
+      loading.value = false;
+    } catch (error) {
+      console.error(error);
+      loading.value = false;
+    }
+  };
+
+  // 导出
+  const openExportModal = ref(false);
+  const spinning = ref(false);
+  const shiftDate = ref<Dayjs>(dayjs());
+  const shiftColor = ['#f50', '#2db7f5', '#87d068'];
+  const shiftPerformanceColumns = ref<any>([]);
+  const showExportModal = () => {
+    openExportModal.value = true;
+    getShiftInfo(3, shiftDate.value);
+  };
+  const onExportXls = () => {
+    const values = getFieldsValue();
+
+    if (!values.storageTime_begin) {
+      createMessage.error('请选择装运日期');
+      return;
+    }
+
+    if (!values.ccmNo) {
+      values.ccmNo = '5';
+    }
+
+    const params = Object.assign(
+      {
+        pageNo: page.value,
+        pageSize: pageSize.value,
+      },
+      values
+    );
+
+    let queryParams = Object.keys(params)
+      .filter((v) => params[v])
+      .map((key) => {
+        if (key === 'createTime_begin') {
+          return `createTimeBegin=${params[key]}`;
+        }
+        if (key === 'createTime_end') {
+          return `createTimeEnd=${params[key]}`;
+        }
+        if (key === 'storageTime_begin') {
+          return `storageTimeBegin=${params[key]}`;
+        }
+        if (key === 'storageTime_end') {
+          return `storageTimeEnd=${params[key]}`;
+        }
+        return `${key}=${params[key]}`;
+      });
+
+    return handleExportXlsx('储运中心', exportExcel() + (queryParams.length > 0 ? '?' + queryParams.join('&') : ''));
+  };
+
+  const exportShiftXls = (item: any) => {
+    const values = getFieldsValue();
+    const title = shiftDate.value.format('YYYY-MM-DD') + '  ' + getTeamShift(item.shift, item.shiftGroup);
+    const params = ['ccmNo=' + (values.ccmNo || '5'), 'changeShiftId=' + (item.id || 0)];
+    return handleExportXlsx(title, exportExcel() + '?' + params.join('&'));
+  };
+
+  const getShiftInfo = async (pageSize: number = 3, date?: Dayjs) => {
+    try {
+      spinning.value = true;
+      const values = getFieldsValue();
+      const machine = values.ccmNo || '5';
+      const createTime_begin = date ? date.subtract(1, 'day').format('YYYY-MM-DD 23:55:00') : undefined;
+      const createTime_end = date ? date.add(1, 'day').format('YYYY-MM-DD 00:05:00') : undefined;
+      const res = await ShiftPerformanceList({
+        ccmNo: machine,
+        pageNo: 1,
+        pageSize: pageSize,
+        column: 'createTime',
+        order: 'asc',
+        ...(date ? { createTime_begin, createTime_end } : {}),
+      });
+      const { records } = res;
+
+      shiftPerformanceColumns.value = (records || [])
+        .filter((item) => item.createTime && item.changeShiftTime) // 过滤掉 createTime 为空的数据
+        .sort((a, b) => {
+          const dateA = new Date(a.createTime).getTime(); // 转换为时间戳
+          const dateB = new Date(b.createTime).getTime(); // 转换为时间戳
+          if (isNaN(dateA) || isNaN(dateB)) {
+            console.warn('Invalid date detected:', a.createTime, b.createTime);
+            return 0; // 如果日期无效,视为相等
+          }
+          return dateA - dateB; // 按时间戳排序
+        });
+    } catch (error) {
+      console.error(error);
+    } finally {
+      spinning.value = false;
+    }
+  };
+
+  onMounted(() => {
+    getList();
+  });
+</script>
+
+<style lang="less" scoped>
+  .storageAndTransportation {
+    margin: 10px;
+
+    .search-wrapper {
+      margin-bottom: 10px;
+      background-color: #fff;
+      padding-top: 30px;
+
+      .search-form {
+        :deep(.btnArea) {
+          .ant-form-item-row {
+            width: 100%;
+            justify-content: flex-end;
+          }
+        }
+      }
+    }
+
+    .ant-list {
+      background-color: #fff;
+      padding: 10px;
+    }
+
+    .heat-wrapper {
+      gap: 10px;
+    }
+
+    .items-content {
+      flex-shrink: 0;
+    }
+
+    .customer-title {
+      line-height: 32px;
+    }
+
+    .heat-img {
+      width: 32px;
+      height: 32px;
+    }
+
+    .heat-title {
+      margin-left: 8px;
+      margin-right: 60px;
+      font-weight: 400;
+      font-size: 14px;
+      color: #000000;
+    }
+
+    .ant-card {
+      width: 100%;
+      height: 100%;
+      overflow: hidden;
+
+      :deep(.ant-card-body) {
+        padding: 10px;
+        display: flex;
+        overflow: auto;
+
+        &::after,
+        &::before {
+          display: none;
+        }
+      }
+    }
+
+    /* 基础票据样式 */
+    .ticket {
+      width: 672px;
+      background: #fff;
+      border-radius: 8px;
+      box-shadow: 5px 0px 6px 2px rgba(0, 0, 0, 0.25);
+      padding: 10px;
+      position: relative;
+      border-left: 4px solid #ddd;
+      transition: transform 0.2s;
+      margin-right: 10px;
+      flex-shrink: 0;
+
+      &:last-child {
+        margin-right: 0;
+      }
+    }
+
+    /* 下一个票据的特殊样式 */
+    .next-ticket {
+      border-left-color: #0958d9; /* 左侧高亮蓝色边框 */
+      background: #f8f9fa; /* 浅灰色背景 */
+    }
+  }
+  .export-btn-wrapper {
+    padding: 20px;
+
+    .shift-performance-tags {
+      margin-top: 20px;
+
+      .ant-tag {
+        padding: 6px 6px;
+        position: relative;
+        cursor: pointer;
+        margin-bottom: 20px;
+        font-size: 14px;
+      }
+    }
+  }
+</style>

+ 2 - 1
src/views/billet/storageAndTransportation/index.vue

@@ -155,7 +155,8 @@
       field: 'storageTime',
       component: 'RangePicker',
       componentProps: {
-        valueFormat: 'YYYY-MM-DD',
+        showTime: true,
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
   ];