|
|
/**
|
|
|
* 方案 A:页面加载时注入,保留双地址 React 状态,阻止 pickup 在切换 delivery 时被清空。
|
|
|
* 纯字符串,供 context.addInitScript 使用。
|
|
|
*/
|
|
|
export const DUAL_ADDRESS_PATCH_INIT = `(() => {
|
|
|
if (window.__rpaDualAddressPatchInstalled) return;
|
|
|
window.__rpaDualAddressPatchInstalled = true;
|
|
|
window.__rpaDualAddressVault = { pickup: null, delivery: null };
|
|
|
|
|
|
function findQuoterStateNode() {
|
|
|
var selectors = [
|
|
|
"#react-quoter-landing-plugin",
|
|
|
"[data-testid='quoter-widget']",
|
|
|
"[class*='Quoter']",
|
|
|
];
|
|
|
var widget = null;
|
|
|
for (var s = 0; s < selectors.length; s += 1) {
|
|
|
widget = document.querySelector(selectors[s]);
|
|
|
if (widget) break;
|
|
|
}
|
|
|
if (!widget) return null;
|
|
|
var fiberKey = Object.keys(widget).find(function(k) {
|
|
|
return k.indexOf("__reactFiber$") === 0 || k.indexOf("__reactInternalInstance$") === 0;
|
|
|
});
|
|
|
if (!fiberKey) return null;
|
|
|
var queue = [widget[fiberKey]];
|
|
|
var seen = new Set();
|
|
|
while (queue.length > 0) {
|
|
|
var fiber = queue.shift();
|
|
|
if (!fiber || seen.has(fiber)) continue;
|
|
|
seen.add(fiber);
|
|
|
var sn = fiber.stateNode;
|
|
|
if (sn && typeof sn.setState === "function" && sn.state) {
|
|
|
var st = sn.state;
|
|
|
if (
|
|
|
"pickupAddress" in st ||
|
|
|
"pickup" in st ||
|
|
|
"deliveryAddress" in st ||
|
|
|
(st.addresses && typeof st.addresses === "object")
|
|
|
) {
|
|
|
return sn;
|
|
|
}
|
|
|
}
|
|
|
if (fiber.child) queue.push(fiber.child);
|
|
|
if (fiber.sibling) queue.push(fiber.sibling);
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
function buildAddressData(entry) {
|
|
|
if (!entry) return null;
|
|
|
return {
|
|
|
placeId: entry.placeId,
|
|
|
location: entry.location || {},
|
|
|
formattedAddress: entry.description || entry.formattedAddress || "",
|
|
|
isCommitted: true,
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function isPickupCleared(partial, prevState) {
|
|
|
if (!partial || typeof partial !== "object") return false;
|
|
|
var keys = ["pickupAddress", "pickup"];
|
|
|
for (var i = 0; i < keys.length; i += 1) {
|
|
|
var k = keys[i];
|
|
|
if (!(k in partial)) continue;
|
|
|
var v = partial[k];
|
|
|
if (v === null || v === undefined || v === "") return true;
|
|
|
if (typeof v === "object" && !v.placeId && !v.formattedAddress) return true;
|
|
|
}
|
|
|
if (partial.addresses && partial.addresses.pickup === null) return true;
|
|
|
if (
|
|
|
partial.activeAddressSide === "delivery" &&
|
|
|
prevState &&
|
|
|
prevState.pickupAddress &&
|
|
|
!partial.pickupAddress
|
|
|
) {
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
function wrapStateNode(node) {
|
|
|
if (!node || node.__rpaDualPatchWrapped) return;
|
|
|
node.__rpaDualPatchWrapped = true;
|
|
|
var orig = node.setState.bind(node);
|
|
|
node.setState = function(partial, callback) {
|
|
|
var vault = window.__rpaDualAddressVault;
|
|
|
if (vault && vault.pickup && isPickupCleared(partial, node.state)) {
|
|
|
var pdata = buildAddressData(vault.pickup);
|
|
|
if (pdata) {
|
|
|
partial = Object.assign({}, partial, { pickupAddress: pdata });
|
|
|
}
|
|
|
}
|
|
|
if (vault && vault.delivery && partial && partial.deliveryAddress === null) {
|
|
|
var ddata = buildAddressData(vault.delivery);
|
|
|
if (ddata) {
|
|
|
partial = Object.assign({}, partial, { deliveryAddress: ddata });
|
|
|
}
|
|
|
}
|
|
|
return orig(partial, callback);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function injectSide(side) {
|
|
|
var entry = window.__rpaDualAddressVault[side];
|
|
|
if (!entry) return false;
|
|
|
var node = findQuoterStateNode();
|
|
|
if (!node) return false;
|
|
|
wrapStateNode(node);
|
|
|
var data = buildAddressData(entry);
|
|
|
if (!data) return false;
|
|
|
var patch = {};
|
|
|
if (side === "pickup") {
|
|
|
patch.pickupAddress = data;
|
|
|
patch.activeAddressSide = "delivery";
|
|
|
} else {
|
|
|
patch.deliveryAddress = data;
|
|
|
}
|
|
|
try {
|
|
|
node.setState(patch);
|
|
|
return true;
|
|
|
} catch (e) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
window.__rpaDualAddressApi = {
|
|
|
register: function(side, payload) {
|
|
|
window.__rpaDualAddressVault[side] = payload;
|
|
|
var node = findQuoterStateNode();
|
|
|
if (node) wrapStateNode(node);
|
|
|
return injectSide(side);
|
|
|
},
|
|
|
preservePickup: function() {
|
|
|
var node = findQuoterStateNode();
|
|
|
if (node) wrapStateNode(node);
|
|
|
return injectSide("pickup");
|
|
|
},
|
|
|
preserveBoth: function() {
|
|
|
var pickupOk = injectSide("pickup");
|
|
|
var deliveryOk = injectSide("delivery");
|
|
|
return pickupOk && deliveryOk;
|
|
|
},
|
|
|
hasPickup: function() {
|
|
|
var v = window.__rpaDualAddressVault.pickup;
|
|
|
return !!(v && v.placeId);
|
|
|
},
|
|
|
stats: function() {
|
|
|
return {
|
|
|
pickupRegistered: !!window.__rpaDualAddressVault.pickup,
|
|
|
deliveryRegistered: !!window.__rpaDualAddressVault.delivery,
|
|
|
wrapped: !!findQuoterStateNode(),
|
|
|
apiInstalled: true,
|
|
|
};
|
|
|
},
|
|
|
releasePickupInput: function() {
|
|
|
var widget = document.querySelector("#react-quoter-landing-plugin");
|
|
|
if (!widget) return false;
|
|
|
var clicked = false;
|
|
|
var nodes = widget.querySelectorAll("*");
|
|
|
for (var i = 0; i < nodes.length; i += 1) {
|
|
|
var t = (nodes[i].textContent || "").trim();
|
|
|
if (/^(Deliver to|Delivery to|Where to\?)$/i.test(t)) {
|
|
|
nodes[i].click();
|
|
|
clicked = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
var inputs = widget.querySelectorAll("input");
|
|
|
for (var j = 0; j < inputs.length; j += 1) {
|
|
|
var ph = (inputs[j].placeholder || "").toLowerCase();
|
|
|
if (!/search/.test(ph)) continue;
|
|
|
if ((inputs[j].value || "").trim().length >= 8) {
|
|
|
inputs[j].blur();
|
|
|
}
|
|
|
}
|
|
|
return clicked;
|
|
|
},
|
|
|
};
|
|
|
|
|
|
var tick = 0;
|
|
|
setInterval(function() {
|
|
|
if (!window.__rpaDualAddressVault.pickup) return;
|
|
|
tick += 1;
|
|
|
if (tick % 4 !== 0) return;
|
|
|
var node = findQuoterStateNode();
|
|
|
if (node) {
|
|
|
wrapStateNode(node);
|
|
|
injectSide("pickup");
|
|
|
}
|
|
|
}, 400);
|
|
|
})();`;
|