用 defineModel 重构部分组件

This commit is contained in:
Hooray Hu
2024-01-07 00:23:00 +08:00
parent 07c8a48386
commit 81d2b12515
13 changed files with 69 additions and 131 deletions
+5 -6
View File
@@ -12,7 +12,6 @@ const props = withDefaults(
headers?: UploadProps['headers']
data?: UploadProps['data']
name?: UploadProps['name']
url?: string
size?: number
width?: number
height?: number
@@ -22,7 +21,6 @@ const props = withDefaults(
}>(),
{
name: 'file',
url: '',
size: 2,
width: 150,
height: 150,
@@ -33,14 +31,15 @@ const props = withDefaults(
)
const emits = defineEmits<{
'update:url': [
url: string,
]
'onSuccess': [
res: any,
]
}>()
const url = defineModel<string>({
default: '',
})
const uploadData = ref({
imageViewerVisible: false,
progress: {
@@ -59,7 +58,7 @@ function previewClose() {
}
// 移除
function remove() {
emits('update:url', '')
url.value = ''
}
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
const fileName = file.name.split('.')
+8 -13
View File
@@ -12,7 +12,6 @@ const props = withDefaults(
headers?: UploadProps['headers']
data?: UploadProps['data']
name?: UploadProps['name']
url?: string[]
size?: number
max?: number
width?: number
@@ -23,7 +22,6 @@ const props = withDefaults(
}>(),
{
name: 'file',
url: () => [],
size: 2,
max: 3,
width: 150,
@@ -35,14 +33,15 @@ const props = withDefaults(
)
const emits = defineEmits<{
'update:url': [
url: string[],
]
'onSuccess': [
res: any,
]
}>()
const url = defineModel<string[]>({
default: [],
})
const uploadData = ref({
dialogImageIndex: 0,
imageViewerVisible: false,
@@ -63,20 +62,16 @@ function previewClose() {
}
// 移除
function remove(index: number) {
const url = props.url
url.splice(index, 1)
emits('update:url', url)
url.value.splice(index, 1)
}
// 移动
function move(index: number, type: 'left' | 'right') {
const url = props.url
if (type === 'left' && index !== 0) {
url[index] = url.splice(index - 1, 1, url[index])[0]
url.value[index] = url.value.splice(index - 1, 1, url.value[index])[0]
}
if (type === 'right' && index !== url.length - 1) {
url[index] = url.splice(index + 1, 1, url[index])[0]
if (type === 'right' && index !== url.value.length - 1) {
url.value[index] = url.value.splice(index + 1, 1, url.value[index])[0]
}
emits('update:url', url)
}
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
+9 -15
View File
@@ -8,10 +8,6 @@ defineOptions({
const props = withDefaults(
defineProps<{
modelValue: string[] | {
code: string
name: string
}[]
disabled?: boolean
type?: 'pc' | 'pca' | 'pcas'
format?: 'code' | 'name' | 'both'
@@ -23,14 +19,12 @@ const props = withDefaults(
},
)
const emits = defineEmits<{
'update:modelValue': [
value: string[] | {
code: string
name: string
}[],
]
}>()
const value = defineModel<string[] | {
code: string
name: string
}[]>({
default: [],
})
interface pcasItem {
code: string
@@ -88,11 +82,11 @@ const pcasData = computed(() => {
const myValue = computed({
// 将入参数据转成 code 码
get: () => {
return anyToCode(props.modelValue)
return anyToCode(value.value)
},
// 将 code 码转成出参数据
set: (value) => {
emits('update:modelValue', value ? codeToAny(value) : [])
set: (val) => {
value.value = val ? codeToAny(val) : []
},
})
+7 -15
View File
@@ -3,38 +3,30 @@ defineOptions({
name: 'SearchBar',
})
const props = withDefaults(
withDefaults(
defineProps<{
fold?: boolean
showToggle?: boolean
background?: boolean
}>(),
{
fold: true,
showToggle: true,
background: false,
},
)
const emits = defineEmits<{
'update:fold': [
value: boolean,
]
'toggle': [
value: boolean,
]
}>()
const isFold = ref(props.fold)
watch(() => props.fold, (value) => {
isFold.value = value
emits('update:fold', value)
const fold = defineModel<boolean>('fold', {
default: true,
})
function toggle() {
isFold.value = !isFold.value
emits('toggle', isFold.value)
fold.value = !fold.value
emits('toggle', fold.value)
}
</script>
@@ -45,10 +37,10 @@ function toggle() {
'px-4 bg-[var(--g-bg)] transition': background,
}"
>
<slot :fold="isFold" />
<slot :fold="fold" :toggle="toggle" />
<div v-if="showToggle" class="absolute bottom-0 left-0 w-full translate-y-1/2 text-center">
<button class="h-5 inline-flex cursor-pointer select-none items-center border-size-0 rounded bg-[var(--g-bg)] px-2 text-xs font-medium outline-none" @click="toggle">
<SvgIcon :name="isFold ? 'ep:caret-bottom' : 'ep:caret-top' " />
<SvgIcon :name="fold ? 'ep:caret-bottom' : 'ep:caret-top' " />
</button>
</div>
</div>
+5 -5
View File
@@ -1,7 +1,6 @@
<script setup lang="ts">
const props = withDefaults(
withDefaults(
defineProps<{
modelValue: string | number | undefined
options: {
label?: string | number
icon?: string
@@ -16,18 +15,19 @@ const props = withDefaults(
)
const emits = defineEmits<{
'update:modelValue': [string | number]
'change': [string | number | undefined]
}>()
watch(() => props.modelValue, (val) => {
const value = defineModel<string | number>()
watch(value, (val) => {
emits('change', val)
})
</script>
<template>
<div class="inline-flex select-none items-center justify-center of-hidden rounded-md bg-stone-3 dark:bg-stone-7">
<button v-for="option in options" :key="option.value" :disabled="disabled || option.disabled" class="flex cursor-pointer items-center truncate border-size-0 bg-inherit px-2 py-1.5 text-sm disabled:cursor-not-allowed disabled:opacity-50 hover:not-disabled:(bg-ui-primary text-ui-text)" :class="{ 'text-ui-text bg-ui-primary': modelValue === option.value }" @click="emits('update:modelValue', option.value)">
<button v-for="option in options" :key="option.value" :disabled="disabled || option.disabled" class="flex cursor-pointer items-center truncate border-size-0 bg-inherit px-2 py-1.5 text-sm disabled:cursor-not-allowed disabled:opacity-50 hover:not-disabled:(bg-ui-primary text-ui-text)" :class="{ 'text-ui-text bg-ui-primary': value === option.value }" @click="value = option.value">
<SvgIcon v-if="option.icon" :name="option.icon" />
<template v-else>
{{ option.label }}
+5 -13
View File
@@ -1,16 +1,14 @@
<script setup lang="ts">
import { Dialog, DialogDescription, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'
const props = withDefaults(
withDefaults(
defineProps<{
modelValue: boolean
appear?: boolean
title?: string
preventClose?: boolean
overlay?: boolean
}>(),
{
modelValue: false,
appear: false,
preventClose: false,
overlay: false,
@@ -18,10 +16,13 @@ const props = withDefaults(
)
const emits = defineEmits<{
'update:modelValue': [boolean]
'close': []
}>()
const isOpen = defineModel<boolean>({
default: false,
})
const slots = useSlots()
const overlayTransitionClass = ref({
@@ -44,15 +45,6 @@ const transitionClass = computed(() => {
}
})
const isOpen = computed({
get() {
return props.modelValue
},
set(value) {
emits('update:modelValue', value)
},
})
function close() {
isOpen.value = false
emits('close')
+3 -10
View File
@@ -1,7 +1,6 @@
<script setup lang="ts">
<script setup lang="ts" generic="T extends string | number">
withDefaults(
defineProps<{
modelValue: string
placeholder?: string
disabled?: boolean
}>(),
@@ -10,16 +9,10 @@ withDefaults(
},
)
const emits = defineEmits<{
'update:modelValue': [string]
}>()
const value = defineModel<T>()
const inputRef = ref()
function handleInput(event: Event) {
emits('update:modelValue', (event.target as HTMLInputElement).value)
}
defineExpose({
ref: inputRef,
})
@@ -27,6 +20,6 @@ defineExpose({
<template>
<div class="relative w-full lg:w-48">
<input ref="inputRef" type="text" :value="modelValue" :placeholder="placeholder" :disabled="disabled" class="relative block w-full border-0 rounded-md bg-white px-2.5 py-1.5 text-sm shadow-sm ring-1 ring-stone-2 ring-inset disabled:cursor-not-allowed dark:bg-dark disabled:opacity-50 focus:outline-none focus:ring-2 dark:ring-stone-8 focus:ring-ui-primary placeholder-stone-4 dark:placeholder-stone-5" @input="handleInput">
<input v-model="value" type="text" :placeholder="placeholder" :disabled="disabled" class="relative block w-full border-0 rounded-md bg-white px-2.5 py-1.5 text-sm shadow-sm ring-1 ring-stone-2 ring-inset disabled:cursor-not-allowed dark:bg-dark disabled:opacity-50 focus:outline-none focus:ring-2 dark:ring-stone-8 focus:ring-ui-primary placeholder-stone-4 dark:placeholder-stone-5">
</div>
</template>
+3 -6
View File
@@ -1,7 +1,6 @@
<script setup lang="ts">
const props = withDefaults(
defineProps<{
modelValue: string | number | undefined
options: {
label: string | number
value: string | number
@@ -14,16 +13,14 @@ const props = withDefaults(
},
)
const emits = defineEmits<{
'update:modelValue': [string | number]
}>()
const value = defineModel<string | number>()
const selected = computed({
get() {
return props.options.find(option => option.value === props.modelValue) ?? props.options[0]
return props.options.find(option => option.value === value.value) ?? props.options[0]
},
set(val) {
emits('update:modelValue', val.value)
value.value = val.value
},
})
</script>
+4 -12
View File
@@ -4,7 +4,6 @@ import { OverlayScrollbarsComponent } from 'overlayscrollbars-vue'
const props = withDefaults(
defineProps<{
modelValue: boolean
appear?: boolean
side?: 'left' | 'right'
title?: string
@@ -12,7 +11,6 @@ const props = withDefaults(
overlay?: boolean
}>(),
{
modelValue: false,
appear: false,
side: 'right',
preventClose: false,
@@ -21,10 +19,13 @@ const props = withDefaults(
)
const emits = defineEmits<{
'update:modelValue': [boolean]
'close': []
}>()
const isOpen = defineModel<boolean>({
default: false,
})
const slots = useSlots()
const overlayTransitionClass = ref({
@@ -47,15 +48,6 @@ const transitionClass = computed(() => {
}
})
const isOpen = computed({
get() {
return props.modelValue
},
set(value) {
emits('update:modelValue', value)
},
})
function close() {
isOpen.value = false
emits('close')
+7 -7
View File
@@ -2,7 +2,6 @@
import { Tab, TabGroup, TabList } from '@headlessui/vue'
const props = defineProps<{
modelValue: T
options: {
label: any
value: T
@@ -10,25 +9,26 @@ const props = defineProps<{
}>()
const emits = defineEmits<{
'update:modelValue': [T]
'change': [T]
}>()
const value = defineModel<T>()
const selectedIndex = computed({
get() {
return props.options.findIndex(option => option.value === props.modelValue)
return props.options.findIndex(option => option.value === value.value)
},
set(val) {
emits('update:modelValue', props.options[val].value)
value.value = props.options[val].value
},
})
watch(() => props.modelValue, (val) => {
emits('change', val)
watch(value, (val) => {
val && emits('change', val)
})
function handleChange(index: number) {
emits('update:modelValue', props.options[index].value)
value.value = props.options[index].value
}
</script>
+2 -14
View File
@@ -1,9 +1,8 @@
<script setup lang="ts">
import { Switch } from '@headlessui/vue'
const props = withDefaults(
withDefaults(
defineProps<{
modelValue?: boolean
disabled?: boolean
onIcon?: string
offIcon?: string
@@ -13,18 +12,7 @@ const props = withDefaults(
},
)
const emits = defineEmits<{
'update:modelValue': [boolean]
}>()
const enabled = computed({
get() {
return props.modelValue
},
set(value) {
emits('update:modelValue', value)
},
})
const enabled = defineModel<boolean>()
</script>
<template>
@@ -19,11 +19,7 @@ const search = ref({
check2: false,
})
const isFold = ref(false)
const isFold2 = ref(true)
const isFold3 = ref(true)
function toggle(fold: boolean) {
function onToggle(fold: boolean) {
ElMessage({
message: fold ? '收起' : '展开',
})
@@ -101,7 +97,7 @@ function toggle(fold: boolean) {
</SearchBar>
</PageMain>
<PageMain title="默认展开">
<SearchBar :fold="isFold">
<SearchBar :fold="false">
<template #default="{ fold }">
<ElForm :model="search" size="default" label-width="120px">
<ElRow>
@@ -235,7 +231,7 @@ function toggle(fold: boolean) {
</SearchBar>
</PageMain>
<PageMain title="切换事件">
<SearchBar @toggle="toggle">
<SearchBar @toggle="onToggle">
<template #default="{ fold }">
<ElForm :model="search" size="default" label-width="120px">
<ElRow>
@@ -302,8 +298,8 @@ function toggle(fold: boolean) {
</SearchBar>
</PageMain>
<PageMain title="自定义切换按钮(搭配自定义 grid 布局)">
<SearchBar :fold="isFold2" :show-toggle="false">
<template #default="{ fold }">
<SearchBar :show-toggle="false">
<template #default="{ fold, toggle }">
<ElForm :model="search" size="default" label-width="120px" inline class="search-form">
<ElFormItem label="姓名 / 手机号" class="search-form-item-name">
<ElInput v-model="search.name" placeholder="请输入姓名或者手机号,支持模糊查询" clearable />
@@ -343,7 +339,7 @@ function toggle(fold: boolean) {
</template>
筛选
</ElButton>
<ElButton link @click="isFold2 = !fold">
<ElButton link @click="toggle">
<template #icon>
<SvgIcon :name="fold ? 'ep:caret-bottom' : 'ep:caret-top' " />
</template>
@@ -355,8 +351,8 @@ function toggle(fold: boolean) {
</SearchBar>
</PageMain>
<PageMain title="自定义切换按钮(搭配 el-row 组件)">
<SearchBar :fold="isFold3" :show-toggle="false">
<template #default="{ fold }">
<SearchBar :show-toggle="false">
<template #default="{ fold, toggle }">
<ElForm :model="search" size="default" label-width="120px">
<ElRow>
<ElCol :span="12">
@@ -408,7 +404,7 @@ function toggle(fold: boolean) {
</template>
筛选
</ElButton>
<ElButton link @click="isFold3 = !fold">
<ElButton link @click="toggle">
<template #icon>
<SvgIcon :name="fold ? 'ep:caret-bottom' : 'ep:caret-top' " />
</template>
@@ -99,10 +99,10 @@ const tableData = [
</template>
</PageHeader>
<PageMain title="单图上传">
<ImageUpload v-model:url="image" action="https://console-mock.apipost.cn/app/mock/project/1f50f1da-5189-4282-d3c7-c133a514c5a8/upload/image" name="image" :width="250" :height="150" @on-success="handleSuccess1" />
<ImageUpload v-model="image" action="https://console-mock.apipost.cn/app/mock/project/1f50f1da-5189-4282-d3c7-c133a514c5a8/upload/image" name="image" :width="250" :height="150" @on-success="handleSuccess1" />
</PageMain>
<PageMain title="多图上传(默认最多3张)">
<ImagesUpload v-model:url="images" action="https://console-mock.apipost.cn/app/mock/project/1f50f1da-5189-4282-d3c7-c133a514c5a8/upload/image" name="image" @on-success="handleSuccess2" />
<ImagesUpload v-model="images" action="https://console-mock.apipost.cn/app/mock/project/1f50f1da-5189-4282-d3c7-c133a514c5a8/upload/image" name="image" @on-success="handleSuccess2" />
</PageMain>
<PageMain title="文件上传(默认最多3个)">
<FileUpload :files="files" action="https://console-mock.apipost.cn/app/mock/project/1f50f1da-5189-4282-d3c7-c133a514c5a8/upload/file" @on-success="handleSuccess3" />