fix:remove ElForm

This commit is contained in:
zyronon
2025-08-17 01:48:58 +08:00
parent fd4fcf5473
commit 82b8ed8039
6 changed files with 158 additions and 40 deletions

View File

@@ -2,8 +2,6 @@
import {Dict, DictId, DictType} from "@/types/types.ts";
import {cloneDeep} from "@/utils";
import {ElForm, ElFormItem, FormInstance, FormRules} from "element-plus";
import Toast from '@/pages/pc/components/base/toast/Toast.ts'
import {onMounted, reactive} from "vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
@@ -12,6 +10,8 @@ import BaseButton from "@/components/BaseButton.vue";
import {getDefaultDict} from "@/types/func.ts";
import {Option, Select} from "@/pages/pc/components/base/select";
import BaseInput from "@/pages/pc/components/base/BaseInput.vue";
import Form from "@/pages/pc/components/base/form/Form.vue";
import FormItem from "@/pages/pc/components/base/form/FormItem.vue";
const props = defineProps<{
isAdd: boolean,
@@ -34,8 +34,8 @@ const DefaultDictForm = {
type: DictType.article
}
let dictForm: any = $ref(cloneDeep(DefaultDictForm))
const dictFormRef = $ref<FormInstance>()
const dictRules = reactive<FormRules>({
const dictFormRef = $ref()
const dictRules = reactive({
name: [
{required: true, message: '请输入名称', trigger: 'blur'},
{max: 20, message: '名称不能超过20个字符', trigger: 'blur'},
@@ -94,38 +94,38 @@ onMounted(() => {
<template>
<div class="w-120 mt-4">
<ElForm
<Form
ref="dictFormRef"
:rules="dictRules"
:model="dictForm"
label-width="8rem">
<ElFormItem label="名称" prop="name">
<FormItem label="名称" prop="name">
<BaseInput v-model="dictForm.name"/>
</ElFormItem>
<ElFormItem label="描述">
</FormItem>
<FormItem label="描述">
<BaseInput v-model="dictForm.description" textarea/>
</ElFormItem>
<ElFormItem label="原文语言">
</FormItem>
<FormItem label="原文语言">
<Select v-model="dictForm.language" placeholder="请选择选项">
<Option label="英语" value="en"/>
<Option label="德语" value="de"/>
<Option label="日语" value="ja"/>
<Option label="代码" value="code"/>
</Select>
</ElFormItem>
<ElFormItem label="译文语言">
</FormItem>
<FormItem label="译文语言">
<Select v-model="dictForm.translateLanguage" placeholder="请选择选项">
<Option label="中文" value="zh-CN"/>
<Option label="英语" value="en"/>
<Option label="德语" value="de"/>
<Option label="日语" value="ja"/>
</Select>
</ElFormItem>
</FormItem>
<div class="center">
<base-button type="info" @click="emit('close')">关闭</base-button>
<base-button type="primary" @click="onSubmit">确定</base-button>
</div>
</ElForm>
</Form>
</div>
</template>

View File

@@ -3,7 +3,6 @@
import {Word} from "@/types/types.ts";
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
import {usePlayWordAudio} from "@/hooks/sound.ts";
import {ElPopover} from 'element-plus'
import Tooltip from "@/pages/pc/components/base/Tooltip.vue";
const props = withDefaults(defineProps<{

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import {ref, watch, defineProps, defineEmits, useAttrs} from 'vue';
import {ref, watch, defineProps, defineEmits, useAttrs, onMounted} from 'vue';
const props = defineProps({
modelValue: [String, Number],
@@ -70,6 +70,7 @@ const clearInput = () => {
validate('');
emit('update:modelValue', '');
};
</script>
<template>

View File

@@ -0,0 +1,44 @@
<template>
<form @submit.prevent>
<slot/>
</form>
</template>
<script setup lang="ts">
import {ref, provide, watch, toRef} from 'vue'
interface Field {
prop: string
modelValue: any
validate: (rules: any[]) => boolean
}
const props = defineProps({
model: Object,
rules: Object // { word: [{required:true,...}, ...], name: [...] }
})
const fields = ref<Field[]>([])
const registerField = (field: Field) => {
fields.value.push(field)
}
// 校验整个表单
const validate = (cb): boolean => {
let valid = true
fields.value.forEach(f => {
const fieldRules = props.rules?.[f.prop] || []
const res = f.validate(fieldRules)
if (!res) valid = false
})
cb(valid)
}
provide('registerField', registerField)
provide('formModel', toRef(props, 'model'))
provide('formValidate', validate)
provide('formRules', props.rules)
defineExpose({validate})
</script>

View File

@@ -0,0 +1,73 @@
<script setup lang="tsx">
import {inject, onMounted, ref, useSlots} from 'vue'
const props = defineProps({
prop: String,
label: String,
})
const value = ref('')
let error = $ref('')
// 拿到 form 的 model 和注册函数
const formModel = inject<ref>('formModel')
const registerField = inject('registerField')
const formRules = inject('formRules', {})
const myRules = $computed(() => {
return formRules?.[props.prop] || []
})
// 校验函数
const validate = (rules) => {
error = ''
const val = formModel.value[props.prop]
for (const rule of rules) {
if (rule.required && (!val || !val.toString().trim())) {
error = rule.message
return false
}
if (rule.max && val && val.toString().length > rule.max) {
error = rule.message
return false
}
}
return true
}
// 自动触发 blur 校验
const handleBlur = () => {
const blurRules = myRules.filter((r) => r.trigger === 'blur')
if (blurRules.length) validate(blurRules)
}
// 注册到 Form
onMounted(() => {
registerField && registerField({prop: props.prop, modelValue: value, validate})
})
let slot = useSlots()
defineRender(() => {
let DefaultNode = slot.default()[0]
return <div class="form-item mb-6 flex gap-space">
{props.label &&
<label class="w-20 flex items-start mt-1 justify-end">
{myRules.length ? <span class="form-error">*</span> : null} {props.label}
</label>}
<div class="flex-1 relative">
<DefaultNode onBlur={handleBlur}/>
<div class="form-error absolute top-[100%] anim" style={{opacity: error ? 1 : 0}}>{error}</div>
</div>
</div>
})
</script>
<style scoped lang="scss">
.form-item {
.form-error {
color: #f56c6c;
font-size: 0.8rem;
}
}
</style>

View File

@@ -9,8 +9,6 @@ import {nanoid} from "nanoid";
import BaseIcon from "@/components/BaseIcon.vue";
import BaseTable from "@/pages/pc/components/BaseTable.vue";
import WordItem from "@/pages/pc/components/WordItem.vue";
import type {FormInstance, FormRules} from "element-plus";
import {ElForm, ElFormItem} from "element-plus";
import Toast from '@/pages/pc/components/base/toast/Toast.ts'
import PopConfirm from "@/pages/pc/components/PopConfirm.vue";
import BackIcon from "@/pages/pc/components/BackIcon.vue";
@@ -21,6 +19,8 @@ import EditBook from "@/pages/pc/article/components/EditBook.vue";
import {getDefaultDict} from "@/types/func.ts";
import BaseInput from "@/pages/pc/components/base/BaseInput.vue";
import Textarea from "@/pages/pc/components/base/Textarea.vue";
import FormItem from "@/pages/pc/components/base/form/FormItem.vue";
import Form from "@/pages/pc/components/base/form/Form.vue";
const runtimeStore = useRuntimeStore()
const base = useBaseStore()
@@ -54,8 +54,8 @@ const getDefaultFormWord = () => {
}
let isOperate = $ref(false)
let wordForm = $ref(getDefaultFormWord())
let wordFormRef = $ref<FormInstance>()
const wordRules = reactive<FormRules>({
let wordFormRef = $ref()
const wordRules = reactive({
word: [
{required: true, message: '请输入单词', trigger: 'blur'},
{max: 30, message: '名称不能超过30个字符', trigger: 'blur'},
@@ -83,6 +83,7 @@ function syncDictInMyStudyList(study = false) {
}
async function onSubmitWord() {
// return console.log('wordFormRef',wordFormRef,wordFormRef.validate)
await wordFormRef.validate((valid) => {
if (valid) {
let data: any = convertToWord(wordForm)
@@ -264,72 +265,72 @@ defineRender(() => {
<div class="common-title">
{wordForm.id ? '修改' : '添加'}单词
</div>
<ElForm
<Form
class="flex-1 overflow-auto pr-2"
ref={e => wordFormRef = e}
rules={wordRules}
model={wordForm}
label-width="7rem">
<ElFormItem label="单词" prop="word">
<FormItem label="单词" prop="word">
<BaseInput
modelValue={wordForm.word}
onUpdate:modelValue={e => wordForm.word = e}
/>
</ElFormItem>
<ElFormItem label="英音音标">
</FormItem>
<FormItem label="英音音标">
<BaseInput
modelValue={wordForm.phonetic0}
onUpdate:modelValue={e => wordForm.phonetic0 = e}
/>
</ElFormItem>
<ElFormItem label="美音音标">
</FormItem>
<FormItem label="美音音标">
<BaseInput
modelValue={wordForm.phonetic1}
onUpdate:modelValue={e => wordForm.phonetic1 = e}/>
</ElFormItem>
<ElFormItem label="翻译">
</FormItem>
<FormItem label="翻译">
<Textarea
modelValue={wordForm.trans}
onUpdate:modelValue={e => wordForm.trans = e}
placeholder="一行一个翻译前面词性后面内容如n.取消);多个翻译请换行"
autosize={{minRows: 6, maxRows: 10}}/>
</ElFormItem>
<ElFormItem label="例句">
</FormItem>
<FormItem label="例句">
<Textarea
modelValue={wordForm.sentences}
onUpdate:modelValue={e => wordForm.sentences = e}
placeholder="一行原文,一行译文;多个请换两行"
autosize={{minRows: 6, maxRows: 10}}/>
</ElFormItem>
<ElFormItem label="短语">
</FormItem>
<FormItem label="短语">
<Textarea
modelValue={wordForm.phrases}
onUpdate:modelValue={e => wordForm.phrases = e}
placeholder="一行原文,一行译文;多个请换两行"
autosize={{minRows: 6, maxRows: 10}}/>
</ElFormItem>
<ElFormItem label="同义词">
</FormItem>
<FormItem label="同义词">
<Textarea
modelValue={wordForm.synos}
onUpdate:modelValue={e => wordForm.synos = e}
placeholder="请参考已有单词格式"
autosize={{minRows: 6, maxRows: 20}}/>
</ElFormItem>
<ElFormItem label="同根词">
</FormItem>
<FormItem label="同根词">
<Textarea
modelValue={wordForm.relWords}
onUpdate:modelValue={e => wordForm.relWords = e}
placeholder="请参考已有单词格式"
autosize={{minRows: 6, maxRows: 20}}/>
</ElFormItem>
<ElFormItem label="词源">
</FormItem>
<FormItem label="词源">
<Textarea
modelValue={wordForm.etymology}
onUpdate:modelValue={e => wordForm.etymology = e}
placeholder="请参考已有单词格式"
autosize={{minRows: 6, maxRows: 10}}/>
</ElFormItem>
</ElForm>
</FormItem>
</Form>
<div class="center">
<BaseButton
type="info"