Skip to content

Instantly share code, notes, and snippets.

@NanamiNakano
Created June 20, 2024 11:07
Show Gist options
  • Save NanamiNakano/59858f5b733c40b3341d96110ac7f40d to your computer and use it in GitHub Desktop.
Save NanamiNakano/59858f5b733c40b3341d96110ac7f40d to your computer and use it in GitHub Desktop.
Forward Rule Page
<script setup lang="ts">
import type { QueryParams, RuleData } from "@nanaminakano/pfsdk"
const pfClient = usePfClient()
const toast = useToast()
const loading = ref(true)
const tableRowData = ref<RuleData[]>([])
const tableColumns = [{
key: "id",
label: "#",
}, {
key: "user_id",
label: "User ID",
}, {
key: "node_id",
label: "Node ID",
}, {
key: "name",
label: "Name",
}, {
key: "mode",
label: "Mode",
}, {
key: "protocol",
label: "Protocol",
}, {
key: "bind",
label: "Bound Port",
}, {
key: "targets",
label: "Targets",
}, {
key: "proxy_protocol",
label: "Proxy Protocol",
}, {
key: "status",
label: "Status",
}, {
key: "actions",
}]
const actionItems = (row: RuleData) => [
[{
label: "Edit",
icon: "i-tabler-pencil",
click: () => { slideIsOpen.value = true },
}, {
label: "Delete",
icon: "i-tabler-trash",
click: async () => {
loading.value = true
await pfClient.forwardRule.delete(row.id).then(async (rps) => {
if (rps.Ok) {
toast.add({ title: "Delete successfully" })
await fetchAll()
}
else {
toast.add({ title: "Delete unsuccessfully", description: rps.Msg, color: "red" })
}
})
loading.value = false
},
}]]
const selected = ref<RuleData[]>([])
const slideIsOpen = ref(false)
async function fetchAll(query?: QueryParams) {
loading.value = true
await pfClient.forwardRule.getRuleList(query).then((rps) => {
if (rps.Ok) {
tableRowData.value = rps.Data!
}
else {
tableRowData.value = []
toast.add({ title: "Unable to load data", color: "red" })
}
})
loading.value = false
}
const filters = tableColumns.filter(item => !!item.label) as { key: string, label: string }[]
onMounted(async () => {
await fetchAll()
})
</script>
<template>
<div class="flex flex-col space-y-2">
<div class="flex space-x-2">
<UButton label="New" />
</div>
<RSearch
:filters="filters"
@search="(params) => { fetchAll(params) }"
/>
<RTable
v-model="selected"
:row-data="tableRowData"
:columns="tableColumns"
:actions="actionItems"
:loading="loading"
/>
<USlideover v-model="slideIsOpen">
<div class="p-4 flex-1">
<UButton
color="gray"
variant="ghost"
size="sm"
icon="i-tabler-x"
class="flex sm:hidden absolute end-5 top-5 z-10"
square
padded
@click="slideIsOpen = false"
/>
<USkeleton class="h-full" />
</div>
</USlideover>
</div>
</template>
<script setup lang="ts">
import type { QueryParams } from "@nanaminakano/pfsdk"
const props = defineProps<{
filters: {
key: string
label: string
select?: { key: number, label: string }[]
}[]
}>()
const emit = defineEmits(["search"])
const search = ref("")
const precisely = ref(true)
const params = computed(() => {
return {
filter: props.filters[filterKey.value].key,
search: search.value,
exact: precisely.value,
} as QueryParams
})
const filterKey = ref(0)
const dropdown = computed(() => {
return props.filters.map((item, index) => ({
key: item.key,
label: item.label,
click: () => {
filterKey.value = index
if (props.filters[index].select) {
precisely.value = true
}
},
}))
})
</script>
<template>
<div class="flex space-x-2 items-center">
<UDropdown :items="[dropdown]">
<UButton
color="white"
:label="`${filters[filterKey].label}`"
trailing-icon="i-tabler-chevron-down"
/>
</UDropdown>
<TablerIcon name="equal" />
<UDropdown
v-if="filters[filterKey].select"
:items="[filters[filterKey].select!]"
/>
<UInput
v-else
v-model="search"
/>
<UButton
:label="$t('r.search.search')"
@click="emit('search', params)"
/>
<div>{{ $t("r.search.precisely") }}</div>
<UToggle
v-model="precisely"
:disabled="!!filters[filterKey].select"
on-icon="i-tabler-check"
off-icon="i-tabler-x"
/>
</div>
</template>
<script setup lang="ts" generic="T extends { [key: string]: any}">
import type { DropdownItem } from "#ui/types"
const props = defineProps<{
rowData: T[]
columns: { key: string, label?: string }[]
actions: (row: T) => DropdownItem[][]
loading: boolean
}>()
const model = defineModel({ type: Array })
const tablePage = ref(1)
const tablePageCount = ref(10)
const tableRows = computed(() => (
props.rowData.slice((tablePage.value - 1) * tablePageCount.value, (tablePage.value) * tablePageCount.value)
))
const pageDropdown = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(item => ({
label: (item * 5).toString(),
click: () => {
tablePageCount.value = item * 5
tablePage.value = 1
},
}))]
</script>
<template>
<div class="flex flex-col space-y-2">
<UTable
v-model="model"
:rows="tableRows"
:columns="columns"
:loading="loading"
class="border-gray-300 dark:border-gray-600 border rounded"
>
<template #actions-data="{ row }">
<UDropdown :items="actions(row)">
<UButton
color="gray"
variant="ghost"
icon="i-tabler-dots"
/>
</UDropdown>
</template>
</UTable>
<div class="flex justify-between">
<div>{{ $t("r.table.count", [tableRows.length, rowData.length]) }}</div>
<div class="flex space-x-2">
<UDropdown
:items="pageDropdown"
:ui="{
width: 'w-24',
}"
>
<UButton
color="white"
:label="`${$t('r.table.pageCount', [tablePageCount])}`"
trailing-icon="i-tabler-chevron-down"
/>
</UDropdown>
<UPagination
v-model="tablePage"
:page-count="tablePageCount"
:total="rowData.length"
/>
</div>
</div>
</div>
</template>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment