在有些情况下,我们会有合并表头、合并列、合并尾部合计的需求,这篇文章是为了记录,如何进行合并,方便日后翻阅。
这个地方是用的两个表格实现的,即两个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>
<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: ',