el-table合并表头、动态合并列、合并尾部合计

news2025/7/19 13:41:37

在有些情况下,我们会有合并表头、合并列、合并尾部合计的需求,这篇文章是为了记录,如何进行合并,方便日后翻阅。

效果图

el-table合并表头

image.png

el-table合并列(动态合并)

image.png

el-table合并尾部合计

image.png

el-table合并表头的实现

这个地方是用的两个表格实现的,即两个el-table,上方的全选、规格、购买时长等属于一个表,下面的阿里云以及数据属于另一个表,将两个表的列宽设为一致,即可实现。

// 主表
<template>
    <div class="pdlr20 h-100" v-if="state.goodsList.length > 0">
      <div class="sticky-top">
      <el-table class="shopping-cart-table" //最上面的表头,需要把表身隐藏
        style="width: 99.9%;"
        :header-row-style="{border: 'none'}"
        :header-cell-style="{border: 'none',height:'60px',fontSize: '14px',fontWeight: 600,color: '#333333',background:'#FFFFFF'}"
        :data="[]">
        <el-table-column width="32px" label="">
          <template #header>
            <el-checkbox
              v-model="state.checkAll"
              :disabled="state.goodsList.length === 0"
              :indeterminate="state.isIndeterminate">
            </el-checkbox>
          </template>
        </el-table-column>
        <el-table-column width="268px" label="全选"></el-table-column>
        <el-table-column width="180px" label="规格"></el-table-column>
        <el-table-column label="购买时长" align="center"></el-table-column>
        <el-table-column width="100px" align="center" label="单价"></el-table-column>
        <el-table-column width="150px" align="center" label="台数"></el-table-column>
        <el-table-column width="120px" align="center" label="小计"></el-table-column>
        <el-table-column width="190px" align="center" label="操作"></el-table-column>
      </el-table>
      </div>
      <div v-for="(item, index) of state.cloudProvider" :key="index"> // 表身中一个个的表
        <cloud-provider-merchant
          v-if="state.goodsClassify[item].length > 0" // 判断子表中的商品长度是否大于0
          :cloudProvider="item" //用于判断当前表是'ali', 'tencent', 'huawei', 'ct', 'baidu', 'jd', 'ks'中的哪一个
          :checkAll="state.checkAll"//是否全选
          :index="index"
          @selection-change="handleSelectionChange"> // 用于计算选中项的预付金额和按需金额
        </cloud-provider-merchant>
      </div>
    </div>
    <el-empty v-if="state.goodsList.length === 0" description="暂无云资源" :image="state.emptyImage" :image-size="240"></el-empty>
  <div class="c6 shopping-cart-footer pdl30r20 font-size-normal font-weight-400 d-flex align-items-center justify-content-between">
    <div class="d-flex align-items-center">
      <el-checkbox
        v-model="state.checkAll"
        :disabled="state.goodsList.length === 0"
        :indeterminate="state.isIndeterminate">全选
      </el-checkbox>
      <el-button
        class="ml50 font-size-normal font-weight-400 c6 pad0 op1"
        type="text"
        @click="deleteCheckedGoods">删除
      </el-button>
    </div>

    <div class="d-flex align-items-center">
      <div class="mr40 d-flex align-items-center" v-if="[].concat(...Object.values(state.checkedGoods)).length > 0">
        <div class="mr20">总计:</div>
        <div class="d-flex text-amount font-size-mini">
          <div class="mr30" v-if="state.reservedTotalPrice > 0">
            <span class="c3 mr6">预付:</span>
            <span class="text-amount">{{ state.reservedTotalPrice.toFixed(2) }}</span>
          </div>
          <div v-if="state.onDemandTotalPrice > 0">
            <span class="c3 mr6">按需:</span>
            <span class="text-amount">{{ state.onDemandTotalPrice.toFixed(2) }}/小时</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { useStore } from 'vuex'
import { reactive, onMounted, getCurrentInstance, watch } from 'vue'

import CloudProviderMerchant from './CloudProviderMerchant'

const store = useStore()
const { proxy } = getCurrentInstance()

const goodsClassifyInitData = {
  ali: [],
  tencent: [],
  huawei: [],
  ct: [],
  baidu: [],
  jd: [],
  ks: []
}

const state = reactive({
  checkAll: false,
  isIndeterminate: false,
  goodsList: [],
  goodsClassify: JSON.parse(JSON.stringify(goodsClassifyInitData)),
  cloudProvider: ['ali', 'tencent', 'huawei', 'ct', 'baidu', 'jd', 'ks'],
  reservedTotalPrice: 0,
  onDemandTotalPrice: 0,
  emptyImage: require('@assets/images/no-data.png'),
  shoppingCartLoading: false,
  checkedGoods: JSON.parse(JSON.stringify(goodsClassifyInitData))
})

onMounted(() => {
  getGoodsList()
  getTotalPrice()
})

watch(() => store.state.shoppingCartChange, () => {
  getGoodsList()
  getTotalPrice()
  getCheckAllStatus()
})

watch(state.checkedGoods, () => {
  getCheckAllStatus()
})

const getCheckAllStatus = () => {
  if (state.goodsList.length === 0) {
    state.checkAll = false
    state.isIndeterminate = false
    return
  }

  const checkedNum = Object.values(state.checkedGoods).map(item => item.length).reduce((pre, val) => {
    return pre + val
  }, 0)

  if (checkedNum === state.goodsList.length) {
    state.checkAll = true
    state.isIndeterminate = false
  } else if (checkedNum > 0 && checkedNum < state.goodsList.length) {
    state.isIndeterminate = true
  } else {
    state.checkAll = false
    state.isIndeterminate = false
  }
}

const getGoodsList = () => {
  const goodsClassify = {
    ali: [],
    tencent: [],
    huawei: [],
    baidu: [],
    ct: [],
    jd: [],
    ks: []
  }
  state.goodsList = JSON.parse(localStorage.getItem('goodsList')) || []
  state.goodsList.forEach(goods => {
    goodsClassify[goods.cloudProvider].push(goods)
  })
  state.goodsClassify = goodsClassify
}

