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
5 changes: 4 additions & 1 deletion packages/vue-generator/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { rules } = require('../../.eslintrc')

/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

Expand All @@ -17,5 +19,6 @@ module.exports = {
}
},
// 忽略 expected 中的内容
ignorePatterns: ['**/**/expected/*', '**/**.ts']
ignorePatterns: ['**/**/expected/*', '**/**.ts'],
rules
}
4 changes: 3 additions & 1 deletion packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
handleI18nAttrHook,
handleObjBindAttrHook,
handleEventAttrHook,
handleTinyIconPropsHook
handleTinyIconPropsHook,
handleJsxModelValueUpdate
} from './generateAttribute'
import {
GEN_SCRIPT_HOOKS,
Expand Down Expand Up @@ -213,6 +214,7 @@ export const genSFCWithDefaultPlugin = (schema, componentsMap, config = {}) => {

const defaultAttributeHook = [
handleTinyGrid,
handleJsxModelValueUpdate,
handleConditionAttrHook,
handleLoopAttrHook,
handleSlotBindAttrHook,
Expand Down
25 changes: 25 additions & 0 deletions packages/vue-generator/src/generator/vue/sfc/generateAttribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -515,3 +515,28 @@ export const handleBindUtilsHooks = (schemaData, globalHooks, config) => {
}
})
}

/**
* 为 modelvalue 绑定自动生成 onUpdate:modelValue 事件绑定
* @param {*} schemaData
* @param {*} globalHooks
* @param {*} config
*/
export const handleJsxModelValueUpdate = (schemaData, globalHooks, config) => {
const { schema: { props = {} } = {} } = schemaData || {}
const isJSX = config.isJSX

if (!isJSX) {
return
}

const propsEntries = Object.entries(props)
const modelValue = propsEntries.find(([_key, value]) => value?.type === JS_EXPRESSION && value?.model === true)
const hasUpdateModelValue = propsEntries.find(([key]) => isOn(key) && key.startsWith(`onUpdate:${modelValue?.[0]}`))

// jsx 形式的 modelvalue, 如果 schema 没有声明,出码需要同时声明 onUpdate:modelValue,否则更新失效
if (modelValue && !hasUpdateModelValue) {
// 添加 onUpdate:modelKey 事件,让后续钩子生成 对应的事件声明
props[`onUpdate:${modelValue?.[0]}`] = { type: JS_EXPRESSION, value: `(value) => ${modelValue[1].value}=value` }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"componentName": "TinyGrid",
"exportName": "Grid",
"package": "@opentiny/vue",
"version": "^3.10.0",
"destructuring": true
},
{
"componentName": "TinyNumeric",
"exportName": "Numeric",
"package": "@opentiny/vue",
"version": "^3.10.0",
"destructuring": true
},
{
"componentName": "TinyInput",
"exportName": "Input",
"package": "@opentiny/vue",
"version": "^3.10.0",
"destructuring": true
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<template>
<div>
<div>
<tiny-grid
:editConfig="{ trigger: 'click', mode: 'cell', showStatus: true }"
:columns="state.columns"
:data="[
{
id: '1',
name: 'GFD科技有限公司',
city: '福州',
employees: 800,
created_date: '2014-04-30 00:56:00',
boole: false
},
{
id: '2',
name: 'WWW科技有限公司',
city: '深圳',
employees: 300,
created_date: '2016-07-08 12:36:22',
boole: true
}
]"
></tiny-grid>
</div>
</div>
</template>

<script setup lang="jsx">
import { Grid as TinyGrid, Numeric as TinyNumeric, Input as TinyInput } 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: [
{ type: 'index', width: 60 },
{ type: 'selection', width: 60 },
{
field: 'employees',
title: '员工数',
slots: {
default: ({ row, column, rowIndex }, h) => (
<div>
<TinyNumeric
allow-empty={true}
placeholder="请输入"
controlsPosition="right"
step={1}
modelValue={row.employees}
onChange={(...eventArgs) => onChangeNumber(eventArgs, row, column, rowIndex)}
onUpdate:modelValue={(value) => (row.employees = value)}
></TinyNumeric>
</div>
)
}
},
{ field: 'created_date', title: '创建日期' },
{
field: 'city',
title: '城市',
slots: {
default: ({ row, column, rowIndex }, h) => (
<div>
<TinyInput
placeholder="请输入"
modelValue={row.city}
onChange={(...eventArgs) => onChangeInput(eventArgs, row, column, rowIndex)}
onUpdate:modelValue={(value) => (row.city = value)}
></TinyInput>
</div>
)
}
}
]
})
wrap({ state })

const onChangeInput = wrap(function onChangeInput(eventArgs, args0, args1, args2) {
console.log('onChangeInput', eventArgs)
})
const onChangeNumber = wrap(function onChangeNumber(eventArgs, args0, args1, args2) {
console.log('onChangeNumber', eventArgs)
})

wrap({ onChangeInput, onChangeNumber })
</script>
<style scoped></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
{
"state": {},
"methods": {
"onChangeInput": {
"type": "JSFunction",
"value": "function onChangeInput(eventArgs,args0,args1,args2) {\n console.log('onChangeInput', eventArgs);\n}"
},
"onChangeNumber": {
"type": "JSFunction",
"value": "function onChangeNumber(eventArgs, args0, args1, args2) {\n console.log('onChangeNumber', eventArgs)\n}"
}
},
"componentName": "Page",
"css": "",
"props": {},
"lifeCycles": {},
"children": [
{
"componentName": "div",
"props": {},
"id": "85375559",
"children": [
{
"componentName": "TinyGrid",
"props": {
"editConfig": {
"trigger": "click",
"mode": "cell",
"showStatus": true
},
"columns": [
{
"type": "index",
"width": 60
},
{
"type": "selection",
"width": 60
},
{
"field": "employees",
"title": "员工数",
"slots": {
"default": {
"type": "JSSlot",
"value": [
{
"componentName": "div",
"id": "44523622",
"children": [
{
"componentName": "TinyNumeric",
"props": {
"allow-empty": true,
"placeholder": "请输入",
"controlsPosition": "right",
"step": 1,
"modelValue": {
"type": "JSExpression",
"value": "row.employees",
"model": true
},
"onChange": {
"type": "JSExpression",
"value": "this.onChangeNumber",
"params": ["row", "column", "rowIndex"]
}
},
"id": "62166343"
}
]
}
],
"params": ["row", "column", "rowIndex"]
}
}
},
{
"field": "created_date",
"title": "创建日期"
},
{
"field": "city",
"title": "城市",
"slots": {
"default": {
"type": "JSSlot",
"value": [
{
"componentName": "div",
"id": "66326314",
"children": [
{
"componentName": "TinyInput",
"props": {
"placeholder": "请输入",
"modelValue": {
"type": "JSExpression",
"value": "row.city",
"model": true
},
"onChange": {
"type": "JSExpression",
"value": "this.onChangeInput",
"params": ["row", "column", "rowIndex"]
}
},
"id": "22396a2a"
}
]
}
],
"params": ["row", "column", "rowIndex"]
}
}
}
],
"data": [
{
"id": "1",
"name": "GFD科技有限公司",
"city": "福州",
"employees": 800,
"created_date": "2014-04-30 00:56:00",
"boole": false
},
{
"id": "2",
"name": "WWW科技有限公司",
"city": "深圳",
"employees": 300,
"created_date": "2016-07-08 12:36:22",
"boole": true
}
]
},
"id": "63623253"
}
]
}
],
"dataSource": {
"list": []
},
"utils": [],
"bridge": [],
"inputs": [],
"outputs": [],
"fileName": "slotModelValueTest"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { expect, test } from 'vitest'
import { genSFCWithDefaultPlugin } from '@/generator/vue/sfc'
import schema from './page.schema.json'
import componentsMap from './components-map.json'
import { formatCode } from '@/utils/formatCode'

test('should generate onUpdate:modelValue event', async () => {
const res = genSFCWithDefaultPlugin(schema, componentsMap)
const formattedCode = formatCode(res, 'vue')

await expect(formattedCode).toMatchFileSnapshot('./expected/slotModelValueTest.vue')
})