Vue.component("csrf-token", { created: function () { this.refreshToken() }, mounted: function () { let that = this var form = this.$refs.token.form // 监听表单提交,在提交前刷新token并确保更新到 DOM form.addEventListener("submit", function (e) { // 如果正在刷新,等待刷新完成 if (that.refreshing) { e.preventDefault() e.stopPropagation() return false } // 阻止默认提交,先刷新 token e.preventDefault() e.stopPropagation() that.refreshing = true // 刷新 token that.refreshToken(function () { // 确保 DOM 中的 token 值是最新的 that.$forceUpdate() that.$nextTick(function () { var tokenInput = form.querySelector('input[name="csrfToken"]') if (tokenInput) { tokenInput.value = that.token } if (that.$refs.token) { that.$refs.token.value = that.token } // 确保 DOM 已更新后,再触发表单提交 setTimeout(function () { that.refreshing = false // 重新触发表单提交 Tea.runActionOn(form) }, 50) }) }) return false }) // 自动刷新 setInterval(function () { that.refreshToken() }, 10 * 60 * 1000) // 监听表单提交失败,如果是 CSRF token 错误,自动刷新 token 并重试 this.setupAutoRetry(form) }, data: function () { return { token: "", retrying: false, refreshing: false } }, methods: { refreshToken: function (callback) { let that = this Tea.action("/csrf/token") .get() .success(function (resp) { that.token = resp.data.token if (callback) { callback() } }) .fail(function () { if (callback) { callback() } }) }, setupAutoRetry: function (form) { let that = this var originalFail = form.getAttribute("data-tea-fail") // 确保 Tea.Vue 存在 if (typeof Tea === "undefined" || Tea.Vue == null) { if (typeof Tea === "undefined") { window.Tea = {} } if (Tea.Vue == null) { Tea.Vue = {} } } // 创建一个包装的 fail 函数 var wrappedFailName = "csrfAutoRetryFail_" + Math.random().toString(36).substr(2, 9) form.setAttribute("data-tea-fail", wrappedFailName) Tea.Vue[wrappedFailName] = function (resp) { // 检查是否是 CSRF token 错误 var isCSRFError = false if (resp && resp.message) { // 检查消息是否包含 "表单已失效" 或 "001" if (resp.message.indexOf("表单已失效") >= 0 || resp.message.indexOf("(001)") >= 0) { isCSRFError = true } } // 检查 HTTP 状态码是否为 403 或 400 if (!isCSRFError && resp && (resp.statusCode === 403 || resp.status === 403 || resp.statusCode === 400 || resp.status === 400)) { isCSRFError = true } if (isCSRFError) { // 如果不是正在重试,则立即刷新 token 并自动重试 if (!that.retrying) { that.retrying = true // 立即刷新 token that.refreshToken(function () { // 强制更新 Vue,确保响应式数据已更新 that.$forceUpdate() // 使用 $nextTick 等待 Vue 完成 DOM 更新 that.$nextTick(function () { // 直接查找并更新 DOM 中的 input 元素(通过 name 属性) var tokenInput = form.querySelector('input[name="csrfToken"]') if (tokenInput) { tokenInput.value = that.token } // 如果 ref 存在,也更新它 if (that.$refs.token) { that.$refs.token.value = that.token } // 使用 setTimeout 确保 DOM 已完全更新 setTimeout(function () { // 再次确认 token 值已更新 var finalTokenInput = form.querySelector('input[name="csrfToken"]') if (finalTokenInput && finalTokenInput.value !== that.token) { finalTokenInput.value = that.token } that.retrying = false // 重新触发表单提交 Tea.runActionOn(form) }, 150) }) }) return // 不调用原始 fail 函数 } else { // 如果正在重试,说明已经刷新过 token,直接调用原始 fail 函数 if (originalFail && typeof Tea.Vue[originalFail] === "function") { return Tea.Vue[originalFail].call(Tea.Vue, resp) } else { Tea.failResponse(resp) } } } // 不是 CSRF 错误,调用原始 fail 函数或默认处理 if (originalFail && typeof Tea.Vue[originalFail] === "function") { return Tea.Vue[originalFail].call(Tea.Vue, resp) } else { Tea.failResponse(resp) } } } }, template: `` })