feat: rename ty-form, add search form collapse, use UnoCSS @apply

- ty-schema-form 改名 ty-form
- ty-search-form 支持折叠/展开(collapseCount 默认 2,查询/重置置右)
- ty-table/ty-crud/ty-search-form 样式改用 @apply 工具类

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
liuyun 2026-10-11 09:27:55 +08:00
parent 45b22acd6c
commit 104b7678d3
7 changed files with 52 additions and 27 deletions

4
components.d.ts vendored
View File

@ -49,10 +49,10 @@ declare module 'vue' {
TyBreadcrumb: typeof import('./src/components/ty-breadcrumb.vue')['default'] TyBreadcrumb: typeof import('./src/components/ty-breadcrumb.vue')['default']
TyCrud: typeof import('./src/components/ty-crud.vue')['default'] TyCrud: typeof import('./src/components/ty-crud.vue')['default']
TyFooter: typeof import('./src/components/ty-footer.vue')['default'] TyFooter: typeof import('./src/components/ty-footer.vue')['default']
TyForm: typeof import('./src/components/ty-form.vue')['default']
TyFormDialog: typeof import('./src/components/ty-form-dialog.vue')['default'] TyFormDialog: typeof import('./src/components/ty-form-dialog.vue')['default']
TyHeader: typeof import('./src/components/ty-header.vue')['default'] TyHeader: typeof import('./src/components/ty-header.vue')['default']
TyMixbar: typeof import('./src/components/ty-mixbar.vue')['default'] TyMixbar: typeof import('./src/components/ty-mixbar.vue')['default']
TySchemaForm: typeof import('./src/components/ty-schema-form.vue')['default']
TySearchForm: typeof import('./src/components/ty-search-form.vue')['default'] TySearchForm: typeof import('./src/components/ty-search-form.vue')['default']
TySetting: typeof import('./src/components/ty-setting.vue')['default'] TySetting: typeof import('./src/components/ty-setting.vue')['default']
TySettingPanel: typeof import('./src/components/ty-setting-panel.vue')['default'] TySettingPanel: typeof import('./src/components/ty-setting-panel.vue')['default']
@ -104,10 +104,10 @@ declare global {
const TyBreadcrumb: typeof import('./src/components/ty-breadcrumb.vue')['default'] const TyBreadcrumb: typeof import('./src/components/ty-breadcrumb.vue')['default']
const TyCrud: typeof import('./src/components/ty-crud.vue')['default'] const TyCrud: typeof import('./src/components/ty-crud.vue')['default']
const TyFooter: typeof import('./src/components/ty-footer.vue')['default'] const TyFooter: typeof import('./src/components/ty-footer.vue')['default']
const TyForm: typeof import('./src/components/ty-form.vue')['default']
const TyFormDialog: typeof import('./src/components/ty-form-dialog.vue')['default'] const TyFormDialog: typeof import('./src/components/ty-form-dialog.vue')['default']
const TyHeader: typeof import('./src/components/ty-header.vue')['default'] const TyHeader: typeof import('./src/components/ty-header.vue')['default']
const TyMixbar: typeof import('./src/components/ty-mixbar.vue')['default'] const TyMixbar: typeof import('./src/components/ty-mixbar.vue')['default']
const TySchemaForm: typeof import('./src/components/ty-schema-form.vue')['default']
const TySearchForm: typeof import('./src/components/ty-search-form.vue')['default'] const TySearchForm: typeof import('./src/components/ty-search-form.vue')['default']
const TySetting: typeof import('./src/components/ty-setting.vue')['default'] const TySetting: typeof import('./src/components/ty-setting.vue')['default']
const TySettingPanel: typeof import('./src/components/ty-setting-panel.vue')['default'] const TySettingPanel: typeof import('./src/components/ty-setting-panel.vue')['default']

View File

@ -176,13 +176,11 @@ onMounted(fetchList)
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.ty-crud { .ty-crud__search {
&__search { @apply mb-md;
margin-bottom: 12px; }
}
&__toolbar { .ty-crud__toolbar {
margin-bottom: 12px; @apply mb-md;
}
} }
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<el-dialog v-model="visible" :title="title" :width="width" destroy-on-close @closed="onClosed"> <el-dialog v-model="visible" :title="title" :width="width" destroy-on-close @closed="onClosed">
<ty-schema-form <ty-form
ref="formRef" ref="formRef"
:schema="schema" :schema="schema"
:model-value="modelValue" :model-value="modelValue"

View File

@ -66,7 +66,7 @@ import { computed, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import type { JsonSchema, JsonSchemaProperty } from '@/types/crud' import type { JsonSchema, JsonSchemaProperty } from '@/types/crud'
defineOptions({ name: 'TySchemaForm' }) defineOptions({ name: 'TyForm' })
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{

View File

@ -1,29 +1,53 @@
<template> <template>
<ty-schema-form :schema="schema" :model-value="modelValue" inline> <ty-form :schema="visibleSchema" :model-value="modelValue" inline>
<el-form-item> <el-form-item class="ty-search-form__actions">
<el-button v-if="collapsible" link type="primary" @click="expanded = !expanded">
{{ expanded ? '收起' : '展开' }}
<icon type="iconify" :icon="expanded ? 'mdi:chevron-up' : 'mdi:chevron-down'" />
</el-button>
<el-button type="primary" @click="emit('search')">查询</el-button> <el-button type="primary" @click="emit('search')">查询</el-button>
<el-button @click="handleReset">重置</el-button> <el-button @click="handleReset">重置</el-button>
</el-form-item> </el-form-item>
</ty-schema-form> </ty-form>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { JsonSchema } from '@/types/crud' import { computed, ref } from 'vue'
import type { JsonSchema, JsonSchemaProperty } from '@/types/crud'
defineOptions({ name: 'TySearchForm' }) defineOptions({ name: 'TySearchForm' })
const props = defineProps<{ const props = withDefaults(
defineProps<{
/** 搜索字段 JSON Schema。 */ /** 搜索字段 JSON Schema。 */
schema: JsonSchema schema: JsonSchema
/** 搜索条件(就地绑定)。 */ /** 搜索条件(就地绑定)。 */
modelValue: Record<string, any> modelValue: Record<string, any>
}>() /** 折叠时展示的字段数量。 */
collapseCount?: number
}>(),
{
collapseCount: 2,
},
)
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'search'): void (e: 'search'): void
(e: 'reset'): void (e: 'reset'): void
}>() }>()
const expanded = ref(false)
const collapsible = computed(() => Object.keys(props.schema.properties).length > props.collapseCount)
const visibleSchema = computed<JsonSchema>(() => {
if (expanded.value || !collapsible.value) return props.schema
const keys = Object.keys(props.schema.properties).slice(0, props.collapseCount)
const properties: Record<string, JsonSchemaProperty> = {}
for (const k of keys) properties[k] = props.schema.properties[k]
return { type: 'object', properties, required: props.schema.required?.filter((r) => keys.includes(r)) }
})
function handleReset(): void { function handleReset(): void {
for (const [key, prop] of Object.entries(props.schema.properties)) { for (const [key, prop] of Object.entries(props.schema.properties)) {
props.modelValue[key] = prop.default ?? '' props.modelValue[key] = prop.default ?? ''
@ -31,3 +55,9 @@ function handleReset(): void {
emit('reset') emit('reset')
} }
</script> </script>
<style scoped lang="scss">
.ty-search-form__actions {
@apply ml-auto mb-0;
}
</style>

View File

@ -105,10 +105,7 @@ function formatDateTime(value: unknown): string {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.ty-table { .ty-table__pagination {
&__pagination { @apply mt-md justify-end;
margin-top: 12px;
justify-content: flex-end;
}
} }
</style> </style>

File diff suppressed because one or more lines are too long