<template>
|
<div>
|
<div class="price-viewer-container" v-loading="loading">
|
<!-- 价格对比表格 -->
|
<div class="pricing-table-container" v-if="showPricePanel && 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>
|
</thead>
|
<tbody>
|
<tr>
|
<td class="feature-label">销售形式</td>
|
<td
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
class="feature-value"
|
>
|
{{ getSalesFormText(priceItem.salesForm) }}
|
</td>
|
</tr>
|
<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"
|
>
|
{{ priceItem.priceSettings.map((m:any) => getPriceSettingText(m)).join('、') }}
|
</td>
|
</tr>
|
<tr>
|
<td class="feature-label">产品价格</td>
|
<td
|
v-for="priceItem in tabData"
|
:key="priceItem.id"
|
:colspan="getColspan(priceItem)"
|
class="feature-value"
|
>
|
{{ renderPrice(priceItem) }}
|
</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-icon class="add-cart-icon"><ShoppingCart /></el-icon>
|
</el-checkbox>
|
</div>
|
</td>
|
</tr>
|
</tbody>
|
</table>
|
</div>
|
</el-tab-pane>
|
</el-tabs>
|
</div>
|
|
<!-- 内嵌订购信息面板 -->
|
<div class="order-panel" v-else-if="showOrderPanel">
|
<!-- 第一部分:产品信息 + 汇总 + 操作(静态数据) -->
|
<el-card class="order-header-card" shadow="never">
|
<div class="basic-grid">
|
<div class="grid-item">
|
<span class="label">产品名称:</span><span class="value strong">{{ productHeader.name }}</span>
|
</div>
|
<div class="grid-item">
|
<span class="label">行业板块:</span><span class="value">{{ productHeader.industrialChainName }}</span>
|
</div>
|
<div class="grid-item">
|
<span class="label">提供者:</span><span class="value">{{ productHeader.submissionUnit }}</span>
|
</div>
|
<div class="grid-item">
|
<span class="label">用户姓名:</span><span class="value">{{ userInfo.name }}</span>
|
</div>
|
<div class="grid-item">
|
<span class="label">单位:</span><span class="value">{{ userInfo.unitName }}</span>
|
</div>
|
<div class="grid-item">
|
<span class="label">部门:</span><span class="value">{{ userInfo.departmentName }}</span>
|
</div>
|
</div>
|
</el-card>
|
|
<!-- 第二部分:选购产品列表 -->
|
<el-card class="selected-suites-card" shadow="never">
|
|
<div v-if="orderSuites.length > 0" class="suite-table-wrapper">
|
<table class="suite-table">
|
<thead>
|
<tr>
|
<th class="th-detail">详情</th>
|
<th class="th-spec">规格</th>
|
<th class="th-price">单价</th>
|
<th class="th-quantity">数量</th>
|
<th class="th-years">年限</th>
|
<th class="th-action">操作</th>
|
</tr>
|
</thead>
|
<tbody>
|
<tr v-for="(suite, idx) in orderSuites" :key="suite.id">
|
<td class="cell-detail">
|
<div class="detail-content">
|
<el-checkbox v-model="suite.selected" />
|
<span class="suite-name">{{ getProductSuiteText(suite.productSuite) }}</span>
|
</div>
|
</td>
|
<td class="cell-spec cell-spec-gg">
|
<div class="spec-line-gg">
|
<div class="spec-line">销售形式:<span class="spec-value">{{ getSalesFormText(suite.salesForm) }}</span></div>
|
<div class="spec-line">客户对象:<span class="spec-value">{{ getCustomerObjectText(suite.customerObject) }}</span></div>
|
</div>
|
<div class="spec-line-gg">
|
<div class="spec-line">账户数量:<span class="spec-value">{{ suite.accountQuantityUnlimited ? '不限' : suite.accountQuantity }}</span></div>
|
<div class="spec-line">并发节点数:<span class="spec-value">{{ suite.concurrentNodeQuantityUnlimited ? '不限' : suite.concurrentNodeQuantity }}</span></div>
|
</div>
|
|
</td>
|
<td class="cell-price">
|
<div class="price-inline">
|
<el-select v-model="suite.priceType" size="large" style="width: 90px">
|
<el-option v-for="t in getAvailablePriceTypes(suite)" :key="t.value" :label="t.label" :value="t.value" />
|
</el-select>
|
<div class="price-display">
|
<template v-if="suite.priceType === 'POINTS'">
|
<span class="price-tag points">{{ formatNumber(suite.pointsAmount) }}</span>
|
<span class="price-unit">{{ getUnitSuffix(suite.priceUnit) }}</span>
|
</template>
|
<template v-else-if="suite.priceType === 'CURRENCY'">
|
<span class="price-tag currency">{{ formatNumber(suite.currencyAmount) }}</span>
|
<span class="price-unit">{{ getUnitSuffix(suite.priceUnit) }}</span>
|
</template>
|
<template v-else-if="suite.priceType === 'AGREEMENT'">
|
<span class="price-agreement">/{{ getPriceUnitText(suite.priceUnit) }}</span>
|
</template>
|
<template v-else>
|
<span class="price-free">/{{ getPriceUnitText(suite.priceUnit) }}</span>
|
</template>
|
</div>
|
</div>
|
</td>
|
<td class="th-center">
|
<el-input-number
|
v-model="suite.quantity"
|
:min="1"
|
:max="999"
|
:controls="true"
|
size="large"
|
class="quantity-input"
|
@change="(value) => handleQuantityChange(suite.id,value?value:1)"
|
/>
|
</td>
|
<td class="th-center">
|
<el-input-number
|
v-model="suite.duration"
|
:min="1"
|
:max="100"
|
:controls="true"
|
size="large"
|
class="duration-input"
|
:disabled="suite.priceType === 'FREE'"
|
@change="(value) => handleDurationChange(suite.id, value?value:1)"
|
/>
|
</td>
|
<td class="th-center">
|
<el-button type="danger" link @click="removeSuite(idx)">删除</el-button>
|
</td>
|
</tr>
|
</tbody>
|
</table>
|
</div>
|
<el-empty v-else description="暂无选中的软件套件" />
|
<div class="table-toolbar">
|
<div class="toolbar-left">
|
<el-checkbox v-model="selectAllChecked">全选</el-checkbox>
|
<span class="summary-info">
|
已选 <span class="summary-count">{{ selectedCount }}</span> 件产品
|
总计 <span class="summary-points">{{ formatNumber(totalByType.points) }}</span> 积分
|
</span>
|
</div>
|
</div>
|
</el-card>
|
</div>
|
|
<!-- 订单状态展示面板 -->
|
<div class="order-status-panel" v-else-if="showOrderStatus">
|
<div class="steps-wrapper">
|
<el-steps :active="activeStep" align-center finish-status="success" process-status="process">
|
<el-step title="提交申请" :description="orderStatus.submitTime" />
|
<el-step title="待授权" />
|
<el-step title="交易确认" />
|
<el-step title="评价" />
|
</el-steps>
|
</div>
|
|
<div class="result-text">
|
您的产品订购申请已成功提交, 请等待授权
|
</div>
|
|
<el-card class="order-info-card" shadow="never">
|
<div class="order-info-title">订单信息</div>
|
<table class="order-info-table">
|
<tbody>
|
<tr>
|
<td class="label">订单编号</td>
|
<td class="value">
|
{{ orderStatus.id }}
|
<el-link type="primary" @click="goBuyerCenter" style="margin-left: 8px">点击查看</el-link>
|
</td>
|
<td class="label">申请时间</td>
|
<td class="value">{{ orderStatus.applyTime }}</td>
|
</tr>
|
<tr>
|
<td class="label">产品名称</td>
|
<td class="value">{{ orderStatus.productName }}</td>
|
<td class="label">提供者</td>
|
<td class="value">{{ orderStatus.provider }}</td>
|
</tr>
|
<tr>
|
<td class="label">订单状态</td>
|
<td class="value">{{ statusText(orderStatus.status) }}</td>
|
<td></td>
|
<td class="value link">
|
<!-- <el-link type="primary" @click="viewOrder">查看订单信息</el-link>-->
|
</td>
|
</tr>
|
</tbody>
|
</table>
|
</el-card>
|
</div>
|
|
<!-- 无价格数据时的提示 -->
|
<div v-else class="no-price-data">
|
<el-empty description="暂无价格信息" />
|
</div>
|
</div>
|
<div class="footer" v-if="showPricePanel">
|
<el-button type="primary" @click="handleOrder">立即订购</el-button>
|
</div>
|
<div class="footer" v-if="showOrderPanel">
|
<el-button @click="returnPricePanel">返回价格对比</el-button>
|
<el-button type="primary" @click="submitOrder">提交申请</el-button>
|
</div>
|
</div>
|
|
<!-- 产品订购对话框移除,改为内嵌展示 -->
|
</template>
|
|
<script setup lang="ts">
|
import { ref, watch, computed, onMounted } from 'vue'
|
import { useRouter } from 'vue-router'
|
import { ElMessage } from 'element-plus'
|
import { useRoute } from 'vue-router'
|
import productPricingApi from '@/api/productPricingApi'
|
import cartApi from '@/api/cartApi'
|
import orderApi from '@/api/orderApi'
|
import { useUserInfo } from '@/stores/modules/userInfo'
|
import { queryUserDetail } from '@/api/userInfo'
|
import productApi from '@/api/productApi'
|
import workFlowApi from '@/api/workFlowApi'
|
|
const route = useRoute()
|
const router = useRouter()
|
|
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'
|
}
|
|
// 兼容 path 参数 productId 与 query 参数 id/productId
|
const currentProductId = computed<string | undefined>(() => {
|
return (route.params.productId as string) || (route.query.productId as string) || (route.query.id as string)
|
})
|
|
|
const loading = ref(false)
|
const activeTab = ref('enterprise')
|
const priceList = ref<PriceItem[]>([])
|
const productName = ref('')
|
const selectedSuiteIds = ref<Set<number>>(new Set())
|
const showOrderPanel = ref(false)
|
const showPricePanel = ref(true)
|
const showOrderStatus = ref(false)
|
// 模拟用户信息(实际应从用户状态获取)
|
const userStore = useUserInfo()
|
const currentUserId = computed(() => userStore.getUserId || userStore.getUserInfo?.userId)
|
const currentUnitId = computed(() => userStore.getUnitId || userStore.getUserInfo?.unitId || '')
|
|
onMounted(async () => {
|
if (!currentUserId.value) {
|
try {
|
const res: any = await queryUserDetail()
|
if (res?.code === 200 && res.data) {
|
userStore.updateUserDetail(res.data)
|
// currentUserId.value = res.data.id || res.data.userId
|
console.log(currentUserId.value)
|
userInfo.value = res.data
|
} else {
|
ElMessage.error(res?.msg || '无法获取用户信息,请先登录')
|
return
|
}
|
} catch (e) {
|
console.error('获取用户详情失败:', e)
|
ElMessage.error('获取用户信息失败,请稍后重试')
|
return
|
}
|
}else{
|
userInfo.value = userStore.getUserInfos
|
}
|
// 获取订购信息
|
fetchProductData(currentProductId.value ? currentProductId.value : '')
|
})
|
type PriceTypeKey = 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE'
|
interface OrderSuite extends PriceItem {
|
selected: boolean
|
quantity: number
|
duration: number
|
priceType?: PriceTypeKey
|
}
|
const orderSuites = ref<OrderSuite[]>([])
|
|
// 购物车相关状态
|
const cartItems = ref<any[]>([])
|
const cartLoading = ref(false)
|
const idempotencyToken = ref<string>('')
|
|
// 订单状态相关
|
type OrderStatus = 'SUBMITTED' | 'AUTHORIZED' | 'CONFIRMED' | 'EVALUATED'
|
interface OrderStatusDetail {
|
id: string
|
productName: string
|
provider: string
|
status: OrderStatus
|
submitTime: string
|
applyTime: string
|
}
|
const orderStatus = ref<OrderStatusDetail>({
|
id: '',
|
productName: '',
|
provider: '',
|
status: 'SUBMITTED',
|
submitTime: '',
|
applyTime: ''
|
})
|
|
const activeStep = computed(() => {
|
switch (orderStatus.value?.status) {
|
case 'SUBMITTED': return 1
|
case 'AUTHORIZED': return 2
|
case 'CONFIRMED': return 3
|
case 'EVALUATED': return 4
|
default: return 1
|
}
|
})
|
|
const statusText = (s: OrderStatus) => {
|
const map: Record<OrderStatus, string> = {
|
SUBMITTED: '待授权',
|
AUTHORIZED: '已授权,待确认交易',
|
CONFIRMED: '交易已确认,待评价',
|
EVALUATED: '已完成'
|
}
|
return map[s]
|
}
|
|
const viewOrder = () => {
|
window.alert('这里可跳转到订单详情页(示例)')
|
}
|
|
const goBuyerCenter = () => {
|
router.push({ name: 'tradeBuyerCenter', query: { t: String(Date.now()) } })
|
}
|
|
const selectedCount = computed(() => orderSuites.value.filter(s => s.selected).length)
|
const totalByType = computed(() => {
|
const result = { points: 0, currency: 0 }
|
orderSuites.value
|
.filter(s => s.selected)
|
.forEach(s => {
|
const q = s.quantity || 1
|
const d = s.duration || 1
|
const multiplier = (s.priceUnit === 'SET_PER_YEAR' || s.priceUnit === 'YEAR') ? d : 1
|
switch (s.priceType) {
|
case 'POINTS':
|
result.points += (s.pointsAmount || 0) * q * multiplier
|
break
|
case 'CURRENCY':
|
result.currency += (s.currencyAmount || 0) * q * multiplier
|
break
|
}
|
})
|
return result
|
})
|
|
// 第一部分:静态积分信息展示
|
const pointsInfo = ref({
|
accountName: '个人积分账户',
|
balance: 50000,
|
frozen: 0,
|
})
|
|
// 产品信息/用户信息(静态)
|
const productHeader = ref({
|
name: '',
|
industrialChainName: '',
|
submissionUnit: '',
|
createUserId: ''
|
})
|
const userInfo = ref({
|
name: '',
|
departmentName: '',
|
unitName: ''
|
})
|
|
// 全选
|
const selectAllChecked = ref(false)
|
watch(selectAllChecked, (val) => {
|
orderSuites.value.forEach(s => { s.selected = !!val })
|
})
|
|
const getAvailablePriceTypes = (suite: OrderSuite) => {
|
const t: { label: string; value: PriceTypeKey }[] = []
|
if (suite.priceSettings.includes('POINTS')) t.push({ label: '积分', value: 'POINTS' })
|
if (suite.priceSettings.includes('CURRENCY')) t.push({ label: '货币', value: 'CURRENCY' })
|
if (suite.priceSettings.includes('AGREEMENT')) t.push({ label: '协议', value: 'AGREEMENT' })
|
if (suite.priceSettings.includes('FREE')) t.push({ label: '免费', value: 'FREE' })
|
return t
|
}
|
const getDefaultPriceType = (suite: OrderSuite): PriceTypeKey => {
|
if (suite.priceSettings.includes('POINTS')) return 'POINTS'
|
if (suite.priceSettings.includes('CURRENCY')) return 'CURRENCY'
|
if (suite.priceSettings.includes('AGREEMENT')) return 'AGREEMENT'
|
return 'FREE'
|
}
|
const getUnitSuffix = (unit?: string) => {
|
const map: Record<string, string> = { SET: '/套', SET_PER_YEAR: '/套/年', YEAR: '/年' }
|
return map[unit || ''] || ''
|
}
|
const removeSuite = async (index: number) => {
|
const suite = orderSuites.value[index]
|
if (!suite) return
|
try {
|
await removeFromCart(String(suite.id))
|
orderSuites.value.splice(index, 1)
|
selectedSuiteIds.value.delete(suite.id)
|
} catch (e) {
|
}
|
}
|
|
// 订购成功回调
|
const handleOrderSuccess = (orderData: any) => {
|
console.log('订购成功:', orderData)
|
ElMessage.success('订购申请提交成功!')
|
// 清空选择状态
|
selectedSuiteIds.value.clear()
|
}
|
|
// 弹窗标题
|
const dialogTitle = computed(() => {
|
return `【${productName.value}】产品定价`
|
})
|
|
|
// 按客户对象分组的价格数据
|
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)
|
)
|
})
|
|
// 映射工具
|
function mapSalesFormFromCN(val: string) {
|
const map: Record<string, string> = {
|
'买断': 'BUYOUT',
|
'租赁': 'LEASE',
|
'私有增包量': 'PRIVATE_INCREMENT',
|
'公有增包量': 'PUBLIC_INCREMENT',
|
'OTA服务': 'OTA',
|
'云服务': 'CLOUD',
|
'资源包': 'RESOURCE_PACKAGE',
|
'个人': 'PERSONAL',
|
}
|
return (map[val] as any) || val
|
}
|
|
function mapCustomerFromCN(val: string) {
|
const map: Record<string, string> = { '企业': 'ENTERPRISE', '个人': 'PERSONAL', '项目部': 'PROJECT_DEPARTMENT' }
|
return (map[val] as any) || val
|
}
|
|
function mapUnitFromCN(val: string) {
|
const map: Record<string, string> = { '套': 'SET', '套/年': 'SET_PER_YEAR', '年': 'YEAR' }
|
return (map[val] as any) || val
|
}
|
|
function mapPriceTypeFromCN(val: string): 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE' | undefined {
|
const map: Record<string, any> = { '积分': 'POINTS', '货币': 'CURRENCY', '协议': 'AGREEMENT', '免费': 'FREE' }
|
return map[val]
|
}
|
|
// 模拟产品名称数据
|
const mockProductNames: Record<string, string> = {
|
'1': '数字化产品A',
|
'2': '数字化产品B'
|
}
|
|
// 获取价格信息(改为接口获取定价列表)
|
const fetchProductData = async (productId: string) => {
|
if (!productId) {
|
ElMessage.warning('未提供产品ID')
|
return
|
}
|
|
loading.value = true
|
try {
|
// 只查询已启用的定价
|
const res: any = await productPricingApi.listBycondition({ productId: productId,isActive: true })
|
if (res?.code === 200) {
|
const list = Array.isArray(res.data) ? res.data : []
|
const mapped: PriceItem[] = list.map((it: any) => {
|
const priceSettings = String(it.priceType || '')
|
.split(',')
|
.map((t: string) => mapPriceTypeFromCN(t))
|
.filter(Boolean)
|
|
const hasPoints = (priceSettings as string[]).includes('POINTS')
|
const hasCurrency = (priceSettings as string[]).includes('CURRENCY')
|
|
const accountUnlimited = it.accountLimit === '不限'
|
const concurrentUnlimited = it.concurrentNodes === '不限'
|
|
return {
|
id: it.id,
|
productId: String(it.productId || productId),
|
productSuite: it.suiteName || '',
|
salesForm: mapSalesFormFromCN(it.salesForm) as any,
|
customerObject: mapCustomerFromCN(it.customerType) as any,
|
accountQuantity: accountUnlimited ? 0 : Number(it.accountLimit || 0),
|
accountQuantityUnlimited: accountUnlimited,
|
concurrentNodeQuantity: concurrentUnlimited ? 0 : Number(it.concurrentNodes || 0),
|
concurrentNodeQuantityUnlimited: concurrentUnlimited,
|
priceSettings: priceSettings as any,
|
pointsAmount: hasPoints ? Number(it.pointsPrice || 0) : 0,
|
currencyAmount: hasCurrency ? Number(it.currencyPrice || 0) : 0,
|
priceUnit: mapUnitFromCN(it.priceUnit) as any,
|
enableStatus: it.isActive ? 'ENABLED' : 'DISABLED',
|
}
|
})
|
priceList.value = mapped
|
if (priceList.value.length === 0) {
|
ElMessage.warning('该产品暂无价格信息')
|
}
|
// 如果后端返回了产品名称可以在此赋值;否则保持现有标题格式
|
if (res.data && res.data.length > 0 && res.data[0].productName) {
|
productName.value = res.data[0].productName
|
}
|
} else {
|
priceList.value = []
|
ElMessage.error(res?.msg || '获取定价列表失败')
|
}
|
} catch (error) {
|
console.error('获取价格数据失败:', error)
|
priceList.value = []
|
ElMessage.error('获取价格数据失败')
|
} finally {
|
loading.value = false
|
}
|
//获取产品详情信息
|
try {
|
// productDetail.value = mockProductMap[productId] || null
|
const data = {
|
id: productId
|
}
|
const detailRes: any = await productApi.getProductById(data)
|
if (detailRes?.code === 200) {
|
productHeader.value = detailRes.data || { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
|
} else {
|
productHeader.value = { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
|
ElMessage.error(detailRes?.msg || '获取产品详情失败')
|
}
|
} catch (e) {
|
productHeader.value = { name: '', industrialChainName: '', submissionUnit: '', createUserId: '' }
|
ElMessage.error('获取产品详情失败')
|
} finally {
|
loading.value = false
|
}
|
// 修改activeTab 默认值
|
if(priceList.value.length > 0){
|
|
activeTab.value = groupedPriceData.value.hasOwnProperty('enterprise') ? 'enterprise'
|
: groupedPriceData.value.hasOwnProperty('project') ? 'project' : 'personal'
|
}
|
|
}
|
|
// 监听产品ID变化
|
watch(currentProductId, (newProductId) => {
|
if (newProductId) {
|
fetchProductData(newProductId)
|
}
|
})
|
|
// 套件选择处理
|
const handleSuiteSelect = (suiteId: number, checked: any) => {
|
const isChecked = Boolean(checked)
|
if (isChecked) {
|
selectedSuiteIds.value.add(suiteId)
|
// 添加到购物车
|
const priceItem = priceList.value.find(item => item.id === suiteId)
|
if (priceItem) {
|
addToCart(priceItem)
|
}
|
} else {
|
selectedSuiteIds.value.delete(suiteId)
|
// 从购物车移除
|
removeFromCart(String(suiteId))
|
}
|
}
|
|
// 立即订购
|
const handleOrder = () => {
|
// 查询购物车信息进行展示
|
fetchCartItems()
|
// 获取一次性防重复提交token
|
orderApi.getIdempotencyToken(currentUserId.value).then((res: any) => {
|
if (res?.code === 200) {
|
idempotencyToken.value = res.data as string
|
}
|
}).catch(() => {})
|
showOrderPanel.value = true
|
showPricePanel.value = false
|
}
|
|
// 处理订购结果
|
const handleOrderResult = (selectedItems: any[]) => {}
|
|
// 启用/停用按钮
|
function handleToggleEnableStatus(row: any) {
|
// ... existing code ...
|
}
|
|
function returnPricePanel() {
|
showOrderPanel.value = false
|
showOrderStatus.value = false
|
showPricePanel.value = true
|
}
|
|
async function submitOrder() {
|
const items = orderSuites.value.filter(s => s.selected)
|
if (items.length === 0) {
|
ElMessage.warning('请至少选择一个套件')
|
return
|
}
|
// 检查是否有货币交易
|
const hasCurrency = items.some(item => item.priceType === 'CURRENCY')
|
if (hasCurrency) {
|
ElMessage.error('暂不支持货币交易,请选择积分或协议支付方式')
|
return
|
}
|
|
if (!idempotencyToken.value) {
|
ElMessage.error('下单Token获取失败,请返回重试')
|
return
|
}
|
const hasPoints = items.some(item => item.priceType === 'POINTS')
|
const hasAGREEMENT = items.some(item => item.priceType === 'AGREEMENT')
|
let paymentType = ''
|
if(hasPoints){
|
paymentType = '积分'
|
}else{
|
paymentType = '协议'
|
}
|
let processdefId: string = ''
|
const type = hasAGREEMENT ? 'trade_agreement' : 'trade_point';
|
// 获取工作流参数
|
const wkParamsRes: any = await workFlowApi.getWorkFlowParams({
|
type: type,
|
// unitId: '1',
|
businessKey: type
|
})
|
if(wkParamsRes?.code === 200 && wkParamsRes.data?.processTemplateId){
|
processdefId = wkParamsRes.data.processTemplateId
|
}else {
|
ElMessage.error('获取工作流参数失败!')
|
return
|
}
|
|
// 组装创建订单参数(CreateOrderDTO)
|
const payload = {
|
userId: currentUserId.value,
|
unitId: currentUnitId.value,
|
productName: productHeader.value.name,
|
providerName: productHeader.value.submissionUnit,
|
providerId: productHeader.value.createUserId,
|
paymentType: paymentType,
|
buyerRemarks: '',
|
processdefId: processdefId,
|
items: items.map(it => ({
|
pricingId: it.id,
|
productId: it.productId,
|
suiteName: getProductSuiteText(it.productSuite),
|
salesForm: getSalesFormText(it.salesForm),
|
customerType: getCustomerObjectText(it.customerObject),
|
accountLimit: it.accountQuantityUnlimited ? '不限' : String(it.accountQuantity),
|
concurrentNodes: it.concurrentNodeQuantityUnlimited ? '不限' : String(it.concurrentNodeQuantity),
|
priceType: (it.priceType === 'POINTS' ? '积分' : it.priceType === 'CURRENCY' ? '货币' : it.priceType === 'AGREEMENT' ? '协议' : '免费'),
|
priceUnit: getPriceUnitText(it.priceUnit),
|
unitPrice: (it.priceType === 'POINTS' ? it.pointsAmount : it.currencyAmount) || 0,
|
quantity: it.quantity,
|
duration: it.duration,
|
totalPrice: undefined,
|
providerId: productHeader.value.createUserId,
|
providerName: productHeader.value.submissionUnit,
|
remarks: ''
|
}))
|
}
|
|
try {
|
const res: any = await orderApi.createOrder(payload, idempotencyToken.value)
|
if (res?.code === 200) {
|
ElMessage.success('订单创建成功')
|
const data = res.data || {}
|
orderStatus.value = {
|
id: data.orderId || '—',
|
productName: data.productName || productHeader.value.name,
|
provider: data.providerName || productHeader.value.submissionUnit,
|
status: 'SUBMITTED',
|
submitTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString(),
|
applyTime: data.applyTime ? String(data.applyTime) : new Date().toLocaleString()
|
}
|
// 调用工作流接口发起审批流程,拿到流程实例ID后回写订单workflow_id
|
// 放后端处理
|
// try {
|
// // 根据是否包含协议明细,配置不同流程定义与业务Key(先用静态值占位)
|
// // const processdefId = hasAGREEMENT ? 'Process_Agreement_Static' : 'Process_Points_Static'
|
// const businessKey = hasAGREEMENT ? 'agreement_biz_key' : 'points_biz_key'
|
// const type = hasAGREEMENT ? 'trade_agreement' : 'trade_point';
|
// // 获取工作流参数
|
// const wkParamsRes: any = await workFlowApi.getWorkFlowParams({
|
// type: type,
|
// unitId: '1'
|
// })
|
// if(wkParamsRes?.code === 200 && wkParamsRes.data?.processTemplateId){
|
// const wfRes: any = await workFlowApi.startWorkflowAndComplete({
|
// processdefId: wkParamsRes.data.processTemplateId,
|
// userid: String(currentUserId.value || ''),
|
// businessKey: businessKey
|
// })
|
// if (wfRes?.code === 200 && wfRes.data?.processinstId) {
|
// await orderApi.updateWorkflowId(data.orderId, wfRes.data.processinstId)
|
// }
|
// }
|
//
|
// } catch (e) {
|
// console.warn('启动工作流失败或更新workflow_id失败', e)
|
// }
|
|
// 清空购物车(后端 + 本地状态)
|
try {
|
const clearRes: any = await cartApi.clearCart(currentUserId.value, currentUnitId.value,currentProductId.value ? currentProductId.value : '')
|
if (clearRes?.code === 200) {
|
cartItems.value = []
|
orderSuites.value = []
|
selectedSuiteIds.value.clear()
|
selectAllChecked.value = false
|
}
|
} catch (e) { /* ignore */ }
|
// 显示订单状态面板
|
showOrderPanel.value = false
|
showPricePanel.value = false
|
showOrderStatus.value = true
|
} else {
|
ElMessage.error(res?.msg || '创建订单失败')
|
}
|
} catch (e) {
|
ElMessage.error('创建订单失败')
|
}
|
}
|
|
// 购物车相关方法
|
const addToCart = async (priceItem: PriceItem) => {
|
try {
|
const cartData = {
|
pricingId: priceItem.id,
|
productId: priceItem.productId,
|
productName: productName.value,
|
suiteName: getProductSuiteText(priceItem.productSuite),
|
salesForm: getSalesFormText(priceItem.salesForm),
|
customerType: getCustomerObjectText(priceItem.customerObject),
|
accountLimit: priceItem.accountQuantityUnlimited ? '不限' : String(priceItem.accountQuantity),
|
concurrentNodes: priceItem.concurrentNodeQuantityUnlimited ? '不限' : String(priceItem.concurrentNodeQuantity),
|
priceType: priceItem.priceSettings.map(item => getPriceSettingText(item)).join(','),
|
priceUnit: getPriceUnitText(priceItem.priceUnit),
|
unitPrice: priceItem.pointsAmount || priceItem.currencyAmount || 0,
|
quantity: 1,
|
duration: 1
|
}
|
|
const res: any = await cartApi.addToCart(cartData, currentUserId.value, currentUnitId.value)
|
if (res?.code === 200) {
|
ElMessage.success('已添加到购物车')
|
} else {
|
ElMessage.error(res?.msg || '添加到购物车失败')
|
}
|
} catch (error) {
|
console.error('添加到购物车失败:', error)
|
ElMessage.error('添加到购物车失败')
|
}
|
}
|
|
const removeFromCart = async (pricingId: string) => {
|
try {
|
const res: any = await cartApi.removeFromCart(currentUserId.value, currentUnitId.value, currentProductId.value? currentProductId.value : '',pricingId)
|
if (res?.code === 200) {
|
ElMessage.success('已从购物车移除')
|
} else {
|
ElMessage.error(res?.msg || '从购物车移除失败')
|
}
|
} catch (error) {
|
console.error('从购物车移除失败:', error)
|
ElMessage.error('从购物车移除失败')
|
}
|
}
|
|
const updateCartItem = async (pricingId: number, quantity: number | null, duration: number | null) => {
|
try {
|
// 这里需要根据实际接口调整,可能需要传递更多参数
|
let res: any = {}
|
if(quantity){
|
res = await cartApi.updateCartItem(currentUserId.value, currentUnitId.value, currentProductId.value? currentProductId.value : '', pricingId, quantity)
|
}
|
if(duration){
|
res = await cartApi.updateCartItemDuration(currentUserId.value, currentUnitId.value, currentProductId.value? currentProductId.value : '', pricingId, duration)
|
}
|
if (res?.code === 200) {
|
ElMessage.success('购物车已更新')
|
} else {
|
ElMessage.error(res?.msg || '更新购物车失败')
|
}
|
} catch (error) {
|
console.error('更新购物车失败:', error)
|
ElMessage.error('更新购物车失败')
|
}
|
}
|
|
const fetchCartItems = async () => {
|
cartLoading.value = true
|
try {
|
const res: any = await cartApi.getCartItems(currentUserId.value, currentUnitId.value, currentProductId.value? currentProductId.value : '')
|
if (res?.code === 200) {
|
cartItems.value = res.data || []
|
// 将购物车数据转换为订单套件格式
|
orderSuites.value = cartItems.value.map((item: any) => ({
|
id: item.pricingId,
|
productId: String(item.productId),
|
productSuite: item.suiteName,
|
salesForm: mapSalesFormFromCN(item.salesForm),
|
customerObject: mapCustomerFromCN(item.customerType),
|
accountQuantity: item.accountLimit === '不限' ? 0 : Number(item.accountLimit),
|
accountQuantityUnlimited: item.accountLimit === '不限',
|
concurrentNodeQuantity: item.concurrentNodes === '不限' ? 0 : Number(item.concurrentNodes),
|
concurrentNodeQuantityUnlimited: item.concurrentNodes === '不限',
|
priceSettings: item.priceType.split(',').map((t: string) => mapPriceTypeFromCN(t)).filter(Boolean),
|
pointsAmount: item.priceType.includes('积分') ? item.unitPrice : 0,
|
currencyAmount: item.priceType.includes('货币') ? item.unitPrice : 0,
|
priceUnit: mapUnitFromCN(item.priceUnit),
|
enableStatus: 'ENABLED',
|
selected: true,
|
quantity: item.quantity || 1,
|
duration: item.duration || 1,
|
priceType: getDefaultPriceType({
|
priceSettings: item.priceType.split(',').map((t: string) => mapPriceTypeFromCN(t)).filter(Boolean)
|
} as any)
|
})) as any
|
} else {
|
orderSuites.value = []
|
ElMessage.error(res?.msg || '获取购物车信息失败')
|
}
|
} catch (error) {
|
console.error('获取购物车信息失败:', error)
|
orderSuites.value = []
|
ElMessage.error('获取购物车信息失败')
|
} finally {
|
cartLoading.value = false
|
}
|
}
|
|
// 处理数量变化
|
const handleQuantityChange = async (pricingId: number, quantity: number) => {
|
try {
|
await updateCartItem(pricingId, quantity, null) // 暂时传递默认duration为1
|
} catch (error) {
|
console.error('更新数量失败:', error)
|
}
|
}
|
|
// 处理年限变化
|
const handleDurationChange = async (pricingId: number, duration: number) => {
|
try {
|
// 这里可能需要根据实际接口调整,传递duration参数
|
await updateCartItem(pricingId, null, duration) // 暂时传递默认quantity为1
|
} catch (error) {
|
console.error('更新年限失败:', error)
|
}
|
}
|
|
// 获取销售形式文本
|
const getSalesFormText = (salesForm: string) => {
|
const map: Record<string, string> = {
|
BUYOUT: '买断',
|
LEASE: '租赁',
|
PRIVATE_INCREMENT: '私有增量包',
|
PUBLIC_INCREMENT: '公有增量包',
|
OTA: 'OTA服务',
|
CLOUD: '云服务',
|
RESOURCE_PACKAGE: '资源包',
|
PERSONAL: '个人'
|
}
|
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 renderPrice = (priceItem: PriceItem) => {
|
const segments: string[] = []
|
if (priceItem.priceSettings.includes('POINTS') && priceItem.pointsAmount > 0) {
|
segments.push(`${formatNumber(priceItem.pointsAmount)} / ${getPriceUnitText(priceItem.priceUnit)}`)
|
}
|
if (priceItem.priceSettings.includes('CURRENCY') && priceItem.currencyAmount > 0) {
|
segments.push(`${formatNumber(priceItem.currencyAmount)} / ${getPriceUnitText(priceItem.priceUnit)}`)
|
}
|
if (priceItem.priceSettings.includes('FREE')) {
|
segments.push('免费')
|
}
|
if (priceItem.priceSettings.includes('AGREEMENT')) {
|
segments.push(`每${getPriceUnitText(priceItem.priceUnit)}`)
|
}
|
return segments.join(';')
|
}
|
|
// 获取分组后的表头数据
|
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">
|
.default-main {
|
padding: 20px;
|
background-color: #f5f5f5;
|
min-height: 100vh;
|
position: relative;
|
z-index: 1;
|
}
|
|
// 全局表头文字大小调整
|
:deep(.el-table__header) {
|
th {
|
.cell {
|
font-size: 14px !important;
|
font-weight: 700;
|
}
|
}
|
}
|
|
.search-card {
|
margin-bottom: 20px;
|
}
|
|
.mt15 {
|
margin-top: 15px;
|
}
|
|
.price-viewer-container {
|
.pricing-table-container {
|
.pricing-tabs {
|
:deep(.el-tabs__header) {
|
margin-bottom: 2px;
|
}
|
/* 居中 tabs 导航 */
|
:deep(.el-tabs__nav-wrap) {
|
display: flex;
|
justify-content: center;
|
}
|
:deep(.el-tabs__nav) {
|
margin: 0 auto;
|
border: none !important;
|
}
|
/* 普通 tabs 样式 */
|
:deep(.el-tabs--card > .el-tabs__header .el-tabs__nav) {
|
border: none;
|
border-radius: 0;
|
background: transparent;
|
box-shadow: none;
|
display: flex;
|
gap: 8px;
|
}
|
:deep(.el-tabs--card > .el-tabs__header .el-tabs__item) {
|
border: 1px solid #e4e7ed;
|
border-radius: 6px;
|
margin: 0;
|
padding: 14px 24px;
|
color: #606266;
|
background: #fff;
|
transition: all .2s ease;
|
font-size: 14px;
|
font-weight: 500;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
&:hover {
|
border-color: #409eff;
|
box-shadow: 0 4px 8px rgba(64, 158, 255, 0.15);
|
}
|
}
|
:deep(.el-tabs__item:hover) {
|
color: #409eff;
|
background: #f5f7fa;
|
}
|
:deep(.el-tabs__item.is-active) {
|
font-weight: 600;
|
color: #409eff;
|
background: #ecf5ff;
|
border-color: #409eff;
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
|
|
&::after {
|
content: '';
|
position: absolute;
|
bottom: -1px;
|
left: 0;
|
right: 0;
|
height: 3px;
|
background: #409eff;
|
border-radius: 0 0 6px 6px;
|
}
|
}
|
}
|
|
.pricing-table-wrapper {
|
overflow-x: auto;
|
background: #fff;
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
padding: 0;
|
border: 1px solid #e4e7ed;
|
margin-top: 2px;
|
|
.pricing-table {
|
width: 100%;
|
border-collapse: collapse;
|
background: #fff;
|
border: 1px solid #e4e7ed;
|
border-radius: 8px;
|
overflow: visible;
|
|
// 统一所有单元格样式
|
th, td {
|
border-top: 1px solid #e4e7ed !important;
|
border-bottom: 1px solid #e4e7ed !important;
|
border-left: none !important;
|
border-right: none !important;
|
padding: 8px 10px;
|
text-align: center;
|
vertical-align: middle;
|
font-size: 14px;
|
line-height: 1.5;
|
color: #303133;
|
background: #fff;
|
transition: all 0.2s ease;
|
}
|
|
// 第一列样式
|
tr th:first-child, td:first-child{
|
font-weight: 700;
|
color: #303133;
|
background: #f8fafc;
|
text-align: left;
|
padding-left: 16px;
|
}
|
|
// 表头样式
|
th {
|
background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
|
font-weight: 600;
|
color: #303133;
|
border-bottom: 2px solid #e4e7ed !important;
|
border-left: none !important;
|
border-right: none !important;
|
position: relative;
|
}
|
|
.feature-column {
|
width: 140px;
|
min-width: 140px;
|
}
|
|
// 子表头样式已移除,销售形式行现在是普通行
|
|
// 功能标签样式
|
.feature-label {
|
font-weight: 600;
|
color: #303133;
|
text-align: left;
|
background: #f8fafc;
|
border-right: none !important;
|
}
|
|
// 功能值样式
|
.feature-value {
|
color: #303133;
|
background: #fff;
|
font-weight: 500;
|
}
|
|
// 行悬停效果
|
tbody tr:hover td {
|
background: #f0f7ff;
|
border-color: #b3d8ff !important;
|
}
|
|
// 偶数行背景
|
tbody tr:nth-child(even) td {
|
background: #fafbfc;
|
}
|
|
tbody tr:nth-child(even):hover td {
|
background: #e6f3ff;
|
}
|
|
// 强制确保最后一行有完整的边框
|
tbody tr:last-child td {
|
border-bottom: 1px solid #e4e7ed !important;
|
}
|
|
// 强制确保所有单元格都有完整的边框
|
tbody td {
|
border: 1px solid #e4e7ed !important;
|
}
|
|
// 特别处理表格底部边框
|
tbody tr:last-child {
|
border-bottom: 1px solid #e4e7ed !important;
|
}
|
|
// 使用更具体的选择器确保边框显示
|
.pricing-table tbody tr:last-child td {
|
border-bottom: 1px solid #e4e7ed !important;
|
}
|
|
// 确保表格本身有完整的边框
|
.pricing-table {
|
border: 1px solid #e4e7ed !important;
|
}
|
|
// 特别处理套件选择行的边框
|
.pricing-table tbody tr:last-child .feature-label,
|
.pricing-table tbody tr:last-child .feature-value {
|
border-bottom: 1px solid #e4e7ed !important;
|
}
|
|
// 确保所有行都有完整的边框
|
.pricing-table tbody tr td {
|
border-top: 1px solid #e4e7ed !important;
|
border-bottom: 1px solid #e4e7ed !important;
|
border-left: none !important;
|
border-right: none !important;
|
}
|
|
.order-methods {
|
display: flex;
|
flex-direction: column;
|
gap: 6px;
|
align-items: center;
|
|
.method-item {
|
font-size: 12px;
|
color: #3a7afe;
|
background: #f4f7ff;
|
border: 1px solid #e3ebff;
|
border-radius: 999px;
|
padding: 2px 10px;
|
}
|
}
|
|
.price-info {
|
display: flex;
|
flex-direction: column;
|
gap: 8px;
|
align-items: center;
|
|
.price-item {
|
display: flex;
|
align-items: center;
|
gap: 6px;
|
padding: 6px 10px;
|
border: 1px solid #f0f2f5;
|
border-radius: 6px;
|
background: #fff;
|
box-shadow: inset 0 -1px 0 rgba(0,0,0,0.02);
|
|
.price-lable-icon {
|
display: flex;
|
align-items: center;
|
gap: 4px;
|
}
|
|
.price-label {
|
font-size: 12px;
|
color: #909399;
|
white-space: nowrap;
|
}
|
|
.price-value {
|
font-weight: 600;
|
font-size: 16px;
|
white-space: nowrap;
|
|
&.points {
|
color: #e7900d;
|
}
|
|
&.currency {
|
color: #10b981;
|
}
|
|
&.free {
|
color: #67c23a;
|
}
|
}
|
|
.price-icon.points {
|
color: #e6a23c;
|
font-size: 14px;
|
}
|
|
.price-icon.currency {
|
color: #16a34a;
|
font-size: 16px;
|
font-weight: 600;
|
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
|
}
|
}
|
|
.add-checkbox {
|
margin-top: 4px;
|
}
|
}
|
|
.suite-selection {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
padding: 8px 0;
|
.add-cart-icon { color: #3a7afe; font-size:22px}
|
}
|
}
|
}
|
}
|
}
|
|
.dialog-footer {
|
display: flex;
|
justify-content: flex-end;
|
gap: 12px;
|
}
|
|
.no-price-data {
|
text-align: center;
|
padding: 60px 0;
|
background: #fff;
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
margin: 20px 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;
|
height: 700px !important;
|
}
|
|
:deep(.el-dialog__body) {
|
max-height: 70vh;
|
overflow-y: auto;
|
}
|
}
|
|
.order-header-card {
|
:deep(.el-card__body) { padding: 16px; }
|
.basic-grid { display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px 16px;}
|
.grid-item {
|
font-size: 14px;
|
color:#606266;
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
line-height: 2;
|
}
|
.label { color:#606266; font-weight: 500; }
|
.value { color:#303133; }
|
.value.strong { font-weight: 600; }
|
margin-bottom: 10px;
|
margin-top: 10px;
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
}
|
|
.points-info-card {
|
:deep(.el-card__body) { padding: 8px 10px; }
|
.points-grid {
|
display: grid;
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
gap: 8px 12px;
|
.points-item { font-size: 12px; color: #606266; }
|
.label { color: #606266; }
|
.value { font-weight: 600; color: #303133; }
|
.value.emphasis { color: #409eff; }
|
.value.warn { color: #e6a23c; }
|
}
|
}
|
|
.selected-suites-card {
|
:deep(.el-card__body) { padding: 8px; }
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
}
|
|
.suite-table-wrapper { overflow-x: auto; width: 100%; }
|
.suite-table {
|
width: 100%;
|
border-collapse: collapse;
|
background: #fff;
|
table-layout: fixed;
|
border: 1px solid #e4e7ed;
|
border-radius: 8px;
|
overflow: hidden;
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
}
|
.suite-table th,
|
.suite-table td {
|
border: 1px solid #e4e7ed;
|
padding: 8px 10px;
|
vertical-align: middle;
|
font-size: 14px;
|
line-height: 1.5;
|
color: #303133;
|
background: #fff;
|
transition: all 0.2s ease;
|
text-align: center;
|
}
|
|
.suite-table thead th {
|
background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
|
color: #303133;
|
font-weight: 600;
|
font-size: 14px;
|
border-bottom: 2px solid #e4e7ed;
|
position: relative;
|
}
|
|
.suite-table tbody tr:hover td {
|
background: #f0f7ff;
|
border-color: #b3d8ff;
|
}
|
|
.suite-table tbody tr:nth-child(even) td {
|
background: #fafbfc;
|
}
|
|
.suite-table tbody tr:nth-child(even):hover td {
|
background: #e6f3ff;
|
}
|
|
// 确保套件表格最后一行有完整的边框
|
.suite-table tbody tr:last-child td {
|
border-bottom: 1px solid #e4e7ed;
|
}
|
|
// 确保套件表格所有单元格都有完整的边框
|
.suite-table tbody td {
|
border: 1px solid #e4e7ed;
|
}
|
|
.table-toolbar {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 12px 16px;
|
background: #f8fafc;
|
border-top: 1px solid #e4e7ed;
|
border-radius: 0 0 8px 8px;
|
}
|
.toolbar-left {
|
display: flex;
|
align-items: center;
|
gap: 16px;
|
width: 100%;
|
justify-content: space-between;
|
}
|
.toolbar-right {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
}
|
.summary-info {
|
font-size: 14px;
|
color: #606266;
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
}
|
.summary-count {
|
color: #409eff;
|
font-weight: 600;
|
background: #ecf5ff;
|
padding: 4px 8px;
|
border-radius: 4px;
|
}
|
.summary-points {
|
color: #e6a23c;
|
font-weight: 600;
|
background: #fdf6ec;
|
padding: 4px 8px;
|
border-radius: 4px;
|
}
|
|
.suite-table .th-detail { text-align: center; width: 25%;}
|
.suite-table .th-spec { text-align: center; width: 25%;}
|
.suite-table .th-price { text-align: center; width: 18%;}
|
.suite-table .th-quantity { text-align: center; width: 12%;}
|
.suite-table .th-years { text-align: center; width: 12%;}
|
.suite-table .th-action { text-align: center; width: 8%;}
|
|
// 详情、规格、价格列的内容左对齐
|
.suite-table .cell-detail,
|
.suite-table .cell-spec,
|
.suite-table .cell-price {
|
text-align: left !important;
|
}
|
|
.cell-detail .detail-content { display: inline-flex; align-items: center; gap: 8px; }
|
.suite-name { font-weight: 600; color: #303133; }
|
.cell-spec .spec-line {
|
color: #606266;
|
font-size: 13px;
|
line-height: 18px;
|
padding: 2px 0;
|
|
// 规格列中数值的统一颜色
|
.spec-value {
|
color: #409eff;
|
font-weight: 500;
|
}
|
}
|
.cell-spec-gg {
|
display: flex;
|
flex-direction: column;
|
gap: 12px;
|
|
.spec-line-gg{
|
display: flex;
|
gap: 24px;
|
}
|
}
|
.cell-price .price-inline {
|
display: inline-flex;
|
align-items: center;
|
gap: 8px;
|
white-space: nowrap;
|
}
|
.cell-price .price-tag.points {
|
color: #E6A23C;
|
font-weight: 600;
|
background: #fdf6ec;
|
padding: 4px 8px;
|
border-radius: 4px;
|
}
|
.cell-price .price-tag.currency {
|
color: #F56C6C;
|
font-weight: 600;
|
background: #fef0f0;
|
padding: 4px 8px;
|
border-radius: 4px;
|
}
|
.cell-price .price-free {
|
color: #67C23A;
|
font-weight: 600;
|
background: #f0f9ff;
|
padding: 4px 8px;
|
border-radius: 4px;
|
}
|
.cell-price .price-agreement {
|
color: #409EFF;
|
background: #ecf5ff;
|
padding: 4px 8px;
|
border-radius: 4px;
|
}
|
.cell-summary .summary-item { font-size: 14px; color: #606266; line-height: 1.5; }
|
|
.order-status-panel {
|
.steps-wrapper {
|
padding: 20px 16px;
|
background: #fff;
|
border-radius: 8px 8px 0 0;
|
border-bottom: 1px solid #e4e7ed;
|
}
|
.result-text {
|
text-align: center;
|
margin: 24px 0 20px;
|
font-size: 18px;
|
color: #303133;
|
font-weight: 500;
|
}
|
.order-info-card {
|
:deep(.el-card__body) { padding: 0; }
|
border-radius: 0 0 8px 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
}
|
.order-info-title {
|
background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
|
padding: 16px 20px;
|
font-weight: 600;
|
border-bottom: 1px solid #e4e7ed;
|
color: #303133;
|
}
|
.order-info-table {
|
width: 100%;
|
border-collapse: collapse;
|
border: 1px solid #e4e7ed;
|
border-radius: 8px;
|
overflow: hidden;
|
|
td {
|
padding: 10px 12px;
|
border-bottom: 1px solid #e4e7ed;
|
border-right: 1px solid #e4e7ed;
|
vertical-align: middle;
|
font-size: 14px;
|
line-height: 1.5;
|
color: #303133;
|
background: #fff;
|
transition: all 0.2s ease;
|
}
|
|
td:last-child {
|
border-right: none;
|
}
|
|
tr:last-child td {
|
border-bottom: none;
|
}
|
|
.label {
|
width: 120px;
|
color: #606266;
|
font-weight: 500;
|
background: #f8fafc;
|
text-align: center;
|
}
|
|
.value {
|
color: #303133;
|
background: #fff;
|
}
|
|
.link {
|
text-align: right;
|
background: #fff;
|
}
|
|
tr:hover td {
|
background: #f0f7ff;
|
border-color: #b3d8ff;
|
}
|
|
tr:nth-child(even) td {
|
background: #fafbfc;
|
}
|
|
tr:nth-child(even):hover td {
|
background: #e6f3ff;
|
}
|
}
|
}
|
|
.order-summary { padding: 10px 0; display:flex; align-items:center; justify-content:flex-end; gap: 8px; }
|
.order-summary .summary-label { color:#606266; }
|
.order-summary .summary-value { font-weight:600; }
|
.order-summary .summary-value.highlight{ color:#409eff; }
|
.order-summary .summary-value.total{ color:#e6a23c; }
|
.order-summary .summary-value.currency{ color:#f56c6c; }
|
.footer {
|
display: flex;
|
flex-direction: row-reverse;
|
margin-right: 50px;
|
gap: 20px;
|
margin-top: 10px;
|
padding: 10px 0;
|
|
.el-button {
|
border-radius: 6px;
|
font-weight: 500;
|
padding: 12px 24px;
|
|
&.el-button--primary {
|
background: linear-gradient(135deg, #409eff 0%, #3a7afe 100%);
|
border: none;
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
|
&:hover {
|
background: linear-gradient(135deg, #66b1ff 0%, #5a8cff 100%);
|
box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
|
}
|
}
|
|
&:not(.el-button--primary) {
|
border: 1px solid #dcdfe6;
|
background: #fff;
|
|
&:hover {
|
border-color: #409eff;
|
color: #409eff;
|
}
|
}
|
}
|
}
|
|
// 确保页面整体层级正确
|
:deep(.el-card) {
|
position: relative;
|
z-index: 1;
|
}
|
|
// 修复可能的全局z-index冲突
|
:deep(.el-table__body-wrapper) {
|
z-index: 1 !important;
|
}
|
|
:deep(.el-table__header-wrapper) {
|
z-index: 1 !important;
|
}
|
|
// 数量和年限输入框样式
|
.quantity-input,
|
.duration-input {
|
width: 100px !important;
|
height: 35px !important;
|
|
:deep(.el-input__wrapper) {
|
padding: 0 8px !important;
|
}
|
|
:deep(.el-input__inner) {
|
width: 30px !important;
|
text-align: center !important;
|
min-width: 30px !important;
|
max-width: 30px !important;
|
}
|
|
:deep(.el-input-number__decrease),
|
:deep(.el-input-number__increase) {
|
width: 32px !important;
|
height: 32px !important;
|
}
|
}
|
|
// 使用更强的选择器确保样式生效
|
.suite-table .quantity-input,
|
.suite-table .duration-input {
|
:deep(.el-input__inner) {
|
width: 40px !important;
|
text-align: center !important;
|
min-width: 40px !important;
|
max-width: 40px !important;
|
box-sizing: border-box !important;
|
}
|
}
|
|
// 全局样式覆盖,确保输入框内部宽度生效
|
:deep(.el-input-number.quantity-input .el-input__inner),
|
:deep(.el-input-number.duration-input .el-input__inner) {
|
width: 40px !important;
|
text-align: center !important;
|
min-width: 40px !important;
|
max-width: 40px !important;
|
box-sizing: border-box !important;
|
}
|
</style>
|