const getTotalPrice = () => {
  const checkedGoods = [].concat(...Object.values(state.checkedGoods)) // Object.values()返回一个数组,其元素是在对象上找到的可枚举属性值。属性的顺序与通过手动循环对象的属性值所给出的顺序相同,此处是用于找到选中的商品
  const filteredList = state.goodsList.filter(goods => {
    return checkedGoods.find(goodsHash => { // 从商品列表中,筛选出选中的商品
      return goodsHash === goods.goodsHash
    })
  })
  state.reservedTotalPrice = formatFloat(filteredList.filter(item => { //选中商品计算预付金额
    return item.pricingType === 'reserved'
  }).reduce((pre, item) => {
    return pre + (item.goodsNum * item.price)
  }, 0), 2)

  state.onDemandTotalPrice = formatFloat(filteredList.filter(item => { //选中商品计算按需金额
    return item.pricingType === 'onDemand'
  }).reduce((pre, item) => {
    return pre + (item.goodsNum * item.price)
  }, 0), 2)
}

const formatFloat = (num, pos = 2) => {
  return parseFloat(num * (pos * 10), 10) / (pos * 10) // parseFloat() 函数可解析一个字符串,并返回一个浮点数。
}

const deleteCheckedGoods = () => { // 删除已选的厂家
  if ([].concat(...Object.values(state.checkedGoods)).length === 0) {
    proxy.$notify.error({
      title: '错误',
      message: '没有选中的云资源'
    })
    return
  }
  proxy.$confirm('确定要删除云资源吗?', '删除云资源').then(result => {
    if (result === 'confirm') {
      const checkedGoods = [].concat(...Object.values(state.checkedGoods))
      const filteredList = state.goodsList.filter(goods => {
        return !checkedGoods.find(goodsHash => {
          return goodsHash === goods.goodsHash
        })
      })
      state.checkedGoods = JSON.parse(JSON.stringify(goodsClassifyInitData))
      updateGoodsList(filteredList)
    }
  })
}

const updateGoodsList = (goodsList) => { // 删除已选商家时更新商品列表
  goodsList.forEach((item) => {
    item.input = ''
    item.detailsInput = ''
  })
  localStorage.setItem('goodsList', JSON.stringify(goodsList))
  state.goodsList = goodsList
  proxy.$store.commit('setShoppingCartChange')
}

const handleSelectionChange = (cloudProvider, val) => { // 子表调用这方法计算已选商品金额
  state.checkedGoods[cloudProvider] = val
  getTotalPrice()
}
</script>

<style scoped lang="scss">
@import "../../assets/styles/vendor/element-variables";

.shopping-cart-footer {
  width: 100%;
  height: 80px;
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 9999;
  background: $color-white;
}

::v-deep .el-input__inner {
    height: 32px !important;
    line-height: 32px !important;
    padding: 0;
    border: 1px solid #dcdfe6;
  }

