Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions packages/vue-generator/src/generator/vue/sfc/generateAttribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ const handleJSExpressionBinding = (key, value, isJSX) => {
}

// expression 使用 v-bind 绑定
return `:${key}="${expressValue}"`
// 如果包含双引号,通过 " 编码避免与属性分隔符冲突
// 比如绑定的值为:[{ "name": "test" }]
// 则转换为: :key="[{ "name": "test" }]"
return `:${key}="${expressValue.replaceAll(/"/g, '"')}"`
Comment thread
chilingling marked this conversation as resolved.
}

const handleBindI18n = (key, value, isJSX) => {
Expand Down Expand Up @@ -176,13 +179,13 @@ export const handleLoopAttrHook = (schemaData = {}, globalHooks, config) => {
if (loop?.value && loop?.type) {
source = loop.value.replace(isJSX ? thisRegexp : thisPropsBindRe, '')
} else {
source = JSON.stringify(loop).replaceAll("'", "\\'").replaceAll(/"/g, "'")
source = JSON.stringify(loop)
}

const iterVar = [...loopArgs]

if (!isJSX) {
attributes.push(`v-for="(${iterVar.join(',')}) in ${source}"`)
attributes.push(`v-for="(${iterVar.join(',')}) in ${source.replaceAll(/"/g, '"')}"`)

return
}
Expand Down Expand Up @@ -455,7 +458,11 @@ const transformObjValue = (renderKey, value, globalHooks, config, transformObjTy
const result = { shouldBindToState: false, res: null }

if (typeof value === 'string') {
result.res = `${renderKey}"${value.replaceAll("'", "\\'").replaceAll(/"/g, "'")}"`
result.res = `${renderKey}"${value
.replaceAll(/\\/g, '\\\\')
.replaceAll(/\n/g, '\\n')
.replaceAll(/\r/g, '\\r')
.replaceAll(/"/g, '\\"')}"`

return result
}
Expand Down Expand Up @@ -575,15 +582,21 @@ export const handleObjBindAttrHook = (schemaData, globalHooks, config) => {
if (shouldBindToState && !isJSX) {
let stateKey = key
let addSuccess = globalHooks.addState(stateKey, `${stateKey}:${res}`)
let retryCount = 0

while (!addSuccess) {
while (!addSuccess && retryCount++ < 100) {
stateKey = `${key}${randomString()}`
addSuccess = globalHooks.addState(stateKey, `${stateKey}:${res}`)
}

attributes.push(`:${key}="state.${stateKey}"`)
if (addSuccess) {
attributes.push(`:${key}="state.${stateKey}"`)
} else {
// state 注册失败,回退到内联绑定
attributes.push(`:${key}="${res.replaceAll(/"/g, '&quot;')}"`)
}
} else {
attributes.push(isJSX ? `${key}={${res}}` : `:${key}="${res.replaceAll(/"/g, "'")}"`)
attributes.push(isJSX ? `${key}={${res}}` : `:${key}="${res.replaceAll(/"/g, '&quot;')}"`)
}

delete props[key]
Expand All @@ -600,7 +613,7 @@ export const handlePrimitiveAttributeHook = (schemaData, globalHooks, config) =>
const valueType = typeof value

if (valueType === 'string') {
attributes.push(`${key}="${value.replaceAll(/"/g, "'")}"`)
attributes.push(`${key}="${value.replaceAll(/"/g, '&quot;')}"`)

delete props[key]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const state = vue.reactive({
nullValue: null,
numberValue: 0,
emptyStr: '',
strVal: 'i am str.',
strVal: "i am 'str'.",
trueVal: true,
falseVal: false,
arrVal: [1, '2', { aaa: 'aaa' }, [3, 4], true, false],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
}
},
"strVal": {
"defaultValue": "i am str.",
"defaultValue": "i am 'str'.",
"accessor": {
"getter": {
"type": "JSFunction",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const { utils } = wrap(function () {
})()
const state = vue.reactive({
IconPlusSquare: utils.IconPlusSquare(),
theme: "{ 'id': 22, 'name': '@cloud/tinybuilder-theme-dark', 'description': '黑暗主题' }",
theme: '{ "id": 22, "name": "@cloud/tinybuilder-theme-dark", "description": "黑暗主题" }',
companyName: '',
companyOptions: null,
companyCity: '',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"componentName": "TinyButton",
"exportName": "Button",
"package": "@opentiny/vue",
"version": "^3.10.0",
"destructuring": true
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<div>
<tiny-grid :columns="state.columns"></tiny-grid>
</div>
</template>

<script setup lang="jsx">
import { Grid as TinyGrid } from '@opentiny/vue'
import * as vue from 'vue'
import { defineProps, defineEmits } from 'vue'
import { I18nInjectionKey } from 'vue-i18n'

const props = defineProps({})

const emit = defineEmits([])
const { t, lowcodeWrap, stores } = vue.inject(I18nInjectionKey).lowcode()
const wrap = lowcodeWrap(props, { emit })
wrap({ stores })

const state = vue.reactive({
columns: [
{ field: 'info', title: '信息', slots: { default: ({ row }, h) => <span title='{"key": "value"}'></span> } }
]
})
wrap({ state })
</script>
<style scoped></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<template>
<div>
<tiny-button
multilineJson='{
"name": "test",
"value": "data"
}'
:objWithMultiline="{ template: 'line1\nline2', label: 'normal' }"
></tiny-button>
</div>
</template>

<script setup>
import * as vue from 'vue'
import { defineProps, defineEmits } from 'vue'
import { I18nInjectionKey } from 'vue-i18n'

const props = defineProps({})

const emit = defineEmits([])
const { t, lowcodeWrap, stores } = vue.inject(I18nInjectionKey).lowcode()
const wrap = lowcodeWrap(props, { emit })
wrap({ stores })

const state = vue.reactive({})
wrap({ state })
</script>
<style scoped></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<template>
<div>
<tiny-button
noQuotes="plain text value"
onlyDouble='{"name": "test", "id": "main"}'
onlySingle="it's a 'test' value"
bothQuotes='She said "hello" and it&apos;s fine'
htmlAttr='class="primary" id="btn-1"'
emptyStr=""
></tiny-button>
</div>
</template>

<script setup>
import * as vue from 'vue'
import { defineProps, defineEmits } from 'vue'
import { I18nInjectionKey } from 'vue-i18n'

const props = defineProps({})

const emit = defineEmits([])
const { t, lowcodeWrap, stores } = vue.inject(I18nInjectionKey).lowcode()
const wrap = lowcodeWrap(props, { emit })
wrap({ stores })

const state = vue.reactive({})
wrap({ state })
</script>
<style scoped></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<template>
<div>
<tiny-button
jsonContent='{"key": "value", "nested": "data"}'
mixedQuotes="She said &quot;hello&quot; and he said 'hi'"
:filteredItems="state.items.filter((i) => i.name === 'test' && i.tag === 'featured')"
></tiny-button>
</div>
</template>

<script setup>
import * as vue from 'vue'
import { defineProps, defineEmits } from 'vue'
import { I18nInjectionKey } from 'vue-i18n'

const props = defineProps({})

const emit = defineEmits([])
const { t, lowcodeWrap, stores } = vue.inject(I18nInjectionKey).lowcode()
const wrap = lowcodeWrap(props, { emit })
wrap({ stores })

const state = vue.reactive({
items: [
{ name: 'test', tag: 'featured' },
{ name: 'hello', tag: 'normal' }
]
})
wrap({ state })
</script>
<style scoped></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<template>
<div>
<tiny-button
v-for="(item, index) in [
{
type: 'primary',
subStr: 'primary\'subStr\''
},
{
type: ''
},
{
type: 'info'
},
{
type: 'success'
},
{
type: 'warning'
},
{
type: 'danger'
}
]"
type="primary"
text="test"
subStr="pri&quot;ma&quot;ry'subStr'"
:customExpressionTest="{
value: [
{
defaultValue: '{&quot;class&quot;: &quot;test-class&quot;, &quot;id&quot;: &quot;test-id&quot;}'
}
]
}"
:customAttrTest="{
value: [
{
defaultValue:
'{&quot;class&quot;: &quot;test-class&quot;, &quot;id&quot;: &quot;test-id&quot;, &quot;class2&quot;: &quot;te\'st\'-class2&quot;}',
subStr: 'test-\'cl\'ass2'
}
]
}"
></tiny-button>
</div>
</template>

<script setup>
import * as vue from 'vue'
import { defineProps, defineEmits } from 'vue'
import { I18nInjectionKey } from 'vue-i18n'

const props = defineProps({})

const emit = defineEmits([])
const { t, lowcodeWrap, stores } = vue.inject(I18nInjectionKey).lowcode()
const wrap = lowcodeWrap(props, { emit })
wrap({ stores })

const state = vue.reactive({
customAttrTest: { value: [{ defaultValue: '{"class": "test-class", "id": "test-id"}' }] }
})
wrap({ state })
</script>
<style scoped></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"componentName": "TinyGrid",
"exportName": "Grid",
"package": "@opentiny/vue",
"version": "^3.10.0",
"destructuring": true
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"state": {
"columns": [
{
"field": "info",
"title": "信息",
"slots": {
"default": {
"type": "JSSlot",
"value": [
{
"componentName": "span",
"props": {
"title": "{\"key\": \"value\"}"
},
"id": "jsx-span-001",
"children": []
}
],
"params": ["row"]
}
}
}
]
},
"methods": {},
"componentName": "Page",
"css": "",
"props": {},
"lifeCycles": {},
"children": [
{
"componentName": "TinyGrid",
"props": {
"columns": {
"type": "JSExpression",
"value": "state.columns"
}
},
"id": "jsx-test-001",
"children": []
}
],
"fileName": "testJsxQuote"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"state": {},
"methods": {},
"componentName": "Page",
"css": "",
"props": {},
"lifeCycles": {},
"children": [
{
"componentName": "TinyButton",
"props": {
"multilineJson": "{\n \"name\": \"test\",\n \"value\": \"data\"\n}",
"objWithMultiline": {
"template": "line1\nline2",
"label": "normal"
}
},
"id": "multiline-test-001",
"children": []
}
],
"fileName": "testMultiline"
}
Loading
Loading