AJAX加载了HTML但是HTML里面的CLICK事件无效?
时间: 2025-05-21 13:37:22 AIGC 浏览: 18
### AJAX加载的HTML中点击事件未触发的原因
当通过AJAX动态加载HTML内容时,新加载的元素可能不会自动继承原有的事件绑定逻辑。这是因为传统的`click()`方法仅适用于页面初始化时已存在的DOM元素[^1]。如果新的HTML片段被注入到文档中,则这些新增加的元素并不会自动获得之前定义的事件监听器。
具体来说,在jQuery中使用`.click()`这样的直接绑定方式只会针对当前存在于DOM树中的节点生效。对于那些后来才加入到页面上的节点(比如由AJAX请求返回并插入的内容),它们自然也就无法响应此类预先设定好的事件处理器[^2]。
### 解决方案
#### 方法一:使用事件委派机制
为了使动态添加至网页内的组件能够正常工作,可以采用**事件委派**技术来解决问题。这涉及到把事件监听设置在一个始终存在且不改变其状态或者位置的父亲级容器之上,并指定子代匹配的选择器作为参数传递进去。这样即使后代发生变化也能有效捕获目标动作的发生情况。
以下是基于上述原理的一个实现例子:
```javascript
$('body').on('click','.dynamic-element-class',function(){
console.log("Dynamic element clicked!");
});
```
在这个案例里面,“body”充当了一个稳定的祖先层角色;“.dynamic-element-class”则是我们希望监控的具体类别名称。每当某个属于该类别的项目遭到触动的时候,相应的回调就会被执行[^2]。
#### 方法二:重新绑定事件
另一种可行的办法就是在每次完成异步数据获取之后立即再次执行一遍有关联操作的部分代码段落,从而确保最新一批生成出来的对象同样具备应有的行为特性。不过这种方法相较于前者效率较低而且容易引起维护困难等问题所以一般推荐优先考虑前面提到过的事件代理模式[^3]。
另外值得注意的是,在某些特殊场景下还可能存在其他干扰因素造成预期之外的结果表现形式,例如CSS样式冲突遮挡实际可交互区域等情况也需要纳入排查范围之内[^4]。
### 结论
综上所述,解决AJAX加载后的HTML元素点击事件失效的最佳实践通常是运用事件委派策略,即利用像`$(document).on('event','selector',callback)`这样的结构来进行全局性的事件管理而非局限于局部特定时刻下的单一赋值过程之中去单独处理每一个可能出现的新实例个体。
阅读全文
相关推荐