::v-deep.el-input__inner:focus {
  background-color: #fff !important;
}
.sticky-top{
  position: sticky;
  top: 0;
  z-index: 99;
}
.shop-plan-btn{
  width: 124px;
  height: 34px;
  line-height: 34px;
  background: #4C66CE;
  border-radius: 17px;
  color: #FFFFFF;
  font-size: 14px;
}
</style>
// 子表
<template>
  <div class="mb10 goods-widget">
    <el-collapse-transition name="el-fade-in">
      <div class="goods-widget-body">
        <!-- 购物车表格内部内容 -->
        <el-table
          style="width: width: 99.9%;"
          :row-style="{height:'38px',fontSize: '12px',color: '#666666',fontWeight: 400}"
          :header-cell-style="handerMethod" //用于合并表头的方法
          row-key="goodsHash"
          :key="index"
          ref="goods-list-table"
          class="goods-widget-table"
          :class="{'goods-widget-body': !state.goodsDetailVisible}"
          :data="state.cloudProviderGoodsList"
          @selection-change="handleSelectionChange">
          <el-table-column width="32px" type="selection" :reserve-selection="true"/>
          <el-table-column width="268px">
            <template #header>
              <div class="d-flex align-items-center">
                <div class="d-flex align-items-center justify-content-between">
                  <div v-if="state.cloudProvider === 'ali'" class="text-amount d-flex align-items-center">
                    <svg-icon class="mr-2" data="@icon/ali_cloud_logo.svg"></svg-icon>
                    <span>阿里云</span>
                  </div>
                  <div v-if="state.cloudProvider === 'tencent'" class="text-primary-blue d-flex align-items-center">
                    <svg-icon class="mr-2" data="@icon/tencent_cloud_logo.svg"></svg-icon>
                    <span>腾讯云</span>
                  </div>
                  <div v-if="state.cloudProvider === 'huawei'" class="text-danger d-flex align-items-center">
                    <svg-icon class="mr-2" data="@icon/huawei_logo.svg"></svg-icon>
                    <span>华为云</span>
                  </div>
                  <div v-if="state.cloudProvider === 'ct'" class="text-ct d-flex align-items-center">
                    <svg-icon class="mr-2" data="@icon/tianyi_cloud_logo.svg"></svg-icon>
                    <span>天翼云</span>
                  </div>
                  <div v-if="state.cloudProvider === 'baidu'" class="d-flex align-items-center">
                    <el-image class="mr-2" :src="require('@assets/images/baidu_logo.png')" style="width: 16px;height: 16px;"/>
                    <span>百度云</span>
                  </div>
                  <div v-if="state.cloudProvider === 'jd'" class="text-ct d-flex align-items-center">
                    <svg-icon class="mr-2" data="@icon/jd_cloud_logo.svg"></svg-icon>
                    <span>京东云</span>
                  </div>
                  <div v-if="state.cloudProvider === 'ks'" class="text-ct d-flex align-items-center">
                    <svg-icon class="mr-2" data="@icon/ks_cloud_logo.svg"></svg-icon>
                    <span>金山云</span>
                  </div>
                  <div>(共 {{ goodsTotalNum }} 台)</div>
                </div>
              </div>
            </template>
            <template #default="scope">
              <el-row>
                {{ pricingTypeMap[scope.row.pricingType] }}{{ scope.row.cpu }}{{ scope.row.mem }}GiB,{{ scope.row.zoneName }}
              </el-row>
              <el-row>
                操作系统:{{ scope.row.systemImage }}
              </el-row>
              <el-row>
                流量带宽:{{ scope.row.netBrandWidth > 0 ?  `${scope.row.netBrandWidth}Mbps` : '--'}}
              </el-row>
            </template>
          </el-table-column>
          <el-table-column width="180px">
            <template #default="scope">
              <el-row>
                系统盘:{{ scope.row.systemDisk ? getSystemDiskDescription(scope.row.systemDisk) : '--'}}
              </el-row>
              <el-row>
                数据盘:
                <span v-if="scope.row.dataDisk.length === 0"> -- </span>
                <span v-else-if="scope.row.dataDisk.length === 1">{{ getDataDiskDescription(scope.row.dataDisk)[0] }}</span>
                <div v-else-if="scope.row.dataDisk.length > 1">
                  {{ getDataDiskSize(scope.row) }}
                  <el-popover
                    class="data-disk-popover"
                    effect="dark"
                    placement="right"
                    :width="90"
                    trigger="click"
                  >
                    <template #reference>
                      <el-button class="data-disk-btn" type="text">详情</el-button>
                    </template>
                    <div v-for="(item, index) of getDataDiskDescription(scope.row.dataDisk)" :key="index">{{ item }}</div>
                  </el-popover>
                </div>

              </el-row>
              <el-row class="data-disk-blank">-</el-row>
            </template>
          </el-table-column>
          <el-table-column align="center">
            <template #default="scope">
              <span class="mr-1">{{ scope.row.duration }}</span>
              <span class="" v-if="scope.row.durationUnit === 'Year'"></span>
              <span class="" v-else-if="scope.row.durationUnit === 'Month'">个月</span>
              <span class="" v-else>小时</span>
            </template>
          </el-table-column>
          <el-table-column width="100px" align="center">
            <template #default="scope">
              <div v-if="scope.row.price">
                <div v-if="scope.row.pricingType === 'onDemand'">
                  <span class="c3">{{ priceDataFormatter(scope.row.price) }}/小时</span>
                </div>
                <div v-else>
                  <span class="c3">{{ priceDataFormatter(scope.row.price) }}</span>
                </div>
              </div>
              <div v-else>--</div>
            </template>
          </el-table-column>
          <el-table-column width="150px" align="center">
            <template #default="scope">
              <el-tooltip content="可选范围 1 ~ 999" placement="top-start">
                <el-input-number
                  class="input-number-box c6"
                  v-model="scope.row.goodsNum"
                  :min="1"
                  :max="999"
                  style="width: 130px;border-radius: 4px;"
                  @change="goodsNumChange(scope.row)">
                </el-input-number>
              </el-tooltip>
            </template>
          </el-table-column>
          <el-table-column width="120px" align="center">
            <template #default="scope">
              <div class="text-amount">{{ getTotalPrice(scope.row) }}</div>
            </template>
          </el-table-column>
          <el-table-column width="190px" align="center">
            <template #header>
              <div class="d-flex justify-content-end align-items-center">
                <div class="d-flex mr20" v-if="reservedTotalPrice > 0">
                  <div class="mr4">预付:</div>
                  <div class="text-amount">{{ reservedTotalPrice }}</div>
                </div>
                <div class="d-flex ml28 mr20" v-if="onDemandTotalPrice > 0">
                  <div class="mr4">按需:</div>
                  <div class="text-amount">{{ onDemandTotalPrice }}/小时</div>
                </div>
                <el-tooltip content="展开/收起" placement="top" :enterable="false">
                  <el-button type="text" @click="goodsDetailVisibleToggle">
                    <svg-icon v-if="state.goodsDetailVisible" data="@icon/unfold.svg" style="width: 14px; height: 14px;"></svg-icon>
                    <svg-icon v-else data="@icon/unfold.svg" style="width: 14px; height: 14px;"></svg-icon>
                  </el-button>
                </el-tooltip>
              </div>
            </template>
            <template #default="scope">
              <el-button class="el-button-operate" type="primary" @click="buyNow(scope.row)">立即购买</el-button>
              <el-button class="el-button-del" type="info" @click="deleteGoods(scope.row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </el-collapse-transition>
  </div>
</template>

<script setup>
import { useStore } from 'vuex'
import { reactive, defineProps, defineEmits, getCurrentInstance, ref, onMounted, watch, computed } from 'vue'
const store = useStore()
const { proxy } = getCurrentInstance()
const emit = defineEmits(['selection-change'])

const props = defineProps({
  checkAll: Boolean,
  cloudProvider: String,
  index: Number
})

const state = reactive({
  cloudProvider: props.cloudProvider,
  goodsList: [],
  cloudProviderGoodsList: [],
  checkedGoodsItem: [],
  goodsDetailVisible: true,
  multipleSelection: []
})

const reservedTotalPrice = computed(() => {
  return getTotalPricingTypePrice('reserved')
})

const onDemandTotalPrice = computed(() => {
  return getTotalPricingTypePrice('onDemand')
})

const goodsTotalNum = computed(() => {
  return state.cloudProviderGoodsList.map(item => item.goodsNum).reduce((pre, val) => {
    return pre + val
  }, 0)
})

watch(() => store.state.shoppingCartChange, () => {
  getGoodsList()
})

watch(() => store.state.shoppingCartDeleteAction, () => {
  proxy.$refs['goods-list-table'].clearSelection()
})

watch(() => props.checkAll, (val) => {
  if (val) {
    checkAllAction()
  } else {
    clearChecked()
  }
})

onMounted(() => {
  getGoodsList()
})

const getTotalPricingTypePrice = (pricingType) => {
  return state.cloudProviderGoodsList.filter(item => {
    return item.pricingType === pricingType
  }).map(goods => {
    return Number(goods.price).floatMul(goods.goodsNum)
  }).reduce((pre, val) => { // reduce() 方法对数组中的每个元素执行一个由您提供的reduce函数(升序执行),将其结果汇总为单个返回值。reduce方法可做的事情特别多,就是循环遍历能做的,reduce都可以做,比如数组求和、数组求积、数组中元素出现的次数、数组去重等等。
    return pre.floatAdd(val) // 相加,计算单个商品小计的总额
  }, 0)
}

