讲师中心 微信公众号
AI工具推荐 视频效率加速

JavaScript价格计算器:根据支付周期动态调整价格及弹窗显示

夜婷大大_3456

夜婷大大_3456

发布时间:2025-11-14 12:57:25

|

601人浏览过

|

来源于php中文网

原创

javascript价格计算器:根据支付周期动态调整价格及弹窗显示

本教程详细阐述如何在JavaScript价格计算器中实现根据用户选择的支付周期(月付或年付)动态调整商品价格。我们将重点介绍如何为月付选项应用20%的加价,并确保这一价格变化不仅体现在总价计算中,也准确地反映在价格详情弹窗的阶梯价格显示里,从而提供一个功能完善且用户友好的价格估算体验。

1. 概述与问题背景

在一个Web应用中构建价格计算器时,常常需要根据用户的不同选择(如支付频率、服务等级等)动态调整最终价格。本案例中,我们面临的需求是:当用户选择“月付”(monthly)时,所有相关商品的基础价格需要增加20%;而选择“年付”(annual)时,价格保持不变。更重要的是,这种价格调整不仅要体现在最终的总价上,还需要在展示商品价格阶梯(例如通过弹窗显示)时同步更新,以避免信息不一致。

原始代码已经具备了基础的价格计算逻辑和UI更新机制,但弹窗中的价格阶梯显示未与支付周期挂钩。解决此问题的关键在于:

  1. 在生成价格阶梯显示时,根据当前选定的支付周期应用相应的价格调整。
  2. 确保当支付周期选项改变时,所有相关的价格显示(包括主计算结果和弹窗阶梯)都能被及时刷新。

2. 核心修改方案

为了实现上述目标,我们需要对JavaScript代码中的两个关键函数进行修改:printPreisstaffel(负责渲染价格阶梯)和 calcSum(负责总价计算及整体UI刷新)。

立即学习“Java免费学习笔记(深入)”;

2.1 修改 printPreisstaffel 函数:动态调整弹窗价格

printPreisstaffel 函数的职责是根据传入的价格模型(preisstaffel)和目标元素ID(idelement)来生成并显示价格阶梯。为了让它能够根据支付周期调整价格,我们需要在每次渲染时获取当前选定的支付周期,并据此对价格进行条件性修改。

修改前(部分):

function printPreisstaffel(preisstaffel, idelement) {
  document.getElementById(idelement).innerHTML ="";
  for (var key in preisstaffel) {
    document.getElementById(idelement).innerHTML += `<div class="price-container"><span class="text">Bis ${key} Benutzer:</span> <span class="value">${(preisstaffel[key]).toFixed(2)}€ </span></div>`;
  }
}

修改后:

在循环遍历价格阶梯之前,我们首先获取支付周期的值。然后,在构建HTML字符串时,根据支付周期是“月付”("M")还是“年付”("J"),决定是否对价格应用20%的加成。

function printPreisstaffel(preisstaffel, idelement) {
  document.getElementById(idelement).innerHTML = ""; // 清空现有内容

  // 获取当前选定的支付周期
  var paymenttype = document.querySelector("#zahlrythmus").value;

  for (var key in preisstaffel) {
    var price;
    if (paymenttype == "M") {
      // 如果是月付,价格增加20%
      price = (preisstaffel[key] * 1.2).toFixed(2);
    } else {
      // 否则(年付),价格保持不变
      price = preisstaffel[key].toFixed(2);
    }
    // 更新弹窗显示的价格
    document.getElementById(idelement).innerHTML += `<div class="price-container"><span class="text">Bis ${key} Benutzer:</span> <span class="value">${price}€ </span></div>`;
  }
}

解释:

  • document.querySelector("#zahlrythmus").value 用于获取ID为 zahlrythmus 的 <select> 元素当前选定的值("M" 或 "J")。
  • 通过 if (paymenttype == "M") 判断是否为月付,如果是,则将 preisstaffel[key] 乘以 1.2 来实现20%的加价。
  • toFixed(2) 用于将价格格式化为两位小数的字符串,符合货币显示习惯。

2.2 修改 calcSum 函数:确保UI全面刷新

calcSum 函数是整个价格计算器的核心,负责根据所有输入计算总价并更新各种显示区域。当支付周期改变时,不仅总价需要重新计算,价格阶梯弹窗也需要刷新以显示正确的加价。

修改前(部分):

在原始代码中,printPreisstaffel 的调用在 DOMContentLoaded 事件监听器中进行初始化,但在 calcSum 中并没有重新调用来更新弹窗。

// ... (calcSum 函数的其他逻辑) ...

  // If abfrage für Backend um Preisstaffel anzeigen zu lassen
  if (backendtype == "ZR") {
    backendpreis = getPrice(pricingbackendzr, percent, backendanzahl);
  } else {
    backendpreis = getPrice(pricingbackendz, percent, backendanzahl);
  }

  apppreis = getPrice(pricingapp, percent, appanzahl);

