国税开票助手

根据表单字段值自动勾选或取消勾选"是否展示"复选框

// ==UserScript==
// @name         国税开票助手
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  根据表单字段值自动勾选或取消勾选"是否展示"复选框
// @author       ChatGPT
// @match        https://dppt.anhui.chinatax.gov.cn:8443/blue-invoice-makeout/invoice-makeout*
// @match        https://dppt.anhui.chinatax.gov.cn/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 配置选择器
    const CONFIG = {
        // 购买方地址和电话
        group1: {
            fields: ['.t-form-item__dz input', '.t-form-item__lxdh input'],
            containers: ['.t-form-item__dz', '.t-form-item__lxdh'],
            checkboxSelector: '.t-checkbox__input'
        },
        // 购方开户银行和银行账号
        group2: {
            fields: ['.t-form-item__yhyywdmc input', '.t-form-item__yhzh input'],
            containers: ['.t-form-item__yhyywdmc', '.t-form-item__yhzh'],
            checkboxSelector: '.t-checkbox__input'
        }
    };

    // 工具函数:防抖
    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    // 查找字段关联的复选框
    function findRelatedCheckbox(container) {
        // 尝试在当前容器内查找
        let checkbox = container.querySelector('.t-checkbox__input');

        // 如果没找到,尝试查找相邻元素
        if (!checkbox) {
            // 查找父元素的下一个同级元素
            let nextSibling = container.nextElementSibling;
            while (nextSibling) {
                checkbox = nextSibling.querySelector('.t-checkbox__input');
                if (checkbox) break;
                nextSibling = nextSibling.nextElementSibling;
            }

            // 如果还没找到,尝试查找父元素
            if (!checkbox) {
                let parent = container.parentElement;
                checkbox = parent.querySelector('.t-checkbox__input');
            }
        }

        return checkbox;
    }

    // 模拟用户点击复选框
    function simulateUserClick(element) {
        if (!element) return;

        // 创建并分发鼠标事件
        const events = [
            new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window }),
            new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window }),
            new MouseEvent('click', { bubbles: true, cancelable: true, view: window })
        ];

        // 依次触发事件
        events.forEach(event => element.dispatchEvent(event));
    }

    // 检查并更新复选框状态
    function updateCheckboxState(group) {
        // 获取所有输入字段
        const fields = [];
        group.fields.forEach(selector => {
            const elements = document.querySelectorAll(selector);
            elements.forEach(el => fields.push(el));
        });

        if (fields.length === 0) return; // 没有找到字段

        // 检查是否有值
        const hasValue = fields.some(field => field.value && field.value.trim() !== '');

        // 查找相关复选框
        let checkbox = null;
        let container = null;
        for (const containerSelector of group.containers) {
            container = document.querySelector(containerSelector);
            if (container) {
                checkbox = findRelatedCheckbox(container);
                if (checkbox) break;
            }
        }

        // 如果找不到复选框,尝试寻找包含"是否展示"文本的复选框
        if (!checkbox) {
            document.querySelectorAll('.t-checkbox').forEach(el => {
                if (el.textContent.includes('是否展示')) {
                    const inputEl = el.querySelector('.t-checkbox__input');
                    if (inputEl && !checkbox) {
                        checkbox = inputEl;
                    }
                }
            });
        }

        // 如果找不到复选框,可能需要查看页面结构
        if (!checkbox) {
            console.log('未找到关联的复选框');
            return;
        }

        // 获取复选框的包装元素
        const checkboxWrapper = checkbox.closest('.t-checkbox');
        if (!checkboxWrapper) {
            console.log('未找到复选框包装元素');
            return;
        }

        // 当前复选框状态与目标状态不一致时才更新
        const isCurrentlyChecked = checkboxWrapper.classList.contains('t-is-checked');
        if (isCurrentlyChecked !== hasValue) {
            console.log(`更新复选框状态: ${isCurrentlyChecked} -> ${hasValue}`);

            // 方法1: 模拟真实用户点击行为
            simulateUserClick(checkboxWrapper);

            // 方法2: 确保视觉状态与实际状态一致
            setTimeout(() => {
                // 再次检查状态,确保一致性
                const isNowChecked = checkboxWrapper.classList.contains('t-is-checked');
                if (isNowChecked !== hasValue) {
                    console.log('使用备用方法强制更新复选框状态');

                    // 直接触发原生的click处理函数
                    if (typeof checkboxWrapper.onclick === 'function') {
                        checkboxWrapper.onclick();
                    }

                    // 尝试访问Vue组件实例
                    tryUpdateVueComponent(checkboxWrapper, hasValue);
                }
            }, 50);
        }
    }

    // 尝试更新Vue组件状态
    function tryUpdateVueComponent(element, checked) {
        // 尝试查找Vue组件实例
        let vueInstance = null;

        // 查找元素上的__vue__属性 (Vue 2)
        if (element.__vue__) {
            vueInstance = element.__vue__;
        }

        // 如果找到Vue实例
        if (vueInstance) {
            // 尝试更新模型值
            if (vueInstance.model) {
                vueInstance.model.value = checked;
            }

            // 尝试调用组件方法
            if (typeof vueInstance.toggle === 'function') {
                vueInstance.toggle();
            }

            // 尝试直接更新数据属性
            if (vueInstance.isChecked !== undefined) {
                vueInstance.isChecked = checked;
            }

            // 强制Vue更新视图
            if (typeof vueInstance.$forceUpdate === 'function') {
                vueInstance.$forceUpdate();
            }
        }

        // 如果是TDesign组件,可能有特定的数据绑定方式
        const onChange = getEventHandlerFromAttribute(element, 'onChange');
        if (onChange) {
            try {
                onChange({ target: { checked: checked } });
            } catch (e) {
                console.error('调用onChange处理器失败', e);
            }
        }
    }

    // 从元素属性中提取事件处理函数
    function getEventHandlerFromAttribute(element, eventName) {
        // 查找特定属性
        for (const key in element) {
            if (key.startsWith('__reactEventHandlers') ||
                key.startsWith('__EMOTION_') ||
                key.startsWith('__vue')) {
                const obj = element[key];
                if (obj && typeof obj[eventName] === 'function') {
                    return obj[eventName];
                }
            }
        }
        return null;
    }

    // 监视DOM变化
    function setupMutationObserver() {
        const observer = new MutationObserver(debounce(() => {
            // 检查并更新两组复选框
            updateCheckboxState(CONFIG.group1);
            updateCheckboxState(CONFIG.group2);
        }, 200));

        // 观察整个文档的变化
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['value', 'class']
        });
    }

    // 监听输入事件
    function setupInputListeners() {
        document.addEventListener('input', debounce(event => {
            const target = event.target;
            if (target.tagName !== 'INPUT') return;

            // 检查目标是否属于第一组
            if (CONFIG.group1.fields.some(selector => target.matches(selector))) {
                updateCheckboxState(CONFIG.group1);
            }

            // 检查目标是否属于第二组
            if (CONFIG.group2.fields.some(selector => target.matches(selector))) {
                updateCheckboxState(CONFIG.group2);
            }
        }, 200));
    }

    // 监听值变化(处理JS赋值)
    function monitorValueChanges() {
        const inputProto = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
        const originalSetter = inputProto.set;

        // 重写setter以捕获编程方式的值更改
        Object.defineProperty(HTMLInputElement.prototype, 'value', {
            set: function(val) {
                const result = originalSetter.call(this, val);

                // 检查此输入字段是否是我们关注的字段
                const isGroup1Field = CONFIG.group1.fields.some(selector => this.matches(selector));
                const isGroup2Field = CONFIG.group2.fields.some(selector => this.matches(selector));

                if (isGroup1Field) {
                    updateCheckboxState(CONFIG.group1);
                }

                if (isGroup2Field) {
                    updateCheckboxState(CONFIG.group2);
                }

                return result;
            },
            get: inputProto.get
        });
    }

    // 添加覆盖原生函数的方法
    function overrideNativeFunctions() {
        // 保存原始的property setter
        const originalDefineProp = Object.defineProperty;

        // 重写defineProperty,拦截复选框相关的属性设置
        Object.defineProperty = function(obj, prop, descriptor) {
            // 先调用原始方法
            const result = originalDefineProp.call(this, obj, prop, descriptor);

            // 如果是设置checked属性,且对象是checkbox
            if (prop === 'checked' && obj instanceof HTMLInputElement && obj.type === 'checkbox') {
                // 设置checked属性后,检查是否需要同步状态
                setTimeout(() => {
                    // 查找所有复选框
                    document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
                        if (checkbox === obj) {
                            const checkboxWrapper = checkbox.closest('.t-checkbox');
                            if (checkboxWrapper) {
                                // 确保视觉状态与实际状态一致
                                if (checkbox.checked && !checkboxWrapper.classList.contains('t-is-checked')) {
                                    checkboxWrapper.classList.add('t-is-checked');
                                } else if (!checkbox.checked && checkboxWrapper.classList.contains('t-is-checked')) {
                                    checkboxWrapper.classList.remove('t-is-checked');
                                }
                            }
                        }
                    });
                }, 50);
            }

            return result;
        };
    }

    // 初始化脚本
    function init() {
        // 等待页面加载,检查字段是否已存在
        const checkFieldsExist = () => {
            const group1FieldExists = CONFIG.group1.fields.some(selector => document.querySelector(selector));
            const group2FieldExists = CONFIG.group2.fields.some(selector => document.querySelector(selector));

            if (group1FieldExists || group2FieldExists) {
                console.log('国税发票自动勾选脚本已初始化');

                // 设置各种监听器
                setupInputListeners();
                setupMutationObserver();
                monitorValueChanges();
                overrideNativeFunctions();

                // 页面加载后立即检查一次
                updateCheckboxState(CONFIG.group1);
                updateCheckboxState(CONFIG.group2);

                // 定期检查以防万一
                setInterval(() => {
                    updateCheckboxState(CONFIG.group1);
                    updateCheckboxState(CONFIG.group2);
                }, 2000);

                return true;
            }

            return false;
        };

        // 如果字段不存在,定期检查
        if (!checkFieldsExist()) {
            const interval = setInterval(() => {
                if (checkFieldsExist()) {
                    clearInterval(interval);
                }
            }, 500);
        }
    }

    // 运行初始化函数
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();