const pricingTypeMap = ref({
  reserved: '预付实例',
  onDemand: '按需实例'
})

const diskTypeMap = {
  standard: '标准性能',
  efficient: '高性能'
}

const deleteGoods = (goodsItem) => { //删除某项商品
  proxy.$confirm('确定要删除云资源吗?', '删除云资源').then(result => {
    if (result === 'confirm') {
      const index = state.goodsList.findIndex(item => {
        return item.goodsHash === goodsItem.goodsHash
      })

      state.goodsList.splice(index, 1)
      updateGoodsList()
      proxy.$message.success({
        message: '成功删除云资源'
      })
    }
  })
}

const getGoodsList = () => {
  state.goodsList = JSON.parse(localStorage.getItem('goodsList')) || []
  state.cloudProviderGoodsList = state.goodsList.filter(goods => {
    return goods.cloudProvider === props.cloudProvider
  })
}

const updateGoodsList = () => { //改变父组件中价格
  localStorage.setItem('goodsList', JSON.stringify(state.goodsList))
  proxy.$store.commit('setShoppingCartChange')
}

const goodsNumChange = (goodsItem) => { //操作台数时,父表中价格做相应计算
  state.goodsList.forEach(item => {
    if (item.goodsHash === goodsItem.goodsHash && item.goodsNum !== goodsItem.goodsNum) { //只对选中的商品价格做相应计算
      item.goodsNum = goodsItem.goodsNum
    }
  })

  updateGoodsList()
}

const getSystemDiskDescription = ({ type, size }) => {
  return `${ diskTypeMap[type] } | ${ size }GB`
}

const getDataDiskDescription = (dataDisks) => {
  return dataDisks?.map(item => {
    return `${ diskTypeMap[item.type] } | ${ item.size }GB`
  })
}

const getDataDiskSize = (dataDisks) => { //计算数据盘大小
  let size = 0
  dataDisks.dataDisk.map(item => {
    size += item.size
  })
  return `${size} G`
}

const priceDataFormatter = (price) => { //单价保留两位小数
  return Number(price).toFixed(2)
}

const getTotalPrice = (item) => { /单价保留两位小数,floatMul是防止精度丢失的问题
  return `${ Number(item.price).floatMul(item.goodsNum, 2) }${ (item.pricingType === 'reserved' ? '元' : '元/小时') }`
}

const handleSelectionChange = (val) => { // 点击选中/取消选中时调父表中的计算金额的方法
  state.multipleSelection = val
  emit('selection-change', props.cloudProvider, state.multipleSelection.map(item => item.goodsHash))
}

const goodsDetailVisibleToggle = () => { //展开和收起
  state.goodsDetailVisible = !state.goodsDetailVisible
  proxy.$nextTick(() => {
    proxy.$refs['goods-list-table'].doLayout() // 对 Table 进行重新布局。当 Table 或其祖先元素由隐藏切换为显示时,可能需要调用此方法
  })
}

const checkAllAction = () => { // 全选
  state.cloudProviderGoodsList.forEach(item => {
    proxy.$refs['goods-list-table'].toggleRowSelection(item, true) // 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中)
  })
}

const clearChecked = () => { // 用于多选表格,清空用户的选择
  proxy.$refs['goods-list-table'].clearSelection()
}

// 立即购买
const buyNow = (row) => {
}

const handerMethod = ({ row, column, rowIndex, columnIndex }) => { //合并表头
      if (row[0].level == 1) { // //这里有个非常坑的bug 必须是row[0]=0 row[1]=2才会生效
        row[4].colSpan = 0 // 表头索引为4、5、6时合并到7
        row[5].colSpan = 0
        row[6].colSpan = 0
        row[7].colSpan = 4
        if (columnIndex === 4 || columnIndex === 5 || columnIndex === 6) { // columnIndex 代表列号/列索引,隐藏
          return { display: 'none' }
        }
      }
}

</script>

<style scoped lang="scss">
@import "../../assets/styles/vendor/element-variables";
.el-table td.el-table__cell div {
  line-height: 32px;

  .data-disk-btn {
    color: #409EFF;
  }

  .data-disk-blank {
    color: #FFF;
  }
}

.text-ct{
  color: $color-tianyi;
}

.el-checkbox {
  --el-checkbox-checked-background-color: #fd852d;
  --el-checkbox-checked-input-border-color: #fd852d;
  --el-checkbox-input-border-color-hover: #fd852d;
}

.goods-widget {
  background: #fff;
  border-radius: 4px;

  &-footer, &-body {
    box-sizing: border-box;
    border-top: 1px solid var(--el-border-color-base);
  }

  ::v-deep(.el-form-item) {
    margin-bottom: 8px;
  }
}
::v-deep .input-number-box{
  height: 32px;
  border: 1px solid #EAEBEF;
  .el-input-number__increase, .el-input-number__decrease{
    font-size: 12px;
    font-weight: 400;
  }
  .el-input__inner{
    height: 28px !important;
    line-height: 28px !important;
    font-size: 12px;
    border: none;
  }
}
::v-deep.el-button-operate{
  width: 80px;
  height: 32px;
  line-height: 32px;
  background-color: #EBEFFB;
  color: #4C66CE;
  font-size: 12px;
  border-radius: 4px;
  border: none;
  &:hover{
    background-color: #4C66CE !important;
    color: #FFFFFF;
  }
}
::v-deep.el-button-del{
  width: 52px;
  height: 32px;
  line-height: 32px;
  background-color: #F2F2F4;
  color: #666666;
  font-size: 12px;
  border-radius: 4px;
  border: none;
  &:hover{
    background-color: #F2F2F4 !important;
    color: #666666;
  }
}
</style>

el-table合并列(动态合并)的实现

