mirror of
https://github.com/allinssl/allinssl.git
synced 2025-12-21 10:14:03 +08:00
【同步】前端项目源码
【修复】工作流兼容问题
This commit is contained in:
286
frontend/packages/vue/naive-ui/src/hooks/useTable.tsx
Normal file
286
frontend/packages/vue/naive-ui/src/hooks/useTable.tsx
Normal file
@@ -0,0 +1,286 @@
|
||||
import { ref, shallowRef, ShallowRef, Ref, effectScope, watch, onUnmounted, isRef, computed } from 'vue'
|
||||
import {
|
||||
type DataTableProps,
|
||||
type DataTableSlots,
|
||||
type PaginationProps,
|
||||
type PaginationSlots,
|
||||
NDataTable,
|
||||
NPagination,
|
||||
NButton,
|
||||
} from 'naive-ui'
|
||||
import { translation, TranslationModule, type TranslationLocale } from '../locals/translation'
|
||||
import { useMessage } from './useMessage'
|
||||
|
||||
import type {
|
||||
UseTableOptions,
|
||||
TableInstanceWithComponent,
|
||||
TableResponse,
|
||||
TablePageInstanceWithComponent,
|
||||
TablePageProps,
|
||||
} from '../types/table'
|
||||
|
||||
// 获取当前语言
|
||||
const currentLocale = localStorage.getItem('locale-active') || 'zhCN'
|
||||
|
||||
// 获取翻译文本
|
||||
const hookT = (key: string, params?: string) => {
|
||||
const locale = currentLocale.replace('-', '_').replace(/"/g, '') as TranslationLocale
|
||||
const translationFn =
|
||||
(translation[locale as TranslationLocale] as TranslationModule).useTable[
|
||||
key as keyof TranslationModule['useTable']
|
||||
] || translation.zhCN.useTable[key as keyof typeof translation.zhCN.useTable]
|
||||
return typeof translationFn === 'function' ? translationFn(params || '') : translationFn
|
||||
}
|
||||
/**
|
||||
* 表格钩子函数
|
||||
* @param options 表格配置选项
|
||||
* @returns 表格实例,包含表格状态和方法
|
||||
*/
|
||||
export default function useTable<T = Record<string, any>, Z extends Record<string, any> = Record<string, any>>({
|
||||
config, // 表格列配置
|
||||
request, // 数据请求函数
|
||||
defaultValue = ref({}) as Ref<Z>, // 默认请求参数,支持响应式
|
||||
watchValue = false, // 监听参数
|
||||
}: UseTableOptions<T, Z>) {
|
||||
const scope = effectScope() // 创建一个作用域,用于管理副作用
|
||||
|
||||
return scope.run(() => {
|
||||
// 表格状态
|
||||
const columns = shallowRef(config) // 表格列配置
|
||||
const loading = ref(false) // 加载状态
|
||||
const data = ref({ list: [], total: 0 }) as Ref<{ list: T[]; total: number }> // 表格数据
|
||||
const alias = ref({ total: 'total', list: 'list' }) // 表格别名
|
||||
const example = ref() // 表格引用
|
||||
const param = (isRef(defaultValue) ? defaultValue : ref({ ...(defaultValue as Z) })) as Ref<Z> // 表格请求参数
|
||||
const total = ref(0) // 分页参数
|
||||
const props = shallowRef({}) as ShallowRef<DataTableProps> // 表格属性
|
||||
// const watchData = ref([]) // 监听参数
|
||||
const { error: errorMsg } = useMessage()
|
||||
|
||||
/**
|
||||
* 获取表格数据
|
||||
*/
|
||||
const fetchData = async <T,>() => {
|
||||
try {
|
||||
loading.value = true
|
||||
const rdata: TableResponse<T> = await request(param.value)
|
||||
total.value = rdata[alias.value.total as keyof TableResponse<T>] as number
|
||||
data.value = {
|
||||
list: rdata[alias.value.list as keyof TableResponse<T>] as [],
|
||||
total: rdata[alias.value.total as keyof TableResponse<T>] as number,
|
||||
}
|
||||
return data.value
|
||||
} catch (error: any) {
|
||||
errorMsg(error.message)
|
||||
console.error('请求数据失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置表格状态和数据
|
||||
*/
|
||||
const reset = async <T,>() => {
|
||||
param.value = defaultValue.value
|
||||
return await fetchData<T>()
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染表格组件
|
||||
*/
|
||||
const component = (props: DataTableProps, context: { slots?: DataTableSlots }) => {
|
||||
const { slots, ...attrs } = props as any
|
||||
const s2 = context
|
||||
return (
|
||||
<NDataTable
|
||||
remote
|
||||
ref={example}
|
||||
loading={loading.value}
|
||||
data={data.value.list}
|
||||
columns={columns.value}
|
||||
{...props}
|
||||
{...attrs}
|
||||
>
|
||||
{{
|
||||
empty: () => (slots?.empty || s2?.slots?.empty ? slots?.empty() || s2?.slots?.empty() : null),
|
||||
loading: () => (slots?.loading || s2?.slots?.loading ? slots?.loading() || s2?.slots?.loading() : null),
|
||||
}}
|
||||
</NDataTable>
|
||||
)
|
||||
}
|
||||
|
||||
// 检测到参数变化时,重新请求数据
|
||||
if (Array.isArray(watchValue)) {
|
||||
// 只监听指定的字段
|
||||
const source = computed(() => watchValue.map((key) => param.value[key]))
|
||||
watch(source, fetchData, { deep: true })
|
||||
}
|
||||
// 检测到默认参数变化时,合并参数
|
||||
// watch(defaultValue, () => (param.value = { ...defaultValue.value, ...param.value }), { deep: true })
|
||||
|
||||
onUnmounted(() => {
|
||||
scope.stop() // 停止作用域
|
||||
}) // 清理副作用
|
||||
|
||||
// 返回表格实例
|
||||
return {
|
||||
loading,
|
||||
example,
|
||||
data,
|
||||
alias,
|
||||
param,
|
||||
total,
|
||||
reset: reset<T>,
|
||||
fetch: fetchData<T>,
|
||||
component,
|
||||
config: columns,
|
||||
props,
|
||||
}
|
||||
}) as TableInstanceWithComponent<T, Z>
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 扩展表格实例方法
|
||||
*/
|
||||
const useTablePage = <T extends Record<string, any> = Record<string, any>>({
|
||||
param,
|
||||
total,
|
||||
alias = { page: 'page', pageSize: 'page_size' }, // 字段别名映射
|
||||
props = {},
|
||||
slot = {},
|
||||
refresh = () => {},
|
||||
}: TablePageProps<T> & { refresh?: () => void }) => {
|
||||
const scope = effectScope() // 创建一个作用域,用于管理副作用
|
||||
return scope.run(() => {
|
||||
const { page, pageSize } = { ...{ page: 'page', pageSize: 'page_size' }, ...alias }
|
||||
const pageSizeOptionsRef = ref([10, 20, 50, 100, 200]) // 当前页码
|
||||
const propsRef = ref({ ...props })
|
||||
|
||||
// 如果分页参数不存在,则设置默认值
|
||||
if (!(param.value as Record<string, unknown>)[page]) {
|
||||
;(param.value as Record<string, unknown>)[page] = 1 // 当前页码
|
||||
}
|
||||
|
||||
// 如果分页参数不存在,则设置默认值
|
||||
if (!(param.value as Record<string, unknown>)[pageSize]) {
|
||||
;(param.value as Record<string, unknown>)[pageSize] = 20 // 每页条数
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 更新页码
|
||||
* @param {number} currentPage 当前页码
|
||||
*/
|
||||
const handlePageChange = (currentPage: number) => {
|
||||
param.value = {
|
||||
...param.value,
|
||||
[page]: currentPage,
|
||||
}
|
||||
if (refresh) {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 更新每页条数
|
||||
* @param {number} size 每页条数
|
||||
*/
|
||||
const handlePageSizeChange = (size: number) => {
|
||||
param.value = {
|
||||
...param.value,
|
||||
[page]: 1, // 重置页码为1
|
||||
[pageSize]: size,
|
||||
}
|
||||
if (refresh) {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染分页组件
|
||||
const component = (props: PaginationProps, context: { slots?: PaginationSlots }) => {
|
||||
// 处理插槽
|
||||
const mergedSlots = {
|
||||
...slot,
|
||||
...(context.slots || {}),
|
||||
}
|
||||
return (
|
||||
<NPagination
|
||||
page={param.value[page]}
|
||||
pageSize={param.value[pageSize]}
|
||||
itemCount={total.value}
|
||||
pageSizes={pageSizeOptionsRef.value}
|
||||
showSizePicker={true}
|
||||
onUpdatePage={handlePageChange}
|
||||
onUpdatePageSize={handlePageSizeChange}
|
||||
{...propsRef.value}
|
||||
{...props}
|
||||
v-slots={mergedSlots}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// 组件卸载
|
||||
onUnmounted(() => {
|
||||
scope.stop() // 停止作用域
|
||||
}) // 清理副作用
|
||||
|
||||
return {
|
||||
component,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
pageSizeOptions: pageSizeOptionsRef,
|
||||
}
|
||||
}) as TablePageInstanceWithComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 表格列hook--操作列
|
||||
*/
|
||||
const useTableOperation = (
|
||||
options: {
|
||||
title: string
|
||||
onClick: (row: any) => void
|
||||
isHide?: boolean | ((row: any) => boolean)
|
||||
}[],
|
||||
others?: any,
|
||||
) => {
|
||||
return {
|
||||
title: hookT('operation'),
|
||||
key: 'CreatedAt',
|
||||
width: 180,
|
||||
fixed: 'right' as const,
|
||||
align: 'right' as const,
|
||||
render: (row: any) => {
|
||||
const buttons: JSX.Element[] = []
|
||||
for (let index = 0; index < options.length; index++) {
|
||||
const option = options[index]
|
||||
const isHide =
|
||||
typeof option.isHide === 'function'
|
||||
? option.isHide(row)
|
||||
: typeof option.isHide === 'boolean'
|
||||
? option.isHide
|
||||
: false
|
||||
if (isHide) continue
|
||||
buttons.push(
|
||||
<NButton size="small" text type="primary" onClick={() => option.onClick(row)}>
|
||||
{option.title}
|
||||
</NButton>,
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex justify-end">
|
||||
{buttons.map((button, index) => (
|
||||
<>
|
||||
{button}
|
||||
{index < buttons.length - 1 && <span class="mx-[.8rem] text-[#dcdfe6]">|</span>}
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
...others,
|
||||
}
|
||||
}
|
||||
|
||||
export { useTablePage, useTableOperation }
|
||||
Reference in New Issue
Block a user