// ... (其他计算和UI更新) ...

修改后:

我们需要在 calcSum 函数中,在确定了 backendtype 之后,重新调用 printPreisstaffel 来刷新 App 和 Backend 的价格阶梯显示。

Wechat HTML Publisher
Wechat HTML Publisher

直接上传HTML富文本到微信公众号草稿箱。支持完整的HTML格式,无需Markdown转换。

下载
function calcSum() {
  // ... (保持 calcSum 函数开头部分不变,获取所有输入值) ...

  var backendanzahl = document.querySelector(".backenduser").value;
  var appanzahl = document.querySelector(".appuser").value;
  var paymenttype = document.querySelector("#zahlrythmus").value; // 再次获取支付类型
  var backendtype = document.querySelector("#backendfunktion").value;

  // 根据支付类型设置全局百分比 (percent)
  if (paymenttype == "M") {
    percent = 1.2; // 月付加价20%
  } else {
    percent = 1;   // 年付保持原价
  }

  // 根据后端类型获取后端价格,并应用百分比
  if (backendtype == "ZR") {
    backendpreis = getPrice(pricingbackendzr, percent, backendanzahl);
  } else {
    backendpreis = getPrice(pricingbackendz, percent, backendanzahl);
  }

  // 获取App价格,并应用百分比
  apppreis = getPrice(pricingapp, percent, appanzahl);

  // 关键:重新调用 printPreisstaffel 来刷新弹窗中的价格阶梯
  // 确保弹窗中的价格也根据当前的 paymenttype 进行调整
  printPreisstaffel(pricingapp, "preisstaffelapp");
  if (backendtype == "ZR") {
    printPreisstaffel(pricingbackendzr, "preisstaffelbackend");
  } else {
    printPreisstaffel(pricingbackendz, "preisstaffelbackend");
  }

  // ... (calcSum 函数的其余部分保持不变,继续计算总价和更新其他UI元素) ...

  var mytext = (((backendanzahl * backendpreis + +appanzahl * apppreis) * 1)).toFixed(2);
  summetext.textContent = mytext + "€";
  // ... (其他UI更新逻辑) ...
}

解释:

  • 通过在 calcSum 中重新调用 printPreisstaffel(pricingapp, "preisstaffelapp") 和 printPreisstaffel(pricingbackendzr/z, "preisstaffelbackend"),我们确保每当用户改变支付周期(或任何其他影响价格的输入)时,弹窗内的价格阶梯也会被重新渲染,并应用最新的价格规则。
  • calcSum 函数本身已经在 onchange 事件中被触发,因此这些修改将确保用户界面的实时响应。

2.3 DOMContentLoaded 和 updatePreisStaffelungBackend 函数的调整

在原始代码中,DOMContentLoaded 和 updatePreisStaffelungBackend 也调用了 printPreisstaffel。为了避免重复逻辑和潜在的冲突,或者确保所有路径都应用了新逻辑,我们需要检查这些地方。

原始 DOMContentLoaded:

document.addEventListener("DOMContentLoaded", function (event) {
  // ... 其他初始化 ...
  printPreisstaffel(pricingapp, "preisstaffelapp"); // 初始加载App价格阶梯
  calcSum(); // 初始计算
  updatePreisStaffelungBackend(); // 初始加载Backend价格阶梯
});

原始 updatePreisStaffelungBackend:

function updatePreisStaffelungBackend() {
  var backendtype = document.querySelector("#backendfunktion").value;
  if (backendtype == "ZR"){
    printPreisstaffel(pricingbackendzr, "preisstaffelbackend");
  }else{
    printPreisstaffel(pricingbackendz, "preisstaffelbackend");
  }
}

由于 calcSum 现在已经包含了刷新所有 printPreisstaffel 的逻辑,并且 calcSum 会在 DOMContentLoaded 时被调用,以及当 backendfunktion 改变时(通过 onchange = "calcSum();updatePreisStaffelungBackend()",尽管 updatePreisStaffelungBackend 内部的 printPreisstaffel 调用现在可以被 calcSum 中的调用替代),我们可以对 DOMContentLoaded 和 updatePreisStaffelungBackend 进行简化或确保一致性。

优化后的 DOMContentLoaded:

calcSum() 已经会调用 printPreisstaffel,所以 DOMContentLoaded 中的 printPreisstaffel(pricingapp, "preisstaffelapp") 这一行可以删除,或者保持,因为它不会造成错误,只是多一次渲染。为了代码简洁,可以移除。

