How to keep your PC clean & up-to-date

本文提供了一套详细的步骤,指导如何定期清理和更新个人电脑,确保系统运行流畅、快速且稳定。介绍了组织文件的方法、软件卸载技巧、注册表清理等关键操作。

How to keep your PC clean & up-to-date


The following step-by-step guide shows how I clean up and update my PC on a regular basis, to keep everything run smooth, fast and nice.
Software used in this guide can be found on this website. Use the Search function on this page to find all.

  • First of all, I prefer to have the My Documents folder point to another partition or drive (D:\ in my case) to keep it seperated from my Windows installation on C:\. In My Documents, I try to sort my files/folders as good as possible:

    - Downloads (Contains all the new, unordered downloaded stuff. This is also my Shared Folder for programs like eMule and it's my default folder for downloads with all kinds of software.)
    - Documents (Contains all my text-documents, spreadsheets, etc.)
    - Drivers (Contains all the latest driver-versions for the hardware in my system.)
    - Firefox (Contains my Mozilla Firefox Profile.)
    - Flash (Contains my funny Flash Movies.)
    - Game-updates (Contains patches for my games.)
    - Homepages (Contains all my temporary, old, new and final versions of the websites I make.)
    - ISO (Contains ISO's of CD's I often use, for use with Daemon Tools. It keeps me from swapping discs all the time.)
    - Intranet (Contains my archived websites, downloaded with HTTrack Website Copier.)
    - Mail (Contains my Mozilla Thunderbird/Outlook Express Mail.)
    - My Backups (Contains all the backups I made with SyncBack or any other backup-program.)
    - My Music (Contains all my MP3-albums.)
    - My Video's (Contains all my temporary and final home video's, some ready and some almost ready for DVD-backup.)
    - My Pictures (Contains all my digital photo-albums.)
    - Nokia (Contains backdrops, ringtones, midlets for my mobile phone.)
    - Software (Contains my freeware collection [A's Absolute Freeware 2005 CD-ROM.)
    - Upload (This folder is use in my home network, and as uploadfolder on my FTP-server, so others can copy files to my system.)
  • If I have a new version of a program already installed on my system, I install SyncBack to easily make backups of the stuff I want to use later in the My Backups folder, like (1) configuration files, (2) savegames, (3) etc.
  • I pay a visit to WindowsUpdate to look for the latest updates for Windows.
  • I install MyUninstaller, run it, and remove (1) all the software I don't want to use anymore, (2) all the software for which I have a better alternative and (3) the software for which I have a new version (I don't like installing new over old software. I like to start from scratch whenever possible).
  • I install CCleaner (Crap Cleaner) and run it to remove all the crap from my PC.
  • I restart my PC and remove all the empty, half-empty and left-behind files/folders related to the software I've just removed. I do this by checking the following folders: (1) \Program Files, (2) \Documents & Settings\Username\Application Data, (3) the Desktop and all other locations I often use.
  • I install JV PowerTools and check the registry for keys related to software that I have uninstalled. I remove those keys. After that I run the Registry Cleaner from the Tools menu and search for all the keys that are save to remove, without the ignored items. When the program is done searching I select them all (CTRL-A) and remove them. Then I run the Registry Cleaner again and search for all the ignored items, and I review them one by one to decide which of those are safe to remove.
  • Then I update my Anti-Virus and Anti-Spyware data files.
  • Now I restart my PC again in Safe Mode and run the Anti-Spyware and Anti-Virus software to be sure my system's clean of that kind of crap.
  • Still in Safe Mode, I run a Defragmentation on all local drives. I use DIRMS to defrag my system. After that I keepBuzzsaw running every time I start Windows to defragment the drives on-the-fly.
  • After this I restart Windows back in Normal Mode and then I'm ready to install new and/or updated software. If needed I restore the configuration files, savegames, etc. to the appropriate folder using SyncBack.
  • I disable System Restore on all drives, and use ERUNT to make my registry backups instead.
  • I install Sysinternals PageDefrag to defrag my registry at boot.

Now just see how smooth everything's running... ;)

#!/bin/bash # # git $Id$ #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: # Copyright (c) 2002-2025 The ROMS Group ::: # Licensed under a MIT/X style license ::: # See License_ROMS.md ::: #::::::::::::::::::::::::::::::::::::::::::::::::::::: Hernan G. Arango ::: # ::: # ROMS Compiling BASH Script ::: # ::: # Script to compile an user application where the application-specific ::: # files are kept separate from the ROMS source code. ::: # ::: # Q: How/why does this script work? ::: # ::: # A: The ROMS makefile configures user-defined options with a set of ::: # flags such as ROMS_APPLICATION. Browse the makefile to see these. ::: # If an option in the makefile uses the syntax ?= in setting the ::: # default, this means that make will check whether an environment ::: # variable by that name is set in the shell that calls make. If so ::: # the environment variable value overrides the default (and the ::: # user need not maintain separate makefiles, or frequently edit ::: # the makefile, to run separate applications). ::: # ::: # Usage: ::: # ::: # ./build_roms.sh [options] ::: # ::: # Options: ::: # ::: # -b Compile a specific ROMS GitHub branch ::: # ::: # build_roms.sh -j 10 -b feature/kernel ::: # ::: # -g Compile with debug flag (slower code) ::: # ::: # build_roms.sh -g -j 10 ::: # ::: # -j [N] Compile in parallel using N CPUs ::: # omit argument for all available CPUs ::: # ::: # -pio Compile with PIO (Parallel I/O) NetCDF library ::: # Otherwise, it used standard NetCDF library (slower) ::: # ::: # build_roms.sh -pio -j 10 ::: # ::: # -p macro Prints any Makefile macro value. For example, ::: # ::: # build_roms.sh -p FFLAGS ::: # ::: # -noclean Do not clean already compiled objects ::: # ::: # Notice that sometimes the parallel compilation fail to find MPI ::: # include file "mpif.h". ::: # ::: # The branch option -b is only possible for ROMS source code from ::: # https://github.com/myroms. Such versions are under development ::: # and targeted to advanced users, superusers, and beta testers. ::: # Regular and novice users must use the default 'develop' branch. ::: # ::: #:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: export which_MPI=openmpi # default, overwritten below g_flags=0 parallel=0 pio_lib=0 clean=1 dprint=0 branch=0 command="build_roms.sh $@" separator=`perl -e "print '<>' x 50;"` export MY_CPP_FLAGS= while [ $# -gt 0 ] do case "$1" in -g ) shift g_flags=1 ;; -pio ) shift pio_lib=1 ;; -j ) shift parallel=1 test=`echo $1 | grep '^[0-9]\+$'` if [ "$test" != "" ]; then NCPUS="-j $1" shift else NCPUS="-j" fi ;; -p ) shift clean=0 dprint=1 debug="print-$1" shift ;; -noclean ) shift clean=0 ;; -b ) shift branch=1 branch_name=`echo $1 | grep -v '^-'` if [ "$branch_name" == "" ]; then echo "Please enter a ROMS GitHub branch name." exit 1 fi shift ;; * ) echo "" echo "${separator}" echo "$0 : Unknown option [ $1 ]" echo "" echo "Available Options:" echo "" echo "-b branch_name Compile specific ROMS GitHub branch name" echo " For example: build_roms.sh -b feature/kernel" echo "" echo "-g Compile with debugging flags, slower code" echo "" echo "-j [N] Compile in parallel using N CPUs" echo " omit argument for all avaliable CPUs" echo "" echo "-pio Compile with the PIO NetCDF Library" echo "" echo "-p macro Prints any Makefile macro value" echo " For example: build_roms.sh -p FFLAGS" echo "" echo "-noclean Do not clean already compiled objects" echo "" echo "${separator}" echo "" exit 1 ;; esac done # Set the CPP option defining the particular application. This will # determine the name of the ".h" header file with the application # CPP definitions. export ROMS_APPLICATION=UPWELLING # Set a local environmental variable to define the path to the directories # where the ROMS source code is located (MY_ROOT_DIR), and this project's # configuration and files are kept (MY_PROJECT_DIR). Notice that if the # User sets the ROMS_ROOT_DIR environment variable in their computer logging # script describing the location from where the ROMS source code was cloned # or downloaded, it uses that value. if [ -n "${ROMS_ROOT_DIR:+1}" ]; then export MY_ROOT_DIR=${ROMS_ROOT_DIR} else export MY_ROOT_DIR=${HOME}/ocean/repository/git fi export MY_PROJECT_DIR=${PWD} # The path to the user's local current ROMS source code. # # If downloading ROMS locally, this would be the user's Working Copy Path. # One advantage of maintaining your source code copy is that when working # simultaneously on multiple machines (e.g., a local workstation, a local # cluster, and a remote supercomputer), you can update with the latest ROMS # release and always get an up-to-date customized source on each machine. # This script allows for differing paths to the code and inputs on other # computers. export MY_ROMS_SRC=${MY_ROOT_DIR}/roms # Set path of the directory containing makefile configuration (*.mk) files. # The user has the option to specify a customized version of these files # in a different directory than the one distributed with the source code, # ${MY_ROMS_SRC}/Compilers. If this is the case, you need to keep these # configurations files up-to-date. export COMPILERS=${MY_ROMS_SRC}/Compilers #export COMPILERS=${HOME}/Compilers/ROMS #-------------------------------------------------------------------------- # Set tunable CPP options. #-------------------------------------------------------------------------- # # Sometimes it is desirable to activate one or more CPP options to run # different variants of the same application without modifying its header # file. If this is the case, specify each options here using the -D syntax. # Notice also that you need to use shell's quoting syntax to enclose the # definition. Both single or double quotes work. For example, # #export MY_CPP_FLAGS="${MY_CPP_FLAGS} -DAVERAGES" #export MY_CPP_FLAGS="${MY_CPP_FLAGS} -DDEBUGGING" # # can be used to write time-averaged fields. Notice that you can have as # many definitions as you want by appending values. if [ $pio_lib -eq 1 ]; then export MY_CPP_FLAGS="${MY_CPP_FLAGS} -DPIO_LIB" fi #-------------------------------------------------------------------------- # Compiler options. #-------------------------------------------------------------------------- # # Other user defined environmental variables. See the ROMS makefile for # details on other options the user might want to set here. Be sure to # leave the switches meant to be off set to an empty string or commented # out. Any string value (including off) will evaluate to TRUE in # conditional if-statements. export USE_MPI=on # distributed-memory parallelism export USE_MPIF90=on # compile with mpif90 script #export which_MPI=intel # compile with mpiifort library #export which_MPI=mpich # compile with MPICH library #export which_MPI=mpich2 # compile with MPICH2 library #export which_MPI=mvapich2 # compile with MVAPICH2 library export which_MPI=openmpi # compile with OpenMPI library #export USE_OpenMP=on # shared-memory parallelism #export FORT=ifx export FORT=ifort #export FORT=gfortran #export FORT=pgi if [ $g_flags -eq 1 ]; then export USE_DEBUG=on # use Fortran debugging flags fi export USE_LARGE=on # activate 64-bit compilation #-------------------------------------------------------------------------- # Building the ROMS executable using the shared library is not recommended # because it requires keeping track of the matching libROMS.{so|dylib} # which is located in the Build_roms or Build_romsG directory and will be # lost and/or replaced with each new build. The option to build the shared # version of libROMS was introduced for use in model coupling systems. #-------------------------------------------------------------------------- #export SHARED=on # build libROMS.{so|dylib} export STATIC=on # build libROMS.a export EXEC=on # build roms{G|M|O|S} executable # ROMS I/O choices and combinations. A more complete description of the # available options can be found in the wiki (https://myroms.org/wiki/IO). # Most users will want to enable at least USE_NETCDF4 because that will # instruct the ROMS build system to use nf-config to determine the # necessary libraries and paths to link into the ROMS executable. export USE_NETCDF4=on # compile with NetCDF-4 library #export USE_PARALLEL_IO=on # Parallel I/O with NetCDF-4/HDF5 if [ $pio_lib -eq 1 ]; then export USE_PIO=on # Parallel I/O with PIO library fi # If any of the coupling component use the HDF5 Fortran API for primary # I/O, we need to compile the main driver with the HDF5 library. #export USE_HDF5=on # compile with HDF5 library #-------------------------------------------------------------------------- # If coupling Earth System Models (ESM), set the location of the ESM # component libraries and modules. #-------------------------------------------------------------------------- source ${MY_ROMS_SRC}/ESM/esm_libs.sh ${MY_ROMS_SRC}/ESM/esm_libs.sh #-------------------------------------------------------------------------- # If applicable, use my specified library paths. #-------------------------------------------------------------------------- export USE_MY_LIBS=no # use system default library paths #export USE_MY_LIBS=yes # use my customized library paths MY_PATHS=${COMPILERS}/my_build_paths.sh if [ "${USE_MY_LIBS}" == "yes" ]; then source ${MY_PATHS} ${MY_PATHS} fi #-------------------------------------------------------------------------- # The rest of this script sets the path to the users header file and # analytical source files, if any. See the templates in User/Functionals. #-------------------------------------------------------------------------- # # If applicable, use the MY_ANALYTICAL_DIR directory to place your # customized biology model header file (like fennel.h, nemuro.h, ecosim.h, # etc). export MY_HEADER_DIR=${MY_PROJECT_DIR} export MY_ANALYTICAL_DIR=${MY_PROJECT_DIR} # Put the binary to execute in the following directory. export BINDIR=${MY_PROJECT_DIR} echo "" echo "${separator}" # Stop if activating both MPI and OpenMP at the same time. if [ -n "${USE_MPI:+1}" ] && [ -n "${USE_OpenMP:+1}" ]; then echo "" echo "You cannot activate USE_MPI and USE_OpenMP at the same time!" exit 1 fi # Put the f90 files in a project specific Build directory to avoid conflict # with other projects. if [ -n "${USE_DEBUG:+1}" ]; then export BUILD_DIR=${MY_PROJECT_DIR}/Build_romsG export myBIN=${BINDIR}/romsG else if [ -n "${USE_OpenMP:+1}" ]; then export BUILD_DIR=${MY_PROJECT_DIR}/Build_romsO export myBIN=${BINDIR}/romsO elif [ -n "${USE_MPI:+1}" ]; then export BUILD_DIR=${MY_PROJECT_DIR}/Build_romsM export myBIN=${BINDIR}/romsM else export BUILD_DIR=${MY_PROJECT_DIR}/Build_roms export myBIN=${BINDIR}/romsS fi fi # For backward compatibility, set deprecated SCRATCH_DIR to compile # older released versions of ROMS. export SCRATCH_DIR=${BUILD_DIR} # If necessary, create ROMS build directory. if [ ! -d ${BUILD_DIR} ]; then echo "" echo "Creating ROMS build directory: ${BUILD_DIR}" echo "" mkdir $BUILD_DIR fi # Go to the users source directory to compile. The options set above will # pick up the application-specific code from the appropriate place. if [ $branch -eq 1 ]; then # Check out requested branch from ROMS GitHub. if [ ! -d ${MY_PROJECT_DIR}/src ]; then echo "" echo "Downloading ROMS source code from GitHub: https://github.com/myroms" echo "" git clone https://www.github.com/myroms/roms.git src fi echo "" echo "Checking out ROMS GitHub branch: $branch_name" echo "" cd src git checkout $branch_name # If we are using the COMPILERS from the ROMS source code # overide the value set above if [[ ${COMPILERS} == ${MY_ROMS_SRC}* ]]; then export COMPILERS=${MY_PROJECT_DIR}/src/Compilers fi export MY_ROMS_SRC=${MY_PROJECT_DIR}/src else echo "" echo "Using ROMS source code from: ${MY_ROMS_SRC}" echo "" cd ${MY_ROMS_SRC} fi #-------------------------------------------------------------------------- # Compile. #-------------------------------------------------------------------------- # Remove build directory. if [ $clean -eq 1 ]; then echo "" echo "Cleaning ROMS build directory: ${BUILD_DIR}" echo "" make clean fi # Compile (the binary will go to BINDIR set above). if [ $dprint -eq 1 ]; then make $debug else echo "" echo "Compiling ROMS source code:" echo "" if [ $parallel -eq 1 ]; then make $NCPUS else make fi HEADER=`echo ${ROMS_APPLICATION} | tr '[:upper:]' '[:lower:]'`.h echo "" echo "${separator}" echo "GNU Build script command: ${command}" echo "ROMS source directory: ${MY_ROMS_SRC}" echo "ROMS header file: ${MY_HEADER_DIR}/${HEADER}" echo "ROMS build directory: ${BUILD_DIR}" echo "ROMS executable: ${myBIN}" if [ $branch -eq 1 ]; then echo "ROMS downloaded from: https://github.com/myroms/roms.git" echo "ROMS compiled branch: $branch_name" fi echo "ROMS Application: ${ROMS_APPLICATION}" FFLAGS=`make print-FFLAGS | cut -d " " -f 3-` echo "Fortran compiler: ${FORT}" echo "Fortran flags: ${FFLAGS}" if [ -n "${MY_CPP_FLAGS:+1}" ]; then echo "Added CPP Options: ${MY_CPP_FLAGS}" fi echo "${separator}" echo "" fi 配置ROMS编译选项:进入roms目录,查看ROMS/External目录下的build_roms.sh脚本,根据需要修改编译选项,例如启用MPI并行计算、指定编译器等。 需要修改哪些内容呢?
07-19
Cart.php <?php /** * Custom Cart Page for WooCommerce with Selective Checkout * RECOMMENDED LOCATION (Woo override): * /wp-content/themes/your-child-theme/woocommerce/cart/cart.php * ALTERNATIVE (if your site is wired this way): * /wp-content/themes/woodmart-child/cart.php */ if (!defined('ABSPATH')) { exit; } do_action('woocommerce_before_cart'); // Provide tiny context for JS (invisible; 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(); ?> <div class="cart-page-section container" style="max-width: 1200px; margin: 0 auto;"> <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'); ?> <table class="shop_table shop_table_responsive cart woocommerce-cart-form__contents"> <thead> <tr> <th class="product-select" style="width: 8%;"> <input type="checkbox" id="select-all-items" /> <label for="select-all-items" style="display: inline-block; margin-left: 5px; cursor: pointer;">全选</label> </th> <th class="product-info"><?php esc_html_e('Product', 'woocommerce'); ?></th> <th class="product-price"><?php esc_html_e('Price', 'woocommerce'); ?></th> <th class="product-quantity"><?php esc_html_e('Quantity', 'woocommerce'); ?></th> <th class="product-subtotal"><?php esc_html_e('Subtotal', 'woocommerce'); ?></th> <th class="product-remove"><?php esc_html_e('操作', 'woocommerce'); ?></th> </tr> </thead> <tbody> <?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); ?> <tr class="woocommerce-cart-form__cart-item <?php echo esc_attr( apply_filters('woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key) ); ?>" data-cart_item_key="<?php echo esc_attr($cart_item_key); ?>" data-product_id="<?php echo esc_attr($product_id); ?>" data-variation_id="<?php echo esc_attr($variation_id); ?>" data-variation="<?php echo esc_attr($variation_json); ?>"> <!-- Checkbox Column --> <td class="product-select" data-title="<?php esc_attr_e('Select', 'woocommerce'); ?>"> <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); ?>" /> </td> <!-- Product Info Column --> <td class="product-info" data-title="<?php esc_attr_e('Product', 'woocommerce'); ?>"> <div class="product-image"> <?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('<a href="%s">%s</a>', esc_url($product_permalink), $thumbnail); // PHPCS: XSS ok. endif; ?> </div> <div class="product-name"> <?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('<a href="%s">%s</a>', 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. ?> </div> </td> <!-- Price Column --> <td class="product-price" data-title="<?php esc_attr_e('Price', 'woocommerce'); ?>"> <?php echo apply_filters('woocommerce_cart_item_price', WC()->cart->get_product_price($_product), $cart_item, $cart_item_key); // PHPCS: XSS ok. ?> </td> <!-- Quantity Column --> <td class="product-quantity" data-title="<?php esc_attr_e('Quantity', 'woocommerce'); ?>"> <?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. ?> <small class="qty-status" style="display:none;margin-left:8px;color:#666;">保存中…</small> </td> <!-- Subtotal Column --> <td class="product-subtotal" data-title="<?php esc_attr_e('Subtotal', 'woocommerce'); ?>"> <?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. ?> </td> <!-- Remove Item Column --> <td class="product-remove" data-title="<?php esc_attr_e('操作', 'woocommerce'); ?>"> <?php echo apply_filters('woocommerce_cart_item_remove_link', sprintf( '<a href="%s" class="remove" aria-label="%s" data-product_id="%s" data-product_sku="%s">×</a>', 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); ?> </td> </tr> <?php endif; ?> <?php endforeach; ?> <?php do_action('woocommerce_after_cart_contents'); ?> </tbody> </table> <?php do_action('woocommerce_after_cart_table'); ?> </form> </div> <!-- Sticky Footer --> <div class="cart-footer-actions sticky-footer" style="position: sticky; bottom: 0; background: white; padding: 15px; border-top: 1px solid #ddd; max-width: 1200px; margin: 0 auto;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;"> <div class="footer-left"> <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> </div> <div class="coupon-section"> <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> </div> </div> <div style="display: flex; justify-content: space-between; align-items: center;"> <div class="selected-summary" style="font-size: 16px; font-weight: bold;"> 已选商品: <span id="selected-count">0</span> 件,共计: <span id="selected-total">RM0.00</span> </div> <a href="<?php echo esc_url( wc_get_checkout_url() ); ?>" class="checkout-button button alt wc-forward" id="partial-checkout">结算</a> </div> </div> <?php do_action('woocommerce_after_cart'); ?> <style> /* Your original styles exactly untouched */ <?php /* Keeping exactly your CSS content */ ?> .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; } @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%; } } <style> /* 添加加载动画样式 */ .quantity-saving-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.8); display: flex; align-items: center; justify-content: center; z-index: 10; border-radius: 4px; } .quantity-saving-loader { width: 24px; height: 24px; border: 3px solid #f3f3f3; border-top: 3px solid #3498db; border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* 使数量输入框容器相对定位 */ .product-quantity .quantity { position: relative; } </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) { // The current page is cart; reload to display rehydrated items location.reload(); } }); } // Remove selected $('#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) { var saved = readSelection().filter(function(k){ return keys.indexOf(k) === -1; }); writeSelection(saved); location.reload(); } else { alert((res && res.data && (res.data.message || res.data)) || '删除失败,请重试'); } }).fail(function(){ alert('删除失败,请重试'); }); }); // Clear cart $('#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 $('#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 (per-row only; no page hold) // 修复后的数量自动保存功能 function parseCartKeyFromInputName(n){ // 修正的正则表达式 - 匹配: cart[<cart_item_key>][qty] var m = (n||'').match(/^cart\[(.+?)\]\[qty\]$/); return m ? m[1] : null; } // 创建加载动画元素 function createLoader() { var $overlay = $('<div class="quantity-saving-overlay"><div class="quantity-saving-loader"></div></div>'); return $overlay; } // Quantity auto-save with loader effect function parseCartKeyFromInputName(n){ // 修正正则表达式 var m = (n||'').match(/^cart\[(.+?)\]\[qty\]$/); return m ? m[1] : null; } var qtyTimers = {}; $(document).on('input change', '.qty', function(){ var $input = $(this); var key = parseCartKeyFromInputName($input.attr('name')); if (!key) return; var row = $input.closest('tr'); var $quantityContainer = $input.closest('.quantity'); var val = parseInt($input.val(), 10); if (isNaN(val) || val < 0) val = 0; // 清除现有定时器 if (qtyTimers[key]) clearTimeout(qtyTimers[key]); // 创建加载动画容器(如果不存在) if (!$quantityContainer.find('.quantity-saving-overlay').length) { $quantityContainer.append(createLoader()); } // 隐藏加载层 $quantityContainer.find('.quantity-saving-overlay').hide(); // 设置新的定时器 qtyTimers[key] = setTimeout(function(){ // 显示加载动画 $quantityContainer.find('.quantity-saving-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 && typeof res.data.line_total_incl_tax === 'number') { $cb.attr('data-price', res.data.line_total_incl_tax); updateSelectedSummary(); } // 数量为0时移除商品行 if (val === 0 || res.data.removed) { row.fadeOut(300, function(){ $(this).remove(); snapshotCartFromDOM(); updateSelectedSummary(); }); } else { snapshotCartFromDOM(); } } else { var msg = (res && res.data && (res.data.message || res.data)) || '数量更新失败'; alert(msg); } }).fail(function(){ alert('数量更新失败,请重试'); }).always(function(){ // 隐藏加载动画并启用输入框 $quantityContainer.find('.quantity-saving-overlay').hide(); $input.prop('disabled', false); }); }, 500); // 0.5秒延迟 }); // 新增错误提示函数 function showError(msg) { if (!$('#qty-error').length) { $('body').append('<div id="qty-error" style="position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#ffebee;padding:10px 20px;border-radius:4px;color:#b71c1c;z-index:9999;display:none;"></div>'); } $('#qty-error').text(msg).fadeIn(200).delay(3000).fadeOut(400); } // 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('创建订单中...'); // Snapshot first for safety 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; // This is /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 removing via the "x" link and update cart 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(); } }); // Init restoreSelectionFromLS(); updateSelectedSummary(); snapshotCartFromDOM(); maybeRehydrateFromLocal(); }); </script> functions.php <?php defined('ABSPATH') || exit; /** * Robust partial checkout to regular /checkout/ with durable cart snapshot * - Snapshot full cart before virtualizing selection for checkout * - Do not remove anything until order is created * - On success (thank-you), rebuild cart as (snapshot - purchased) * - On cancel/back (visit cart), restore snapshot * - Guest resilience: localStorage + rehydrate AJAX */ /* ------------------------------------------------- * Helpers * ------------------------------------------------- */ 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 (!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; } function pc_restore_cart_from_items($items) { if (!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) { WC()->cart->add_to_cart($pid, $qty, $vid, $var); } } WC()->cart->calculate_totals(); } function pc_transient_key($token) { return 'pc_partial_payload_' . sanitize_key($token); } /* ------------------------------------------------- * AJAX: Local rehydrate when Woo cart is empty * ------------------------------------------------- */ 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' => 'Invalid items.'), 400); } if (!WC()->cart) wc_load_cart(); if (!WC()->cart->is_empty()) { wp_send_json_success(array('message' => 'Cart not empty.')); } 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) { WC()->cart->add_to_cart($pid, $qty, $vid, $var); } } WC()->cart->calculate_totals(); wp_send_json_success(array('rehydrated' => true)); } /* ------------------------------------------------- * AJAX: Update qty (per-row; no page reload) * ------------------------------------------------- */ 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' => 'Missing params.'), 400); } if (!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' => 'Cart item not found after update.'), 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); // Use line_total + line_tax (after totals) for your checkbox data-price $line_total_incl_tax = (float)($cart_item['line_total'] + $cart_item['line_tax']); // functions.php 中的更新 wp_send_json_success(array( 'subtotal_html' => $subtotal_html, 'line_total_incl_tax' => $line_total_incl_tax, 'removed' => ($qty <= 0) // 明确返回是否移除 )); } } /* ------------------------------------------------- * AJAX: Remove selected * ------------------------------------------------- */ 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 (!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: Empty cart * ------------------------------------------------- */ 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 (!WC()->cart) wc_load_cart(); WC()->cart->empty_cart(); wp_send_json_success(true); } /* ------------------------------------------------- * AJAX: Apply coupon * ------------------------------------------------- */ 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 (!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) { // Woo often pushes notices instead of bool; we return generic message wp_send_json_error(array('message' => __('优惠券应用失败', 'woocommerce')), 400); } wp_send_json_success(true); } /* ------------------------------------------------- * AJAX: Start partial checkout to regular checkout page * - Save snapshot + selected items in a transient keyed by token * - Put token in session * - Return /checkout/?pc_token=... * ------------------------------------------------- */ 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 (!WC()->cart) wc_load_cart(); // Snapshot full cart $snapshot = pc_snapshot_current_cart(); // Build selected items from current cart based on cart_item_key list $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); // Put token in session (used across checkout AJAX calls) if (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)); } /* ------------------------------------------------- * Virtualize cart on the regular checkout for token * - On initial checkout load with ?pc_token=... * - Ensure re-virtualization before checkout processing * - Tag order with token * - On thank-you, rebuild cart = snapshot - purchased * - On returning to cart without completing: restore snapshot * ------------------------------------------------- */ // Entering checkout with token: virtualize cart to selected items 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 (!WC()->cart) wc_load_cart(); // Virtualize to selected items only pc_restore_cart_from_items($payload['selected']); // Persist token in session for all subsequent checkout AJAX calls if (method_exists(WC()->session, 'set')) { WC()->session->set('pc_partial_token', $token); } }, 1); // Safety: just-in-time re-virtualization before order processing add_action('woocommerce_before_checkout_process', function() { if (!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; // Ensure cart still equals selected set (another tab might have restored) pc_restore_cart_from_items($payload['selected']); }, 1); // Tag order with token so we can resolve on thank-you add_action('woocommerce_checkout_create_order', function($order /* WC_Order */) { $token = null; if (isset($_GET['pc_token'])) { $token = sanitize_text_field(wp_unslash($_GET['pc_token'])); } elseif (method_exists(WC()->session, 'get')) { $token = WC()->session->get('pc_partial_token'); } if ($token) { $order->update_meta_data('_pc_partial_token', $token); } }, 10, 1); // On thank-you: rebuild cart as (snapshot - purchased), then cleanup 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'])) { // Nothing to rebuild, just cleanup session token if (method_exists(WC()->session, 'set')) { WC()->session->set('pc_partial_token', null); } delete_transient(pc_transient_key($token)); return; } // Build purchased map pid|vid => qty $purchased = array(); foreach ($order->get_items('line_item') as $item) { $pid = (int)$item->get_product_id(); $vid = (int)$item->get_variation_id(); $qty = (int)$item->get_quantity(); $k = pc_build_item_key($pid, $vid); if (!isset($purchased[$k])) $purchased[$k] = 0; $purchased[$k] += $qty; } // Remainder = snapshot - purchased $remainder = array(); foreach ($payload['snapshot'] as $it) { $pid = (int)$it['product_id']; $vid = (int)$it['variation_id']; $qty = wc_stock_amount($it['quantity']); $var = isset($it['variation']) ? $it['variation'] : array(); $k = pc_build_item_key($pid, $vid); $take = isset($purchased[$k]) ? (int)$purchased[$k] : 0; $left = max(0, $qty - $take); if ($left > 0) { $remainder[] = array( 'product_id' => $pid, 'variation_id' => $vid, 'variation' => $var, 'quantity' => $left, ); $purchased[$k] = max(0, $take - $qty); // if over-purchased (unlikely), keep non-negative } } // Rebuild cart to remainder pc_restore_cart_from_items($remainder); // Cleanup token if (method_exists(WC()->session, 'set')) { WC()->session->set('pc_partial_token', null); } delete_transient(pc_transient_key($token)); // Also update localStorage best-effort on the thank-you page (client side) is optional; // your cart.php JS already re-snapshots on next visit. }, 20); // If user visits the cart page with an active partial token (abandoned/cancelled), restore full snapshot add_action('woocommerce_before_cart', function() { if (!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; // Restore full snapshot so cart page always shows everything pc_restore_cart_from_items($payload['snapshot']); // Keep token so user can retry checkout; or clear it if you prefer ending the flow here: // WC()->session->set('pc_partial_token', null); }, 1); Currently You are now a website builder. These are the code in my cart.php & functions.php. Currently, i am very satisfied with my layout. However, I want to achieve cart like Shopee / Taobao / Lazada, that allow my customer to 1) Do selective checkout, where only selected items are sent to checkout page, and after order created, only these items will be removed from cart. On the other side, non-selected items are retained inside cart, until customer remove them manually through 清除購物車 / 移除選中商品 or customer check out them. Only remove when order is created. If customer go back from checkout page or payment page, or anything, as long as order is not created, dont remove them. Currently im using restore cart way to achieve this, but everything from cart is removed currently after order created. theres some mistake that stop me from achieving this. I turned on feature where customer can see how many items are inside cart through the cart icon at header. At the same time, i dont want my customer to notice that their cart is cleared and recovered before (the amount of items in cart icon will show 0 when havent recover and the number of items after recover). I dont want them to notice the change, therefore, we have to recover it fast until customer did not notice them. Please be considerate regarding customer that use PC because they might access my website through another tab, at there, they can see the amount of items in cart icon. Therefore, i want it to always show the correct amount, only remove when the order is created and maintain if the order is not created. I dont want them to notice how it works (recovering) 2) All these 全选 刪除選中的商品 清空購物車 输入优惠券代码 应用优惠券 已选商品: 0 件,共计: RM0.00 结算 works perfectly fine, please remain them. Maintaining all the layout and achieved functions. Please check which part stopped me from achieving the selective checkout (maintain non-selected items inside cart, only remove selected to checkout items when order created, and dont let customer nmotice the change in their cart. I want them to no matter access from where, either new tab, other device etc, they will see the correct items in their cart) For now, If i cancel the payment halfway, i am logged out when back from payment page, and when i check from other tab, non-selected items are removed from cart, only selected items are still inside cart. Also, when i login back after payment page, it redirects me to pay-only page. Please refer how taobao cart works, and review which part is stopping me from achieve taobao cart, give me the part of code to be changed and the new code to replaced with the current one.
08-30
【电动汽车充电站有序充电调度的分散式优化】基于蒙特卡诺和拉格朗日的电动汽车优化调度(分时电价调度)(Matlab代码实现)内容概要:本文介绍了基于蒙特卡洛和拉格朗日方法的电动汽车充电站有序充电调度优化方案,重点在于采用分散式优化策略应对分时电价机制下的充电需求管理。通过构建数学模型,结合不确定性因素如用户充电行为和电网负荷波动,利用蒙特卡洛模拟生成大量场景,并运用拉格朗日松弛法对复杂问题进行分解求解,从而实现全局最优或近似最优的充电调度计划。该方法有效降低了电网峰值负荷压力,提升了充电站运营效率与经济效益,同时兼顾用户充电便利性。 适合人群:具备一定电力系统、优化算法和Matlab编程基础的高校研究生、科研人员及从事智能电网、电动汽车相关领域的工程技术人员。 使用场景及目标:①应用于电动汽车充电站的日常运营管理,优化充电负荷分布;②服务于城市智能交通系统规划,提升电网与交通系统的协同水平;③作为学术研究案例,用于验证分散式优化算法在复杂能源系统中的有效性。 阅读建议:建议读者结合Matlab代码实现部分,深入理解蒙特卡洛模拟与拉格朗日松弛法的具体实施步骤,重点关注场景生成、约束处理与迭代收敛过程,以便在实际项目中灵活应用与改进。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值