<template>
|
<el-dialog
|
v-model="visible"
|
:title="dialogTitle"
|
:before-close="handleClose"
|
destroy-on-close
|
class="product-price-dialog"
|
:width="dialogWidth"
|
>
|
<div class="price-viewer-container" v-loading="loading">
|
<!-- 价格对比表格 -->
|
<div class="pricing-table-container" v-if="priceList.length > 0">
|
<el-tabs v-model="activeTab" type="card" class="pricing-tabs">
|
<el-tab-pane
|
v-for="(tabData, tabKey) in groupedPriceData"
|
:key="tabKey"
|
:label="getTabLabel(tabKey)"
|
:name="tabKey"
|
>
|
<div class="pricing-table-wrapper">
|
<table class="pricing-table">
|
<thead>
|
<tr>
|
<th class="feature-column">功能对比</th>
|
<th
|
v-for="(group, groupIndex) in getGroupedHeaders(tabData)"
|
:key="groupIndex"
|
:colspan="group.count"
|
>
|
{{ group.productSuite }}
|
</th>
|
</tr>
|
<tr class="sub-header">
|
<th>销售形式</th>
|
<th
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
>
|
{{ getSalesFormText(priceItem.salesForm) }}
|
</th>
|
</tr>
|
</thead>
|
<tbody>
|
<tr>
|
<td class="feature-label">客户对象</td>
|
<td
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
class="feature-value"
|
>
|
{{ getCustomerObjectText(priceItem.customerObject) }}
|
</td>
|
</tr>
|
<tr>
|
<td class="feature-label">账户数量</td>
|
<td
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
class="feature-value"
|
>
|
{{ priceItem.accountQuantityUnlimited ? '不限' : priceItem.accountQuantity }}
|
</td>
|
</tr>
|
<tr>
|
<td class="feature-label">并发节点数</td>
|
<td
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
class="feature-value"
|
>
|
{{ priceItem.concurrentNodeQuantityUnlimited ? '不限' : priceItem.concurrentNodeQuantity }}
|
</td>
|
</tr>
|
<tr>
|
<td class="feature-label">订购方式</td>
|
<td
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
class="feature-value"
|
>
|
<div class="order-methods">
|
<span
|
v-for="method in priceItem.priceSettings"
|
:key="method"
|
class="method-item"
|
>
|
✓ {{ getPriceSettingText(method) }}
|
</span>
|
</div>
|
</td>
|
</tr>
|
<tr>
|
<td class="feature-label">产品价格</td>
|
<td
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
class="feature-value"
|
>
|
<div class="price-info">
|
<div
|
v-if="priceItem.priceSettings.includes('POINTS') && priceItem.pointsAmount > 0"
|
class="price-item"
|
>
|
<span class="price-label">积分:</span>
|
<span class="price-value points">
|
{{ formatNumber(priceItem.pointsAmount) }} 积分{{ getPriceUnitText(priceItem.priceUnit) }}
|
</span>
|
</div>
|
<div
|
v-if="priceItem.priceSettings.includes('CURRENCY') && priceItem.currencyAmount > 0"
|
class="price-item"
|
>
|
<span class="price-label">¥货币:</span>
|
<span class="price-value currency">
|
{{ formatNumber(priceItem.currencyAmount) }} 元{{ getPriceUnitText(priceItem.priceUnit) }}
|
</span>
|
</div>
|
<div
|
v-if="priceItem.priceSettings.includes('FREE')"
|
class="price-item"
|
>
|
<span class="price-label">¥货币:</span>
|
<span class="price-value free">免费</span>
|
</div>
|
<div
|
v-if="priceItem.priceSettings.includes('AGREEMENT')"
|
class="price-item"
|
>
|
<span class="price-label">协议:</span>
|
<span class="price-value agreement">每{{ getPriceUnitText(priceItem.priceUnit) }}</span>
|
</div>
|
</div>
|
</td>
|
</tr>
|
<tr>
|
<td class="feature-label">套件选择</td>
|
<td
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
class="feature-value"
|
>
|
<div class="suite-selection">
|
<el-checkbox
|
:model-value="selectedSuiteIds.has(priceItem.id)"
|
@change="handleSuiteSelect(priceItem.id, $event)"
|
>
|
添加
|
</el-checkbox>
|
</div>
|
</td>
|
</tr>
|
</tbody>
|
</table>
|
</div>
|
</el-tab-pane>
|
</el-tabs>
|
</div>
|
|
<!-- 无价格数据时的提示 -->
|
<div v-else class="no-price-data">
|
<el-empty description="暂无价格信息" />
|
</div>
|
</div>
|
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="handleClose">关闭</el-button>
|
<el-button type="primary" @click="handleOrder">立即订购</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
|
<!-- 产品订购对话框 -->
|
<ProductOrderDialog
|
v-model="showOrderDialog"
|
:product-id="props.productId"
|
:suite-ids="Array.from(selectedSuiteIds)"
|
@order-success="handleOrderSuccess"
|
/>
|
</template>
|
|
<script setup lang="ts">
|
import { ref, watch, computed } from 'vue'
|
import { ElMessage } from 'element-plus'
|
import ProductOrderDialog from '../productOrderDialog/index.vue'
|
|
interface PriceItem {
|
id: number
|
productId: string
|
productSuite: string
|
salesForm: string
|
customerObject: 'ENTERPRISE' | 'PERSONAL' | 'PROJECT_DEPARTMENT'
|
accountQuantity: number
|
accountQuantityUnlimited: boolean
|
concurrentNodeQuantity: number
|
concurrentNodeQuantityUnlimited: boolean
|
priceSettings: Array<'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE'>
|
pointsAmount: number
|
currencyAmount: number
|
priceUnit: string
|
enableStatus: 'ENABLED' | 'DISABLED'
|
}
|
|
interface Props {
|
modelValue: boolean
|
productId?: string
|
width?: string
|
}
|
|
interface Emits {
|
(e: 'update:modelValue', value: boolean): void
|
(e: 'order', selectedItems: PriceItem[]): void
|
}
|
|
const props = withDefaults(defineProps<Props>(), {
|
modelValue: false,
|
productId: '',
|
width: '90%'
|
})
|
|
const emit = defineEmits<Emits>()
|
|
const visible = computed({
|
get: () => props.modelValue,
|
set: (value) => emit('update:modelValue', value)
|
})
|
|
const loading = ref(false)
|
const activeTab = ref('enterprise')
|
const priceList = ref<PriceItem[]>([])
|
const productName = ref('')
|
const selectedSuiteIds = ref<Set<number>>(new Set())
|
const showOrderDialog = ref(false)
|
|
// 弹窗标题
|
const dialogTitle = computed(() => {
|
return `【${productName.value}】产品定价`
|
})
|
|
// 弹窗宽度
|
const dialogWidth = computed(() => {
|
return props.width
|
})
|
|
// 按客户对象分组的价格数据
|
const groupedPriceData = computed(() => {
|
const groups: Record<string, PriceItem[]> = {
|
enterprise: [],
|
project: [],
|
personal: []
|
}
|
|
priceList.value.forEach(item => {
|
switch (item.customerObject) {
|
case 'ENTERPRISE':
|
groups.enterprise.push(item)
|
break
|
case 'PROJECT_DEPARTMENT':
|
groups.project.push(item)
|
break
|
case 'PERSONAL':
|
groups.personal.push(item)
|
break
|
}
|
})
|
|
// 过滤掉空的组
|
return Object.fromEntries(
|
Object.entries(groups).filter(([_, items]) => items.length > 0)
|
)
|
})
|
|
// 模拟价格数据
|
const mockPriceData: Record<string, PriceItem[]> = {
|
'10001': [
|
{
|
id: 1,
|
productId: '10001',
|
productSuite: 'ENTERPRISE_PRIVATE_SAAS_LICENSE',
|
salesForm: 'BUYOUT',
|
customerObject: 'ENTERPRISE',
|
accountQuantity: 50,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 50,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['POINTS', 'CURRENCY', 'AGREEMENT'],
|
pointsAmount: 50000,
|
currencyAmount: 50000,
|
priceUnit: 'SET',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 2,
|
productId: '10001',
|
productSuite: 'ENTERPRISE_PRIVATE_SAAS_LICENSE',
|
salesForm: 'LEASE',
|
customerObject: 'ENTERPRISE',
|
accountQuantity: 0,
|
accountQuantityUnlimited: true,
|
concurrentNodeQuantity: 50,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['POINTS', 'CURRENCY', 'AGREEMENT'],
|
pointsAmount: 800000,
|
currencyAmount: 800000,
|
priceUnit: 'YEAR',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 3,
|
productId: '10001',
|
productSuite: 'ENTERPRISE_PRIVATE_SAAS_OTA',
|
salesForm: 'OTA',
|
customerObject: 'ENTERPRISE',
|
accountQuantity: 50,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 50,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['CURRENCY'],
|
pointsAmount: 0,
|
currencyAmount: 7500,
|
priceUnit: 'SET_PER_YEAR',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 4,
|
productId: '10001',
|
productSuite: 'ENTERPRISE_PRIVATE_SAAS_USER_INCREMENT',
|
salesForm: 'PRIVATE_INCREMENT',
|
customerObject: 'ENTERPRISE',
|
accountQuantity: 100,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 100,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['CURRENCY'],
|
pointsAmount: 0,
|
currencyAmount: 10000,
|
priceUnit: 'YEAR',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 9,
|
productId: '10001',
|
productSuite: 'PROJECT_PRIVATE_SAAS_OTA',
|
salesForm: 'OTA',
|
customerObject: 'PROJECT_DEPARTMENT',
|
accountQuantity: 50,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 50,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['FREE'],
|
pointsAmount: 0,
|
currencyAmount: 0,
|
priceUnit: 'SET',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 10,
|
productId: '10001',
|
productSuite: 'PROJECT_PRIVATE_SAAS_USER_INCREMENT',
|
salesForm: 'PRIVATE_INCREMENT',
|
customerObject: 'PROJECT_DEPARTMENT',
|
accountQuantity: 50,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 50,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['CURRENCY'],
|
pointsAmount: 0,
|
currencyAmount: 50000,
|
priceUnit: 'YEAR',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 5,
|
productId: '10001',
|
productSuite: 'ENTERPRISE_PUBLIC_SAAS_LICENSE',
|
salesForm: 'LEASE',
|
customerObject: 'PERSONAL',
|
accountQuantity: 50,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 50,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['CURRENCY'],
|
pointsAmount: 0,
|
currencyAmount: 15000,
|
priceUnit: 'YEAR',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 6,
|
productId: '10001',
|
productSuite: 'ENTERPRISE_PUBLIC_SAAS_USER_INCREMENT',
|
salesForm: 'PUBLIC_INCREMENT',
|
customerObject: 'PERSONAL',
|
accountQuantity: 50,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 50,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['CURRENCY'],
|
pointsAmount: 0,
|
currencyAmount: 5000,
|
priceUnit: 'YEAR',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 7,
|
productId: '10001',
|
productSuite: 'ENTERPRISE_PUBLIC_SAAS_CLOUD',
|
salesForm: 'CLOUD',
|
customerObject: 'PERSONAL',
|
accountQuantity: 1,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 1,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['FREE'],
|
pointsAmount: 0,
|
currencyAmount: 0,
|
priceUnit: 'SET',
|
enableStatus: 'ENABLED'
|
}
|
],
|
'10002': [
|
{
|
id: 8,
|
productId: '10002',
|
productSuite: 'ENTERPRISE_PRIVATE_SAAS_LICENSE',
|
salesForm: 'BUYOUT',
|
customerObject: 'ENTERPRISE',
|
accountQuantity: 30,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 30,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['POINTS', 'CURRENCY', 'AGREEMENT'],
|
pointsAmount: 30000,
|
currencyAmount: 30000,
|
priceUnit: 'SET',
|
enableStatus: 'ENABLED'
|
},
|
{
|
id: 11,
|
productId: '10002',
|
productSuite: 'PROJECT_PRIVATE_SAAS_OTA',
|
salesForm: 'OTA',
|
customerObject: 'PROJECT_DEPARTMENT',
|
accountQuantity: 30,
|
accountQuantityUnlimited: false,
|
concurrentNodeQuantity: 30,
|
concurrentNodeQuantityUnlimited: false,
|
priceSettings: ['FREE'],
|
pointsAmount: 0,
|
currencyAmount: 0,
|
priceUnit: 'SET',
|
enableStatus: 'ENABLED'
|
}
|
]
|
}
|
|
// 模拟产品名称数据
|
const mockProductNames: Record<string, string> = {
|
'10001': '数字化产品A',
|
'10002': '数字化产品B'
|
}
|
|
// 获取价格信息
|
const fetchProductData = async (productId: string) => {
|
if (!productId) {
|
ElMessage.warning('未提供产品ID')
|
return
|
}
|
|
loading.value = true
|
try {
|
// 模拟API调用
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
// 获取产品名称
|
productName.value = mockProductNames[productId] || '未知产品'
|
|
// 获取价格数据
|
priceList.value = mockPriceData[productId] || []
|
|
if (priceList.value.length === 0) {
|
ElMessage.warning('该产品暂无价格信息')
|
}
|
} catch (error) {
|
console.error('获取价格数据失败:', error)
|
ElMessage.error('获取价格数据失败')
|
} finally {
|
loading.value = false
|
}
|
}
|
|
// 监听产品ID变化
|
watch(() => props.productId, (newProductId) => {
|
if (newProductId && visible.value) {
|
fetchProductData(newProductId)
|
}
|
})
|
|
// 监听弹窗显示状态
|
watch(() => visible.value, (newVisible) => {
|
if (newVisible && props.productId) {
|
fetchProductData(props.productId)
|
}
|
})
|
|
// 关闭弹窗
|
const handleClose = () => {
|
visible.value = false
|
}
|
|
// 套件选择处理
|
const handleSuiteSelect = (suiteId: number, checked: any) => {
|
const isChecked = Boolean(checked)
|
if (isChecked) {
|
selectedSuiteIds.value.add(suiteId)
|
} else {
|
selectedSuiteIds.value.delete(suiteId)
|
}
|
}
|
|
// 立即订购
|
const handleOrder = () => {
|
if (selectedSuiteIds.value.size === 0) {
|
ElMessage.warning('请选择要订购的软件套件')
|
return
|
}
|
|
// 获取选中的价格项
|
const selectedItems = priceList.value.filter(item => selectedSuiteIds.value.has(item.id))
|
emit('order', selectedItems)
|
|
// 打开订购对话框
|
showOrderDialog.value = true
|
}
|
|
// 订购成功回调
|
const handleOrderSuccess = (orderData: any) => {
|
console.log('订购成功:', orderData)
|
ElMessage.success('订购申请提交成功!')
|
// 清空选择状态
|
selectedSuiteIds.value.clear()
|
// 关闭价格查看器
|
visible.value = false
|
}
|
|
// 获取销售形式文本
|
const getSalesFormText = (salesForm: string) => {
|
const map: Record<string, string> = {
|
BUYOUT: '买断',
|
LEASE: '租赁',
|
PRIVATE_INCREMENT: '私有增量包',
|
PUBLIC_INCREMENT: '公有增量包',
|
OTA: 'OTA服务',
|
CLOUD: '云服务'
|
}
|
return map[salesForm] || salesForm
|
}
|
|
// 获取客户对象文本
|
const getCustomerObjectText = (customerObject: string) => {
|
const map: Record<string, string> = {
|
ENTERPRISE: '企业',
|
PERSONAL: '个人',
|
PROJECT_DEPARTMENT: '项目部'
|
}
|
return map[customerObject] || customerObject
|
}
|
|
// 获取标签页标签
|
const getTabLabel = (tabKey: string | number) => {
|
const map: Record<string, string> = {
|
enterprise: '企业版',
|
project: '项目部版',
|
personal: '个人版'
|
}
|
return map[String(tabKey)] || String(tabKey)
|
}
|
|
// 获取列跨度
|
const getColspan = (priceItem: PriceItem) => {
|
// 根据价格设置的数量决定列跨度
|
return priceItem.priceSettings.length > 0 ? 1 : 1
|
}
|
|
// 获取产品套件文本
|
const getProductSuiteText = (productSuite: string) => {
|
const map: Record<string, string> = {
|
ENTERPRISE_PRIVATE_SAAS_LICENSE: '企业私有SaaS版许可',
|
ENTERPRISE_PRIVATE_SAAS_OTA: '企业私有SaaS版OTA升级服务',
|
ENTERPRISE_PRIVATE_SAAS_USER_INCREMENT: '企业私有SaaS版用户增量包',
|
PROJECT_PRIVATE_SAAS_OTA: '项目私有SaaS版OTA升级服务',
|
PROJECT_PRIVATE_SAAS_USER_INCREMENT: '项目私有SaaS版用户增量包',
|
ENTERPRISE_PUBLIC_SAAS_LICENSE: '企业公有SaaS版许可',
|
ENTERPRISE_PUBLIC_SAAS_OTA: '企业公有SaaS版OTA服务',
|
ENTERPRISE_PUBLIC_SAAS_USER_INCREMENT: '企业公有SaaS版用户增量包',
|
ENTERPRISE_PUBLIC_SAAS_CLOUD: '企业公有SaaS版云服务',
|
PERSONAL_PUBLIC_SAAS_LICENSE: '个人公有SaaS化许可',
|
WEB_SOFTWARE: 'web软件',
|
DESKTOP_SOFTWARE: '桌面软件'
|
}
|
return map[productSuite] || productSuite
|
}
|
|
// 获取价格设置文本
|
const getPriceSettingText = (setting: string) => {
|
const map: Record<string, string> = {
|
POINTS: '积分',
|
CURRENCY: '货币',
|
AGREEMENT: '协议',
|
FREE: '免费'
|
}
|
return map[setting] || setting
|
}
|
|
// 格式化数字
|
const formatNumber = (num: number) => {
|
return num.toLocaleString()
|
}
|
|
// 获取价格单位文本
|
const getPriceUnitText = (unit: string) => {
|
const map: Record<string, string> = {
|
SET: '套',
|
SET_PER_YEAR: '套/年',
|
YEAR: '年'
|
}
|
return map[unit] || unit
|
}
|
|
// 获取分组后的表头数据
|
const getGroupedHeaders = (tabData: PriceItem[]) => {
|
const groups: Record<string, { count: number; productSuite: string }> = {}
|
|
tabData.forEach(item => {
|
const productSuite = getProductSuiteText(item.productSuite)
|
if (groups[productSuite]) {
|
groups[productSuite].count++
|
} else {
|
groups[productSuite] = {
|
count: 1,
|
productSuite: productSuite
|
}
|
}
|
})
|
|
return Object.values(groups)
|
}
|
</script>
|
|
<style scoped lang="scss">
|
.price-viewer-container {
|
.pricing-table-container {
|
.pricing-tabs {
|
:deep(.el-tabs__header) {
|
margin-bottom: 5px;
|
}
|
|
:deep(.el-tabs__item.is-active) {
|
font-weight: bold;
|
}
|
}
|
|
.pricing-table-wrapper {
|
overflow-x: auto;
|
|
.pricing-table {
|
width: 100%;
|
border-collapse: collapse;
|
border: 1px solid #e4e7ed;
|
background: #fff;
|
|
th, td {
|
border: 1px solid #e4e7ed;
|
padding: 6px 8px;
|
text-align: center;
|
vertical-align: middle;
|
font-size: 12px;
|
}
|
|
th {
|
background-color: #f5f7fa;
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.feature-column {
|
width: 120px;
|
background-color: #f5f7fa;
|
}
|
|
.sub-header {
|
th {
|
background-color: #fafafa;
|
font-weight: 500;
|
font-size: 12px;
|
color: #606266;
|
}
|
}
|
|
.feature-label {
|
background-color: #f5f7fa;
|
font-weight: 500;
|
color: #606266;
|
text-align: center;
|
}
|
|
.feature-value {
|
color: #303133;
|
}
|
|
.order-methods {
|
display: flex;
|
flex-direction: column;
|
gap: 4px;
|
align-items: center;
|
|
.method-item {
|
font-size: 13px;
|
color: #409eff;
|
}
|
}
|
|
.price-info {
|
display: flex;
|
flex-direction: column;
|
gap: 8px;
|
align-items: center;
|
|
.price-item {
|
display: flex;
|
align-items: center;
|
gap: 4px;
|
|
.price-label {
|
font-size: 13px;
|
color: #606266;
|
}
|
|
.price-value {
|
font-weight: 500;
|
|
&.points {
|
color: #e6a23c;
|
}
|
|
&.currency {
|
color: #f56c6c;
|
}
|
|
&.free {
|
color: #67c23a;
|
}
|
}
|
}
|
|
.add-checkbox {
|
margin-top: 4px;
|
}
|
}
|
|
.suite-selection {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
padding: 8px 0;
|
}
|
}
|
}
|
}
|
}
|
|
.dialog-footer {
|
display: flex;
|
justify-content: flex-end;
|
gap: 12px;
|
}
|
|
.no-price-data {
|
text-align: center;
|
padding: 40px 0;
|
}
|
|
.price-value.agreement {
|
color: #409eff;
|
}
|
|
.product-price-dialog {
|
:deep(.el-dialog__title) {
|
font-size: 14px;
|
font-weight: 600;
|
}
|
|
:deep(.el-dialog) {
|
max-width: 95vw;
|
min-width: 800px;
|
}
|
|
:deep(.el-dialog__body) {
|
max-height: 70vh;
|
overflow-y: auto;
|
}
|
}
|
</style>
|