更新前后端代码
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { projectApi, type Project } from '../api'
|
||||
import { formatSize, formatTime } from '../utils/format'
|
||||
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const list = ref<Project[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const keyword = ref('')
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const editing = ref<Project | null>(null)
|
||||
const form = reactive({ name: '', description: '', storage_limit: 0 })
|
||||
const submitting = ref(false)
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const r = await projectApi.list({ page: page.value, page_size: pageSize.value, keyword: keyword.value || undefined })
|
||||
list.value = r.data!.list || []
|
||||
total.value = r.data!.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
editing.value = null
|
||||
form.name = ''
|
||||
form.description = ''
|
||||
form.storage_limit = 0
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function openEdit(p: Project) {
|
||||
editing.value = p
|
||||
form.name = p.name
|
||||
form.description = p.description
|
||||
form.storage_limit = p.storage_limit / (1024 * 1024) // 转为MB编辑
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (!form.name.trim()) {
|
||||
ElMessage.warning('请输入项目名称')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
const limitBytes = form.storage_limit > 0 ? Math.round(form.storage_limit * 1024 * 1024) : 0
|
||||
try {
|
||||
if (editing.value) {
|
||||
await projectApi.update(editing.value.id, {
|
||||
name: form.name,
|
||||
description: form.description,
|
||||
storage_limit: limitBytes,
|
||||
})
|
||||
ElMessage.success('项目已更新')
|
||||
} else {
|
||||
await projectApi.create({
|
||||
name: form.name,
|
||||
description: form.description,
|
||||
storage_limit: limitBytes || undefined,
|
||||
})
|
||||
ElMessage.success('项目已创建')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
load()
|
||||
} catch {
|
||||
/* 拦截器已提示 */
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(p: Project) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定删除项目「${p.name}」?项目下所有文件将被一并删除且不可恢复。`,
|
||||
'删除项目',
|
||||
{ type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' },
|
||||
)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
await projectApi.remove(p.id)
|
||||
ElMessage.success('项目已删除')
|
||||
load()
|
||||
}
|
||||
|
||||
function goFiles(p: Project) {
|
||||
router.push({ path: '/files', query: { project_id: p.id } })
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<div class="page-toolbar">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
placeholder="搜索项目名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="page = 1; load()"
|
||||
@clear="page = 1; load()"
|
||||
>
|
||||
<template #prefix><el-icon><Search /></el-icon></template>
|
||||
</el-input>
|
||||
<el-button type="primary" @click="openCreate">
|
||||
<el-icon><Plus /></el-icon>新建项目
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="list" v-loading="loading" empty-text="暂无项目,点击右上角新建">
|
||||
<el-table-column prop="id" label="ID" width="70" />
|
||||
<el-table-column label="项目名称" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="goFiles(row)">{{ row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" label="描述" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="存储用量" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<el-progress
|
||||
:percentage="row.storage_limit ? Math.min(100, Math.round(row.storage_used / row.storage_limit * 100)) : 0"
|
||||
:stroke-width="10"
|
||||
>
|
||||
<span class="mono">{{ formatSize(row.storage_used) }} / {{ formatSize(row.storage_limit) }}</span>
|
||||
</el-progress>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'" size="small">
|
||||
{{ row.status === 1 ? '正常' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" width="160">
|
||||
<template #default="{ row }">{{ formatTime(row.created_at) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="goFiles(row)">文件</el-button>
|
||||
<el-button link type="primary" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button link type="danger" @click="remove(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-if="total > pageSize"
|
||||
style="margin-top: 16px; justify-content: flex-end"
|
||||
layout="total, prev, pager, next"
|
||||
:total="total"
|
||||
:page-size="pageSize"
|
||||
v-model:current-page="page"
|
||||
@current-change="load"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="editing ? '编辑项目' : '新建项目'" width="460px">
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="项目名称" required>
|
||||
<el-input v-model="form.name" placeholder="请输入项目名称" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="项目描述">
|
||||
<el-input v-model="form.description" type="textarea" :rows="3" placeholder="选填" />
|
||||
</el-form-item>
|
||||
<el-form-item label="存储配额">
|
||||
<el-input-number v-model="form.storage_limit" :min="0" :step="512" style="width: 180px" />
|
||||
<span style="margin-left: 8px; color: #909399">MB(0 表示使用系统默认)</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user