Please 2x check, this is my full code in cart.php
<?php
defined('ABSPATH') || exit;
do_action('woocommerce_before_cart'); ?>
<style>
.cart-container {
max-width: 1024px;
margin: 0 auto;
padding: 20px;
}
.cart-footer {
position: sticky;
bottom: 0;
background: white;
padding: 15px;
border-top: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
}
.cart-footer-left {
display: flex;
align-items: center;
gap: 15px;
}
.cart-footer-right {
display: flex;
align-items: center;
gap: 15px;
}
.selected-info {
font-weight: bold;
}
.checkout-btn {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
.cart-actions {
margin: 20px 0;
display: flex;
gap: 10px;
}
.action-btn {
padding: 8px 15px;
background: #f1f1f1;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
.action-btn:hover {
background: #e9e9e9;
}
</style>
<form class="woocommerce-cart-form" action="<?php echo esc_url(wc_get_cart_url()); ?>" method="post">
<?php do_action('woocommerce_before_cart_table'); ?>
<input type="checkbox" id="select-all">
<?php esc_html_e('Product', 'woocommerce'); ?>
<?php esc_html_e('Attributes', 'woocommerce'); ?>
<?php esc_html_e('Price', 'woocommerce'); ?>
<?php esc_html_e('Quantity', 'woocommerce'); ?>
<?php esc_html_e('Subtotal', 'woocommerce'); ?>
<?php esc_html_e('Action', 'woocommerce'); ?>
<?php do_action('woocommerce_before_cart_contents'); ?>
<?php
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
$_product = apply_filters('woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key);
$product_id = apply_filters('woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key);
if ($_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters('woocommerce_cart_item_visible', true, $cart_item, $cart_item_key)) {
$product_permalink = apply_filters('woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink($cart_item) : '', $cart_item, $cart_item_key);
?>
<input type="checkbox" name="selected_items[]" value="<?php echo $cart_item_key; ?>" class="item-checkbox">
<?php
$thumbnail = apply_filters('woocommerce_cart_item_thumbnail', $_product->get_image(), $cart_item, $cart_item_key);
if (!$product_permalink) {
echo $thumbnail; // PHPCS: XSS ok.
} else {
printf('%s', esc_url($product_permalink), $thumbnail); // PHPCS: XSS ok.
}
?>
<?php
if (!$product_permalink) {
echo wp_kses_post(apply_filters('woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key) . ' ');
} else {
echo wp_kses_post(apply_filters('woocommerce_cart_item_name', sprintf('%s', esc_url($product_permalink), $_product->get_name()), $cart_item, $cart_item_key));
}
do_action('woocommerce_after_cart_item_name', $cart_item, $cart_item_key);
// Meta data.
echo wc_get_formatted_cart_item_data($cart_item); // PHPCS: XSS ok.
// Backorder notification.
if ($_product->backorders_require_notification() && $_product->is_on_backorder($cart_item['quantity'])) {
echo wp_kses_post(apply_filters('woocommerce_cart_item_backorder_notification', '' . esc_html__('Available on backorder', 'woocommerce') . '', $product_id));
}
?>
<?php
// 显示自定义属性
$attributes = $_product->get_attributes();
if ($attributes) {
foreach ($attributes as $attribute) {
if ($attribute->get_visible()) {
echo ''.wc_attribute_label($attribute->get_name()).': ';
$values = array();
if ($attribute->is_taxonomy()) {
$attribute_taxonomy = $attribute->get_taxonomy_object();
$attribute_values = wc_get_product_terms($product_id, $attribute->get_name(), array('fields' => 'all'));
foreach ($attribute_values as $attribute_value) {
$value_name = esc_html($attribute_value->name);
$values[] = $value_name;
}
} else {
$values = $attribute->get_options();
foreach ($values as &$value) {
$value = esc_html($value);
}
}
echo apply_filters('woocommerce_attribute', wptexturize(implode(', ', $values)), $attribute, $values);
echo '';
}
}
}
?>
<?php
echo apply_filters('woocommerce_cart_item_price', WC()->cart->get_product_price($_product), $cart_item, $cart_item_key); // PHPCS: XSS ok.
?>
<?php
if ($_product->is_sold_individually()) {
$product_quantity = sprintf('1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key);
} else {
$product_quantity = woocommerce_quantity_input(
array(
'input_name' => "cart[{$cart_item_key}][qty]",
'input_value' => $cart_item['quantity'],
'max_value' => $_product->get_max_purchase_quantity(),
'min_value' => '0',
'product_name' => $_product->get_name(),
),
$_product,
false
);
}
echo apply_filters('woocommerce_cart_item_quantity', $product_quantity, $cart_item_key, $cart_item); // PHPCS: XSS ok.
?>
<?php
echo apply_filters('woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal($_product, $cart_item['quantity']), $cart_item, $cart_item_key); // PHPCS: XSS ok.
?>
<?php
echo apply_filters( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'woocommerce_cart_item_remove_link',
sprintf(
'×',
esc_url(wc_get_cart_remove_url($cart_item_key)),
esc_html__('Remove this item', 'woocommerce'),
esc_attr($product_id),
esc_attr($_product->get_sku())
),
$cart_item_key
);
?>
<?php
}
}
?>
<?php do_action('woocommerce_cart_contents'); ?>
<button type="button" class="action-btn" id="remove-selected"><?php esc_html_e('Delete Selected', 'woocommerce'); ?></button>
<button type="button" class="action-btn" id="clear-cart"><?php esc_html_e('Clear Cart', 'woocommerce'); ?></button>
<?php if (wc_coupons_enabled()) { ?>
<label for="coupon_code"><?php esc_html_e('Coupon:', 'woocommerce'); ?></label>
<input type="text" name="coupon_code" class="input-text" id="coupon_code" value="" placeholder="<?php esc_attr_e('Coupon code', 'woocommerce'); ?>" />
<button type="submit" class="button" name="apply_coupon" value="<?php esc_attr_e('Apply coupon', 'woocommerce'); ?>"><?php esc_attr_e('Apply coupon', 'woocommerce'); ?></button>
<?php do_action('woocommerce_cart_coupon'); ?>
<?php } ?>
<button type="submit" class="button" name="update_cart" value="<?php esc_attr_e('Update cart', 'woocommerce'); ?>"><?php esc_html_e('Update cart', 'woocommerce'); ?></button>
<?php do_action('woocommerce_cart_actions'); ?>
<?php wp_nonce_field('woocommerce-cart', 'woocommerce-cart-nonce'); ?>
<?php do_action('woocommerce_after_cart_contents'); ?>
<?php do_action('woocommerce_after_cart_table'); ?>
</form>
<input type="checkbox" id="footer-select-all">
<label for="footer-select-all"><?php esc_html_e('Select all', 'woocommerce'); ?></label>
<?php esc_html_e('Selected items:', 'woocommerce'); ?>
0
<?php esc_html_e('Total:', 'woocommerce'); ?>
RM0.00
<button type="button" class="checkout-btn" id="partial-checkout">
<?php esc_html_e('Proceed to checkout', 'woocommerce'); ?>
</button>
<?php do_action('woocommerce_before_cart_collaterals'); ?>
<?php
/**
* Cart collaterals hook.
*
* @hooked woocommerce_cross_sell_display
* @hooked woocommerce_cart_totals - 10
*/
do_action('woocommerce_cart_collaterals');
?>
<?php do_action('woocommerce_after_cart'); ?>
<script>
jQuery(document).ready(function($) {
// 全选功能
$('#select-all, #footer-select-all').change(function() {
$('.item-checkbox').prop('checked', this.checked);
updateSelectedInfo();
});
// 更新选中商品信息
function updateSelectedInfo() {
let selectedCount = $('.item-checkbox:checked').length;
let total = 0;
$('.item-checkbox:checked').each(function() {
let itemKey = $(this).val();
let subtotal = $('tr.cart_item').has(this).find('.product-subtotal .amount').html();
if (subtotal) {
total += parseFloat(subtotal.replace(/[^\d.-]/g, ''));
}
});
$('#selected-count').text(selectedCount);
$('#selected-total').text('RM' + total.toFixed(2));
}
// 初始更新
updateSelectedInfo();
// 监听单个商品选择
$('.item-checkbox').change(updateSelectedInfo);
// 删除选中商品
$('#remove-selected').click(function() {
let selectedItems = [];
$('.item-checkbox:checked').each(function() {
selectedItems.push($(this).val());
});
if (selectedItems.length === 0) {
alert('<?php esc_html_e("Please select items to remove", "woocommerce"); ?>');
return;
}
if (confirm('<?php esc_html_e("Are you sure you want to remove the selected items?", "woocommerce"); ?>')) {
$.each(selectedItems, function(index, itemKey) {
$.ajax({
type: 'POST',
url: wc_cart_params.ajax_url,
data: {
action: 'woocommerce_remove_cart_item',
cart_item_key: itemKey,
security: wc_cart_params.remove_item_nonce
},
success: function(response) {
$(document.body).trigger('wc_fragment_refresh');
}
});
});
}
});
// 清空购物车
$('#clear-cart').click(function() {
if (confirm('<?php esc_html_e("Are you sure you want to clear your cart?", "woocommerce"); ?>')) {
$.ajax({
type: 'POST',
url: wc_cart_params.ajax_url,
data: {
action: 'woocommerce_clear_cart',
security: wc_cart_params.nonce
},
success: function(response) {
$(document.body).trigger('wc_fragment_refresh');
}
});
}
});
// 部分结算点击处理 - 安全版本
$('#partial-checkout').click(function() {
let $btn = $(this);
let selectedItems = $('.item-checkbox:checked').map(function() {
return $(this).val();
}).get();
if (selectedItems.length === 0) {
alert(<?php echo json_encode(__("Please select at least one item to checkout", "woocommerce")); ?>);
return;
}
// 添加加载指示器
$btn.prop('disabled', true).html(<?php echo json_encode(__("Processing...", "woocommerce")); ?>);
// 使用AJAX保存原始购物车状态
$.ajax({
type: 'POST',
url: wc_cart_params.ajax_url,
data: {
action: 'wc_save_original_cart',
security: wc_cart_params.nonce
},
success: function() {
// 创建临时表单提交
$('<form>', {
'method': 'post',
'action': '<?php echo esc_url(wc_get_checkout_url()); ?>'
}).append($('<input>', {
'type': 'hidden',
'name': 'selected_cart_items',
'value': JSON.stringify(selectedItems)
})).appendTo('body').submit();
},
error: function() {
alert(<?php echo json_encode(__("An error occurred. Please try again.", "woocommerce")); ?>);
$btn.prop('disabled', false).html(<?php echo json_encode(__("Proceed to checkout", "woocommerce")); ?>);
},
complete: function() {
// 添加超时恢复按钮状态
setTimeout(function() {
$btn.prop('disabled', false).html(<?php echo json_encode(__("Proceed to checkout", "woocommerce")); ?>);
}, 5000);
}
});
});
});
</script>
this is my full code in functions.php
// 添加清空购物车功能
add_action('wp_ajax_woocommerce_clear_cart', 'woocommerce_clear_cart');
add_action('wp_ajax_nopriv_woocommerce_clear_cart', 'woocommerce_clear_cart');
function woocommerce_clear_cart() {
// 修正 nonce 验证
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'woocommerce-cart-nonce')) {
wp_send_json_error('Invalid nonce');
}
WC()->cart->empty_cart();
wp_send_json_success();
}
// 处理部分结算 - 最终版
add_action('template_redirect', 'handle_partial_checkout');
function handle_partial_checkout() {
// 仅在进入结算页面时处理
if (is_checkout() && !is_wc_endpoint_url('order-received') && isset($_POST['selected_cart_items'])) {
$selected_items = json_decode(stripslashes($_POST['selected_cart_items']), true);
if (!empty($selected_items) && is_array($selected_items)) {
// 保存原始购物车到用户元数据(更持久)
if (is_user_logged_in()) {
$user_id = get_current_user_id();
update_user_meta($user_id, 'wc_original_cart', WC()->cart->get_cart_for_session());
} else {
WC()->session->set('wc_original_cart', WC()->cart->get_cart_for_session());
}
// 设置临时购物车标记
WC()->session->set('wc_partial_checkout', true);
// 创建新购物车只包含选中商品
$new_cart = [];
foreach ($selected_items as $item_key) {
if (isset(WC()->cart->cart_contents[$item_key])) {
$new_cart[$item_key] = WC()->cart->cart_contents[$item_key];
}
}
// 更新购物车
WC()->cart->cart_contents = $new_cart;
// 确保持久化存储更新
WC()->cart->persistent_cart_update();
WC()->cart->set_session();
WC()->cart->calculate_totals();
}
}
}
// 订单完成后恢复原始购物车
add_action('woocommerce_checkout_order_processed', 'restore_original_cart_after_order', 10, 3);
function restore_original_cart_after_order($order_id, $posted_data, $order) {
if (WC()->session->get('wc_partial_checkout')) {
restore_original_cart();
}
}
// 用户放弃结算时恢复购物车
add_action('template_redirect', 'maybe_restore_cart_on_cart_page');
function maybe_restore_cart_on_cart_page() {
if (is_cart() && WC()->session->get('wc_partial_checkout')) {
restore_original_cart();
}
}
// 通用恢复函数 - 增强版
function restore_original_cart() {
$original_cart = null;
// 尝试从用户元数据获取
if (is_user_logged_in()) {
$user_id = get_current_user_id();
$original_cart = get_user_meta($user_id, 'wc_original_cart', true);
delete_user_meta($user_id, 'wc_original_cart');
}
// 尝试从会话获取
else {
$original_cart = WC()->session->get('wc_original_cart');
WC()->session->set('wc_original_cart', null);
}
// 确保有有效的购物车数据
if ($original_cart && is_array($original_cart) && !empty($original_cart)) {
WC()->cart->cart_contents = $original_cart;
WC()->session->set('wc_partial_checkout', false);
// 关键步骤:更新会话和持久化存储
WC()->cart->persistent_cart_update();
WC()->cart->set_session();
WC()->cart->calculate_totals();
// 强制刷新购物车
WC()->cart->get_cart_from_session();
}
}
// 保存原始购物车状态 - 修正版
add_action('wp_ajax_wc_save_original_cart', 'wc_save_original_cart');
add_action('wp_ajax_nopriv_wc_save_original_cart', 'wc_save_original_cart');
function wc_save_original_cart() {
// 修正 nonce 验证
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'woocommerce-cart-nonce')) {
wp_send_json_error('Invalid nonce');
}
// 保存原始购物车
if (is_user_logged_in()) {
$user_id = get_current_user_id();
update_user_meta($user_id, 'wc_original_cart', WC()->cart->get_cart_for_session());
} else {
WC()->session->set('wc_original_cart', WC()->cart->get_cart_for_session());
}
wp_send_json_success();
}
Correct?

function https(url,data,fu){
$.ajax({
contentType:"application/json",
url: url, // 假设后端 API 地址
method: "POST",
data: JSON.stringify(data),
type: "json",
dataType: "json",
success:function(e){
console.log(e.data)
if(e.status==200){
console.log(e.data)
fu(e.data)
}else{
alert(e.text)
}
},
error: function (e) {
console.log(e.data)
console.error(e)
alert("请求失败,请稍后再试!"+e);
}
});
}
function checkLoginStatus() {
if (window.location.href.includes('/login.html')) return;
}
window.addEventListener('beforeunload', function(e) {
console.trace('触发页面跳转的堆栈跟踪:');
});
//安全验证函数
function validateUserSession() {
try {
// 防御性数据获取 + 数据清洗
const rawValue = localStorage.getItem("name");
const username = String(rawValue ?? '')
.trim()
.replace(/[\u200B-\u200D\uFEFF]/g, ''); // 移除零宽字符
// 调试信息
console.log('[Auth] Raw:', rawValue, 'Processed:', username);
// 复合验证条件
const isValid = (
username.length >= 2 && // 最小长度要求
!/[<>]/.test(username) && // 防止XSS
username !== 'null' &&
username !== 'undefined'
);
if (!isValid) {
console.warn('无效用户标识:', username);
// 安全跳转方法
//window.location.replace('/KuCun2/login.html');
// 立即终止执行
return Promise.reject('Invalid session');
}
// 返回清洗后的用户名
return username;
} catch (error) {
console.error('会话验证失败:', error);
// window.location.replace('/KuCun2/login.html');
return Promise.reject(error);
}
}
function deepMergeArrays(frontend, backend) {
const resultMap = new Map();
// 遍历前端数据并存入 Map 中以便快速查找
frontend.forEach(item => resultMap.set(item.id, { ...item }));
// 遍历后端数据并与前端数据进行合并
backend.forEach(item => {
if (resultMap.has(item.id)) {
// 如果存在相同 ID,则合并两者的内容
resultMap.set(
item.id,
Object.assign(resultMap.get(item.id), item)
);
} else {
// 如果不存在相同 ID,则新增该条目
resultMap.set(item.id, { ...item });
}
});
// 将最终结果转回数组形式
return Array.from(resultMap.values());
}
(function ($){
// 页面加载时检查登录状态
checkLoginStatus();
})(jQuery);
function removeSpecificCharactersAndConvertToNumber(str, charsToRemove) {
const regex = new RegExp([${charsToRemove}], 'g'); // 创建用于匹配指定字符的正则表达式
const cleanedStr = str.replace(regex, ''); // 移除指定字符
const numberValue = parseFloat(cleanedStr); // 转换为浮点数
return isNaN(numberValue) ? null : numberValue; // 如果无法解析,则返回 null
}
<modelVersion>4.0.0</modelVersion>
<groupId>KuCun2</groupId>
<artifactId>KuCun2</artifactId>
<version>0.0.1-SNAPSHOT</version>
war
<name>KuCun2</name>
<description/>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/>
<webVersion>4.0</webVersion>
UTF-8
3.21.12
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.servlet.jsp.jstl</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
<exclusions>
<exclusion>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.30</version>
</dependency>
</dependencies>
<build>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</build>
package com.kucun;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication(
)
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
//
// ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
// Arrays.stream(ctx.getBeanNamesForType(SecurityFilterChain.class))
// .forEach(System.out::println);
//
//
// // 测试密码加密示例
// BCryptPasswordEncoder encoder = new BCrypt();
// String rawPassword = "987987";
// String encodedPassword = encoder.encode(rawPassword);
// System.out.println("加密后的密码:" + encodedPassword);
SpringApplication.run(DemoApplication.class, args);
}
}package com.kucun.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.kucun.Config.Role.RoleConverter;
import com.kucun.Config.user.CustomUserDetails;
import com.kucun.data.entity.User;
import com.kucun.dataDo.UserRepository;
/**
* 获取数据
* @author Administrator
*
*/
@Service
public class CustomUserDetailsService/* implements UserDetailsService */{
//
// @Autowired
// private UserRepository userRepository;
//
// @Autowired
// private RoleConverter roleConverter;
//
// public CustomUserDetailsService() {
//
// super();
// System.out.println("11111");
// }
///**
// * 获取数据库中用户信息
// * @param andy 账号
// *
// * @return
// *
// */
// @Override
// public UserDetails loadUserByUsername(String andy) {
// System.out.println(andy);
// User user = userRepository.findByAndy(andy);
// System.out.println(user);
// return new CustomUserDetails(user,
// roleConverter.convert(user.getRole()) // 关键转换点[^1]
// );
// }
}package com.kucun.Config;
// 2. 基础安全配置
//@Configuration
//@EnableWebSecurity // 启用Web安全功能
public class SecurityConfig
//extends WebSecurityConfigurerAdapter
{
// @Override
// public void configure(WebSecurity web) {
// web.ignoring().antMatchers("/check-session");
// }
// // 添加自定义Controller
// @RestController
// public static class SessionCheckController {
// @GetMapping("/check-session")
// public ResponseEntity<?> checkSession(HttpServletRequest request) {
// return request.getSession(false) != null ?
// ResponseEntity.ok().build() :
// ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
// }
// }
// /**
// * 核心安全过滤器链配置
// * @param http HTTP安全构建器
// * @return 安全过滤器链
// * @throws Exception 配置异常
// *
// * █ 配置逻辑说明:
// * 1. authorizeHttpRequests: 定义访问控制规则
// * 2. formLogin: 配置表单登录
// * 3. logout: 配置注销行为
// * 4. exceptionHandling: 处理权限异常[^3]
// */
//
// // 修正后的配置方法
// @Override
// protected void configure(HttpSecurity http) throws Exception {
//
//
//
// http
// .csrf().disable()
// .sessionManagement()
// .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
// .invalidSessionUrl("/login.html?session=invalid")
// .maximumSessions(1)
// .maxSessionsPreventsLogin(false)
// .and()
// .and()
// .addFilterBefore(jsonAuthFilter(), UsernamePasswordAuthenticationFilter.class) // 关键配置
// .authorizeRequests()
// .antMatchers("/login.html", "/users/login").permitAll()
// .antMatchers("/js/**", "/css/**", "/fonts/**", "/images/**","/check-session","/main/bootstrap-3.3.7-dist/**").permitAll()
// .antMatchers("/users/guanli/**").hasAuthority("ROLE_ADMIN")
// .anyRequest().authenticated()
// .and()
// .formLogin().disable()
//// .loginPage("/login.html")
//// .loginProcessingUrl("/users/login")
////
//// .successHandler(ajaxAuthenticationSuccessHandler()) // 自定义成功处理器
//// .failureHandler(ajaxAuthenticationFailureHandler()) // 自定义失败处理器
//// .defaultSuccessUrl("/index.html")
//// .failureUrl("/login.html?error=true")
//// .usernameParameter("andy") // 修改用户名参数名
//// .passwordParameter("pass") // 修改密码参数名
//// .and()
//
// .logout()
// .logoutUrl("/logout")
// .logoutSuccessUrl("/login.html")
// .and()
// .csrf()
// .ignoringAntMatchers("/users/login")
// .and()
// .headers()
// .frameOptions().sameOrigin()
// .and()
// .exceptionHandling()
// .accessDeniedHandler(accessDeniedHandler()); // 统一使用Handler
// }
//
//
//
//
//
// // 返回JSON格式的成功响应
// @Bean
// public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() {
// return (request, response, authentication) -> {
//
//
//
// // 强制创建服务端会话
// request.getSession(true);
//
//
//
//
// String contextPath = request.getContextPath();
// HttpSession session = request.getSession(true);
// Cookie cookie = new Cookie("JSESSIONID", session.getId());
// cookie.setPath(contextPath.isEmpty() ? "/" : contextPath + "/");
// cookie.setMaxAge(1800); // 30分钟
// response.addCookie(cookie);
//
//
// //构建安全响应数据
// Map<String, Object> responseData = new HashMap<>();
// responseData.put("sessionId", request.getSession().getId());
// responseData.put("userInfo",Collections.unmodifiableMap(new HashMap<String, Object>() {/**
// *
// */
// private static final long serialVersionUID = 1L;
//
// {
// put("Name", ((CustomUserDetails)authentication.getPrincipal()).getName());
// put("role", ((CustomUserDetails)authentication.getPrincipal()).getRole());
// }}));
//
//
//
// // 统一返回JSON格式
// response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// // new ObjectMapper().writeValue(response.getWriter(), responseData);
//
//
//
//
//
//
//
//
//
// response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
//
// response.setStatus(HttpStatus.OK.value());
// System.out.println(authentication.getPrincipal()+""+authentication.getName());
//
// if (request.getHeader("X-Requested-With") == null) { // 非AJAX请求
// response.sendRedirect("/index.html");
// } else {
//
// //String re=userDetails.getUser().toString()
// new ObjectMapper().writeValue(response.getWriter(), userDetails.getUser()
// );
//
// }
//
//
//
//
//
//
// };
// }
//
// // 返回401状态码和错误信息
// @Bean
// public AuthenticationFailureHandler ajaxAuthenticationFailureHandler() {
// return (request, response, exception) -> {
// if (request.getHeader("X-Requested-With") == null) {
// response.sendRedirect("/login.html?error=true");
// } else {
// response.setStatus(HttpStatus.UNAUTHORIZED.value());
// response.getWriter().write("{\"error\":\"Authentication failed\"}");
// }
// };
// }
//
// // 处理未认证请求
// @Bean
// public AuthenticationEntryPoint ajaxAuthenticationEntryPoint() {
// return (request, response, exception) -> {
// if (request.getHeader("X-Requested-With") == null) {
// response.sendRedirect("/login.html?error=true");
// } else {
// response.setStatus(HttpStatus.UNAUTHORIZED.value());
// response.getWriter().write("{\"error\":\"Authentication failed\"}");
// }
// };
// }
//
//
//
//
//
// @Bean
// public JsonUsernamePasswordAuthenticationFilter jsonAuthFilter() throws Exception {
//
// JsonUsernamePasswordAuthenticationFilter filter =
// new JsonUsernamePasswordAuthenticationFilter();
// filter.setAuthenticationManager(authenticationManagerBean());
// filter.setUsernameParameter("andy"); // 设置自定义参数名
// filter.setPasswordParameter("pass");
// filter.setFilterProcessesUrl("/users/login");
// filter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler());
// filter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler());
//
// return filter;
// }
//
//
// /**
// * 密码编码器(必须配置)
// * 使用BCrypt强哈希算法加密
// */
// @Bean
// public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
// }
//
//
// @Bean
// public AccessDeniedHandler accessDeniedHandler() {
// System.out.println("0000");
// return (request, response, ex) -> {
// if (!response.isCommitted()) {
// response.sendRedirect("/error/403");
// }
// };
// }
//
//
//}
//
//
//
//
//
//
//class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
// private final ObjectMapper objectMapper = new ObjectMapper();
//
// @Override
// public Authentication attemptAuthentication(HttpServletRequest request,
// HttpServletResponse response)
// throws AuthenticationException {
// System.out.println("收到认证请求,路径:" + request.getRequestURI());
// System.out.println("请求方法:" + request.getMethod());
// System.out.println("Content-Type:" + request.getContentType());
// if (request.getContentType() != null &&
// request.getContentType().startsWith(MediaType.APPLICATION_JSON_VALUE)) {
//
// try (InputStream is = request.getInputStream()) {
// Map<String, String> authMap = objectMapper.readValue(is, Map.class);
//
// String username = authMap.getOrDefault(getUsernameParameter(), "");
// String password = authMap.getOrDefault(getPasswordParameter(), "");
//
// // 调试日志
// System.out.println("Authentication attempt with: " + username+'_'+ password);
//
// UsernamePasswordAuthenticationToken authRequest =
// new UsernamePasswordAuthenticationToken(username, password);
//
// setDetails(request, authRequest);
// return this.getAuthenticationManager().authenticate(authRequest);
// } catch (IOException e) {
// throw new AuthenticationServiceException("认证请求解析失败", e);
// }
// }
// Authentication aut= super.attemptAuthentication(request, response);
// System.out.println("结果:"+aut.isAuthenticated());
//
// return aut;
// }
}
package com.kucun.Config.Role;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.json.Json;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 权限转化
* @author Administrator
*
*/
@Component
public class RoleConverter {
// private static final Map<Integer, String> ROLE_MAP = new HashMap<>();
//
// @PostConstruct
// public void init() {
// ROLE_MAP.put(0, "ROLE_ADMIN");
// ROLE_MAP.put(1, "ROLE_USER");
// ROLE_MAP.put(2, "ROLE_MANAGER");
// ROLE_MAP.put(3, "ROLE_AUDITOR");
// }
//
// public List<GrantedAuthority> convert(int roleCode) {
// ObjectMapper mapper = new ObjectMapper();
// try {
// System.out.println(mapper.writeValueAsString(Collections.singletonList(
// new SimpleGrantedAuthority(ROLE_MAP.getOrDefault(roleCode, "ROLE_GUEST")))).toString());//输出[{"authority":"ROLE_ADMIN"}]
// } catch (JsonProcessingException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// return Collections.singletonList(
// new SimpleGrantedAuthority(ROLE_MAP.getOrDefault(roleCode, "ROLE_GUEST"))
// );
// }
//
}package com.kucun.Config.user;
import java.util.Collection;
import com.kucun.data.entity.User;
public class CustomUserDetails /*implements UserDetails*/ {
// /**
// *
// */
// private static final long serialVersionUID = 1L;
// private final String andy; // 对应andy字段
// private final String name;
// private final int role;
// private final String password;
// private final User users;
// private final Collection<? extends GrantedAuthority> authorities;
//
// public CustomUserDetails(User user, Collection<? extends GrantedAuthority> authorities) {
// this.andy = user.getAndy();
// this.name = user.getName();
// this.role = user.getRole();
// this.password = user.getPass();
// user.setPass(null);
// this.users=user;
// this.authorities = authorities;
// }
//
// // 实现UserDetails接口方法
// @Override public String getUsername() { return andy; }
// @Override public String getPassword() { return password; }
// @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; }
//
// // 自定义字段访问方法
// public String getName() { return name; }
// public User getUser() { return users; }
// public int getRole() { return role; }
//
// // 其他必要方法
// @Override public boolean isAccountNonExpired() { return true; }
// @Override public boolean isAccountNonLocked() { return true; }
// @Override public boolean isCredentialsNonExpired() { return true; }
// @Override public boolean isEnabled() { return true; }
} <!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>峤丞板材库存管理</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
*{
margin:0;
padding:0;
}
.frame-header {
height: 60px;
background-color: #23262E;
justify-content: space-between;
}
.frame-header-li{
font-family: Arial, Helvetica, sans-serif;
font-size:40px;
}
.frame-ul{
}
.frame-ul li{
border-style: solid;
border-width:1px 0px 1px 0px;
margin-top: 1px;
height: 35px;
text-align: center
}
#username{
position: absolute;
right: 0; /* 靠右 */
}
.frame-body {
position: fixed;
top: 60px;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: row;
}
.frame-side {
scrollbar-width: none; /* firefox隐藏滚动条 */
-ms-overflow-style: none; /* IE 10+隐藏滚动条 */
overflow-x: hidden;
overflow-y: auto;
width: 200px;
background-color:#9e5;
}
.frame-side::-webkit-scrollbar {
display: none; /* Chrome Safari 隐藏滚动条*/
}
.frame-main {
flex-grow: 1;
background-color:#fff;
}
.jiaoluo{
margin: auto;
margin-right: 0px;
}
</style>
</head>
<body>
峤丞木材仓库管理
峤丞木材仓库管理
<button class=".menu-button" style="">注销</button>
首页
材料录入
材料录入
<iframe id="iframeid" name="main" src="main/index.html" width="100%" height="100%" frameborder="0">
</iframe>
</body>
<script type="text/javascript" src="js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="js/jsyilai.js"></script>
</html><!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>扁平简洁的登录页面演示_dowebok</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
登 录
<form class="login100-form validate-form">
用户名
<input class="input100" type="text" name="andy" placeholder="请输入用户名">
密码
<input class="input100" type="password" name="pass" placeholder="请输入用户密码">
<input class="input-checkbox100" id="ckb1" type="checkbox" name="remember-me">
<label class="label-checkbox100" for="ckb1">记住我</label>
忘记密码?
<button class="login100-form-btn">登 录</button>
</form>
<script type="text/javascript">
</script>
<script src="js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="js/jsyilai.js"></script>
</body>
</html> yilai.js
(function ($) {
var pathName = window.location.pathname;
console.log(pathName); // 输出类似于 '/index.html'
//alert(pathName)
switch (pathName){
case "/KuCun2/login.html":
jQuery.getScript('/KuCun2/js/main.js?'+Date.now())
jQuery.getScript('/KuCun2/js/login.js?'+Date.now())
break;
case "/KuCun2/main/Guanli.html":
jQuery.getScript('/KuCun2/js/main.js?'+Date.now())
jQuery.getScript('/KuCun2/js/guanli.js?'+Date.now())
jQuery.getScript('/KuCun2/main/bootstrap-3.3.7-dist/js/FileSaver.js?'+Date.now())
jQuery.getScript('/KuCun2/main/bootstrap-3.3.7-dist/js/MyTable.js?'+Date.now())
break;
case "/KuCun2/index.html":
jQuery.getScript('/KuCun2/js/main.js?'+Date.now())
jQuery.getScript('/KuCun2/js/index.js?'+Date.now())
break;
}
})(jQuery) main.js
function https(url,data,fu){
$.ajax({
contentType:"application/json",
url: url, // 假设后端 API 地址
method: "POST",
data: JSON.stringify(data),
type: "json",
dataType: "json",
success:function(e){
console.log(e.data)
if(e.status==200){
console.log(e.data)
fu(e.data)
}else{
alert(e.text)
}
},
error: function (e) {
console.log(e.data)
console.error(e)
alert("请求失败,请稍后再试!"+e);
}
});
}
function checkLoginStatus() {
if (window.location.href.includes('/login.html')) return;
}
window.addEventListener('beforeunload', function(e) {
console.trace('触发页面跳转的堆栈跟踪:');
});
//安全验证函数
function validateUserSession() {
try {
// 防御性数据获取 + 数据清洗
const rawValue = localStorage.getItem("name");
const username = String(rawValue ?? '')
.trim()
.replace(/[\u200B-\u200D\uFEFF]/g, ''); // 移除零宽字符
// 调试信息
console.log('[Auth] Raw:', rawValue, 'Processed:', username);
// 复合验证条件
const isValid = (
username.length >= 2 && // 最小长度要求
!/[<>]/.test(username) && // 防止XSS
username !== 'null' &&
username !== 'undefined'
);
if (!isValid) {
console.warn('无效用户标识:', username);
// 安全跳转方法
//window.location.replace('/KuCun2/login.html');
// 立即终止执行
return Promise.reject('Invalid session');
}
// 返回清洗后的用户名
return username;
} catch (error) {
console.error('会话验证失败:', error);
// window.location.replace('/KuCun2/login.html');
return Promise.reject(error);
}
}
function deepMergeArrays(frontend, backend) {
const resultMap = new Map();
// 遍历前端数据并存入 Map 中以便快速查找
frontend.forEach(item => resultMap.set(item.id, { ...item }));
// 遍历后端数据并与前端数据进行合并
backend.forEach(item => {
if (resultMap.has(item.id)) {
// 如果存在相同 ID,则合并两者的内容
resultMap.set(
item.id,
Object.assign(resultMap.get(item.id), item)
);
} else {
// 如果不存在相同 ID,则新增该条目
resultMap.set(item.id, { ...item });
}
});
// 将最终结果转回数组形式
return Array.from(resultMap.values());
}
(function ($){
// 页面加载时检查登录状态
checkLoginStatus();
})(jQuery);
function removeSpecificCharactersAndConvertToNumber(str, charsToRemove) {
const regex = new RegExp([${charsToRemove}], 'g'); // 创建用于匹配指定字符的正则表达式
const cleanedStr = str.replace(regex, ''); // 移除指定字符
const numberValue = parseFloat(cleanedStr); // 转换为浮点数
return isNaN(numberValue) ? null : numberValue; // 如果无法解析,则返回 null
}
logi.js/// <reference path="jquery.d.ts" />
// $(document).ready(function(){
// $("#submit").click(function(){
//
//
// $.ajax({
// url:"../users/login",
// async:false,
// data:JSON.stringify({
// andy:$("#andy").val(),
// pass:$("#pass").val(),
// name:$("#name").val()}),
// type:"post",
// contentType:"application/json",
// success:function(e){
// alert(e)
// }
// });
//
//
// });
// })
(function ($) {
"use strict";
/*==================================================================
[ Focus Contact2 ]*/
$('.input100').each(function () {
$(this).on('blur', function () {
if ($(this).val().trim() != "") {
$(this).addClass('has-val');
} else {
$(this).removeClass('has-val');
}
})
})
/*==================================================================
[ Validate ]*/
var input = $('.validate-input .input100');
$('.validate-form').on('submit', function (e) {
e.preventDefault();
var check = true;
for (var i = 0; i < input.length; i++) {
if (validate(input[i]) == false) {
showValidate(input[i]);
check = false;
}
}
confirm(input)
if(check) login(input);
return check;
});
$('.validate-form .input100').each(function () {
$(this).focus(function () {
hideValidate(this);
});
});
function validate(input) {
if ($(input).attr('type') == 'email' || $(input).attr('name') == 'email') {
if ($(input).val().trim().match(/^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{1,5}|[0-9]{1,3})(\]?)$/) == null) {
return false;
}
} else {
if ($(input).val().trim() == '') {
return false;
}
}
}
function showValidate(input) {
var thisAlert = $(input).parent();
alert(input)
$(thisAlert).addClass('alert-validate');
}
function hideValidate(input) {
var thisAlert = $(input).parent();
$(thisAlert).removeClass('alert-validate');
}
// 登录按钮点击事件
function login(datas) {
var data={}
datas.each(function(a,element){
data[$(element).attr('name')]=$(element).val()
})
alert(data.name);
//var data={ andy,pass }
// 模拟 AJAX 请求
https("/KuCun2/users/login",data,function (response) {
alert("122222");
if (response.name) {
localStorage.setItem("name", response.name); // 保存用户名到本地存储
localStorage.setItem("role", response.role); // 保存权限到本地存储
alert( response.name)
window.location.href = '/KuCun2/index.html';
} else {
alert("登录失败,请检查用户名和密码!");
}
})
};
// 注销按钮点击事件
$("#logout-btn").click(function () {
localStorage.removeItem("name"); // 清除本地存储中的用户名
checkLoginStatus(); // 更新登录状态
});
})(jQuery);index.js$().ready(function () {
const username = localStorage.getItem("name");
$("#username").text(username)
const $username = $('#username');
const $menuButton = $('.menu-button');
let hideTimeout = null;
// 点击显示按钮
$username.on('click', function(e) {
e.stopPropagation();
$menuButton.show();
});
// 区域外点击隐藏
$(document).on('click', function(e) {
if (!$username.add($menuButton).is(e.target) &&
$username.has(e.target).length === 0 &&
$menuButton.has(e.target).length === 0) {
$menuButton.hide();
}
});
// 智能隐藏逻辑
const elements = $username.add($menuButton);
elements.on('mouseleave', function() {
hideTimeout = setTimeout(() => {
$menuButton.hide();
}, 200);
}).on('mouseenter', function() {
clearTimeout(hideTimeout);
});
var $iframe = $('<iframe>')
// 使用 jQuery 监听 iframe 的 load 事件
$iframe.on('load', function() {
try {
// 确保 iframe 的内容可以被访问(非跨域情况下)
var iframeDocument = $(this).contents();
var result = iframeDocument.find('body').text(); // 获取 iframe 内部的内容作为返回值
console.log("Iframe 加载完成后的返回值:", result);
window.location.href="/KuCun2/login.html"
} catch (error) {
console.error("无法访问 iframe 内容,可能是因为跨域限制:", error.message);
}
});
// 将 iframe 添加到文档中
$('body').append($iframe);
});
guanli.js(function ($) {
//checkLoginStatus();//权限
https("/KuCun2/users/guanli/getusers",null,function(e){
$("#DataTable").remove()
const c=[{id:" 序号 ",
name:"名字",
andy:"账号",
role:"权限"
}]
let row = e.length+1;
//c.concat(b);
//const combinedArray = [c,e];
var r=deepMergeArrays(c,e)
console.log(r )
let sclass = "table table-hover";
let bodys=[]
let table=$("#TableView")
table.append(bodys)
let te= table.find("table")
$.each(r, function(index, value) {
var tr=""
var boo=false
if(index==0){var tr=""; boo=false}
tr+=""
$.each(value, function(name,val) {
if(name!="pass"){
if(name!="id"){
tr+=${val} ;
}else{
tr+=${val} ;
}
}
})
tr+=" "
if(index==0){ tr+=""}
te.append(tr)
});
addRight("#DataTable");
//
// let sclass = "table table-striped";
// let bodys=[]
//
// for(let i = -1; i <= row; i++) {
// bodys.push("");
//
// if(i=-1){
//
// for(var a in c[0]) {
//
// var bo= ""+c[0][a]+"";
// bodys.push(bo)
//
// }
//
//
// }else{
//
//
// for(let j = 0; j <= col; j++) {
// if(j == 0) {
// var bo = "<td><span>" + e[i][b[j]] + "# ";
// bodys.push(bo)
// } else {
// var bo= ""
// +
//
// ''
// +" ";
// bodys.push(bo)
// }
// }
// }
// bodys.push( " ");
//
// }
// bodys.push("");
// var s=bodys.join('')
// $("#TableView").append(s);
// addRight("#DataTable");
//
})
})(jQuery) 访问index.html会被重定向到login.html其他以页面不回