<template>
     <el-table
              class="procurement-plan-table procurement-plan-table-noborder"
              :row-style="{height: '48px',fontSize: '12px',color: '#666666'}"
              :header-cell-style="{height: '48px',background: '#F6F6F8',fontSize: '14px',color: '#333333',fontWeight: 400}"
              :cell-class-name="cellClassName" //因为设计稿上是只有首位才需要左右边框,合并的单元格需要去除右侧的边框
              :data="state.tempGoodList"
              :span-method="objectSpanMethod" //合并单元格
              ref="table"
              border
              :summary-method="getSummaries"
              show-summary
              :style="{borderColor: '#E6E6E6'}"
            >
              <el-table-column label="云厂商" width="120px" align="center" prop="cloudProvider">
                <template #default="scope">
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'ali'">
                    <div class="text-center">阿里云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'tencent'">
                    <div class="text-center">腾讯云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'huawei'">
                    <div class="text-center">华为云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'ct'">
                    <div class="text-center">天翼云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'baidu'">
                    <div class="text-center">百度云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'jd'">
                    <div class="text-center">京东云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'ks'">
                    <div class="text-center">金山云</div>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="类型" width="120px" align="center">
                  云服务器
              </el-table-column>
              <el-table-column label="付费方式" width="110px" align="center">
                <template #default="scope">
                  {{ scope.row.pricingType === 'reserved' ? '预付' :  '按需'}}
                </template>
              </el-table-column>
              <el-table-column label="数量" width="110px" align="center">
                <template #default="scope">
                  {{ scope.row.goodsNum }}
                </template>
              </el-table-column>
              <el-table-column label="小计" width="130px">
                <template #default="scope">
                  <div>
                    <span>{{  scope.row.price * scope.row.goodsNum }}</span>
                    <span v-if="scope.row.pricingType === 'reserved'"></span>
                    <span v-else>/小时</span>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="备注" align="center">
                <template #default="scope">
                  <el-input class="procurement-plan-table-input" v-model="scope.row.input" type="textarea" placeholder="请输入备注" @blur="saveMemo" />
                </template>
              </el-table-column>
     </el-table>
</template>
<script setup>
import { reactive, onMounted, getCurrentInstance } from 'vue'
import { ArrowLeft } from '@element-plus/icons-vue'
import transform from './common/toExcel.js'
import moment from 'moment'

import AppMenuChange from '@component/AppMenuChange'

const { proxy } = getCurrentInstance()

onMounted(() => {
  getGoodsList()
  getCollectTableData()
})

const diskTypeMap = {
  standard: '标准性能',
  efficient: '高性能'
}

const durationUnitMap = {
  Hour: '小时',
  Month: '月',
  Year: '年'
}

const state = reactive({
  cloudProvider: {
    ali: [],
    tencent: [],
    huawei: [],
    ct: [],
    baidu: [],
    jd: [],
    ks: []
  },
  goodsList: [],
  collectTableData: [
    { cloudProvider: '', reserved: { num: 0, price: 0 }, onDemand: { num: 0, price: 0 } }
  ],
  tableData: [],
  purchasePurpose: '部署高可用的云上网站架构,支持业务流量跨可用区进行并发,并具备跨可用区故障容灾能力。' || JSON.parse(localStorage.getItem('purchasePurpose')),
  printObj: {
    id: 'pdf',
    popTitle: '',
    // extraHead: '打印', // 最上方的头部文字,附加在head标签上的额外标签,使用逗号分割
    preview: false, // 是否启动预览模式,默认是false
    previewTitle: ' ', // 打印预览的标题
    extraCss: '',
    extraHead: '<meta http-equiv="Content-Language"content="zh-cn"/>,<style> #pdf { width: 100%; height: auto !important; } <style>'
  },
  detailsForm: [],
  tempStr: '',
  cloudProviderInfo: [
    { id: 'ali', name: '阿里云' },
    { id: 'baidu', name: '百度云' },
    { id: 'huawei', name: '华为云' },
    { id: 'ct', name: '天翼云' },
    { id: 'tencent', name: '腾讯云' },
    { id: 'ks', name: '金山云' },
    { id: 'jd', name: '京东' }
  ],
  tempCloudName: null,
  tempGoodList: []
})

const typeNameArr = []
let typeNamePos = 0

// 导出为excel
const toExcel = () => {
  state.detailsForm = []
  state.tempStr = ''
  state.goodsList.forEach((item) => {
    let tempCloudName = ''
    state.cloudProviderInfo.filter((subitem) => {
      if (subitem.id === item.cloudProvider) {
        tempCloudName = subitem.name
        return subitem.name
      }
    })
    if (item.dataDisk) {
      state.tempStr = getDataDiskDescription(item.dataDisk)
    }

    state.detailsForm.push(
      {
        cloudProvider: tempCloudName,
        standardID: item.standardID,
        info: `${item.cpu}${item.mem}GiB, ${item.zoneName}, 系统盘:${item.systemDisk ? getSystemDiskDescription(item.systemDisk) : '--'}, 数据盘:${state.tempStr},固定带宽:${item.netBrandWidth}M`,
        time: `${getDuration(item) ? getDuration(item) : '按需'}`,
        price: `${item.price} ${item.pricingType === 'reserved' ? '元' : '元/小时'}`,
        memo: item.detailsInput ? item.detailsInput : ''
      }
    )
  })
  transform(state.detailsForm, '云服务器采购清单')
}

// 合并单元格
const objectSpanMethod = ({
  row,
  column,
  rowIndex,
  columnIndex
}) => {
  if (columnIndex === 0) {
    const _row = typeNameArr[rowIndex]
    const _col = _row > 0 ? 1 : 0
    return {
      rowspan: _row,
      colspan: _col
    }
  }
  proxy.$nextTick(() => {
    if (proxy.$refs.table.$el) {
      const current = proxy.$refs.table.$el.querySelector('.el-table__footer-wrapper').querySelector('.el-table__footer')
      const cell = current.rows[0].cells
      cell[1].style.display = 'none'
      cell[2].classList.remove('is-left')
      cell[2].colSpan = '2'
      cell[3].style.display = 'none'
      cell[4].classList.remove('is-left')
      cell[4].colSpan = '2'
    }
  })
}

// 设置cell样式
const cellClassName = ({
  row,
  column,
  rowIndex,
  columnIndex
}) => {
  if (columnIndex !== 0) {
    return 'noRightBorderClass'
  }
}

