Template
1
0
mirror of https://github.com/un-pany/v3-admin-vite.git synced 2025-04-21 03:19:19 +08:00

refactor: 重写 useFetchSelect 与 useFullscreenLoading

This commit is contained in:
pany 2023-02-01 18:09:44 +08:00
parent a0058a174a
commit e270308f10
11 changed files with 179 additions and 175 deletions

View File

@ -0,0 +1,36 @@
/** 模拟接口响应数据 */
const SELECT_DATA = {
code: 0,
data: [
{
label: "苹果",
value: 1
},
{
label: "香蕉",
value: 2
},
{
label: "橘子",
value: 3,
disabled: true
}
],
message: "获取 Select 数据成功"
}
/** 模拟接口 */
export function getSelectDataApi() {
return new Promise<typeof SELECT_DATA>((resolve, reject) => {
// 模拟接口响应时间 2s
setTimeout(() => {
// 模拟接口调用成功
if (Math.random() < 0.8) {
resolve(SELECT_DATA)
} else {
// 模拟接口调用出错
reject(new Error("接口发生错误"))
}
}, 2000)
})
}

View File

@ -0,0 +1,24 @@
/** 模拟接口响应数据 */
const SUCCESS_DATA = {
code: 0,
data: {},
message: "获取成功"
}
/** 模拟请求接口成功 */
export function getSuccessApi() {
return new Promise<typeof SUCCESS_DATA>((resolve) => {
setTimeout(() => {
resolve(SUCCESS_DATA)
}, 1000)
})
}
/** 模拟请求接口失败 */
export function getErrorApi() {
return new Promise((_resolve, reject) => {
setTimeout(() => {
reject(new Error("发生错误"))
}, 1000)
})
}

View File

@ -1,53 +0,0 @@
export function getRemoteSelectData() {
return new Promise<any>((resolve, reject) => {
setTimeout(() => {
// 模拟接口调用有概率出错
if (Math.random() > 0.5) {
resolve({
code: 0,
data: [
{
key: 1,
label: "苹果",
value: 1
},
{
key: 2,
label: "香蕉",
value: 2
},
{
key: 3,
label: "橘子",
value: 3
}
],
message: "成功"
})
} else {
reject(new Error("不小心出错了!"))
}
}, 3000)
})
}
export interface IComicsItem {
id: number
name: string
}
export const getComics = () => {
return new Promise<IComicsItem[]>((resolve) => {
setTimeout(() => {
resolve([...Array(5)].map((_t, index) => ({ id: index, name: `c${index}` })))
}, 1000)
})
}
export const getAnimations = (id: number) => {
return new Promise((_resolve, reject) => {
setTimeout(() => {
reject(new Error(`Sorry, there is an error here. The error id is ${id}`))
}, 1000)
})
}

View File