document.addEventListener("DOMContentLoaded", function (event) {
  // ... 其他初始化 ...
  // printPreisstaffel(pricingapp, "preisstaffelapp"); // 这一行可以删除,因为 calcSum 会处理
  calcSum(); // 确保初始加载时所有价格(包括弹窗)都正确显示
  // updatePreisStaffelungBackend(); // 这一行也可以删除,因为 calcSum 内部已包含其逻辑
});

优化后的 updatePreisStaffelungBackend:

由于 calcSum 已经负责根据 backendtype 调用正确的 printPreisstaffel,updatePreisStaffelungBackend 可以只调用 calcSum 来触发全面刷新,或者将其内部的 printPreisstaffel 调用移除。在HTML中,onchange = "calcSum();updatePreisStaffelungBackend()" 意味着 calcSum 和 updatePreisStaffelungBackend 都会被调用。如果 updatePreisStaffelungBackend 内部仍有 printPreisstaffel 调用,就会导致重复。最简洁的方式是让 updatePreisStaffelungBackend 仅作为触发 calcSum 的辅助函数。

HTML中的 onchange 属性:

<select style="width: 150px;background-color:rgba(96,125,139,0.33)" class="backendfunc"id = "backendfunktion" onchange = "calcSum();updatePreisStaffelungBackend()" >
  <option value="Z">Zeiterfassung</option>
  <option value="ZR"> Zeiterfassung + Rechnungswesen</option>
</select>

因为 calcSum() 已经包含了 printPreisstaffel 的调用,并且会根据 backendtype 决定显示哪个价格阶梯,所以 updatePreisStaffelungBackend() 函数本身可以被简化,或者其在 onchange 中的调用可以被移除,只保留 calcSum()。

推荐的 updatePreisStaffelungBackend 简化:

function updatePreisStaffelungBackend() {
  // 仅调用 calcSum 来触发所有UI更新,避免重复逻辑
  calcSum();
}

或者,如果HTML中的 onchange 保持 onchange = "calcSum();updatePreisStaffelungBackend()",那么 updatePreisStaffelungBackend 内部的 printPreisstaffel 调用可以直接移除,因为它会被 calcSum 覆盖。

function updatePreisStaffelungBackend() {
  // 仅获取backendtype,不再直接调用printPreisstaffel
  // 因为 calcSum() 已经会处理价格阶梯的更新
  var backendtype = document.querySelector("#backendfunktion").value;
  // 实际上,如果 calcSum 已经被调用,这里不需要做额外的事情
  // 除非有其他与 backendtype 相关的、calcSum 未处理的UI更新
}

考虑到用户提供的解决方案,它选择在 calcSum 内部重新调用 printPreisstaffel,并且在 updatePreisStaffelungBackend 中也保留了 printPreisstaffel 的调用。这种方式虽然有些冗余,但在功能上是正确的,因为它确保了无论哪个函数触发,价格阶梯都会被更新。对于本教程,我们将遵循提供的解决方案,并在最终代码中体现。

3. 完整JavaScript代码示例 (app.js)

var percent = 1; // Standard value for yearly (年付标准值)

apppreis = 7.5;
backendpreis = 35;
// Preisstaffelung Zeiterfassung+Rechnungswesen für Backendpreis (后端价格阶梯:时间跟踪+会计)
var pricingbackendzr = { 4: 35.00, 10: 32.50, 20: 30.00 };
// Preisstaffelung Zeiterfassung für Backendpreis (后端价格阶梯:时间跟踪)
var pricingbackendz = { 4: 20.00, 10: 18.00, 20: 16.00 };
// Preisstaffelung App (App价格阶梯)
var pricingapp = { 10: 7.50, 25: 7.00, 50: 6.50, 75: 6.00, 500: 5.50 };


document.addEventListener("DOMContentLoaded", function (event) {
  // 滑块颜色更新逻辑
  document.querySelectorAll(".updatecolor").forEach(updateinputs =>
    updateinputs.addEventListener("input", function () {
      var percent = calcPercent(this.value, this.min, this.max);
      const bg = getComputedStyle(this).getPropertyValue('--background');
      const slider = getComputedStyle(this).getPropertyValue('--slider');
      var colorchanger = this.getAttribute('rangecolorid');
      var sliderselect = document.querySelector('#' + colorchanger);
      sliderselect.setAttribute(
        'style',
        ` background:linear-gradient(to right,${slider},${slider} ${percent}%,${bg} ${percent}%) `
      )
    })
  );

  // 初始显示App价格阶梯 (注意:calcSum中也会调用,这里可以移除以避免冗余)
  printPreisstaffel(pricingapp, "preisstaffelapp");

  // 初始计算并显示所有数据
  calcSum();
  // 初始显示后端价格阶梯 (注意:calcSum中也会调用,这里可以移除以避免冗余)
  updatePreisStaffelungBackend();
});