function https(url,data,fu){
$.ajax({
contentType:“application/json”,
url: url, // 假设后端 API 地址
method: “POST”,
data: JSON.stringify(data),
type: “json”,
dataType: “json”,
success:function(e){
console.log(e.data)
if(e.status==200){
console.log(e.data)
fu(e.data)
}else{
alert(e.text)
}
},
error: function (e) {
console.log(e.data)
console.error(e)
alert(“请求失败,请稍后再试!”+e);
}
});
}
function checkLoginStatus() {
if (window.location.href.includes(‘/login.html’)) return;
}
window.addEventListener(‘beforeunload’, function(e) {
console.trace(‘触发页面跳转的堆栈跟踪:’);
});
//安全验证函数
function validateUserSession() {
try {
// 防御性数据获取 + 数据清洗
const rawValue = localStorage.getItem(“name”);
const username = String(rawValue ?? ‘’)
.trim()
.replace(/[\u200B-\u200D\uFEFF]/g, ‘’); // 移除零宽字符
// 调试信息
console.log('[Auth] Raw:', rawValue, 'Processed:', username);
// 复合验证条件
const isValid = (
username.length >= 2 && // 最小长度要求
!/[<>]/.test(username) && // 防止XSS
username !== 'null' &&
username !== 'undefined'
);
if (!isValid) {
console.warn('无效用户标识:', username);
// 安全跳转方法
//window.location.replace('/KuCun2/login.html');
// 立即终止执行
return Promise.reject('Invalid session');
}
// 返回清洗后的用户名
return username;
} catch (error) {
console.error(‘会话验证失败:’, error);
// window.location.replace(‘/KuCun2/login.html’);
return Promise.reject(error);
}
}
function deepMergeArrays(frontend, backend) {
const resultMap = new Map();
// 遍历前端数据并存入 Map 中以便快速查找
frontend.forEach(item => resultMap.set(item.id, { ...item }));
// 遍历后端数据并与前端数据进行合并
backend.forEach(item => {
if (resultMap.has(item.id)) {
// 如果存在相同 ID,则合并两者的内容
resultMap.set(
item.id,
Object.assign(resultMap.get(item.id), item)
);
} else {
// 如果不存在相同 ID,则新增该条目
resultMap.set(item.id, { ...item });
}
});
// 将最终结果转回数组形式
return Array.from(resultMap.values());
}
(function ($){
// 页面加载时检查登录状态
checkLoginStatus();
})(jQuery);
function removeSpecificCharactersAndConvertToNumber(str, charsToRemove) {
const regex = new RegExp([${charsToRemove}], ‘g’); // 创建用于匹配指定字符的正则表达式
const cleanedStr = str.replace(regex, ‘’); // 移除指定字符
const numberValue = parseFloat(cleanedStr); // 转换为浮点数
return isNaN(numberValue) ? null : numberValue; // 如果无法解析,则返回 null
}
<modelVersion>4.0.0</modelVersion>
<groupId>KuCun2</groupId>
<artifactId>KuCun2</artifactId>
<version>0.0.1-SNAPSHOT</version>
war
<name>KuCun2</name>
<description/>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/>
<webVersion>4.0</webVersion>
UTF-8
3.21.12
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.servlet.jsp.jstl</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
<exclusions>
<exclusion>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.30</version>
</dependency>
</dependencies>
<build>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</build>
package com.kucun;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication(
)
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
//
// ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
// Arrays.stream(ctx.getBeanNamesForType(SecurityFilterChain.class))
// .forEach(System.out::println);
//
//
// // 测试密码加密示例
// BCryptPasswordEncoder encoder = new BCrypt();
// String rawPassword = “987987”;
// String encodedPassword = encoder.encode(rawPassword);
// System.out.println(“加密后的密码:” + encodedPassword);
SpringApplication.run(DemoApplication.class, args);
}
}package com.kucun.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.kucun.Config.Role.RoleConverter;
import com.kucun.Config.user.CustomUserDetails;
import com.kucun.data.entity.User;
import com.kucun.dataDo.UserRepository;
/**
获取数据
@author Administrator
/
@Service
public class CustomUserDetailsService/ implements UserDetailsService /{
//
// @Autowired
// private UserRepository userRepository;
//
// @Autowired
// private RoleConverter roleConverter;
//
// public CustomUserDetailsService() {
//
// super();
// System.out.println(“11111”);
// }
///*
// * 获取数据库中用户信息
// * @param andy 账号
// *
// * @return
// *
// */
// @Override
// public UserDetails loadUserByUsername(String andy) {
// System.out.println(andy);
// User user = userRepository.findByAndy(andy);
// System.out.println(user);
// return new CustomUserDetails(user,
// roleConverter.convert(user.getRole()) // 关键转换点1
// );
// }
}package com.kucun.Config;
// 2. 基础安全配置
//@Configuration
//@EnableWebSecurity // 启用Web安全功能
public class SecurityConfig
//extends WebSecurityConfigurerAdapter
{
// @Override
// public void configure(WebSecurity web) {
// web.ignoring().antMatchers(“/check-session”);
// }
// // 添加自定义Controller
// @RestController
// public static class SessionCheckController {
// @GetMapping(“/check-session”)
// public ResponseEntity<?> checkSession(HttpServletRequest request) {
// return request.getSession(false) != null ?
// ResponseEntity.ok().build() :
// ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
// }
// }
// /**
// * 核心安全过滤器链配置
// * @param http HTTP安全构建器
// * @return 安全过滤器链
// * @throws Exception 配置异常
// *
// * █ 配置逻辑说明:
// * 1. authorizeHttpRequests: 定义访问控制规则
// * 2. formLogin: 配置表单登录
// * 3. logout: 配置注销行为
// * 4. exceptionHandling: 处理权限异常3
// /
//
// // 修正后的配置方法
// @Override
// protected void configure(HttpSecurity http) throws Exception {
//
//
//
// http
// .csrf().disable()
// .sessionManagement()
// .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
// .invalidSessionUrl(“/login.html?session=invalid”)
// .maximumSessions(1)
// .maxSessionsPreventsLogin(false)
// .and()
// .and()
// .addFilterBefore(jsonAuthFilter(), UsernamePasswordAuthenticationFilter.class) // 关键配置
// .authorizeRequests()
// .antMatchers(“/login.html”, “/users/login”).permitAll()
// .antMatchers(“/js/", "/css/”, “/fonts/", "/images/”,“/check-session”,“/main/bootstrap-3.3.7-dist/“).permitAll()
// .antMatchers(”/users/guanli/”).hasAuthority(“ROLE_ADMIN”)
// .anyRequest().authenticated()
// .and()
// .formLogin().disable()
//// .loginPage(“/login.html”)
//// .loginProcessingUrl(“/users/login”)
////
//// .successHandler(ajaxAuthenticationSuccessHandler()) // 自定义成功处理器
//// .failureHandler(ajaxAuthenticationFailureHandler()) // 自定义失败处理器
//// .defaultSuccessUrl(“/index.html”)
//// .failureUrl(“/login.html?error=true”)
//// .usernameParameter(“andy”) // 修改用户名参数名
//// .passwordParameter(“pass”) // 修改密码参数名
//// .and()
//
// .logout()
// .logoutUrl(“/logout”)
// .logoutSuccessUrl(“/login.html”)
// .and()
// .csrf()
// .ignoringAntMatchers(“/users/login”)
// .and()
// .headers()
// .frameOptions().sameOrigin()
// .and()
// .exceptionHandling()
// .accessDeniedHandler(accessDeniedHandler()); // 统一使用Handler
// }
//
//
//
//
//
// // 返回JSON格式的成功响应
// @Bean
// public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() {
// return (request, response, authentication) -> {
//
//
//
// // 强制创建服务端会话
// request.getSession(true);
//
//
//
//
// String contextPath = request.getContextPath();
// HttpSession session = request.getSession(true);
// Cookie cookie = new Cookie(“JSESSIONID”, session.getId());
// cookie.setPath(contextPath.isEmpty() ? “/” : contextPath + “/”);
// cookie.setMaxAge(1800); // 30分钟
// response.addCookie(cookie);
//
//
// //构建安全响应数据
// Map<String, Object> responseData = new HashMap<>();
// responseData.put(“sessionId”, request.getSession().getId());
// responseData.put(“userInfo”,Collections.unmodifiableMap(new HashMap<String, Object>() {/*
// *
// /
// private static final long serialVersionUID = 1L;
//
// {
// put(“Name”, ((CustomUserDetails)authentication.getPrincipal()).getName());
// put(“role”, ((CustomUserDetails)authentication.getPrincipal()).getRole());
// }}));
//
//
//
// // 统一返回JSON格式
// response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// // new ObjectMapper().writeValue(response.getWriter(), responseData);
//
//
//
//
//
//
//
//
//
// response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
//
// response.setStatus(HttpStatus.OK.value());
// System.out.println(authentication.getPrincipal()+“”+authentication.getName());
//
// if (request.getHeader(“X-Requested-With”) == null) { // 非AJAX请求
// response.sendRedirect(“/index.html”);
// } else {
//
// //String re=userDetails.getUser().toString()
// new ObjectMapper().writeValue(response.getWriter(), userDetails.getUser()
// );
//
// }
//
//
//
//
//
//
// };
// }
//
// // 返回401状态码和错误信息
// @Bean
// public AuthenticationFailureHandler ajaxAuthenticationFailureHandler() {
// return (request, response, exception) -> {
// if (request.getHeader(“X-Requested-With”) == null) {
// response.sendRedirect(“/login.html?error=true”);
// } else {
// response.setStatus(HttpStatus.UNAUTHORIZED.value());
// response.getWriter().write(“{"error":"Authentication failed"}”);
// }
// };
// }
//
// // 处理未认证请求
// @Bean
// public AuthenticationEntryPoint ajaxAuthenticationEntryPoint() {
// return (request, response, exception) -> {
// if (request.getHeader(“X-Requested-With”) == null) {
// response.sendRedirect(“/login.html?error=true”);
// } else {
// response.setStatus(HttpStatus.UNAUTHORIZED.value());
// response.getWriter().write(“{"error":"Authentication failed"}”);
// }
// };
// }
//
//
//
//
//
// @Bean
// public JsonUsernamePasswordAuthenticationFilter jsonAuthFilter() throws Exception {
//
// JsonUsernamePasswordAuthenticationFilter filter =
// new JsonUsernamePasswordAuthenticationFilter();
// filter.setAuthenticationManager(authenticationManagerBean());
// filter.setUsernameParameter(“andy”); // 设置自定义参数名
// filter.setPasswordParameter(“pass”);
// filter.setFilterProcessesUrl(“/users/login”);
// filter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler());
// filter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler());
//
// return filter;
// }
//
//
// /*
// * 密码编码器(必须配置)
// * 使用BCrypt强哈希算法加密
// */
// @Bean
// public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
// }
//
//
// @Bean
// public AccessDeniedHandler accessDeniedHandler() {
// System.out.println(“0000”);
// return (request, response, ex) -> {
// if (!response.isCommitted()) {
// response.sendRedirect(“/error/403”);
// }
// };
// }
//
//
//}
//
//
//
//
//
//
//class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
// private final ObjectMapper objectMapper = new ObjectMapper();
//
// @Override
// public Authentication attemptAuthentication(HttpServletRequest request,
// HttpServletResponse response)
// throws AuthenticationException {
// System.out.println(“收到认证请求,路径:” + request.getRequestURI());
// System.out.println(“请求方法:” + request.getMethod());
// System.out.println(“Content-Type:” + request.getContentType());
// if (request.getContentType() != null &&
// request.getContentType().startsWith(MediaType.APPLICATION_JSON_VALUE)) {
//
// try (InputStream is = request.getInputStream()) {
// Map<String, String> authMap = objectMapper.readValue(is, Map.class);
//
// String username = authMap.getOrDefault(getUsernameParameter(), “”);
// String password = authMap.getOrDefault(getPasswordParameter(), “”);
//
// // 调试日志
// System.out.println("Authentication attempt with: " + username+‘_’+ password);
//
// UsernamePasswordAuthenticationToken authRequest =
// new UsernamePasswordAuthenticationToken(username, password);
//
// setDetails(request, authRequest);
// return this.getAuthenticationManager().authenticate(authRequest);
// } catch (IOException e) {
// throw new AuthenticationServiceException(“认证请求解析失败”, e);
// }
// }
// Authentication aut= super.attemptAuthentication(request, response);
// System.out.println(“结果:”+aut.isAuthenticated());
//
// return aut;
// }
}
package com.kucun.Config.Role;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.json.Json;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
权限转化
@author Administrator
*/
@Component
public class RoleConverter {
// private static final Map<Integer, String> ROLE_MAP = new HashMap<>();
//
// @PostConstruct
// public void init() {
// ROLE_MAP.put(0, “ROLE_ADMIN”);
// ROLE_MAP.put(1, “ROLE_USER”);
// ROLE_MAP.put(2, “ROLE_MANAGER”);
// ROLE_MAP.put(3, “ROLE_AUDITOR”);
// }
//
// public List<GrantedAuthority> convert(int roleCode) {
// ObjectMapper mapper = new ObjectMapper();
// try {
// System.out.println(mapper.writeValueAsString(Collections.singletonList(
// new SimpleGrantedAuthority(ROLE_MAP.getOrDefault(roleCode, “ROLE_GUEST”)))).toString());//输出[{“authority”:“ROLE_ADMIN”}]
// } catch (JsonProcessingException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// return Collections.singletonList(
// new SimpleGrantedAuthority(ROLE_MAP.getOrDefault(roleCode, “ROLE_GUEST”))
// );
// }
//
}package com.kucun.Config.user;
import java.util.Collection;
import com.kucun.data.entity.User;
public class CustomUserDetails /implements UserDetails/ {
// /**
// *
// */
// private static final long serialVersionUID = 1L;
// private final String andy; // 对应andy字段
// private final String name;
// private final int role;
// private final String password;
// private final User users;
// private final Collection<? extends GrantedAuthority> authorities;
//
// public CustomUserDetails(User user, Collection<? extends GrantedAuthority> authorities) {
// this.andy = user.getAndy();
// this.name = user.getName();
// this.role = user.getRole();
// this.password = user.getPass();
// user.setPass(null);
// this.users=user;
// this.authorities = authorities;
// }
//
// // 实现UserDetails接口方法
// @Override public String getUsername() { return andy; }
// @Override public String getPassword() { return password; }
// @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; }
//
// // 自定义字段访问方法
// public String getName() { return name; }
// public User getUser() { return users; }
// public int getRole() { return role; }
//
// // 其他必要方法
// @Override public boolean isAccountNonExpired() { return true; }
// @Override public boolean isAccountNonLocked() { return true; }
// @Override public boolean isCredentialsNonExpired() { return true; }
// @Override public boolean isEnabled() { return true; }
} <!doctype html>
<html>
<head>
<meta charset=“utf-8”>
<meta name=“viewport” content=“width=device-width, initial-scale=1, maximum-scale=1”>
<title>峤丞板材库存管理</title>
<meta name=“viewport” content=“width=device-width, initial-scale=1”>
<style type=“text/css”>
*{
margin:0;
padding:0;
}
.frame-header {
height: 60px;
background-color: #23262E;
justify-content: space-between;
}
.frame-header-li{
font-family: Arial, Helvetica, sans-serif;
font-size:40px;
}
.frame-ul{
}
.frame-ul li{
border-style: solid;
border-width:1px 0px 1px 0px;
margin-top: 1px;
height: 35px;
text-align: center
}
#username{
position: absolute;
right: 0; /* 靠右 */
}
.frame-body {
position: fixed;
top: 60px;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: row;
}
.frame-side {
scrollbar-width: none; /* firefox隐藏滚动条 /
-ms-overflow-style: none; / IE 10+隐藏滚动条 /
overflow-x: hidden;
overflow-y: auto;
width: 200px;
background-color:#9e5;
}
.frame-side::-webkit-scrollbar {
display: none; / Chrome Safari 隐藏滚动条*/
}
.frame-main {
flex-grow: 1;
background-color:#fff;
}
.jiaoluo{
margin: auto;
margin-right: 0px;
}
</style>
</head>
<body>
峤丞木材仓库管理
峤丞木材仓库管理
<button class=“.menu-button” style=“”>注销</button>
首页
材料录入
材料录入
<iframe id="iframeid" name="main" src="main/index.html" width="100%" height="100%" frameborder="0">
</iframe>
</body>
<script type=“text/javascript” src=“js/jquery-3.2.1.min.js”></script>
<script type=“text/javascript” src=“js/jsyilai.js”></script>
</html><!DOCTYPE html>
<html lang=“zh-CN”>
<head>
<title>扁平简洁的登录页面演示_dowebok</title>
<meta charset=“utf-8”>
<meta name=“viewport” content=“width=device-width, initial-scale=1”>
</head>
<body>
登 录
<form class="login100-form validate-form">
用户名
<input class="input100" type="text" name="andy" placeholder="请输入用户名">
密码
<input class="input100" type="password" name="pass" placeholder="请输入用户密码">
<input class="input-checkbox100" id="ckb1" type="checkbox" name="remember-me">
<label class="label-checkbox100" for="ckb1">记住我</label>
忘记密码?
<button class="login100-form-btn">登 录</button>
</form>
<script type=“text/javascript”>
</script>
<script src=“js/jquery-3.2.1.min.js”></script>
<script type=“text/javascript” src=“js/jsyilai.js”></script>
</body>
</html> yilai.js
(function ($) {
var pathName = window.location.pathname;
console.log(pathName); // 输出类似于 ‘/index.html’
//alert(pathName)
switch (pathName){
case "/KuCun2/login.html":
jQuery.getScript('/KuCun2/js/main.js?'+Date.now())
jQuery.getScript('/KuCun2/js/login.js?'+Date.now())
break;
case "/KuCun2/main/Guanli.html":
jQuery.getScript('/KuCun2/js/main.js?'+Date.now())
jQuery.getScript('/KuCun2/js/guanli.js?'+Date.now())
jQuery.getScript('/KuCun2/main/bootstrap-3.3.7-dist/js/FileSaver.js?'+Date.now())
jQuery.getScript('/KuCun2/main/bootstrap-3.3.7-dist/js/MyTable.js?'+Date.now())
break;
case "/KuCun2/index.html":
jQuery.getScript('/KuCun2/js/main.js?'+Date.now())
jQuery.getScript('/KuCun2/js/index.js?'+Date.now())
break;
}
})(jQuery) main.js
function https(url,data,fu){
$.ajax({
contentType:“application/json”,
url: url, // 假设后端 API 地址
method: “POST”,
data: JSON.stringify(data),
type: “json”,
dataType: “json”,
success:function(e){
console.log(e.data)
if(e.status==200){
console.log(e.data)
fu(e.data)
}else{
alert(e.text)
}
},
error: function (e) {
console.log(e.data)
console.error(e)
alert(“请求失败,请稍后再试!”+e);
}
});
}
function checkLoginStatus() {
if (window.location.href.includes(‘/login.html’)) return;
}
window.addEventListener(‘beforeunload’, function(e) {
console.trace(‘触发页面跳转的堆栈跟踪:’);
});
//安全验证函数
function validateUserSession() {
try {
// 防御性数据获取 + 数据清洗
const rawValue = localStorage.getItem(“name”);
const username = String(rawValue ?? ‘’)
.trim()
.replace(/[\u200B-\u200D\uFEFF]/g, ‘’); // 移除零宽字符
// 调试信息
console.log('[Auth] Raw:', rawValue, 'Processed:', username);
// 复合验证条件
const isValid = (
username.length >= 2 && // 最小长度要求
!/[<>]/.test(username) && // 防止XSS
username !== 'null' &&
username !== 'undefined'
);
if (!isValid) {
console.warn('无效用户标识:', username);
// 安全跳转方法
//window.location.replace('/KuCun2/login.html');
// 立即终止执行
return Promise.reject('Invalid session');
}
// 返回清洗后的用户名
return username;
} catch (error) {
console.error(‘会话验证失败:’, error);
// window.location.replace(‘/KuCun2/login.html’);
return Promise.reject(error);
}
}
function deepMergeArrays(frontend, backend) {
const resultMap = new Map();
// 遍历前端数据并存入 Map 中以便快速查找
frontend.forEach(item => resultMap.set(item.id, { ...item }));
// 遍历后端数据并与前端数据进行合并
backend.forEach(item => {
if (resultMap.has(item.id)) {
// 如果存在相同 ID,则合并两者的内容
resultMap.set(
item.id,
Object.assign(resultMap.get(item.id), item)
);
} else {
// 如果不存在相同 ID,则新增该条目
resultMap.set(item.id, { ...item });
}
});
// 将最终结果转回数组形式
return Array.from(resultMap.values());
}
(function ($){
// 页面加载时检查登录状态
checkLoginStatus();
})(jQuery);
function removeSpecificCharactersAndConvertToNumber(str, charsToRemove) {
const regex = new RegExp([${charsToRemove}], ‘g’); // 创建用于匹配指定字符的正则表达式
const cleanedStr = str.replace(regex, ‘’); // 移除指定字符
const numberValue = parseFloat(cleanedStr); // 转换为浮点数
return isNaN(numberValue) ? null : numberValue; // 如果无法解析,则返回 null
}
logi.js/// <reference path=“jquery.d.ts” />
// ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: …function(){
// (“#submit”).click(function(){
//
//
// ParseError: KaTeX parse error: Expected '}', got 'EOF' at end of input: …
// andy:(“#andy”).val(),
// pass:ParseError: KaTeX parse error: Expected 'EOF', got '#' at position 3: ("#̲pass").val(),
/…(“#name”).val()}),
// type:“post”,
// contentType:“application/json”,
// success:function(e){
// alert(e)
// }
// });
//
//
// });
// })
(function ($) {
“use strict”;
/*==================================================================
[ Focus Contact2 ]*/
$('.input100').each(function () {
$(this).on('blur', function () {
if ($(this).val().trim() != "") {
$(this).addClass('has-val');
} else {
$(this).removeClass('has-val');
}
})
})
/*==================================================================
[ Validate ]*/
var input = $('.validate-input .input100');
$('.validate-form').on('submit', function (e) {
e.preventDefault();
var check = true;
for (var i = 0; i < input.length; i++) {
if (validate(input[i]) == false) {
showValidate(input[i]);
check = false;
}
}
confirm(input)
if(check) login(input);
return check;
});
$('.validate-form .input100').each(function () {
$(this).focus(function () {
hideValidate(this);
});
});
function validate(input) {
if ($(input).attr('type') == 'email' || $(input).attr('name') == 'email') {
if ($(input).val().trim().match(/^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{1,5}|[0-9]{1,3})(\]?)$/) == null) {
return false;
}
} else {
if ($(input).val().trim() == '') {
return false;
}
}
}
function showValidate(input) {
var thisAlert = $(input).parent();
alert(input)
$(thisAlert).addClass('alert-validate');
}
function hideValidate(input) {
var thisAlert = $(input).parent();
$(thisAlert).removeClass('alert-validate');
}
// 登录按钮点击事件
function login(datas) {
var data={}
datas.each(function(a,element){
data[$(element).attr('name')]=$(element).val()
})
alert(data.name);
//var data={ andy,pass }
// 模拟 AJAX 请求
https("/KuCun2/users/login",data,function (response) {
alert("122222");
if (response.name) {
localStorage.setItem("name", response.name); // 保存用户名到本地存储
localStorage.setItem("role", response.role); // 保存权限到本地存储
alert( response.name)
window.location.href = '/KuCun2/index.html';
} else {
alert("登录失败,请检查用户名和密码!");
}
})
};
// 注销按钮点击事件
$("#logout-btn").click(function () {
localStorage.removeItem("name"); // 清除本地存储中的用户名
checkLoginStatus(); // 更新登录状态
});
})(jQuery);index.js$().ready(function () {
const username = localStorage.getItem("name");
$("#username").text(username)
const $username = $('#username');
const $menuButton = $('.menu-button');
let hideTimeout = null;
// 点击显示按钮
$username.on('click', function(e) {
e.stopPropagation();
$menuButton.show();
});
// 区域外点击隐藏
$(document).on('click', function(e) {
if (!$username.add($menuButton).is(e.target) &&
$username.has(e.target).length === 0 &&
$menuButton.has(e.target).length === 0) {
$menuButton.hide();
}
});
// 智能隐藏逻辑
const elements = $username.add($menuButton);
elements.on('mouseleave', function() {
hideTimeout = setTimeout(() => {
$menuButton.hide();
}, 200);
}).on('mouseenter', function() {
clearTimeout(hideTimeout);
});
var $iframe = $('<iframe>')
// 使用 jQuery 监听 iframe 的 load 事件
$iframe.on('load', function() {
try {
// 确保 iframe 的内容可以被访问(非跨域情况下)
var iframeDocument = $(this).contents();
var result = iframeDocument.find('body').text(); // 获取 iframe 内部的内容作为返回值
console.log("Iframe 加载完成后的返回值:", result);
window.location.href="/KuCun2/login.html"
} catch (error) {
console.error("无法访问 iframe 内容,可能是因为跨域限制:", error.message);
}
});
// 将 iframe 添加到文档中
$('body').append($iframe);
});
guanli.js(function ($) {
//checkLoginStatus();//权限
https(“/KuCun2/users/guanli/getusers”,null,function(e){
$(“#DataTable”).remove()
const c=[{id:" 序号 ",
name:“名字”,
andy:“账号”,
role:“权限”
}]
let row = e.length+1;
//c.concat(b);
//const combinedArray = [c,e];
var r=deepMergeArrays(c,e)
console.log(r )
let sclass = "table table-hover";
let bodys=[]
let table=$("#TableView")
table.append(bodys)
let te= table.find("table")
$.each(r, function(index, value) {
var tr=""
var boo=false
if(index==0){var tr=""; boo=false}
tr+=""
$.each(value, function(name,val) {
if(name!="pass"){
if(name!="id"){
tr+=${val} ;
}else{
tr+=${val} ;
}
}
})
tr+=" "
if(index==0){ tr+=""}
te.append(tr)
});
addRight("#DataTable");
//
// let sclass = “table table-striped”;
// let bodys=[]
//
// for(let i = -1; i <= row; i++) {
// bodys.push(“”);
//
// if(i=-1){
//
// for(var a in c[0]) {
//
// var bo= “”+c[0][a]+“”;
// bodys.push(bo)
//
// }
//
//
// }else{
//
//
// for(let j = 0; j <= col; j++) {
// if(j == 0) {
// var bo = “ ” + e[i][b[j]] + “# ”;
// bodys.push(bo)
// } else {
// var bo= “”
// +
//
// ‘’
// +“ ”;
// bodys.push(bo)
// }
// }
// }
// bodys.push( “ ”);
//
// }
// bodys.push(“”);
// var s=bodys.join(‘’)
// $(“#TableView”).append(s);
// addRight(“#DataTable”);
//
})
})(jQuery) 登录后访问index.html会被重定向到login.html其他以页面不回,




cart.php
<?php
/**
* Custom Cart Page for WooCommerce with Selective Checkout
*/
if (!defined('ABSPATH')) { exit; }
do_action('woocommerce_before_cart');
// Provide context for JS (no layout change)
$pc_cart_is_empty = WC()->cart->is_empty();
function pc_uid_for_view() {
if (is_user_logged_in()) return 'user_' . get_current_user_id();
if (empty($_COOKIE['pc_cart_uid'])) {
$token = wp_generate_uuid4();
setcookie('pc_cart_uid', $token, time() + YEAR_IN_SECONDS, COOKIEPATH ?: '/', '', is_ssl(), false);
$_COOKIE['pc_cart_uid'] = $token;
}
return 'guest_' . sanitize_text_field(wp_unslash($_COOKIE['pc_cart_uid']));
}
$pc_uid = pc_uid_for_view();
?>
<form class="woocommerce-cart-form" action="<?php echo esc_url( wc_get_cart_url() ); ?>" method="post">
<?php do_action('woocommerce_before_cart_table'); ?>
<?php wp_nonce_field('woocommerce-cart', 'woocommerce-cart-nonce'); ?>
<input type="checkbox" id="select-all-items" />
<label for="select-all-items" style="display: inline-block; margin-left: 5px; cursor: pointer;">全选</label>
<?php esc_html_e('Product', 'woocommerce'); ?>
<?php esc_html_e('Price', 'woocommerce'); ?>
<?php esc_html_e('Quantity', 'woocommerce'); ?>
<?php esc_html_e('Subtotal', 'woocommerce'); ?>
<?php esc_html_e('操作', 'woocommerce'); ?>
<?php do_action('woocommerce_before_cart_contents'); ?>
<?php foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) : ?>
<?php
$_product = apply_filters('woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key);
$product_id = apply_filters('woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key);
if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters('woocommerce_cart_item_visible', true, $cart_item, $cart_item_key) ) :
$product_permalink = apply_filters('woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink($cart_item) : '', $cart_item, $cart_item_key);
$line_total_value = (float) ($cart_item['line_total'] + $cart_item['line_tax']);
$variation_id = isset($cart_item['variation_id']) ? (int)$cart_item['variation_id'] : 0;
$variation_data = isset($cart_item['variation']) ? $cart_item['variation'] : array();
$variation_json = wp_json_encode($variation_data);
?>
<input type="checkbox"
class="item-checkbox"
name="selected_items[]"
value="<?php echo esc_attr($cart_item_key); ?>"
data-price="<?php echo esc_attr($line_total_value); ?>" />
<?php
$thumbnail = apply_filters('woocommerce_cart_item_thumbnail', $_product->get_image(), $cart_item, $cart_item_key);
if ( ! $product_permalink ) :
echo $thumbnail; // PHPCS: XSS ok.
else :
printf('%s', esc_url($product_permalink), $thumbnail); // PHPCS: XSS ok.
endif;
?>
<?php
if ( ! $product_permalink ) :
echo wp_kses_post( apply_filters('woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key) . ' ' );
else :
echo wp_kses_post( apply_filters('woocommerce_cart_item_name', sprintf('%s', esc_url($product_permalink), $_product->get_name()), $cart_item, $cart_item_key) );
endif;
do_action('woocommerce_after_cart_item_name', $cart_item, $cart_item_key);
echo wc_get_formatted_cart_item_data($cart_item); // PHPCS: XSS ok.
?>
<?php
echo apply_filters('woocommerce_cart_item_price', WC()->cart->get_product_price($_product), $cart_item, $cart_item_key); // PHPCS: XSS ok.
?>
<?php
if ( $_product->is_sold_individually() ) :
$product_quantity = sprintf('1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key);
else :
$product_quantity = woocommerce_quantity_input(
array(
'input_name' => "cart[{$cart_item_key}][qty]",
'input_value' => $cart_item['quantity'],
'max_value' => $_product->get_max_purchase_quantity(),
'min_value' => '0',
'product_name' => $_product->get_name(),
),
$_product,
false
);
endif;
echo apply_filters('woocommerce_cart_item_quantity', $product_quantity, $cart_item_key, $cart_item); // PHPCS: XSS ok.
?>
保存中…
<?php
echo apply_filters('woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal($_product, $cart_item['quantity']), $cart_item, $cart_item_key); // PHPCS: XSS ok.
?>
<?php
echo apply_filters('woocommerce_cart_item_remove_link', sprintf(
'×',
esc_url( wc_get_cart_remove_url($cart_item_key) ),
esc_attr__('Remove this item', 'woocommerce'),
esc_attr($product_id),
esc_attr($_product->get_sku())
), $cart_item_key);
?>
<?php endif; ?>
<?php endforeach; ?>
<?php do_action('woocommerce_after_cart_contents'); ?>
<?php do_action('woocommerce_after_cart_table'); ?>
</form>
<input type="checkbox" id="footer-select-all">
<label for="footer-select-all" style="display: inline-block; margin-left: 5px; cursor: pointer;">全选</label>
<button type="button" class="button" id="remove-selected-items">刪除選中的商品</button>
<button type="button" class="button" id="clear-cart">清空購物車</button>
<input type="text" name="coupon_code" class="input-text" id="coupon_code" value="" placeholder="输入优惠券代码" style="padding: 8px; width: 200px; border: 1px solid #ddd; border-radius: 4px; margin-right: 5px;" />
<button type="button" class="button" id="apply-coupon">应用优惠券</button>
已选商品: 0 件,共计: RM0.00
结算
<?php do_action('woocommerce_after_cart'); ?>
<style>
/* Layout styles (unchanged) */
.cart-page-section {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
.cart-page-section table.cart {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.cart-page-section table.cart th,
.cart-page-section table.cart td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.cart-page-section table.cart th {
background-color: #f8f8f8;
font-weight: bold;
}
.cart-page-section table.cart th.product-select,
.cart-page-section table.cart td.product-select {
width: 8%;
text-align: center;
vertical-align: middle;
white-space: nowrap;
}
.cart-page-section table.cart td.product-info {
width: 37%;
vertical-align: top;
}
.cart-page-section table.cart .product-info .product-image {
float: left;
margin-right: 15px;
width: 100px;
height: 100px;
}
.cart-page-section table.cart .product-info .product-image img {
max-width: 100%;
height: auto;
display: block;
}
.cart-page-section table.cart .product-info .product-name {
overflow: hidden;
}
.cart-page-section table.cart td.product-price,
.cart-page-section table.cart td.product-quantity,
.cart-page-section table.cart td.product-subtotal {
width: 15%;
vertical-align: middle;
}
.cart-page-section table.cart td.product-remove {
width: 5%;
text-align: center;
vertical-align: middle;
}
.cart-footer-actions {
position: sticky;
bottom: 0;
background: #fff;
padding: 15px;
border-top: 1px solid #ddd;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
display: flex;
flex-direction: column;
gap: 15px;
}
.footer-left {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px;
}
.coupon-section {
display: flex;
align-items: center;
gap: 5px;
}
.selected-summary {
font-size: 16px;
font-weight: bold;
flex: 1;
}
.cart-footer-actions .button {
padding: 10px 20px;
background-color: #f44336;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
white-space: nowrap;
}
.cart-footer-actions .button:hover {
background-color: #d32f2f;
}
#partial-checkout {
padding: 12px 24px;
background-color: #2196F3;
color: white;
text-decoration: none;
border-radius: 4px;
display: inline-block;
transition: background-color 0.3s;
white-space: nowrap;
text-align: center;
font-weight: bold;
}
#partial-checkout:hover {
background-color: #0b7dda;
}
#coupon_code {
padding: 8px;
width: 200px;
border: 1px solid #ddd;
border-radius: 4px;
transition: border-color 0.3s;
}
#coupon_code:focus {
border-color: #2196F3;
outline: none;
}
/* Responsive (unchanged) */
@media (max-width: 768px) {
.cart-page-section table.cart thead { display: none; }
.cart-page-section table.cart td {
display: block;
width: 100% !important;
text-align: right;
padding: 10px;
position: relative;
padding-left: 50%;
}
.cart-page-section table.cart td::before {
content: attr(data-title);
position: absolute;
left: 15px;
font-weight: bold;
text-align: left;
}
.cart-page-section table.cart .product-info .product-image {
float: none;
margin: 0 auto 10px;
}
.cart-page-section table.cart td.product-select::before { content: "选择"; }
.cart-footer-actions { flex-direction: column; align-items: flex-start; }
.footer-left { width: 100%; justify-content: space-between; }
.coupon-section { width: 100%; margin-top: 10px; }
.coupon-section input { flex: 1; }
.cart-footer-actions .footer-bottom-row { flex-direction: column; align-items: stretch; }
.selected-summary { text-align: center; margin-bottom: 10px; }
#partial-checkout { width: 100%; padding: 15px; }
.cart-footer-actions .button { padding: 12px 15px; margin: 5px 0; width: 100%; text-align: center; }
}
@media (max-width: 480px) {
.cart-page-section { padding: 15px; }
.cart-page-section table.cart td { padding-left: 45%; }
.cart-page-section table.cart td::before { font-size: 14px; }
.cart-footer-actions { padding: 10px; }
#coupon_code { width: 100%; }
}
/* Loader overlay above quantity input */
.product-quantity .quantity {
position: relative; /* anchor for overlay */
}
.quantity-saving-overlay {
position: absolute;
top: 50%; /* 关键修改:垂直居中 */
right: -25px; /* 关键修改:距离右侧8px */
transform: translateY(-50%); /* 关键修改:垂直居中偏移 */
display: none;
background: rgba(255,255,255,0.0);
z-index: 10;
pointer-events: none;
}
.quantity-saving-loader {
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
0% { transform: translateY(-50%) rotate(0deg); }
100% { transform: translateY(-50%) rotate(360deg); }
}
</style>
<script>
jQuery(function($){
// Context
var PC = {
ajax_url: (window.wc_cart_params && window.wc_cart_params.ajax_url) || '<?php echo esc_url( admin_url("admin-ajax.php") ); ?>',
security: (function(){
var n = $('input[name="woocommerce-cart-nonce"]').val();
if (n) return n;
if (window.wc_cart_params && window.wc_cart_params.cart_nonce) return window.wc_cart_params.cart_nonce;
return '<?php echo wp_create_nonce("woocommerce-cart"); ?>';
})(),
cart_is_empty: <?php echo $pc_cart_is_empty ? 'true' : 'false'; ?>,
cart_uid: '<?php echo esc_js($pc_uid); ?>'
};
// Keys
function lsKey(name){ return 'pc_' + name + '_' + PC.cart_uid; }
var LS_SELECTION = lsKey('selected_items');
var LS_MASTER = lsKey('cart_master');
// Utilities
function getSelectedKeys(){
return $('.item-checkbox:checked').map(function(){ return this.value; }).get();
}
function fmtRM(n){ n = isNaN(n) ? 0 : n; return 'RM' + Number(n).toFixed(2); }
// Selection persistence
function readSelection(){ try { return JSON.parse(localStorage.getItem(LS_SELECTION) || '[]'); } catch(e){ return []; } }
function writeSelection(keys){ try { localStorage.setItem(LS_SELECTION, JSON.stringify(keys||[])); } catch(e){} }
function restoreSelectionFromLS(){
var saved = readSelection(); if (!Array.isArray(saved)) saved = [];
$('.item-checkbox').each(function(){ $(this).prop('checked', saved.indexOf(this.value) !== -1); });
}
window.addEventListener('storage', function(ev){
if (ev.key === LS_SELECTION) { restoreSelectionFromLS(); updateSelectedSummary(); }
});
// Selected summary
function updateSelectedSummary(){
var total = 0, count = 0;
$('.item-checkbox:checked').each(function(){
var price = parseFloat($(this).data('price'));
if (!isNaN(price)) { total += price; count++; }
});
$('#selected-count').text(count);
$('#selected-total').text(fmtRM(total));
var totalCbs = $('.item-checkbox').length;
var checkedCbs = $('.item-checkbox:checked').length;
var allChecked = totalCbs > 0 && checkedCbs === totalCbs;
$('#select-all-items, #footer-select-all').prop('checked', allChecked);
writeSelection(getSelectedKeys());
}
// Select all
$('#select-all-items, #footer-select-all').off('change.sc').on('change.sc', function(){
var checked = $(this).prop('checked');
$('.item-checkbox').prop('checked', checked);
updateSelectedSummary();
});
// 单个勾选项变化时,更新统计与“全选”状态
$(document).on('change', '.item-checkbox', updateSelectedSummary);
// Snapshot cart DOM -> localStorage (guest resilience)
function snapshotCartFromDOM() {
var items = [];
$('tr.cart_item').each(function(){
var $row = $(this);
var pid = parseInt($row.attr('data-product_id'),10)||0;
var vid = parseInt($row.attr('data-variation_id'),10)||0;
var qty = parseFloat($row.find('input.qty').val())||0;
var variation = {};
try { variation = JSON.parse($row.attr('data-variation')||'{}'); } catch(e){ variation = {}; }
if (pid && qty > 0) {
items.push({ product_id: pid, variation_id: vid, variation: variation, quantity: qty });
}
});
try { localStorage.setItem(LS_MASTER, JSON.stringify({ ts: Date.now(), items: items })); } catch(e){}
}
function maybeRehydrateFromLocal() {
if (!PC.cart_is_empty) return;
var raw = localStorage.getItem(LS_MASTER);
if (!raw) return;
var data; try { data = JSON.parse(raw); } catch(e){ return; }
var items = (data && data.items) ? data.items : [];
if (!items.length) return;
$.ajax({
url: PC.ajax_url, method: 'POST', dataType: 'json',
data: { action: 'pc_rehydrate_cart', security: PC.security, items: JSON.stringify(items) }
}).done(function(res){
if (res && res.success) {
location.reload(); // display rehydrated items
}
});
}
// 统一的购物车数量更新函数
function updateHeaderCartCount() {
$.ajax({
url: PC.ajax_url,
method: 'POST',
data: { action: 'get_cart_count', security: PC.security }
}).done(function(res) {
if (res && res.success) {
$('.header-cart-count, .cart-count').text(res.data.count);
}
}).fail(function() {
console.error('Failed to update cart count');
});
}
// AFTER
$('#remove-selected-items').off('click.sc').on('click.sc', function(){
var keys = getSelectedKeys();
if (!keys.length) { alert('请选择要删除的商品'); return; }
if (!confirm('确定要删除选中的商品吗?')) return;
$.ajax({
url: PC.ajax_url,
method: 'POST',
dataType: 'json',
data: { action: 'remove_selected_cart_items', selected_items: keys, security: PC.security }
}).done(function(res){
if (res && res.success) {
keys.forEach(function(k){
var $row = $('tr.cart_item[data-cart_item_key="'+k+'"]');
$row.fadeOut(250, function(){
$(this).remove();
updateSelectedSummary();
updateHeaderCartCount(); // 更新购物车数量
});
});
snapshotCartFromDOM();
// 从本地选择集合中剔除已删除项
var saved = readSelection().filter(function(k0){ return keys.indexOf(k0) === -1; });
writeSelection(saved);
} else {
alert((res && res.data && (res.data.message || res.data)) || '删除失败,请重试');
}
}).fail(function(){
alert('删除失败,请重试');
});
});
// AFTER
$('#clear-cart').off('click.sc').on('click.sc', function(){
if (!confirm('确定要清空购物车吗?')) return;
$.ajax({
url: PC.ajax_url,
method: 'POST',
dataType: 'json',
data: { action: 'empty_cart', security: PC.security }
}).done(function(res){
if (res && res.success) {
writeSelection([]);
localStorage.removeItem(LS_MASTER);
location.reload();
} else {
alert((res && res.data && (res.data.message || res.data)) || '清空购物车失败,请重试');
}
}).fail(function(){
alert('清空购物车失败,请重试');
});
});
// Apply coupon
// AFTER
$('#apply-coupon').off('click.sc').on('click.sc', function(){
var code = ($.trim($('#coupon_code').val()) || '');
if (!code) { alert('请输入优惠券代码'); return; }
var $btn = $(this).prop('disabled', true).text('处理中...');
$.ajax({
url: PC.ajax_url,
method: 'POST',
dataType: 'json',
data: { action: 'apply_coupon', coupon_code: code, security: PC.security }
}).done(function(res){
if (res && res.success) {
location.reload();
} else {
alert((res && res.data && (res.data.message || res.data)) || '优惠券应用失败,请重试');
$btn.prop('disabled', false).text('应用优惠券');
}
}).fail(function(){
alert('发生错误,请重试');
$btn.prop('disabled', false).text('应用优惠券');
});
});
// ---- Quantity auto-save (fixed regex + loader above input) ----
// 已修正:正确的正则表达式
function parseCartKeyFromInputName(n) {
var m = (n || '').match(/^cart\[([a-zA-Z0-9_]+)\]\[qty\]$/);
return m ? m[1] : null;
}
function ensureLoader($quantityContainer){
var $overlay = $quantityContainer.find('.quantity-saving-overlay');
if (!$overlay.length) {
$overlay = $('');
$quantityContainer.append($overlay);
}
return $overlay;
}
var qtyTimers = {};
$(document).on('input change', 'input.qty', function(){
var $input = $(this);
var key = parseCartKeyFromInputName($input.attr('name'));
if (!key) return;
var $row = $input.closest('tr.cart_item');
var $quantityContainer = $input.closest('.quantity');
var val = parseInt($input.val(), 10);
if (isNaN(val) || val < 0) val = 0;
// debounce
if (qtyTimers[key]) clearTimeout(qtyTimers[key]);
var $overlay = ensureLoader($quantityContainer);
$overlay.hide();
qtyTimers[key] = setTimeout(function(){
$overlay.show();
$input.prop('disabled', true);
$.ajax({
url: PC.ajax_url,
method: 'POST',
dataType: 'json',
data: {
action: 'update_cart_item_qty',
cart_item_key: key,
qty: val,
security: PC.security
}
}).done(function(res){
if (res && res.success && res.data){
if (res.data.subtotal_html) {
$row.find('td.product-subtotal').html(res.data.subtotal_html);
}
var $cb = $row.find('.item-checkbox');
if ($cb.length) {
// 计算新的总价(单价*数量)
var newPrice = res.data.line_total_incl_tax;
$cb.attr('data-price', newPrice);
}
if (val === 0 || res.data.removed) {
$row.fadeOut(300, function(){
$(this).remove();
snapshotCartFromDOM();
updateSelectedSummary(); // 确保更新汇总
updateHeaderCartCount();
});
} else {
snapshotCartFromDOM();
updateSelectedSummary(); // 关键:立即更新底部汇总
updateHeaderCartCount();
}
} else {
var msg = (res && res.data && (res.data.message || res.data)) || '数量更新失败';
alert(msg);
}
}).fail(function(){
alert('数量更新失败,请重试');
}).always(function(){
$overlay.hide();
$input.prop('disabled', false);
});
}, 500); // 0.5s debounce
});
// Partial checkout -> regular checkout page
$('#partial-checkout').off('click.sc').on('click.sc', function(e){
e.preventDefault();
var keys = getSelectedKeys();
if (!keys.length) { alert('请至少选择一件商品结算'); return; }
var $btn = $(this), t = $btn.text();
$btn.prop('disabled', true).text('创建订单中...');
snapshotCartFromDOM();
$.ajax({
url: PC.ajax_url,
method: 'POST',
dataType: 'json',
data: { action: 'create_direct_order', selected_items: keys, security: PC.security }
}).done(function(res){
if (res && res.success && res.data && res.data.checkout_url) {
window.location.href = res.data.checkout_url; // /checkout/?pc_token=...
} else {
alert((res && res.data && (res.data.message || res.data)) || '创建订单失败,请重试');
$btn.prop('disabled', false).text(t);
}
}).fail(function(xhr){
var msg = '创建订单失败';
if (xhr && xhr.responseJSON && xhr.responseJSON.data) {
msg += ':' + (xhr.responseJSON.data.message || xhr.responseJSON.data);
}
alert(msg);
$btn.prop('disabled', false).text(t);
});
});
// Keep LS selection after clicking "x" remove; also snapshot
$(document).on('click', 'a.remove', function(){
var key = $(this).closest('tr.cart_item').attr('data-cart_item_key');
if (key) {
var saved = readSelection().filter(function(k){ return k !== key; });
writeSelection(saved);
snapshotCartFromDOM();
updateHeaderCartCount(); // 更新购物车数量
}
});
// Init
restoreSelectionFromLS();
updateSelectedSummary();
snapshotCartFromDOM();
maybeRehydrateFromLocal();
updateHeaderCartCount(); // 初始加载时更新购物车数量
});
</script>
functions.php
<?php defined('ABSPATH') || exit;
/**
* 健壮的部分结算系统 - 带持久化购物车快照
* - 结算前创建完整购物车快照
* - 订单创建成功后才移除商品
* - 感谢页面重建购物车(快照 - 已购买商品)
* - 取消/返回时恢复完整快照
* - 访客支持:localStorage + AJAX 重新水合
*/
/* -------------------------------------------------
* 辅助函数
* ------------------------------------------------- */
/**
* 购物车数量AJAX端点
*/
add_action('wp_ajax_get_cart_count', 'pc_get_cart_count');
add_action('wp_ajax_nopriv_get_cart_count', 'pc_get_cart_count');
function pc_get_cart_count() {
check_ajax_referer('woocommerce-cart', 'security');
$count = WC()->cart->get_cart_contents_count();
wp_send_json_success(array('count' => $count));
}
function pc_get_cart_uid() {
if (is_user_logged_in()) {
return 'user_' . get_current_user_id();
}
if (empty($_COOKIE['pc_cart_uid'])) {
$token = wp_generate_uuid4();
setcookie('pc_cart_uid', $token, time() + YEAR_IN_SECONDS, COOKIEPATH ?: '/', '', is_ssl(), false);
$_COOKIE['pc_cart_uid'] = $token;
}
return 'guest_' . sanitize_text_field(wp_unslash($_COOKIE['pc_cart_uid']));
}
function pc_build_item_key($product_id, $variation_id = 0) {
return (int)$product_id . '|' . (int)$variation_id;
}
function pc_snapshot_current_cart() {
if (!isset(WC()->cart)) {
wc_load_cart();
}
$items = array();
foreach (WC()->cart->get_cart() as $ci_key => $ci) {
$pid = isset($ci['product_id']) ? (int)$ci['product_id'] : 0;
$vid = isset($ci['variation_id']) ? (int)$ci['variation_id'] : 0;
$qty = isset($ci['quantity']) ? wc_stock_amount($ci['quantity']) : 0;
$var = isset($ci['variation']) && is_array($ci['variation']) ? $ci['variation'] : array();
if ($pid && $qty > 0) {
$items[] = array(
'product_id' => $pid,
'variation_id' => $vid,
'variation' => array_map('wc_clean', $var),
'quantity' => $qty,
);
}
}
return [
'items' => $items,
'coupons' => WC()->cart->get_applied_coupons()
];
}
/**
* 恢复购物车优惠券
*/
function pc_restore_cart_coupons($coupons) {
if (!isset(WC()->cart)) {
wc_load_cart();
}
// 先移除所有现有优惠券
foreach (WC()->cart->get_applied_coupons() as $coupon_code) {
WC()->cart->remove_coupon($coupon_code);
}
// 应用快照中的优惠券
foreach ($coupons as $coupon_code) {
if (WC()->cart->is_valid_coupon($coupon_code)) {
WC()->cart->apply_coupon($coupon_code);
}
}
WC()->cart->calculate_totals();
}
function pc_restore_cart_from_items($items) {
if (!isset(WC()->cart)) {
wc_load_cart();
}
WC()->cart->empty_cart();
foreach ((array)$items as $it) {
$pid = isset($it['product_id']) ? (int)$it['product_id'] : 0;
$vid = isset($it['variation_id']) ? (int)$it['variation_id'] : 0;
$qty = isset($it['quantity']) ? wc_stock_amount($it['quantity']) : 0;
$var = isset($it['variation']) && is_array($it['variation']) ? array_map('wc_clean', $it['variation']) : array();
if ($pid && $qty > 0) {
$product = wc_get_product($pid);
// 检查商品是否有效且库存可用
if ($product && $product->exists() && $product->is_in_stock()) {
// 获取库存数量
$stock_qty = $product->get_stock_quantity();
if ($stock_qty !== null) {
$actual_qty = min($qty, $stock_qty);
} else {
$actual_qty = $qty;
}
WC()->cart->add_to_cart(
$pid,
$actual_qty,
$vid,
$var
);
}
}
}
WC()->cart->calculate_totals();
}
// 完全重构的购物车恢复逻辑 - 确保WC会话存在
add_action('wp_loaded', function() {
// 严格检查WC会话可用性
$wc = function_exists('WC') ? WC() : null;
if (!$wc) return;
$session = property_exists($wc, 'session') ? $wc->session : null;
if (!$session) return;
$token = method_exists($session, 'get')
? $session->get('pc_partial_token')
: null;
if (!$token) return;
// 检查购物车是否为空
$cart = property_exists($wc, 'cart') ? $wc->cart : null;
if (!$cart) return;
if ($cart->is_empty()) {
$payload = get_transient(pc_transient_key($token));
if (!empty($payload['snapshot'])) {
pc_restore_cart_from_items($payload['snapshot']['items']);
pc_restore_cart_coupons($payload['snapshot']['coupons']);
$cart->calculate_totals();
}
}
}, 5);
function pc_transient_key($token) {
return 'pc_partial_payload_' . sanitize_key($token);
}
/* -------------------------------------------------
* AJAX: 当Woo购物车为空时本地重新水合
* ------------------------------------------------- */
add_action('wp_ajax_pc_rehydrate_cart', 'pc_rehydrate_cart');
add_action('wp_ajax_nopriv_pc_rehydrate_cart', 'pc_rehydrate_cart');
function pc_rehydrate_cart() {
check_ajax_referer('woocommerce-cart', 'security');
$raw = isset($_POST['items']) ? wp_unslash($_POST['items']) : '';
$items = is_string($raw) ? json_decode($raw, true) : (array)$raw;
if (!is_array($items)) {
wp_send_json_error(array('message' => '无效的商品数据'), 400);
}
if (!isset(WC()->cart)) {
wc_load_cart();
}
if (!WC()->cart->is_empty()) {
wp_send_json_success(array('message' => '购物车非空'));
}
foreach ($items as $it) {
$pid = isset($it['product_id']) ? (int)$it['product_id'] : 0;
$vid = isset($it['variation_id']) ? (int)$it['variation_id'] : 0;
$qty = isset($it['quantity']) ? wc_stock_amount($it['quantity']) : 0;
$var = isset($it['variation']) && is_array($it['variation']) ? array_map('wc_clean', $it['variation']) : array();
if ($pid && $qty > 0) {
$product = wc_get_product($pid);
if ($product && $product->exists() && $product->is_in_stock()) {
// 检查库存
$stock_qty = $product->get_stock_quantity();
if ($stock_qty !== null) {
$qty = min($qty, $stock_qty);
}
WC()->cart->add_to_cart($pid, $qty, $vid, $var);
}
}
}
WC()->cart->calculate_totals();
wp_send_json_success(array('rehydrated' => true));
}
/* -------------------------------------------------
* AJAX: 更新购物车商品数量(无需刷新页面)
* ------------------------------------------------- */
add_action('wp_ajax_update_cart_item_qty', 'pc_update_cart_item_qty');
add_action('wp_ajax_nopriv_update_cart_item_qty', 'pc_update_cart_item_qty');
function pc_update_cart_item_qty() {
check_ajax_referer('woocommerce-cart', 'security');
$key = isset($_POST['cart_item_key']) ? wc_clean(wp_unslash($_POST['cart_item_key'])) : '';
$qty = isset($_POST['qty']) ? wc_stock_amount($_POST['qty']) : null;
if (!$key || $qty === null) {
wp_send_json_error(array('message' => '参数缺失'), 400);
}
if (!isset(WC()->cart)) {
wc_load_cart();
}
if ($qty <= 0) {
$removed = WC()->cart->remove_cart_item($key);
WC()->cart->calculate_totals();
wp_send_json_success(array('removed' => (bool)$removed));
} else {
$set = WC()->cart->set_quantity($key, $qty, true);
WC()->cart->calculate_totals();
$cart_item = WC()->cart->get_cart_item($key);
if (!$cart_item) {
wp_send_json_error(array('message' => '更新后未找到购物车商品'), 404);
}
$_product = $cart_item['data'];
$subtotal_html = apply_filters(
'woocommerce_cart_item_subtotal',
WC()->cart->get_product_subtotal($_product, $cart_item['quantity']),
$cart_item,
$key
);
// 小计计算(含税)
$line_total_incl_tax = (float)($cart_item['line_total'] + $cart_item['line_tax']);
wp_send_json_success(array(
'subtotal_html' => $subtotal_html,
'line_total_incl_tax' => $line_total_incl_tax,
'single_price' => $_product->get_price(), // 新增单件价格
'qty' => $cart_item['quantity'], // 新增当前数量
'removed' => false,
));
}
}
/* -------------------------------------------------
* AJAX: 删除选中商品
* ------------------------------------------------- */
add_action('wp_ajax_remove_selected_cart_items', 'pc_remove_selected_cart_items');
add_action('wp_ajax_nopriv_remove_selected_cart_items', 'pc_remove_selected_cart_items');
function pc_remove_selected_cart_items() {
check_ajax_referer('woocommerce-cart', 'security');
$keys = isset($_POST['selected_items']) ? (array) $_POST['selected_items'] : array();
if (!isset(WC()->cart)) {
wc_load_cart();
}
foreach ($keys as $k) {
$k = wc_clean(wp_unslash($k));
WC()->cart->remove_cart_item($k);
}
WC()->cart->calculate_totals();
wp_send_json_success(true);
}
/* -------------------------------------------------
* AJAX: 清空购物车
* ------------------------------------------------- */
add_action('wp_ajax_empty_cart', 'pc_empty_cart');
add_action('wp_ajax_nopriv_empty_cart', 'pc_empty_cart');
function pc_empty_cart() {
check_ajax_referer('woocommerce-cart', 'security');
if (!isset(WC()->cart)) {
wc_load_cart();
}
WC()->cart->empty_cart();
wp_send_json_success(true);
}
/* -------------------------------------------------
* AJAX: 应用优惠券
* ------------------------------------------------- */
add_action('wp_ajax_apply_coupon', 'pc_apply_coupon');
add_action('wp_ajax_nopriv_apply_coupon', 'pc_apply_coupon');
function pc_apply_coupon() {
check_ajax_referer('woocommerce-cart', 'security');
$code = isset($_POST['coupon_code']) ? wc_format_coupon_code(wp_unslash($_POST['coupon_code'])) : '';
if (!$code) {
wp_send_json_error(array('message' => __('请输入优惠券代码', 'woocommerce')), 400);
}
if (!isset(WC()->cart)) {
wc_load_cart();
}
$applied = WC()->cart->apply_coupon($code);
WC()->cart->calculate_totals();
if (is_wp_error($applied)) {
wp_send_json_error(array('message' => $applied->get_error_message()), 400);
}
if (!$applied) {
wp_send_json_error(array('message' => __('优惠券应用失败', 'woocommerce')), 400);
}
wp_send_json_success(true);
}
/* -------------------------------------------------
* AJAX: 启动部分结算到常规结算页面
* ------------------------------------------------- */
add_action('wp_ajax_create_direct_order', 'pc_create_direct_order');
add_action('wp_ajax_nopriv_create_direct_order', 'pc_create_direct_order');
function pc_create_direct_order() {
check_ajax_referer('woocommerce-cart', 'security');
$selected_keys = isset($_POST['selected_items']) ? (array) $_POST['selected_items'] : array();
if (empty($selected_keys)) {
wp_send_json_error(array('message' => __('请选择要结算的商品', 'woocommerce')), 400);
}
if (!isset(WC()->cart)) {
wc_load_cart();
}
// 创建完整购物车快照
$snapshot = pc_snapshot_current_cart();
// 从当前购物车构建选中商品
$selected = array();
foreach (WC()->cart->get_cart() as $ci_key => $ci) {
if (!in_array($ci_key, $selected_keys, true)) {
continue; // 只处理选中的商品
}
$pid = (int)$ci['product_id'];
$vid = (int)$ci['variation_id'];
$qty = wc_stock_amount($ci['quantity']);
$var = isset($ci['variation']) && is_array($ci['variation']) ? array_map('wc_clean', $ci['variation']) : array();
if ($pid && $qty > 0) {
$selected[] = array(
'product_id' => $pid,
'variation_id' => $vid,
'variation' => $var,
'quantity' => $qty,
);
}
}
if (empty($selected)) {
wp_send_json_error(array('message' => __('没有可结算的商品', 'woocommerce')), 400);
}
$token = wp_generate_uuid4();
$payload = array(
'uid' => pc_get_cart_uid(),
'snapshot' => $snapshot,
'selected' => $selected, // 关键:只保存选中的商品
'created' => time(),
);
set_transient(pc_transient_key($token), $payload, 2 * DAY_IN_SECONDS);
// 将会话令牌存入WooCommerce会话
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', $token);
}
$checkout_url = add_query_arg('pc_token', rawurlencode($token), wc_get_checkout_url());
wp_send_json_success(array('checkout_url' => $checkout_url));
}
/* -------------------------------------------------
* 结账时虚拟化购物车并在购买后重建
* ------------------------------------------------- */
// 加载结账页面时虚拟化购物车
add_action('woocommerce_before_checkout_form', function() {
if (!isset($_GET['pc_token'])) return;
$token = sanitize_text_field(wp_unslash($_GET['pc_token']));
$payload = get_transient(pc_transient_key($token));
if (empty($payload) || empty($payload['selected'])) return;
if (!isset(WC()->cart)) {
wc_load_cart();
}
// 仅加载选中的商品
pc_restore_cart_from_items($payload['selected']);
// 持久化令牌到会话
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', $token);
}
}, 1);
// 订单处理前确保虚拟化
add_action('woocommerce_before_checkout_process', function() {
if (!isset(WC()->session) || !method_exists(WC()->session, 'get')) return;
$token = WC()->session->get('pc_partial_token');
if (!$token) return;
$payload = get_transient(pc_transient_key($token));
if (empty($payload) || empty($payload['selected'])) return;
// 确保购物车仅包含选中商品
pc_restore_cart_from_items($payload['selected']);
}, 1);
// 为订单添加令牌标记
add_action('woocommerce_checkout_create_order', function($order) {
$token = null;
if (isset($_GET['pc_token'])) {
$token = sanitize_text_field(wp_unslash($_GET['pc_token']));
} elseif (isset(WC()->session) && method_exists(WC()->session, 'get')) {
$token = WC()->session->get('pc_partial_token');
}
if ($token) {
$order->update_meta_data('_pc_partial_token', $token);
$order->update_meta_data('_pc_cart_snapshot', $token);
}
}, 10, 1);
// 成功结账后仅移除已购买商品
add_action('woocommerce_thankyou', function($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
$token = $order->get_meta('_pc_partial_token');
if (!$token) return;
$payload = get_transient(pc_transient_key($token));
if (empty($payload) || empty($payload['snapshot'])) {
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', null);
}
delete_transient(pc_transient_key($token));
return;
}
// 1. 恢复完整快照
pc_restore_cart_from_items($payload['snapshot']['items']);
pc_restore_cart_coupons($payload['snapshot']['coupons']);
WC()->cart->calculate_totals();
// 2. 只移除已购买商品
$removed_count = 0;
foreach ($payload['selected'] as $selected_item) {
$cart_item_key = pc_find_cart_item($selected_item['product_id'], $selected_item['variation_id']);
if ($cart_item_key) {
WC()->cart->remove_cart_item($cart_item_key);
$removed_count++;
}
}
// 3. 如果有商品被移除才重新计算
if ($removed_count > 0) {
WC()->cart->calculate_totals();
}
// 清理令牌
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', null);
}
delete_transient(pc_transient_key($token));
}, 20);
// 辅助函数:通过产品和变体ID查找购物车项
function pc_find_cart_item($product_id, $variation_id = 0) {
if (!isset(WC()->cart)) {
wc_load_cart();
}
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
$cart_pid = isset($cart_item['product_id']) ? (int)$cart_item['product_id'] : 0;
$cart_vid = isset($cart_item['variation_id']) ? (int)$cart_item['variation_id'] : 0;
if ($cart_pid == $product_id && $cart_vid == $variation_id) {
return $cart_item_key;
}
}
return false;
}
// 访问购物车页面时恢复完整快照(返回/取消操作)
add_action('woocommerce_before_cart', function() {
// 严格检查会话可用性
if (!isset(WC()->session) || !method_exists(WC()->session, 'get')) return;
$token = WC()->session->get('pc_partial_token');
if (!$token) return;
$payload = get_transient(pc_transient_key($token));
if (empty($payload) || empty($payload['snapshot'])) return;
// 恢复完整快照
pc_restore_cart_from_items($payload['snapshot']['items']);
pc_restore_cart_coupons($payload['snapshot']['coupons']);
WC()->cart->calculate_totals();
});
/* -------------------------------------------------
* 保持购物车数量在结算过程中准确
* ------------------------------------------------- */
add_filter('woocommerce_cart_contents_count', function($count) {
// 检查是否存在部分结算令牌
if (!isset(WC()->session) || !method_exists(WC()->session, 'get')) return $count;
$token = WC()->session->get('pc_partial_token');
if (!$token) return $count;
$payload = get_transient(pc_transient_key($token));
// 始终显示完整购物车数量
if (!empty($payload['snapshot']) && is_array($payload['snapshot']['items'])) {
$snapshot_count = 0;
foreach ($payload['snapshot']['items'] as $item) {
$snapshot_count += (int)($item['quantity'] ?? 0);
}
return $snapshot_count;
}
return $count;
});
// 确保购物车一致性
add_action('woocommerce_before_cart', 'pc_maintain_cart_consistency');
add_action('woocommerce_before_checkout_form', 'pc_maintain_cart_consistency');
function pc_maintain_cart_consistency() {
if (!isset(WC()->session) || !method_exists(WC()->session, 'get')) return;
$token = WC()->session->get('pc_partial_token');
if (!$token) return;
$payload = get_transient(pc_transient_key($token));
if (empty($payload) || empty($payload['snapshot'])) return;
// 立即恢复完整购物车以确保UI一致性
pc_restore_cart_from_items($payload['snapshot']['items']);
pc_restore_cart_coupons($payload['snapshot']['coupons']);
WC()->cart->calculate_totals();
}
After applying these code, my 共计 still did not show the correct total on time. After i edit the quantity, the subtotal changed, but the 共计 did not change together with the subtotal. Even if i reselect, the 共计 still stuck at the original quantity's price, it did not change.
Also, when i edit quantity, it did show the saved effect and i really thought its saved. However, when i refresh the page, the quantity go back to the original one. For example, i added the quantity from 1 to 2, and its saved based on the effect, but when i refresh the page, it goes back friom 2 to 1.
Also, when i click 结算, non-selected items are also sent to cart. This is old mistake that i mentioned previously. Please check again and generate new code for me
