From 23f5a4ad902648ea005d19ed39985042860b8124 Mon Sep 17 00:00:00 2001
From: chouchouji <1305974212@qq.com>
Date: Thu, 3 Jul 2025 12:03:03 +0800
Subject: [PATCH] =?UTF-8?q?fix(mp-weixin):=20=E4=BF=AE=E5=A4=8D=20input=20?=
=?UTF-8?q?=E5=B5=8C=E5=A5=97=20keyboard-accessory=20=E6=97=B6=E7=BC=96?=
=?UTF-8?q?=E8=AF=91=E5=90=8E=E4=B8=A4=E8=80=85=E6=9C=AA=E4=BF=9D=E6=8C=81?=
=?UTF-8?q?=E5=B5=8C=E5=A5=97=E5=B1=82=E7=BA=A7=E7=9A=84Bug=20(question/20?=
=?UTF-8?q?9237)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/uni-mp-compiler/src/parserOptions.ts | 9 +-
packages/uni-mp-vite/__tests__/input.spec.ts | 109 ++++++++++++++++++
packages/uni-mp-vite/src/index.ts | 3 +-
packages/uni-mp-vite/src/plugins/input.ts | 74 ++++++++++++
4 files changed, 189 insertions(+), 6 deletions(-)
create mode 100644 packages/uni-mp-vite/__tests__/input.spec.ts
create mode 100644 packages/uni-mp-vite/src/plugins/input.ts
diff --git a/packages/uni-mp-compiler/src/parserOptions.ts b/packages/uni-mp-compiler/src/parserOptions.ts
index 5242979cec7..bb3cbaab63e 100644
--- a/packages/uni-mp-compiler/src/parserOptions.ts
+++ b/packages/uni-mp-compiler/src/parserOptions.ts
@@ -15,11 +15,10 @@ export const enum DOMNamespaces {
export const parserOptions: ParserOptions = {
isVoidTag(tag) {
- // 开启 input 标签判断,会导致 编译失败
- // 微信小程序允许 Input 嵌套其他组件 https://ask.dcloud.net.cn/question/202776
- // if (tag === 'input') {
- // return false
- // }
+ // 微信小程序允许 input 嵌套其他组件 https://ask.dcloud.net.cn/question/202776
+ if (tag === 'input') {
+ return false
+ }
return isVoidTagRaw(tag)
},
isNativeTag: (tag) => isHTMLTag(tag) || isSVGTag(tag),
diff --git a/packages/uni-mp-vite/__tests__/input.spec.ts b/packages/uni-mp-vite/__tests__/input.spec.ts
new file mode 100644
index 00000000000..030be14ac26
--- /dev/null
+++ b/packages/uni-mp-vite/__tests__/input.spec.ts
@@ -0,0 +1,109 @@
+import { formatInputTag } from '../src/plugins/input'
+
+describe('test input tag', () => {
+ test('with end input tag', () => {
+ expect(
+ formatInputTag(`
+
+
+
+
+ {{ title }}
+
+
+ `)
+ ).toBe(`
+
+
+
+
+ {{ title }}
+
+
+ `)
+
+ expect(
+ formatInputTag(
+ ``
+ )
+ ).toBe(
+ ``
+ )
+
+ expect(formatInputTag(``)).toBe(
+ ``
+ )
+
+ expect(
+ formatInputTag(`
+
+
+
+
+ {{i * 5}}
+
+
+
+ `)
+ ).toBe(`
+
+
+
+
+ {{i * 5}}
+
+
+
+ `)
+ })
+
+ test('no end input tag', () => {
+ expect(formatInputTag(``)).toBe(
+ ``
+ )
+
+ expect(
+ formatInputTag(
+ ``
+ )
+ ).toBe(
+ ``
+ )
+
+ expect(
+ formatInputTag(
+ ``
+ )
+ ).toBe(
+ ``
+ )
+
+ expect(
+ formatInputTag(`
+
+
+
+
+ Some content
+ xxx
+
+
+ 234
+
+
+ `)
+ ).toBe(`
+
+
+
+
+ Some content
+ xxx
+
+
+ 234
+
+
+ `)
+ })
+})
diff --git a/packages/uni-mp-vite/src/index.ts b/packages/uni-mp-vite/src/index.ts
index c3c70300293..5614fc456e1 100644
--- a/packages/uni-mp-vite/src/index.ts
+++ b/packages/uni-mp-vite/src/index.ts
@@ -25,7 +25,7 @@ import { uniMainJsPlugin } from './plugins/mainJs'
import { uniManifestJsonPlugin } from './plugins/manifestJson'
import { uniPagesJsonPlugin } from './plugins/pagesJson'
import { uniEntryPlugin } from './plugins/entry'
-
+import { uniInputAutoClosePlugin } from './plugins/input'
import { uniRenderjsPlugin } from './plugins/renderjs'
import { uniRuntimeHooksPlugin } from './plugins/runtimeHooks'
import { uniSubpackagePlugin } from './plugins/subpackage'
@@ -52,6 +52,7 @@ export default (options: UniMiniProgramPluginOptions) => {
)
return [
+ uniInputAutoClosePlugin(),
...(isEnableConsole() ? [uniHBuilderXConsolePlugin('uni.__f__')] : []),
...(process.env.UNI_APP_X === 'true'
? [
diff --git a/packages/uni-mp-vite/src/plugins/input.ts b/packages/uni-mp-vite/src/plugins/input.ts
new file mode 100644
index 00000000000..0508ad308e9
--- /dev/null
+++ b/packages/uni-mp-vite/src/plugins/input.ts
@@ -0,0 +1,74 @@
+export function formatInputTag(code: string) {
+ const inputStartRegex = /]*)?>/g
+ let result = code
+ let match
+
+ // 收集所有标签的位置
+ const inputStarts: Array<{
+ index: number
+ length: number
+ fullMatch: string
+ }> = []
+ while ((match = inputStartRegex.exec(code)) !== null) {
+ inputStarts.push({
+ index: match.index,
+ length: match[0].length,
+ fullMatch: match[0],
+ })
+ }
+
+ // 检查每个是否有对应的
+ const toReplace: Array<{
+ index: number
+ length: number
+ replacement: string
+ }> = []
+ for (let i = 0; i < inputStarts.length; i++) {
+ const start = inputStarts[i]
+ const nextStart = inputStarts[i + 1]?.index || code.length
+ const substring = code.slice(start.index + start.length, nextStart)
+
+ // 检查是否有对应的
+ const hasClosingTag = substring.includes('')
+
+ // 检查是否是自闭合形式(以/>结尾)
+ const isSelfClosing = start.fullMatch.endsWith('/>')
+
+ if (!hasClosingTag && !isSelfClosing) {
+ // 需要转换的标签
+ toReplace.push({
+ index: start.index,
+ length: start.length,
+ replacement: start.fullMatch.replace(/>$/, ' />'),
+ })
+ }
+ }
+
+ // 从后往前替换,避免影响索引
+ for (let i = toReplace.length - 1; i >= 0; i--) {
+ const item = toReplace[i]
+ result =
+ result.slice(0, item.index) +
+ item.replacement +
+ result.slice(item.index + item.length)
+ }
+
+ return result
+}
+
+export function uniInputAutoClosePlugin() {
+ return {
+ name: 'uni:mp-input-auto-close',
+ enforce: 'pre',
+ transform(code, id) {
+ if (!/\.(vue|nvue|uvue)$/.test(id)) {
+ return
+ }
+ if (!code.includes('