fix:remove ElForm
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -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<{
|
||||
|
||||
@@ -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>
|
||||
|
||||
44
src/pages/pc/components/base/form/Form.vue
Normal file
44
src/pages/pc/components/base/form/Form.vue
Normal 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>
|
||||
73
src/pages/pc/components/base/form/FormItem.vue
Normal file
73
src/pages/pc/components/base/form/FormItem.vue
Normal 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>
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user