| | |
| | | </div> |
| | | </template> |
| | | |
| | | <el-descriptions v-if="productDetail" :column="3" border> |
| | | <el-descriptions-item label="产品名称">{{ productDetail.name }}</el-descriptions-item> |
| | | <el-descriptions-item label="提报单位">{{ productDetail.submitUnit }}</el-descriptions-item> |
| | | <el-descriptions-item label="提报人">{{ productDetail.submitter }}</el-descriptions-item> |
| | | <el-descriptions-item label="行业领域">{{ productDetail.industry }}</el-descriptions-item> |
| | | <el-descriptions-item label="单位工程">{{ productDetail.projectUnit }}</el-descriptions-item> |
| | | <el-descriptions-item label="产业阶段">{{ productDetail.industryStage }}</el-descriptions-item> |
| | | <el-descriptions-item label="产品类型">{{ productDetail.productType }}</el-descriptions-item> |
| | | <el-descriptions v-if="productDetail" :column="2" border> |
| | | <el-descriptions-item label="产品名称" label-width="10%">{{ productDetail.name }}</el-descriptions-item> |
| | | <el-descriptions-item label="提报单位" label-width="10%">{{ productDetail.submitUnit }}</el-descriptions-item> |
| | | <el-descriptions-item label="提报人" label-width="10%">{{ productDetail.submitter }}</el-descriptions-item> |
| | | <el-descriptions-item label="行业领域" label-width="10%">{{ productDetail.industry }}</el-descriptions-item> |
| | | <el-descriptions-item label="单位工程" label-width="10%">{{ productDetail.projectUnit }}</el-descriptions-item> |
| | | <el-descriptions-item label="产业阶段" label-width="10%">{{ productDetail.industryStage }}</el-descriptions-item> |
| | | <el-descriptions-item label="产品类型"label-width="10%">{{ productDetail.productType }}</el-descriptions-item> |
| | | <el-descriptions-item label="产品简介" :span="3"> |
| | | <div class="intro">{{ productDetail.description }}</div> |
| | | </el-descriptions-item> |
| | |
| | | </div> |
| | | </template> |
| | | <div class="pricing-cards-container" v-if="priceList.length > 0"> |
| | | <div class="pricing-cards-wrapper"> |
| | | <div |
| | | v-for="pricing in priceList" |
| | | :key="pricing.id" |
| | | class="pricing-card" |
| | | :class="{ |
| | | 'pricing-card-enabled': pricing.enableStatus === 'ENABLED', |
| | | 'pricing-card-disabled': pricing.enableStatus === 'DISABLED' |
| | | }" |
| | | > |
| | | <div |
| | | v-for="group in priceList" |
| | | :key="group.suiteName" |
| | | class="pricing-group" |
| | | > |
| | | <div class="group-header">{{ group.suiteName }}</div> |
| | | <div class="pricing-cards-wrapper"> |
| | | <div |
| | | v-for="pricing in group.items" |
| | | :key="pricing.id" |
| | | class="pricing-card" |
| | | :class="{ |
| | | 'pricing-card-enabled': pricing.enableStatus === 'ENABLED', |
| | | 'pricing-card-disabled': pricing.enableStatus === 'DISABLED' |
| | | }" |
| | | > |
| | | <div class="pricing-card-table"> |
| | | <div class="pricing-row"> |
| | | <div class="pricing-cell label-cell">销售形式</div> |
| | |
| | | <div class="pricing-cell value-cell purchase-methods-cell"> |
| | | <div class="method-item"> |
| | | <span class="method-label">积分:</span> |
| | | <span class="method-value points">{{ pricing.priceSettings?.includes('POINTS') ? (pricing.pointsAmount || '-') + '/套' : '-' }}</span> |
| | | <span class="method-value points">{{ pricing.priceSettings?.includes('POINTS') ? (pricing.pointsPrice || '-') + '/' + getPriceUnitText(pricing.priceUnit) : '-' }}</span> |
| | | </div> |
| | | <div class="method-item"> |
| | | <div class="method-item" v-if="pricing.priceSettings?.includes('CURRENCY')"> |
| | | <span class="method-label">货币:</span> |
| | | <span class="method-value currency">{{ pricing.priceSettings?.includes('CURRENCY') ? (pricing.currencyAmount || '-') + '/套' : '-' }}</span> |
| | | <span class="method-value currency">{{ (pricing.currencyPrice || '-') + '/' + getPriceUnitText(pricing.priceUnit) }}</span> |
| | | </div> |
| | | <div class="method-item"> |
| | | <div class="method-item" v-if="pricing.priceSettings?.includes('AGREEMENT')"> |
| | | <span class="method-label">协议:</span> |
| | | <span class="method-value agreement">{{ pricing.priceSettings?.includes('AGREEMENT') ? '每套' : '-' }}</span> |
| | | <span class="method-value agreement">{{ '/' + getPriceUnitText(pricing.priceUnit) }}</span> |
| | | </div> |
| | | <div class="method-item"> |
| | | <div class="method-item" v-if="pricing.priceSettings?.includes('FREE')"> |
| | | <span class="method-label">免费:</span> |
| | | <span class="method-value free">{{ pricing.priceSettings?.includes('FREE') ? '免费' : '-' }}</span> |
| | | <span class="method-value free">{{ '/' + getPriceUnitText(pricing.priceUnit) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </el-icon> |
| | | {{ pricing.enableStatus === 'ENABLED' ? '停用' : '启用' }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | size="large" |
| | | class="price-form" |
| | | > |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产品套件" prop="productSuite"> |
| | | <el-select v-model="formData.productSuite" placeholder="请选择" style="width: 100%"> |
| | | |
| | | <el-form-item label="产品套件" prop="productSuite"> |
| | | <el-select v-model="formData.productSuite" placeholder="请选择" style="width: 60%"> |
| | | <el-option v-for="opt in productSuiteOptions" :key="opt.value" :label="opt.label" :value="opt.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客户对象" prop="customerObject"> |
| | | <el-form-item label="客户对象" prop="customerObject"> |
| | | <el-radio-group v-model="formData.customerObject"> |
| | | <el-radio v-for="opt in customerObjectOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | </el-form-item> |
| | | <el-form-item label="销售形式" prop="salesForm" class="sales-form-item"> |
| | | <el-radio-group v-model="formData.salesForm" class="sales-form-group"> |
| | | <el-radio v-for="opt in salesFormOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | |
| | | <el-form-item label="账户数量" prop="accountQuantity"> |
| | | <div class="quantity-input-group"> |
| | | <el-input-number v-model="formData.accountQuantity" :min="1" :controls="false" style="width: 180px" :disabled="formData.accountQuantityUnlimited" /> |
| | | <el-checkbox v-model="formData.accountQuantityUnlimited" style="margin-left: 6px">不限</el-checkbox> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | |
| | | <el-form-item label="并发节点" prop="concurrentNodeQuantity"> |
| | | <div class="quantity-input-group"> |
| | | <el-input-number v-model="formData.concurrentNodeQuantity" :min="1" :controls="false" style="width: 180px" :disabled="formData.concurrentNodeQuantityUnlimited" /> |
| | | <el-checkbox v-model="formData.concurrentNodeQuantityUnlimited" style="margin-left: 6px">不限</el-checkbox> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | |
| | | <el-form-item label="价格设置" prop="priceSettings"> |
| | | <div class="price-settings"> |
| | | <div class="price-setting-item"> |
| | | <el-checkbox label="POINTS" :disabled="formData.priceSettings.includes('FREE')" v-model="checkboxPoints">积分</el-checkbox> |
| | | <el-input-number v-if="formData.priceSettings.includes('POINTS')" v-model="formData.pointsAmount" :min="0" :precision="2" :controls="false" placeholder="积分金额" style="width: 120px; margin-left: 6px" /> |
| | | <el-input-number v-if="formData.priceSettings.includes('POINTS')" v-model="formData.pointsPrice" :min="0" :precision="2" :controls="false" placeholder="积分金额" style="width: 120px; margin-left: 6px" /> |
| | | </div> |
| | | <div class="price-setting-item"> |
| | | <el-checkbox label="CURRENCY" :disabled="formData.priceSettings.includes('FREE')" v-model="checkboxCurrency">货币</el-checkbox> |
| | | <el-input-number v-if="formData.priceSettings.includes('CURRENCY')" v-model="formData.currencyAmount" :min="0" :precision="2" :controls="false" placeholder="货币金额" style="width: 120px; margin-left: 6px" /> |
| | | <el-input-number v-if="formData.priceSettings.includes('CURRENCY')" v-model="formData.currencyPrice" :min="0" :precision="2" :controls="false" placeholder="货币金额" style="width: 120px; margin-left: 6px" /> |
| | | </div> |
| | | <div class="price-setting-item"> |
| | | <el-checkbox label="AGREEMENT" :disabled="formData.priceSettings.includes('FREE')" v-model="checkboxAgreement">协议</el-checkbox> |
| | |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <el-row :gutter="8"> |
| | | <el-col :span="12"> |
| | | |
| | | |
| | | <el-form-item label="价格单位" prop="priceUnit"> |
| | | <el-select v-model="formData.priceUnit" placeholder="请选择" style="width: 100%"> |
| | | <el-option v-for="opt in priceUnitOptions" :key="opt.value" :label="opt.label" :value="opt.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | |
| | | <el-form-item label="启用状态" prop="enableStatus"> |
| | | <el-radio-group v-model="formData.enableStatus"> |
| | | <el-radio v-for="opt in enableStatusOptions" :key="opt.value" :label="opt.value">{{ opt.label }}</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | |
| | | |
| | | |
| | | <el-form-item label="备注"> |
| | | <el-input type="textarea" v-model="formData.remark" :rows="3" placeholder="请输入备注" /> |
| | |
| | | import { useRoute } from 'vue-router' |
| | | import { Goods, Lock, Unlock } from '@element-plus/icons-vue' |
| | | import ProductPriceViewer from '@/views/productManage/productPriceViewer/index.vue' |
| | | import productPricingApi from '@/api/productPricingApi' |
| | | |
| | | |
| | | interface ProductDetail { |
| | | id: string |
| | |
| | | concurrentNodeQuantity: number | '' |
| | | concurrentNodeQuantityUnlimited: boolean |
| | | priceSettings: Array<'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE'> |
| | | pointsAmount: number | '' |
| | | currencyAmount: number | '' |
| | | pointsPrice: number | '' |
| | | currencyPrice: number | '' |
| | | priceUnit: '' | 'SET' | 'SET_PER_YEAR' | 'YEAR' |
| | | enableStatus: 'ENABLED' | 'DISABLED' |
| | | authorizationStartTime?: string |
| | |
| | | |
| | | // 模拟产品详情数据源 |
| | | const mockProductMap: Record<string, ProductDetail> = { |
| | | '10001': { |
| | | id: '10001', |
| | | '1': { |
| | | id: '1', |
| | | name: '数字化产品A', |
| | | submitUnit: '中交一公局', |
| | | submitter: '张三', |
| | |
| | | description: '本产品定位为以建设期BIM数字资产作为数字底盘,结合项目运营维保需求的实时性、交互性、便捷性的三维可视化运维管理系统。系统提供项目数字化、智能化运维管理功能,能够解决建筑运行维护管理中的实际问题,实现信息快速整合与查询、信息有效共享与传递,提升项目综合管理与维护水平。', |
| | | shelfStatus: '待上架' |
| | | }, |
| | | '10002': { |
| | | id: '10002', |
| | | '2': { |
| | | id: '2', |
| | | name: '数字化产品B', |
| | | submitUnit: '中交二航局', |
| | | submitter: '李四', |
| | |
| | | productSuite: '', |
| | | salesForm: 'BUYOUT', |
| | | customerObject: 'ENTERPRISE', |
| | | accountQuantity: '', |
| | | accountQuantity: 1, |
| | | accountQuantityUnlimited: false, |
| | | concurrentNodeQuantity: '', |
| | | concurrentNodeQuantity: 1, |
| | | concurrentNodeQuantityUnlimited: false, |
| | | priceSettings: [], |
| | | pointsAmount: '', |
| | | currencyAmount: '', |
| | | pointsPrice: '', |
| | | currencyPrice: '', |
| | | priceUnit: '', |
| | | enableStatus: 'ENABLED', |
| | | authorizationStartTime: undefined, |
| | |
| | | |
| | | // 选项 |
| | | const productSuiteOptions = [ |
| | | { label: '企业私有SaaS版许可', value: 'ENTERPRISE_PRIVATE_SAAS_LICENSE' }, |
| | | { label: '企业私有SaaS版OTA服务', value: 'ENTERPRISE_PRIVATE_SAAS_OTA' }, |
| | | { label: '企业私有SaaS版用户增量包', value: 'ENTERPRISE_PRIVATE_SAAS_USER_INCREMENT' }, |
| | | { label: '企业公有SaaS版许可', value: 'ENTERPRISE_PUBLIC_SAAS_LICENSE' }, |
| | | { label: '企业公有SaaS版OTA服务', value: 'ENTERPRISE_PUBLIC_SAAS_OTA' }, |
| | | { label: '企业公有SaaS版用户增量包', value: 'ENTERPRISE_PUBLIC_SAAS_USER_INCREMENT' }, |
| | | { label: '个人公有SaaS化许可', value: 'PERSONAL_PUBLIC_SAAS_LICENSE' }, |
| | | { label: 'web软件', value: 'WEB_SOFTWARE' }, |
| | | { label: '桌面软件', value: 'DESKTOP_SOFTWARE' } |
| | | { label: '企业私有SaaS版许可', value: '企业私有SaaS版许可' }, |
| | | { label: '企业私有SaaS版OTA服务', value: '企业私有SaaS版OTA服务' }, |
| | | { label: '企业私有SaaS版用户增量包', value: '企业私有SaaS版用户增量包' }, |
| | | { label: '企业公有SaaS版许可', value: '企业公有SaaS版许可' }, |
| | | { label: '企业公有SaaS版OTA服务', value: '企业公有SaaS版OTA服务' }, |
| | | { label: '企业公有SaaS版用户增量包', value: '企业公有SaaS版用户增量包' }, |
| | | { label: '个人公有SaaS化许可', value: '个人公有SaaS化许可' }, |
| | | { label: 'web软件', value: 'web软件' }, |
| | | { label: '桌面软件', value: '桌面软件' } |
| | | ] |
| | | const salesFormOptions = [ |
| | | { label: '买断', value: 'BUYOUT' }, |
| | |
| | | // 若选择 FREE,清空其他 |
| | | if (key === 'FREE') { |
| | | formData.priceSettings = ['FREE'] |
| | | formData.pointsAmount = '' |
| | | formData.currencyAmount = '' |
| | | formData.pointsPrice = '' |
| | | formData.currencyPrice = '' |
| | | return |
| | | } |
| | | // 若此前有 FREE,去掉 FREE |
| | |
| | | validator: (_: any, value: any[], cb: any) => { |
| | | if (!value || value.length === 0) return cb(new Error('请至少选择一种价格设置')) |
| | | if (value.includes('FREE') && value.length > 1) return cb(new Error('免费选项不能与其他选项同时选择')) |
| | | if (value.includes('POINTS') && (formData.pointsAmount === '' || formData.pointsAmount === undefined)) return cb(new Error('请输入积分金额')) |
| | | if (value.includes('CURRENCY') && (formData.currencyAmount === '' || formData.currencyAmount === undefined)) return cb(new Error('请输入货币金额')) |
| | | if (value.includes('POINTS') && (formData.pointsPrice === '' || formData.pointsPrice === undefined)) return cb(new Error('请输入积分金额')) |
| | | if (value.includes('CURRENCY') && (formData.currencyPrice === '' || formData.currencyPrice === undefined)) return cb(new Error('请输入货币金额')) |
| | | cb() |
| | | }, |
| | | trigger: 'change' |
| | |
| | | return map[unit || ''] || unit || '' |
| | | } |
| | | |
| | | function fetchProductDetail(productId?: string) { |
| | | async function fetchProductDetail(productId?: string) { |
| | | if (!productId) { |
| | | productDetail.value = null |
| | | ElMessage.warning('未提供产品ID,无法加载产品详情') |
| | | return |
| | | } |
| | | // 模拟异步 |
| | | loading.value = true |
| | | setTimeout(() => { |
| | | try { |
| | | productDetail.value = mockProductMap[productId] || null |
| | | // 构造卡片所需的演示定价数据 |
| | | priceList.value = productDetail.value |
| | | ? [ |
| | | { |
| | | id: 1, |
| | | productId: productId, |
| | | productSuite: 'STANDARD', |
| | | salesForm: 'BUYOUT', |
| | | customerObject: 'ENTERPRISE', |
| | | accountQuantity: 50, |
| | | accountQuantityUnlimited: false, |
| | | concurrentNodeQuantity: 50, |
| | | concurrentNodeQuantityUnlimited: false, |
| | | priceSettings: ['POINTS'], |
| | | pointsAmount: 50000, |
| | | currencyAmount: 0, |
| | | priceUnit: 'SET_PER_YEAR', |
| | | enableStatus: 'ENABLED', |
| | | authorizationStartTime: '2025-01-01', |
| | | authorizationEndTime: '2025-12-31', |
| | | remark: '演示数据' |
| | | }, |
| | | { |
| | | id: 2, |
| | | productId: productId, |
| | | productSuite: 'PRO', |
| | | salesForm: 'PRIVATE_INCREMENT', |
| | | customerObject: 'ENTERPRISE', |
| | | accountQuantity: 100, |
| | | accountQuantityUnlimited: false, |
| | | concurrentNodeQuantity: 100, |
| | | concurrentNodeQuantityUnlimited: false, |
| | | priceSettings: ['CURRENCY'], |
| | | pointsAmount: 0, |
| | | currencyAmount: 7500, |
| | | priceUnit: 'SET', |
| | | enableStatus: 'DISABLED', |
| | | authorizationStartTime: '2025-01-01', |
| | | authorizationEndTime: '2025-06-30', |
| | | remark: '演示数据' |
| | | } |
| | | ] |
| | | : [] |
| | | // const detailRes: any = await productApi.getProductDetail({ id: productId }) |
| | | // if (detailRes?.code === 200) { |
| | | // productDetail.value = detailRes.data || null |
| | | // } else { |
| | | // productDetail.value = null |
| | | // ElMessage.error(detailRes?.msg || '获取产品详情失败') |
| | | // } |
| | | await loadPricingList(productId) |
| | | } catch (e) { |
| | | productDetail.value = null |
| | | ElMessage.error('获取产品详情失败') |
| | | } finally { |
| | | loading.value = false |
| | | }, 300) |
| | | } |
| | | } |
| | | |
| | | function mapSalesFormToCN(val: string) { |
| | | const map: Record<string, string> = { |
| | | BUYOUT: '买断', |
| | | LEASE: '租赁', |
| | | PRIVATE_INCREMENT: '私有增包量', |
| | | PUBLIC_INCREMENT: '公有增包量', |
| | | OTA: 'OTA服务', |
| | | CLOUD: '云服务', |
| | | RESOURCE_PACKAGE: '资源包', |
| | | PERSONAL: '个人', |
| | | } |
| | | return map[val] || val |
| | | } |
| | | |
| | | 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 mapCustomerToCN(val: string) { |
| | | const map: Record<string, string> = { ENTERPRISE: '企业', PERSONAL: '个人', PROJECT_DEPARTMENT: '项目部' } |
| | | return map[val] || val |
| | | } |
| | | |
| | | function mapCustomerFromCN(val: string) { |
| | | const map: Record<string, string> = { '企业': 'ENTERPRISE', '个人': 'PERSONAL', '项目部': 'PROJECT_DEPARTMENT' } |
| | | return (map[val] as any) || val |
| | | } |
| | | |
| | | function mapUnitToCN(val: string) { |
| | | const map: Record<string, string> = { SET: '套', SET_PER_YEAR: '套/年', YEAR: '年' } |
| | | return map[val] || val |
| | | } |
| | | |
| | | function mapUnitFromCN(val: string) { |
| | | const map: Record<string, string> = { '套': 'SET', '套/年': 'SET_PER_YEAR', '年': 'YEAR' } |
| | | return (map[val] as any) || val |
| | | } |
| | | |
| | | function mapPriceTypeToCN(key: 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE') { |
| | | const map: Record<string, string> = { POINTS: '积分', CURRENCY: '货币', AGREEMENT: '协议', FREE: '免费' } |
| | | return map[key] |
| | | } |
| | | |
| | | function mapPriceTypeFromCN(val: string): 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE' | undefined { |
| | | const map: Record<string, any> = { '积分': 'POINTS', '货币': 'CURRENCY', '协议': 'AGREEMENT', '免费': 'FREE' } |
| | | return map[val] |
| | | } |
| | | |
| | | async function loadPricingList(productId: string) { |
| | | try { |
| | | const res: any = await productPricingApi.listByProductId(productId) |
| | | if (res?.code === 200) { |
| | | const list = Array.isArray(res.data) ? res.data : [] |
| | | const newList = list.map((it: any) => { |
| | | const priceSettings = it.priceType.split(',').map((t:string) => mapPriceTypeFromCN(t)) |
| | | const pointsPrice = priceSettings.includes('POINTS') ? Number(it.pointsPrice || 0) : 0 |
| | | const currencyPrice = priceSettings.includes('CURRENCY') ? Number(it.currencyPrice || 0) : 0 |
| | | return { |
| | | id: it.id, |
| | | productId: String(it.productId || productId), |
| | | productSuite: it.suiteName || '', |
| | | salesForm: mapSalesFormFromCN(it.salesForm) as any, |
| | | customerObject: mapCustomerFromCN(it.customerType) as any, |
| | | accountQuantityUnlimited: (it.accountLimit === '不限'), |
| | | accountQuantity: it.accountLimit === '不限' ? '' : Number(it.accountLimit || ''), |
| | | concurrentNodeQuantityUnlimited: (it.concurrentNodes === '不限'), |
| | | concurrentNodeQuantity: it.concurrentNodes === '不限' ? '' : Number(it.concurrentNodes || ''), |
| | | priceSettings: priceSettings, |
| | | pointsPrice, |
| | | currencyPrice, |
| | | priceUnit: mapUnitFromCN(it.priceUnit) as any, |
| | | enableStatus: it.isActive ? 'ENABLED' : 'DISABLED', |
| | | authorizationStartTime: undefined, |
| | | authorizationEndTime: undefined, |
| | | remark: it.description || '', |
| | | } |
| | | }) |
| | | const groupedList = newList.reduce((acc: any[], it: any) => { |
| | | // 按productSuite分组 |
| | | const productSuite = it.productSuite |
| | | if (!acc[productSuite]) { |
| | | acc[productSuite] = [] |
| | | } |
| | | acc[productSuite].push(it) |
| | | return acc |
| | | }, {}) |
| | | const newGroupedList:any = [] |
| | | Object.entries(groupedList).forEach(([k,v]) =>{ |
| | | newGroupedList.push({ |
| | | suiteName: k, |
| | | items: v |
| | | }) |
| | | }) |
| | | priceList.value = newGroupedList |
| | | console.log(priceList.value) |
| | | console.log(groupedList) |
| | | } else { |
| | | priceList.value = [] |
| | | } |
| | | } catch (e) { |
| | | priceList.value = [] |
| | | } |
| | | } |
| | | |
| | | function buildPayloadFromRow(row: any, overrides: Partial<any> = {}) {const priceTypeCN = row.priceSettings?.[0] ? mapPriceTypeToCN(row.priceSettings[0]) : undefined |
| | | |
| | | return { |
| | | id: row.id, |
| | | productId: Number(row.productId), |
| | | productName: productDetail.value?.name, |
| | | suiteName: row.productSuite, |
| | | salesForm: mapSalesFormToCN(row.salesForm), |
| | | customerType: mapCustomerToCN(row.customerObject), |
| | | accountLimit: row.accountQuantityUnlimited ? '不限' : String(row.accountQuantity || ''), |
| | | concurrentNodes: row.concurrentNodeQuantityUnlimited ? '不限' : String(row.concurrentNodeQuantity || ''), |
| | | priceType: row.priceSettings.map((t:string)=>mapPriceTypeToCN(t as any)).join(','), |
| | | priceUnit: mapUnitToCN(row.priceUnit || ''), |
| | | pointsPrice: row.priceSettings.includes('POINTS') ? row.pointsPrice : '', |
| | | currencyPrice: row.priceSettings.includes('CURRENCY') ? row.currencyPrice : '', |
| | | isActive: row.enableStatus === 'ENABLED', |
| | | description: row.remark || '', |
| | | ...overrides, |
| | | } |
| | | } |
| | | |
| | | // 查看价格 |
| | |
| | | isEditMode.value = false |
| | | resetForm() |
| | | formData.productId = currentProductId.value |
| | | formData.id = generateId() |
| | | dialogVisible.value = true |
| | | } |
| | | |
| | |
| | | isEditMode.value = true |
| | | resetForm() |
| | | Object.assign(formData, { |
| | | id: row.id ?? generateId(), |
| | | id: row.id, |
| | | productId: currentProductId.value, |
| | | productSuite: row.productSuite || '', |
| | | salesForm: row.salesForm || 'BUYOUT', |
| | |
| | | concurrentNodeQuantity: row.concurrentNodeQuantity ?? '', |
| | | concurrentNodeQuantityUnlimited: !!row.concurrentNodeQuantityUnlimited, |
| | | priceSettings: Array.isArray(row.priceSettings) ? row.priceSettings : [], |
| | | pointsAmount: row.pointsAmount ?? '', |
| | | currencyAmount: row.currencyAmount ?? '', |
| | | pointsPrice: row.pointsPrice ?? '', |
| | | currencyPrice: row.currencyPrice ?? '', |
| | | priceUnit: row.priceUnit || '', |
| | | enableStatus: row.enableStatus || 'ENABLED', |
| | | authorizationStartTime: row.authorizationStartTime, |
| | |
| | | type: 'warning' |
| | | } |
| | | ) |
| | | .then(() => { |
| | | const idx = priceList.value.findIndex((p: any) => p.id === row.id) |
| | | if (idx !== -1) priceList.value.splice(idx, 1) |
| | | .then(async () => { |
| | | await productPricingApi.remove(row.id) |
| | | ElMessage.success('删除成功') |
| | | if (currentProductId.value) await loadPricingList(currentProductId.value) |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info('已取消删除') |
| | |
| | | productSuite: '', |
| | | salesForm: 'BUYOUT', |
| | | customerObject: 'ENTERPRISE', |
| | | accountQuantity: '', |
| | | accountQuantity: 1, |
| | | accountQuantityUnlimited: false, |
| | | concurrentNodeQuantity: '', |
| | | concurrentNodeQuantity: 1, |
| | | concurrentNodeQuantityUnlimited: false, |
| | | priceSettings: [], |
| | | pointsAmount: '', |
| | | currencyAmount: '', |
| | | pointsPrice: '', |
| | | currencyPrice: '', |
| | | priceUnit: '', |
| | | enableStatus: 'ENABLED', |
| | | authorizationStartTime: undefined, |
| | |
| | | if (formRef.value) formRef.value.clearValidate() |
| | | } |
| | | |
| | | function generateId() { |
| | | const maxId = priceList.value.reduce((m: number, it: any) => Math.max(m, Number(it.id || 0)), 0) |
| | | return maxId + 1 |
| | | } |
| | | |
| | | async function handleSave() { |
| | | try { |
| | |
| | | return |
| | | } |
| | | |
| | | if (!currentProductId.value) { |
| | | ElMessage.warning('未提供产品ID,无法保存定价') |
| | | return |
| | | } |
| | | |
| | | const selectedTypes = formData.priceSettings |
| | | if (!selectedTypes || selectedTypes.length === 0) { |
| | | ElMessage.warning('请至少选择一种价格设置') |
| | | return |
| | | } |
| | | |
| | | const mapSalesFormToCN = (val: string) => ({ |
| | | BUYOUT: '买断', |
| | | LEASE: '租赁', |
| | | PRIVATE_INCREMENT: '私有增包量', |
| | | PUBLIC_INCREMENT: '公有增包量', |
| | | OTA: 'OTA服务', |
| | | CLOUD: '云服务', |
| | | RESOURCE_PACKAGE: '资源包', |
| | | PERSONAL: '个人', |
| | | } as any)[val] || val |
| | | |
| | | const mapCustomerToCN = (val: string) => ({ ENTERPRISE: '企业', PERSONAL: '个人', PROJECT_DEPARTMENT: '项目部' } as any)[val] || val |
| | | const mapUnitToCN = (val: string) => ({ SET: '套', SET_PER_YEAR: '套/年', YEAR: '年' } as any)[val] || val |
| | | const mapPriceTypeToCN = (key: 'POINTS' | 'CURRENCY' | 'AGREEMENT' | 'FREE') => ({ POINTS: '积分', CURRENCY: '货币', AGREEMENT: '协议', FREE: '免费' } as any)[key] |
| | | |
| | | const buildPayload = () => { |
| | | |
| | | return { |
| | | id: isEditMode.value && formData.id ? formData.id : undefined, |
| | | productId: Number(currentProductId.value), |
| | | productName: productDetail.value?.name, |
| | | suiteName: formData.productSuite, |
| | | salesForm: mapSalesFormToCN(formData.salesForm), |
| | | customerType: mapCustomerToCN(formData.customerObject), |
| | | accountLimit: formData.accountQuantityUnlimited ? '不限' : String(formData.accountQuantity || ''), |
| | | concurrentNodes: formData.concurrentNodeQuantityUnlimited ? '不限' : String(formData.concurrentNodeQuantity || ''), |
| | | priceType: formData.priceSettings.map(t=>mapPriceTypeToCN(t)).join(','), |
| | | priceUnit: mapUnitToCN(formData.priceUnit || ''), |
| | | pointsPrice: formData.pointsPrice, |
| | | currencyPrice: formData.currencyPrice, |
| | | isActive: formData.enableStatus === 'ENABLED', |
| | | description: formData.remark || '', |
| | | } |
| | | } |
| | | |
| | | saving.value = true |
| | | try { |
| | | const record: PriceItem = JSON.parse(JSON.stringify(formData)) |
| | | const idx = priceList.value.findIndex((p: any) => p.id === record.id) |
| | | if (idx === -1) { |
| | | priceList.value.push(record) |
| | | if (isEditMode.value && formData.id) { |
| | | const payload = buildPayload() |
| | | const res: any = await productPricingApi.update(payload) |
| | | if (res?.code === 200) { |
| | | ElMessage.success('修改成功') |
| | | } else { |
| | | ElMessage.error(res?.msg || '修改失败') |
| | | return |
| | | } |
| | | } else { |
| | | priceList.value.splice(idx, 1, record) |
| | | // const tasks = selectedTypes.map(t => productPricingApi.add(buildPayload(t))) |
| | | // const results: any[] = await Promise.all(tasks) |
| | | // const ok = results.every(r => r && r.code === 200) |
| | | const results: any = await productPricingApi.add(buildPayload()) |
| | | if (results?.code !== 200) { |
| | | ElMessage.error('部分定价保存失败') |
| | | return |
| | | } |
| | | ElMessage.success('新建成功') |
| | | } |
| | | ElMessage.success(isEditMode.value ? '修改成功' : '新建成功') |
| | | |
| | | dialogVisible.value = false |
| | | await loadPricingList(currentProductId.value) |
| | | } finally { |
| | | saving.value = false |
| | | } |
| | |
| | | '状态变更', |
| | | { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' } |
| | | ) |
| | | .then(() => { |
| | | const idx = priceList.value.findIndex((p: any) => p.id === row.id) |
| | | if (idx !== -1) { |
| | | priceList.value[idx] = { ...priceList.value[idx], enableStatus: newStatus } |
| | | } |
| | | .then(async () => { |
| | | const payload = buildPayloadFromRow(row, { isActive: newStatus === 'ENABLED' }) |
| | | await productPricingApi.update(payload) |
| | | await loadPricingList(currentProductId.value as string) |
| | | ElMessage.success(newStatus === 'ENABLED' ? '启用成功' : '停用成功') |
| | | }) |
| | | .catch(() => {}) |
| | |
| | | |
| | | .content-area { |
| | | .price-card { |
| | | /* 让价格列表可滚动 */ |
| | | :deep(.el-card__body) { |
| | | max-height: 600px; |
| | | overflow-y: auto; |
| | | } |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | |
| | | .quantity-input-group { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; } |
| | | .sales-form-item :deep(.el-form-item__content) { white-space: nowrap; } |
| | | .sales-form-group { display: inline-flex; gap: 12px; flex-wrap: nowrap; } |
| | | .price-form :deep(.el-form-item) { margin-bottom: 12px; } |
| | | .price-form :deep(.el-form-item) { margin-bottom: 20px; } |
| | | /* 上架状态颜色 */ |
| | | .status-online { color: #67C23A; } |
| | | .status-pending { color: #E6A23C; } |