mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 07:57:21 +08:00
273 lines
6.8 KiB
Vue
273 lines
6.8 KiB
Vue
<script lang="ts" setup>
|
|
import { NButton, NDataTable, NFlex, NPopconfirm, NTag } from 'naive-ui'
|
|
import { useGettext } from 'vue3-gettext'
|
|
|
|
import project from '@/api/panel/project'
|
|
import systemctl from '@/api/panel/systemctl'
|
|
import RealtimeLog from '@/components/common/RealtimeLog.vue'
|
|
import { useFileStore } from '@/store'
|
|
|
|
const type = defineModel<string>('type', { type: String, required: true })
|
|
const createModal = defineModel<boolean>('createModal', { type: Boolean, required: true })
|
|
const editModal = defineModel<boolean>('editModal', { type: Boolean, required: true })
|
|
const editId = defineModel<number>('editId', { type: Number, required: true })
|
|
const logModal = ref(false)
|
|
const logService = ref('')
|
|
|
|
const fileStore = useFileStore()
|
|
const { $gettext } = useGettext()
|
|
const router = useRouter()
|
|
const selectedRowKeys = ref<any>([])
|
|
|
|
const typeMap: Record<string, string> = {
|
|
general: $gettext('General'),
|
|
php: 'PHP',
|
|
java: 'Java',
|
|
go: 'Go',
|
|
python: 'Python',
|
|
nodejs: 'Node.js'
|
|
}
|
|
|
|
const columns: any = [
|
|
{ type: 'selection', fixed: 'left' },
|
|
{
|
|
title: $gettext('Name'),
|
|
key: 'name',
|
|
width: 160,
|
|
resizable: true,
|
|
ellipsis: { tooltip: true }
|
|
},
|
|
{
|
|
title: $gettext('Description'),
|
|
key: 'description',
|
|
width: 300,
|
|
resizable: true,
|
|
ellipsis: { tooltip: true }
|
|
},
|
|
{
|
|
title: $gettext('Type'),
|
|
key: 'type',
|
|
width: 120,
|
|
render(row: any) {
|
|
return h(NTag, { type: 'info' }, { default: () => typeMap[row.type] || row.type })
|
|
}
|
|
},
|
|
{
|
|
title: $gettext('Status'),
|
|
key: 'status',
|
|
width: 100,
|
|
render(row: any) {
|
|
return h(
|
|
NTag,
|
|
{ type: row.status === 'active' ? 'success' : 'default' },
|
|
{
|
|
default: () => {
|
|
switch (row.status) {
|
|
case 'active':
|
|
return $gettext('Running')
|
|
case 'inactive':
|
|
return $gettext('Stopped')
|
|
case 'failed':
|
|
return $gettext('Failed')
|
|
default:
|
|
return $gettext('Inactive')
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
},
|
|
{
|
|
title: $gettext('Directory'),
|
|
key: 'root_dir',
|
|
minWidth: 200,
|
|
resizable: true,
|
|
render(row: any) {
|
|
return h(
|
|
NTag,
|
|
{
|
|
class: 'cursor-pointer hover:opacity-60',
|
|
type: 'info',
|
|
onClick: () => {
|
|
fileStore.path = row.root_dir
|
|
router.push({ name: 'file-index' })
|
|
}
|
|
},
|
|
{ default: () => row.root_dir }
|
|
)
|
|
}
|
|
},
|
|
{
|
|
title: $gettext('Actions'),
|
|
key: 'actions',
|
|
width: 300,
|
|
hideInExcel: true,
|
|
render(row: any) {
|
|
return [
|
|
h(
|
|
NButton,
|
|
{
|
|
size: 'small',
|
|
type: row.status === 'active' ? 'warning' : 'success',
|
|
onClick: () => handleToggleStatus(row)
|
|
},
|
|
{ default: () => (row.status === 'active' ? $gettext('Stop') : $gettext('Start')) }
|
|
),
|
|
h(
|
|
NButton,
|
|
{
|
|
size: 'small',
|
|
type: 'info',
|
|
style: 'margin-left: 10px;',
|
|
onClick: () => handleShowLog(row)
|
|
},
|
|
{ default: () => $gettext('Logs') }
|
|
),
|
|
h(
|
|
NButton,
|
|
{
|
|
size: 'small',
|
|
type: 'primary',
|
|
style: 'margin-left: 10px;',
|
|
onClick: () => handleEdit(row)
|
|
},
|
|
{ default: () => $gettext('Edit') }
|
|
),
|
|
h(
|
|
NPopconfirm,
|
|
{
|
|
showIcon: false,
|
|
onPositiveClick: () => handleDelete(row.id)
|
|
},
|
|
{
|
|
default: () =>
|
|
$gettext('Are you sure you want to delete project %{ name }?', { name: row.name }),
|
|
trigger: () =>
|
|
h(
|
|
NButton,
|
|
{
|
|
size: 'small',
|
|
type: 'error',
|
|
style: 'margin-left: 10px;'
|
|
},
|
|
{ default: () => $gettext('Delete') }
|
|
)
|
|
}
|
|
)
|
|
]
|
|
}
|
|
}
|
|
]
|
|
|
|
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
|
|
(page, pageSize) => project.list(type.value, page, pageSize),
|
|
{
|
|
initialData: { total: 0, list: [] },
|
|
initialPageSize: 20,
|
|
total: (res: any) => res.total,
|
|
data: (res: any) => res.items
|
|
}
|
|
)
|
|
|
|
const handleToggleStatus = (row: any) => {
|
|
if (row.status === 'active') {
|
|
useRequest(systemctl.stop(row.name)).onSuccess(() => {
|
|
row.status = 'inactive'
|
|
window.$message.success($gettext('Stopped successfully'))
|
|
})
|
|
} else {
|
|
useRequest(systemctl.start(row.name)).onSuccess(() => {
|
|
row.status = 'active'
|
|
window.$message.success($gettext('Started successfully'))
|
|
})
|
|
}
|
|
}
|
|
|
|
const handleShowLog = (row: any) => {
|
|
logService.value = row.name
|
|
logModal.value = true
|
|
}
|
|
|
|
const handleEdit = (row: any) => {
|
|
editId.value = row.id
|
|
editModal.value = true
|
|
}
|
|
|
|
const handleDelete = (id: number) => {
|
|
useRequest(project.delete(id)).onSuccess(() => {
|
|
refresh()
|
|
window.$message.success($gettext('Deleted successfully'))
|
|
})
|
|
}
|
|
|
|
const bulkDelete = async () => {
|
|
const promises = selectedRowKeys.value.map((id: any) => project.delete(id))
|
|
await Promise.all(promises)
|
|
|
|
selectedRowKeys.value = []
|
|
refresh()
|
|
window.$message.success($gettext('Deleted successfully'))
|
|
}
|
|
|
|
onMounted(() => {
|
|
refresh()
|
|
window.$bus.on('project:refresh', refresh)
|
|
})
|
|
|
|
watch(type, () => {
|
|
refresh()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<n-flex vertical>
|
|
<n-flex>
|
|
<n-button type="primary" @click="createModal = true">
|
|
{{ $gettext('Create Project') }}
|
|
</n-button>
|
|
<n-popconfirm @positive-click="bulkDelete">
|
|
<template #trigger>
|
|
<n-button type="error" :disabled="selectedRowKeys.length === 0" ghost>
|
|
{{ $gettext('Delete') }}
|
|
</n-button>
|
|
</template>
|
|
{{ $gettext('Are you sure you want to delete the selected projects?') }}
|
|
</n-popconfirm>
|
|
</n-flex>
|
|
<n-data-table
|
|
striped
|
|
remote
|
|
:loading="loading"
|
|
:scroll-x="1200"
|
|
:columns="columns"
|
|
:data="data"
|
|
:row-key="(row: any) => row.id"
|
|
v-model:checked-row-keys="selectedRowKeys"
|
|
v-model:page="page"
|
|
v-model:pageSize="pageSize"
|
|
:pagination="{
|
|
page: page,
|
|
pageCount: pageCount,
|
|
pageSize: pageSize,
|
|
itemCount: total,
|
|
showQuickJumper: true,
|
|
showSizePicker: true,
|
|
pageSizes: [20, 50, 100, 200]
|
|
}"
|
|
/>
|
|
</n-flex>
|
|
<n-modal
|
|
v-model:show="logModal"
|
|
preset="card"
|
|
:title="$gettext('Logs')"
|
|
style="width: 60vw"
|
|
size="huge"
|
|
:bordered="false"
|
|
:segmented="false"
|
|
@close="logModal = false"
|
|
@mask-click="logModal = false"
|
|
>
|
|
<realtime-log :service="logService" />
|
|
</n-modal>
|
|
</template>
|