// 在本地存储里获取购物清单
const getGoodsList = () => {
  state.goodsList = JSON.parse(localStorage.getItem('checkedGoodsList')) || []

  const tempGoodObject = {
    ali: [],
    tencent: [],
    huawei: [],
    baidu: [],
    ks: [],
    ct: [],
    jd: []
  }
  // tempGoodList.push(state.goodsList[0])

  state.goodsList.forEach((item) => {
    if (item.cloudProvider === 'ali') tempGoodObject.ali.push(item)
    if (item.cloudProvider === 'tencent') tempGoodObject.tencent.push(item)
    if (item.cloudProvider === 'huawei') tempGoodObject.huawei.push(item)
    if (item.cloudProvider === 'baidu') tempGoodObject.baidu.push(item)
    if (item.cloudProvider === 'ks') tempGoodObject.ks.push(item)
    if (item.cloudProvider === 'ct') tempGoodObject.ct.push(item)
    if (item.cloudProvider === 'jd') tempGoodObject.jd.push(item)
  })
  state.tempGoodList = [
    ...tempGoodObject.ali,
    ...tempGoodObject.tencent,
    ...tempGoodObject.huawei,
    ...tempGoodObject.baidu,
    ...tempGoodObject.ks,
    ...tempGoodObject.ct,
    ...tempGoodObject.jd
  ]

  for (let i = 0; i < state.tempGoodList.length; i += 1) {
    if (i === 0) {
      typeNameArr.push(1)
      typeNamePos = 0
    } else {
      if (state.tempGoodList[i].cloudProvider === state.tempGoodList[i - 1].cloudProvider) {
        typeNameArr[typeNamePos] += 1
        typeNameArr.push(0)
      } else {
        typeNameArr.push(1)
        typeNamePos = i
      }
    }
  }
}
</script>

el-table合并尾部合计的实现

<template>
     <el-table
              class="procurement-plan-table procurement-plan-table-noborder"
              :row-style="{height: '48px',fontSize: '12px',color: '#666666'}"
              :header-cell-style="{height: '48px',background: '#F6F6F8',fontSize: '14px',color: '#333333',fontWeight: 400}"
              :cell-class-name="cellClassName" //因为设计稿上是只有首位才需要左右边框,合并的单元格需要去除右侧的边框
              :data="state.tempGoodList"
              :span-method="objectSpanMethod" //合并单元格
              ref="table"
              border
              :summary-method="getSummaries" //底部合计行
              show-summary
              :style="{borderColor: '#E6E6E6'}"
            >
              <el-table-column label="云厂商" width="120px" align="center" prop="cloudProvider">
                <template #default="scope">
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'ali'">
                    <div class="text-center">阿里云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'tencent'">
                    <div class="text-center">腾讯云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'huawei'">
                    <div class="text-center">华为云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'ct'">
                    <div class="text-center">天翼云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'baidu'">
                    <div class="text-center">百度云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'jd'">
                    <div class="text-center">京东云</div>
                  </div>
                  <div class="d-flex align-items-center justify-content-center" v-if="scope.row.cloudProvider === 'ks'">
                    <div class="text-center">金山云</div>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="类型" width="120px" align="center">
                  云服务器
              </el-table-column>
              <el-table-column label="付费方式" width="110px" align="center">
                <template #default="scope">
                  {{ scope.row.pricingType === 'reserved' ? '预付' :  '按需'}}
                </template>
              </el-table-column>
              <el-table-column label="数量" width="110px" align="center">
                <template #default="scope">
                  {{ scope.row.goodsNum }}
                </template>
              </el-table-column>
              <el-table-column label="小计" width="130px">
                <template #default="scope">
                  <div>
                    <span>{{  scope.row.price * scope.row.goodsNum }}</span>
                    <span v-if="scope.row.pricingType === 'reserved'"></span>
                    <span v-else>/小时</span>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="备注" align="center">
                <template #default="scope">
                  <el-input class="procurement-plan-table-input" v-model="scope.row.input" type="textarea" placeholder="请输入备注" @blur="saveMemo" />
                </template>
              </el-table-column>
     </el-table>
</template>
<script setup>
import { reactive, onMounted, getCurrentInstance } from 'vue'
import { ArrowLeft } from '@element-plus/icons-vue'
import transform from './common/toExcel.js'
import moment from 'moment'

import AppMenuChange from '@component/AppMenuChange'

const { proxy } = getCurrentInstance()

onMounted(() => {
  getGoodsList()
  getCollectTableData()
})

const diskTypeMap = {
  standard: '标准性能',
  efficient: '高性能'
}

const durationUnitMap = {
  Hour: '小时',
  Month: '月',
  Year: '年'
}

const state = reactive({
  cloudProvider: {
    ali: [],
    tencent: [],
    huawei: [],
    ct: [],
    baidu: [],
    jd: [],
    ks: []
  },
  goodsList: [],
  collectTableData: [
    { cloudProvider: '', reserved: { num: 0, price: 0 }, onDemand: { num: 0, price: 0 } }
  ],
  tableData: [],
  purchasePurpose: '部署高可用的云上网站架构,支持业务流量跨可用区进行并发,并具备跨可用区故障容灾能力。' || JSON.parse(localStorage.getItem('purchasePurpose')),
  printObj: {
    id: 'pdf',
    popTitle: '',
    // extraHead: '打印', // 最上方的头部文字,附加在head标签上的额外标签,使用逗号分割
    preview: false, // 是否启动预览模式,默认是false
    previewTitle: ' ', // 打印预览的标题
    extraCss: '',
    extraHead: '<meta http-equiv="Content-Language"content="zh-cn"/>,<style> #pdf { width: 100%; height: auto !important; } <style>'
  },
  detailsForm: [],
  tempStr: '',
  cloudProviderInfo: [
    { id: 'ali', name: '阿里云' },
    { id: 'baidu', name: '百度云' },
    { id: 'huawei', name: '华为云' },
    { id: 'ct', name: '天翼云' },
    { id: 'tencent', name: '腾讯云' },
    { id: 'ks', name: '金山云' },
    { id: 'jd', name: '京东' }
  ],
  tempCloudName: null,
  tempGoodList: []
})