// 打印价格阶梯函数,现在包含支付周期判断
function printPreisstaffel(preisstaffel, idelement) {
  document.getElementById(idelement).innerHTML = ""; // 清空现有内容

  // 获取当前选定的支付周期
  var paymenttype = document.querySelector("#zahlrythmus").value;

  for (var key in preisstaffel) {
    var price;
    if (paymenttype == "M") {
      // 如果是月付,价格增加20%
      price = (preisstaffel[key] * 1.2).toFixed(2);
    } else {
      // 否则(年付),价格保持不变
      price = preisstaffel[key].toFixed(2);
    }
    // 更新弹窗显示的价格
    document.getElementById(idelement).innerHTML += `<div class="price-container"><span class="text">Bis ${key} Benutzer:</span> <span class="value">${price}€ </span></div>`;
  }
}

// 更新用户输入和滑块联动
function updateUser(val, inputtype) {
  if (inputtype == 'Appuserrangecolor') {
    document.getElementById('AppInput').value = val;
  }
  if (inputtype == 'AppInput') {
    document.getElementById('Appuserrangecolor').value = val;
  }
  if (inputtype == 'Backendrangecolor') {
    document.getElementById('BackendInput').value = val;
  }
  if (inputtype == 'BackendInput') {
    document.getElementById('Backendrangecolor').value = val;
  }
  calcSum(); // 任何输入改变都重新计算
}

// 计算滑块百分比
function calcPercent(curval, min, max) {
  return ((curval - min) / (max - min)) * 100;
}

// 更新后端价格阶梯显示 (现在会调用 calcSum 来确保所有UI更新)
function updatePreisStaffelungBackend() {
  var backendtype = document.querySelector("#backendfunktion").value;
  if (backendtype == "ZR"){
    printPreisstaffel(pricingbackendzr, "preisstaffelbackend");
  }else{
    printPreisstaffel(pricingbackendz, "preisstaffelbackend");
  }
  // 实际上,如果 calcSum() 已经在 HTML onchange 中被调用,
  // 并且 calcSum() 内部也更新了价格阶梯,这里的逻辑可以被移除或简化为只调用 calcSum()
  // 但为了与原答案保持一致,这里保留。
}


// 核心计算函数,现在会刷新所有价格显示
function calcSum() {
  var preisproapp = document.querySelector('.proapp');
  var backendpreissum = document.querySelector('.backendpreissum');
  var appstk = document.querySelector('.appanzahl');
  var preisprobackend = document.querySelector('.probackend');
  var jaehrlich = document.querySelector('.jaehrlichsum');
  var summetext = document.querySelector('.summe');
  var rabatt = document.querySelector('.rabattsum');
  var backendanzahl = document.querySelector(".backenduser").value;
  var appanzahl = document.querySelector(".appuser").value;
  var paymenttype = document.querySelector("#zahlrythmus").value;
  var backendtype = document.querySelector("#backendfunktion").value;

  // 根据支付类型设置全局百分比
  if (paymenttype == "M") {
    percent = 1.2; // 月付加价20%
  } else {
    percent = 1;   // 年付保持原价
  }

  // 根据后端类型获取后端价格,并应用百分比
  if (backendtype == "ZR") {
    backendpreis = getPrice(pricingbackendzr, percent, backendanzahl);
  } else {
    backendpreis = getPrice(pricingbackendz, percent, backendanzahl);
  }

  // 获取App价格,并应用百分比
  apppreis = getPrice(pricingapp, percent, appanzahl);

  // 关键:重新调用 printPreisstaffel 来刷新弹窗中的价格阶梯
  // 确保弹窗中的价格也根据当前的 paymenttype 进行调整
  printPreisstaffel(pricingapp, "preisstaffelapp");
  if (backendtype == "ZR") {
    printPreisstaffel(pricingbackendzr, "preisstaffelbackend");
  } else {
    printPreisstaffel(pricingbackendz, "preisstaffelbackend");
  }


  // 计算并更新总价
  var mytext = (((backendanzahl * backendpreis + +appanzahl * apppreis) * 1)).toFixed(2);
  summetext.textContent = mytext + "€";
  // 计算并更新后端总价
  var backendpreissumme = (backendanzahl * backendpreis).toFixed(2);
  backendpreissum.textContent = backendpreissumme + "€";
  // 计算并更新App总价
  var apppreissumme = (appanzahl * apppreis).toFixed(2);
  appstk.textContent = apppreissumme + "€";
  // 计算并更新每个后端用户的价格
  var probackend2 = ((backendpreis * backendanzahl)

热门AI工具

更多
火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1618

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2424

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

5904

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

5169

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

6894

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

9554

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

4859

2025.07.29

c++字符串相关教程
c++字符串相关教程

本专题整合了c++字符串相关教程,阅读专题下面的文章了解更多详细内容。

5127

2025.08.07

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

80

2026.09.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 12.1万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn