/** * 价格套餐 · 纯函数与数据工厂 * * 只做数据转换,不依赖组件实例 / 字典 / 接口, * create.vue、detail.vue 及 set/components 下的组件共用。 */ import { parseTime } from '@/utils/ruoyi' /** 计价类型枚举 */ export const SET_TYPE = { FIXED: 'FIXED', DISCOUNT: 'DISCOUNT' } /** 套餐状态枚举:D 草稿 / A 审核中 / E 生效中 / R 驳回 / S 已停用 */ export const SET_STATUS = { DRAFT: 'D', AUDIT: 'A', EFFECTIVE: 'E', REJECT: 'R', STOPPED: 'S' } const STATUS_LABEL = { D: '草稿', A: '审核中', E: '生效中', R: '驳回', S: '已停用' } const STATUS_TAG = { D: 'warning', A: 'primary', E: 'success', R: 'danger', S: 'info' } /** 默认取值方式 */ export const DEFAULT_ROUND_MODE = 'HALF_UP' /** version status: 0 draft / 3 pending / 1 active / 2 expired */ export const VER_STATUS = { DRAFT: '0', PENDING: '3', EFFECT: '1', EXPIRED: '2' } export function versionStatusLabel(status) { const map = { '0': '草稿', '3': '待生效', '1': '生效', '2': '失效' } return map[status] || status || '-' } export function versionStatusTag(status) { const map = { '0': 'warning', '3': 'primary', '1': 'success', '2': 'info' } return map[status] || 'info' } /** True draft only (not pending) */ export function isDraftVersion(row) { return !!row && String(row.status) === VER_STATUS.DRAFT } export function isPendingVersion(row) { return !!row && String(row.status) === VER_STATUS.PENDING } /** Draft or pending: may continue editing price details */ export function isPriceEditableVersion(row) { return isDraftVersion(row) || isPendingVersion(row) } export function nextVersionNo(versions) { const seqs = (versions || []).map(v => Number(v.versionSeq) || 0) const max = seqs.length ? Math.max.apply(null, seqs) : 0 return 'V' + (max + 1) } /** 状态文案(已过期优先) */ export function setStatusLabel(set) { if (set && set.expired) return '已过期' return STATUS_LABEL[set && set.status] || (set && set.status) || '-' } /** 状态标签色(已过期优先) */ export function setStatusTagType(set) { if (set && set.expired) return 'danger' return STATUS_TAG[set && set.status] || 'info' } /** 明细 / 客户是否可维护:草稿、驳回、生效中 */ export function isChildEditable(status) { return [SET_STATUS.DRAFT, SET_STATUS.REJECT, SET_STATUS.EFFECTIVE].includes(status) } /** 日期显示 yyyy-MM-dd */ export function formatDate(val) { return val ? parseTime(val, '{y}-{m}-{d}') : '' } /** 有效期区间文案,结束为空表示长期 */ export function formatDateRange(begin, end) { return `${formatDate(begin) || '-'} ~ ${end ? formatDate(end) : '长期'}` } /** 是否固定价模式 */ export function isFixedType(setType) { return setType === SET_TYPE.FIXED } /** 是否折扣模式 */ export function isDiscountType(setType) { return setType === SET_TYPE.DISCOUNT } /** 计价类型展示文案 */ export function setTypeLabel(setType) { return isFixedType(setType) ? 'FIXED 固定价' : 'DISCOUNT 折扣' } /** * 数字转字符串,抹掉浮点误差尾巴 * @param {number|string} v * @returns {string} 0.1 + 0.2 -> '0.3' */ export function trimNum(v) { if (v == null || v === '') return '' const n = Number(v) if (Number.isNaN(n)) return String(v) return String(parseFloat(n.toFixed(6))) } /** Weight label: left-open right-closed, e.g. 0.3} */ export function buildCountryOptions(rows) { const map = new Map() ;(rows || []).forEach(item => { const key = countryKeyOf(item) if (!key || map.has(key)) return map.set(key, { key, code: item.countryCode || undefined, name: item.countryName, label: item.countryCode ? `${item.countryName} ${item.countryCode}` : item.countryName }) }) return [...map.values()] } /** * 重量段下拉选项(按已选国家过滤去重) * @param {Array} rows bms_product_country 行 * @param {Object} row 当前明细行(含国家) * @returns {Array<{value, label, detailId, freightPrice, registrationFee}>} */ /** * Zone options for selected country (from product-country rows). * @returns {Array<{key, zoneId, zoneCode, zoneName, label}>} */ export function buildZoneOptions(rows, row) { const map = new Map() ;(rows || []).filter(item => matchCountry(row, item)).forEach(item => { if (!item.zoneCode && item.zoneId == null) return const key = item.zoneCode || String(item.zoneId) if (map.has(key)) return map.set(key, { key, zoneId: item.zoneId, zoneCode: item.zoneCode || undefined, zoneName: item.zoneName || undefined, label: item.zoneName || item.zoneCode || key }) }) return [...map.values()] } /** Whether selected country has any zone rows */ export function countryHasZones(rows, row) { return buildZoneOptions(rows, row).length > 0 } export function buildWeightOptions(rows, row) { const list = rows || [] if (countryHasZones(list, row) && !(row && (row.zoneCode || row.zoneId))) { return [] } const map = new Map() list.filter(item => matchCountryZone(row, item)).forEach(item => { if (item.weightMin == null && item.weightMax == null) return const value = formatWeightLabel(item.weightMin, item.weightMax) if (map.has(value)) return map.set(value, { value, label: value, detailId: item.detailId, zoneId: item.zoneId, zoneCode: item.zoneCode, zoneName: item.zoneName, freightPrice: item.freightPrice, registrationFee: item.registrationFee, weightMin: item.weightMin, weightMax: item.weightMax, effectiveDate: item.effectiveDate, expireDate: item.expireDate }) }) return [...map.values()] } /** 附加费金额展示:RATE 转百分比,其余带币种 */ export function formatChargeValue(row) { if (!row || row.chargeValue == null) return '-' const v = Number(row.chargeValue) if (row.chargeType === 'RATE') return (v * 100).toFixed(2) + '%' return v + ' ' + (row.currency || 'CNY') } /** 附加费作用范围文案 */ export function surchargeScopeLabel(s) { // 主表(prc_surcharge)数据:country_code 有值 = 该国专属(计费归属),空 = 全部国家 if (s.detailId == null && s.psdId == null) { return s.countryCode ? `国家 ${s.countryCode}` : '全部国家' } const w = s.weightMin != null && s.weightMax != null ? `${s.weightMin}-${s.weightMax}kg` : '-' return `${s.countryName || '-'} · ${w}` } /** * 产品关联附加费(prc_surcharge_rel)是否作用于该明细行 * 与后端 snapshotVersionSurcharges 的过滤口径保持一致: * 国家三字码一致 + 产品级(detailId 空)直接命中,明细级需 productCountryId 相同 */ export function surchargeAppliesToRow(rel, row) { if (!rel || !row) return false if (rel.countryCode && row.countryCode && String(rel.countryCode).toUpperCase() !== String(row.countryCode).toUpperCase()) { return false } if (rel.detailId == null) return true return row.productCountryId != null && Number(rel.detailId) === Number(row.productCountryId) } /** 附加费标签文案 */ export function surchargeTagLabel(s) { return `${s.scName} ${formatChargeValue(s)}` } /** 新增一条空明细行 */ export function createEmptyDetailRow() { return { productCode: undefined, productName: undefined, countryCode: undefined, countryName: undefined, _countryKey: undefined, zoneId: undefined, zoneCode: undefined, zoneName: undefined, weightLabel: undefined, weightMin: undefined, weightMax: undefined, productCountryId: undefined, fvValue: undefined, fvRegistrationFee: undefined, /** product default freight when copied from bms_product_country */ _baseFreight: undefined, /** product default registration fee */ _baseReg: undefined, discountValue: undefined, roundMode: DEFAULT_ROUND_MODE, effectiveDate: undefined, expireDate: undefined } } /** 明细行 -> 可编辑行(补齐取值方式与国家键) */ export function toEditableDetailRow(row) { return { ...row, roundMode: row.roundMode || DEFAULT_ROUND_MODE, _countryKey: countryKeyOf(row), _baseFreight: row._baseFreight != null ? row._baseFreight : undefined, _baseReg: row._baseReg != null ? row._baseReg : undefined } } /** FIXED row changed vs product default */ export function isDetailPriceChanged(row) { if (!row) return false if (row._baseFreight != null && Number(row.fvValue) !== Number(row._baseFreight)) return true if (row._baseReg != null && Number(row.fvRegistrationFee) !== Number(row._baseReg)) return true return false } /** 新增一条空客户绑定行 */ export function createEmptyCustomerRow({ effectiveDate, expireDate, seq = 1 } = {}) { return { customerId: undefined, customerCode: undefined, customerName: '', bindPriority: seq, prBegindate: effectiveDate, prEnddate: expireDate } } /** 新建套餐的基本信息默认值 */ export function createEmptyBasicInfo() { return { priceSetName: '', setType: SET_TYPE.FIXED, defaultDiscount: 88, effectiveDate: undefined, expireDate: undefined, priceSetNote: '' } } /** 明细行 -> 提交后端的 payload */ export function toDetailPayload(d) { return { psdId: d.psdId || undefined, psvId: d.psvId || undefined, priceSetId: d.priceSetId || undefined, productCode: d.productCode, productName: d.productName, countryCode: d.countryCode || undefined, countryName: d.countryName || undefined, zoneId: d.zoneId || undefined, zoneCode: d.zoneCode || undefined, zoneName: d.zoneName || undefined, weightLabel: d.weightLabel || undefined, weightMin: d.weightMin, weightMax: d.weightMax, productCountryId: d.productCountryId || undefined, fvValue: d.fvValue, fvRegistrationFee: d.fvRegistrationFee, discountValue: d.discountValue, roundMode: d.roundMode || DEFAULT_ROUND_MODE, effectiveDate: d.effectiveDate || undefined, expireDate: d.expireDate || undefined, /** 勾选保存的附加费:带数组即按勾选落快照,空数组=一条都不选 */ surcharges: Array.isArray(d.surcharges) ? d.surcharges.map(s => ({ scId: s.scId })) : undefined } } /** 客户绑定行 -> 提交后端的 payload */ export function toCustomerPayload(c) { return { ...c } }