const typeNameArr = []
let typeNamePos = 0

// 导出为excel
const toExcel = () => {
  state.detailsForm = []
  state.tempStr = ''
  state.goodsList.forEach((item) => {
    let tempCloudName = ''
    state.cloudProviderInfo.filter((subitem) => {
      if (subitem.id === item.cloudProvider) {
        tempCloudName = subitem.name
        return subitem.name
      }
    })
    if (item.dataDisk) {
      state.tempStr = getDataDiskDescription(item.dataDisk)
    }

    state.detailsForm.push(
      {
        cloudProvider: tempCloudName,
        standardID: item.standardID,
        info: `${item.cpu}${item.mem}GiB, ${item.zoneName}, 系统盘:${item.systemDisk ? getSystemDiskDescription(item.systemDisk) : '--'}, 数据盘:${state.tempStr},固定带宽:${item.netBrandWidth}M`,
        time: `${getDuration(item) ? getDuration(item) : '按需'}`,
        price: `${item.price} ${item.pricingType === 'reserved' ? '元' : '元/小时'}`,
        memo: item.detailsInput ? item.detailsInput : ''
      }
    )
  })
  transform(state.detailsForm, '云服务器采购清单')
}

// 合并单元格
const objectSpanMethod = ({
  row,
  column,
  rowIndex,
  columnIndex
}) => {
  if (columnIndex === 0) {
    const _row = typeNameArr[rowIndex]
    const _col = _row > 0 ? 1 : 0
    return {
      rowspan: _row,
      colspan: _col
    }
  }
  proxy.$nextTick(() => {
    if (proxy.$refs.table.$el) {
      const current = proxy.$refs.table.$el.querySelector('.el-table__footer-wrapper').querySelector('.el-table__footer')
      const cell = current.rows[0].cells
      cell[1].style.display = 'none'
      cell[2].classList.remove('is-left')
      cell[2].colSpan = '2'
      cell[3].style.display = 'none'
      cell[4].classList.remove('is-left')
      cell[4].colSpan = '2'
    }
  })
}

// 设置cell样式
const cellClassName = ({
  row,
  column,
  rowIndex,
  columnIndex
}) => {
  if (columnIndex !== 0) {
    return 'noRightBorderClass'
  }
}

// 在本地存储里获取购物清单
const getGoodsList = () => {
  state.goodsList = JSON.parse(localStorage.getItem('checkedGoodsList')) || []

  const tempGoodObject = {
    ali: [],
    tencent: [],
    huawei: [],
    baidu: [],
    ks: [],
    ct: [],
    jd: []
  }
  // tempGoodList.push(state.goodsList[0])

  state.goodsList.forEach((item) => {
    if (item.cloudProvider === 'ali') tempGoodObject.ali.push(item)
    if (item.cloudProvider === 'tencent') tempGoodObject.tencent.push(item)
    if (item.cloudProvider === 'huawei') tempGoodObject.huawei.push(item)
    if (item.cloudProvider === 'baidu') tempGoodObject.baidu.push(item)
    if (item.cloudProvider === 'ks') tempGoodObject.ks.push(item)
    if (item.cloudProvider === 'ct') tempGoodObject.ct.push(item)
    if (item.cloudProvider === 'jd') tempGoodObject.jd.push(item)
  })
  state.tempGoodList = [
    ...tempGoodObject.ali,
    ...tempGoodObject.tencent,
    ...tempGoodObject.huawei,
    ...tempGoodObject.baidu,
    ...tempGoodObject.ks,
    ...tempGoodObject.ct,
    ...tempGoodObject.jd
  ]

  for (let i = 0; i < state.tempGoodList.length; i += 1) {
    if (i === 0) {
      typeNameArr.push(1)
      typeNamePos = 0
    } else {
      if (state.tempGoodList[i].cloudProvider === state.tempGoodList[i - 1].cloudProvider) {
        typeNameArr[typeNamePos] += 1
        typeNameArr.push(0)
      } else {
        typeNameArr.push(1)
        typeNamePos = i
      }
    }
  }
}

const getSummaries = () => { // 底部合计行
  const reservedNum = getSummariesNum('reserved')
  const reservedPrice = getSummariesPrice('reserved')
  const onDemandNum = getSummariesNum('onDemand')
  return ['合计', '', `按需实例: ${onDemandNum}台,预付实例: ${reservedNum}`, '', `预付: ${reservedPrice}`, '按需实例为后付费,云账户有一定与余额即可']
}

const getSummariesNum = (type) => { // 计算按需/预付多少台
  return state.collectTableData.map(item => {
    return item[type].num
  }).reduce((pre, value) => pre + value)
}

const getSummariesPrice = (type) => { // 计算预付价格
  return state.collectTableData.map(item => {
    return item[type].price
  }).reduce((pre, value) => {
    return (parseInt((pre + value) * 100, 10) / 100)
  })
}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/846402.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

64x8com的LCD液晶屏驱动芯片IC,VK1625完美兼容市面所有1625驱动芯片

产品型号&#xff1a;VK1625 产品品牌&#xff1a;VINKA/元泰 封装形式&#xff1a;QFP100 LQFP100 DICE/裸片 COB邦定片 定制COG 专业工程服务&#xff0c;技术支持&#xff01;用芯服务&#xff01; 概述: VK1625B是一个64x8的LCD駆动器. 可软件程控使其适用于多样化的…

在 Amazon SageMaker 上使用 Amazon Inferentia2 实现 AI 作画

在前一篇文章中&#xff0c;我们介绍了如何使用 Amazon EC2 Inf2 实例部署大语言模型&#xff0c;有不少用户询问 Amazon Inf2 实例是否支持当下流行的 AIGC 模型&#xff0c;答案是肯定的。同时&#xff0c;图片生成时间、QPS、服务器推理成本、如何在云中部署&#xff0c;也是…

【数学建模】--聚类模型

聚类模型的定义&#xff1a; “物以类聚&#xff0c;人以群分”&#xff0c;所谓的聚类&#xff0c;就是将样本划分为由类似的对象组成的多个类的过程。聚类后&#xff0c;我们可以更加准确的在每个类中单独使用统计模型进行估计&#xff0c;分析或预测&#xff1b;也可以探究不…