@ -1,51 +1,44 @@
import { onMounted, ref } from "vue"
import { ref, onMounted } from "vue"
// 定义下拉框接收的数据格式
export interface SelectOption {
value: string
type OptionValueType = string | number
/** Select 需要的数据格式 */
interface ISelectOption {
value: OptionValueType
label: string
disabled?: boolean
key?: string
}
// 定义入参格式
interface FetchSelectProps {
apiFun: () => Promise<any>
/** 接口响应格式 */
interface IApiData {
code: number
data: ISelectOption[]
message: string
}
export function useFetchSelect(props: FetchSelectProps) {
const { apiFun } = props
/** 入参格式,暂时只需要传递 api 函数即可 */
interface IFetchSelectProps {
api: () => Promise<IApiData>
}
const options = ref<SelectOption[]>([])
export function useFetchSelect(props: IFetchSelectProps) {
const { api } = props
const loading = ref(false)
const loading = ref<boolean>(false)
const options = ref<ISelectOption[]>([])
const value = ref<OptionValueType>("")
const selectedValue = ref("")
/* 调用接口请求数据 */
/** 调用接口获取数据 */
const loadData = () => {
loading.value = true
options.value = []
return apiFun().then(
(res) => {
loading.value = false
api()
.then((res) => {
options.value = res.data
return res.data
},
(err) => {
// 未知错误,可能是代码抛出的错误,或是网络错误
})
.finally(() => {
loading.value = false
options.value = [
{
value: "-1",
label: err.message,
disabled: true
}
]
// 接着抛出错误
return Promise.reject(err)
}
)
})
}
onMounted(() => {
@ -53,8 +46,8 @@ export function useFetchSelect(props: FetchSelectProps) {
})
return {
options,
loading,
selectedValue
options,
value
}
}

View File

@ -1,53 +1,53 @@
import { ElLoading, LoadingOptions } from "element-plus"
import { type LoadingOptions, ElLoading } from "element-plus"
const defaultOption = {
const defaultOptions = {
lock: true,
text: "加载中...",
background: "rgba(0, 0, 0, 0.7)"
text: "加载中..."
}
interface ILoading {
interface ILoadingInstance {
close: () => void
}
/**
* fnloading
* fnloading
*
* 1. fn loading
* 2. resolve loading
* 1. fn loading
* 2. fn Promiseresolve reject loading
* 3. loading
* @param {*} fn
* @param options
* @param options LoadingOptions
* @returns Function
*/
export const useFullscreenLoading = <T>(
fn: (...args: any[]) => T,
export function useFullscreenLoading<T>(
fn: (...args: any[]) => T | Promise<T>,
options: LoadingOptions = {}
): ((...args: any[]) => Promise<T>) => {
let loading: ILoading | undefined
): (...args: any[]) => Promise<T> {
let loadingInstance: ILoadingInstance
const showLoading = (options: LoadingOptions) => {
loading = ElLoading.service(options)
loadingInstance = ElLoading.service(options)
}
const hideLoading = () => {
loading && loading.close()
loadingInstance && loadingInstance.close()
}
const _options = { ...defaultOption, ...options }
const newFn = (...args: any[]) => {
const _options = { ...defaultOptions, ...options }
return (...args: any[]) => {
try {
showLoading(_options)
const result = fn(...args)
const isPromise = result instanceof Promise
// 同步函数
if (!isPromise) {
hideLoading()
return Promise.resolve(result)
}
// Promise
return result
.then((res: any) => {
.then((res) => {
hideLoading()
return res
})
.catch((err: Error) => {
.catch((err) => {
hideLoading()
throw err
})
@ -56,5 +56,4 @@ export const useFullscreenLoading = <T>(
throw err
}
}
return newFn
}

View File

@ -25,7 +25,7 @@ const defaultPaginationData: IDefaultPaginationData = {
layout: "total, sizes, prev, pager, next, jumper"
}
export const usePagination = (_paginationData: IPaginationData = {}) => {
export function usePagination(_paginationData: IPaginationData = {}) {
/** 合并分页参数 */
const paginationData = reactive(Object.assign({ ...defaultPaginationData }, _paginationData))

View File

@ -190,19 +190,19 @@ export const constantRoutes: RouteRecordRaw[] = [
]
},
{
path: "/hooks",
path: "/hook-demo",
component: Layout,
redirect: "/hooks/use-fetch-select",
name: "Hooks",
redirect: "/hook-demo/use-fetch-select",
name: "HookDemo",
meta: {
title: "hooks",
title: "hook 示例",
elIcon: "Menu",
alwaysShow: true
},
children: [
{
path: "use-fetch-select",
component: () => import("@/views/hooks/use-fetch-select.vue"),
component: () => import("@/views/hook-demo/use-fetch-select.vue"),
name: "UseFetchSelect",
meta: {
title: "useFetchSelect"
@ -210,7 +210,7 @@ export const constantRoutes: RouteRecordRaw[] = [
},
{
path: "use-fullscreen-loading",
component: () => import("@/views/hooks/use-fullscreen-loading.vue"),
component: () => import("@/views/hook-demo/use-fullscreen-loading.vue"),
name: "UseFullscreenLoading",
meta: {
title: "useFullscreenLoading"

View File

@ -0,0 +1,20 @@
<script lang="ts" setup>
import { useFetchSelect } from "@/hooks/useFetchSelect"
import { getSelectDataApi } from "@/api/hook-demo/use-fetch-select"
const { loading, options, value } = useFetchSelect({
api: getSelectDataApi
})
</script>
<template>
<div class="app-container">
<h4>该示例是演示通过 hook 自动调用 api 后拿到 Select 组件需要的数据并传递给 Select 组件</h4>
<h5>Select 示例</h5>
<el-select :loading="loading" v-model="value" filterable>
<el-option v-for="(item, index) in options" v-bind="item" :key="index" placeholder="请选择" />
</el-select>
<h5>Select V2 示例如果数据量过多可以选择该组件</h5>
<el-select-v2 :loading="loading" v-model="value" :options="options" filterable placeholder="请选择" />
</div>
</template>

View File

@ -0,0 +1,44 @@
<script lang="ts" setup>
import { useFullscreenLoading } from "@/hooks/useFullscreenLoading"
import { getSuccessApi, getErrorApi } from "@/api/hook-demo/use-fullscreen-loading"
import { ElMessage } from "element-plus"
const svg = `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`
const options = {
text: "即将发生错误...",
background: "#F56C6C20",
svg,
svgViewBox: "-10, -10, 50, 50"
}
const querySuccess = async () => {
const res = await useFullscreenLoading(getSuccessApi)()
ElMessage.success(res.message)
}
const queryError = async () => {
try {
await useFullscreenLoading(getErrorApi, options)()
} catch (err: any) {
ElMessage.error(err.message)
}
}
</script>
<template>
<div class="app-container">
<h4>该示例是演示通过将要执行的函数传递给 hook hook 自动开启全屏 loading函数执行结束后自动关闭 loading</h4>
<el-button @click="querySuccess">查询成功</el-button>
<el-button @click="queryError">查询失败</el-button>
</div>
</template>

View File

@ -1,18 +0,0 @@
<script setup name="Select" lang="ts">
import { getRemoteSelectData } from "@/api/mock"
import { useFetchSelect } from "@/hooks/useFetchSelect"
const { loading, options, selectedValue } = useFetchSelect({
apiFun: getRemoteSelectData
})
</script>
<template>
<div class="app-container">
<el-select :loading="loading" v-model="selectedValue">
<el-option v-for="(item, index) in options" v-bind="item" :key="index" />
</el-select>
<span class="m-x">Select V2 示例</span>
<el-select-v2 :loading="loading" v-model="selectedValue" :options="options" />
</div>
</template>

View File

@ -1,41 +0,0 @@
<template>
<div class="app-container">
<el-button @click="querySuccess">查询成功</el-button>
<el-button @click="queryFailed">查询失败</el-button>
</div>
</template>
<script lang="ts" setup>
import { getComics, getAnimations, type IComicsItem } from "@/api/mock"
import { useFullscreenLoading } from "@/hooks/useFullscreenLoading"
import { ElMessage } from "element-plus"
const querySuccess = async () => {
const comics = await useFullscreenLoading(getComics)()
ElMessage.success("Successfully get comics: " + comics.map((t: IComicsItem) => t.name).join())
}
const svg = `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`
const queryFailed = async () => {
try {
await useFullscreenLoading(getAnimations, {
text: "自定义加载文字",
background: "rgba(255, 214, 210, 0.7)",
svg,
svgViewBox: "-10, -10, 50, 50"
})(233)
} catch (err: any) {
ElMessage.error(err.message)
}
}
</script>