bigemap如何添加高清在线地图?

说明&#xff1a;批量添加可以同时添加多个在线地图&#xff0c;一次性添加完成&#xff08;批量添加无法验证地址是否可以访问&#xff09; 添加后如下图&#xff1a; 第一步 &#xff1a; 制作地图配置文件&#xff1a;选择添加在线地图&#xff08;查看帮助&#xff09;。 …

Linux6.33 Kubernetes kubectl详解

文章目录 计算机系统5G云计算第三章 LINUX Kubernetes kubectl详解一、陈述式资源管理方法1.基本信息查看2.项目的生命周期&#xff1a;创建-->发布-->更新-->回滚-->删除 二、声明式管理方法 计算机系统 5G云计算 第三章 LINUX Kubernetes kubectl详解 一、陈述…

【力扣每日一题】2023.8.7 反转字符串

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一个字符数组形式的字符串&#xff0c;让我们直接原地修改反转字符串&#xff0c;不必返回。 给出的条件是使用O(1)的额外空间…

JS进阶-Day3

&#x1f954;&#xff1a;永远做自己的聚光灯 JS进阶-Day1——点击此处&#xff08;作用域、函数、解构赋值等&#xff09; JS进阶-Day2——点击此处&#xff08;深入对象之构造函数、实例成员、静态成员等&#xff1b;内置构造函数之引用类型、包装类型等&#xff09; 更多JS…

工业以太网交换机-SCALANCE X200 环网组态

1.概述 SCALANCE X200 系列交换机自从2004年8月推入市场&#xff0c;当时交换机只能接入环网&#xff0c;不能做环网管理器。在各个工业现场得到了广泛的应用。2007年5月发布了X200系列新的硬件版本平台&#xff0c;普通交换机可以用HSR&#xff08;高速冗余&#xff09;方法做…

[虚幻引擎] UE DTBase64 插件说明 使用蓝图对字符串或文件进行Base64加密解密

本插件可以在虚幻引擎中使用蓝图对字符串&#xff0c;字节数组&#xff0c;文件进行Base64的加密和解密。 目录 1. 节点说明 String To Base64 Base64 To String Binary To Base64 Base64 To Binary File To Base64 Base64 To File 2. 案例演示 3. 插件下载 1. 节点说…

【用于全变分去噪的分裂布雷格曼方法】实施拆分布雷格曼方法进行总变异去噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

FFmpeg 使用总结

FFmpeg 简介 FFmpeg的名称来自MPEG视频编码标准&#xff0c;前面的“FF”代表“Fast Forward”&#xff0c;FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。可以轻易地实现多种视频格式之间的相互转换。包括如下几个部分&#xf…

iframe 标签的作用是什么?用法是什么?属性有什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ iframe 标签是什么&#xff1f;⭐ iframe 标签的作用什么&#xff1f;⭐ iframe 标签的用法⭐ iframe 标签的属性⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你…

HTTP连接之出现400 Bad Request分析

1、400简介 400是一种HTTP状态码&#xff0c;告诉客户端它发送了一条异常请求。400页面是当用户在打开网页时&#xff0c;返回给用户界面带有400提示符的页面。其含义是你访问的页面域名不存在或者请求错误。主要分为两种。 1、语义有误&#xff0c;当前请求无法被服务器理解…

PPT忘记密码如何解除?

PPT文件所带有的两种加密方式&#xff0c;打开密码以及修改权限&#xff0c;两种密码在打开文件的时候都会有相应的提示&#xff0c;但不同的是两种加密忘记密码之后是不同的。 如果忘记了打开密码&#xff0c;我们就没办法打开PPT文件了&#xff1b;如果是忘记了修改密码&…

RAR压缩包密码,如何删除?

Rar压缩包设置了密码&#xff0c;需要输入正确密码才能够解压文件&#xff0c;这有效保护了文件内容&#xff0c;不过文件可能不再需要加密了&#xff0c;那么我们应该如何删除压缩包密码呢&#xff1f; Rar格式问题&#xff0c;即使有些带有删除密码功能的压缩软件也不支持ra…

【ChatGPT 指令大全】怎么使用ChatGPT来帮我们写作

在数字化时代&#xff0c;人工智能为我们的生活带来了无数便利和创新。在写作领域&#xff0c;ChatGPT作为一种智能助手&#xff0c;为我们提供了强大的帮助。不论是作文、文章&#xff0c;还是日常函电&#xff0c;ChatGPT都能成为我们的得力助手&#xff0c;快速提供准确的文…

1.Wiindow对象

1.1 BOM BOM(Browser Object Model )是浏览器对象模型 ●window对象是一个全局对象&#xff0c;也可以说是JavaScript中的顶级对象 ●像document、alert()、 console. log()这些都是window的属性&#xff0c;基本BOM的属性和方法都是window的 ●所有通过var定义在全局作用域中…

C++ libcurl 编译cmake imap 协议读取邮件

github下载源码,openssl&#xff08;libcurl的ssl请求需要用到&#xff09; libcurl openssl 记得点enable_openssl&#xff0c;点了之后重新configure会出现&#xff0c;输入openssl path的选项 configure后genrate然后open project,先配置好自己的架构win32 还是x64 然后…

视频怎么做成GIF?GIF制作方法很简单

GIF动图一种非常有趣的方式来呈现图像和动画。GIF动图的趣味性在于它们可以展现出很多不同的情感和概念&#xff0c;比如搞笑、可爱、悲伤等等。此外&#xff0c;GIF动图通常是短暂的&#xff0c;循环播放&#xff0c;这种形式可以让人们对图像和动画进行更深入的观察和理解。G…

内存卡无法读取怎么办?揭秘6种解决方法

当内存卡无法读取时&#xff0c;意味着设备无法访问或识别内存卡中的数据。这可能由未正确插入/连接、设备不兼容、驱动程序、文件系统损坏等多种原因引起。在遇到内存卡无法读取的问题时&#xff0c;可以尝试下面常见的方法来解决问题&#xff0c;以及分享如何读取内存卡数据恢…