refactor(project): Reorganize frontend and backend directories

This commit is contained in:
2026-03-18 10:45:15 +08:00
parent cc9eaf4cdf
commit 92a139e470
513 changed files with 28251 additions and 287 deletions
@@ -0,0 +1,170 @@
<template>
<div class="category-page">
<el-card shadow="never">
<template #header>
<div class="card-header">
<span>分类管理</span>
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>添加分类
</el-button>
</div>
</template>
<el-table v-loading="loading" :data="categories" row-key="id" default-expand-all>
<el-table-column prop="name" label="分类名称" />
<el-table-column prop="sort" label="排序" width="100" />
<el-table-column label="操作" width="200">
<template #default="{ row }">
<el-button type="primary" text @click="handleEdit(row)">编辑</el-button>
<el-button type="primary" text @click="handleAddChild(row)">添加子分类</el-button>
<el-button type="danger" text @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="400px">
<el-form :model="form" label-width="80px">
<el-form-item label="分类名称" required>
<el-input v-model="form.name" placeholder="请输入分类名称" />
</el-form-item>
<el-form-item label="父级分类">
<el-tree-select
v-model="form.parentId"
:data="categoryTree"
:props="{ label: 'name', value: 'id' }"
check-strictly
clearable
placeholder="选择父级分类(可选)"
/>
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sort" :min="0" />
</el-form-item>
<el-form-item label="图标">
<el-input v-model="form.icon" placeholder="图标URL" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitting" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { categoryApi, type Category } from '@/api/category.ts'
const loading = ref(false)
const categories = ref<Category[]>([])
const dialogVisible = ref(false)
const dialogTitle = ref('添加分类')
const submitting = ref(false)
const form = reactive({
id: undefined as number | undefined,
name: '',
parentId: 0,
sort: 0,
icon: ''
})
const categoryTree = computed(() => {
const buildTree = (list: Category[], parentId: number = 0): Category[] => {
return list.filter(item => item.parentId === parentId).map(item => ({
...item,
children: buildTree(list, item.id)
}))
}
return buildTree(categories.value)
})
const loadCategories = async () => {
loading.value = true
try {
categories.value = await categoryApi.getList()
} catch (error) {
ElMessage.error('加载失败')
} finally {
loading.value = false
}
}
const resetForm = () => {
form.id = undefined
form.name = ''
form.parentId = 0
form.sort = 0
form.icon = ''
}
const handleAdd = () => {
resetForm()
dialogTitle.value = '添加分类'
dialogVisible.value = true
}
const handleAddChild = (row: Category) => {
resetForm()
form.parentId = row.id
dialogTitle.value = '添加子分类'
dialogVisible.value = true
}
const handleEdit = (row: Category) => {
form.id = row.id
form.name = row.name
form.parentId = row.parentId
form.sort = row.sort
form.icon = row.icon || ''
dialogTitle.value = '编辑分类'
dialogVisible.value = true
}
const handleDelete = async (row: Category) => {
try {
await ElMessageBox.confirm('确定要删除该分类吗?', '提示', { type: 'warning' })
await categoryApi.delete(row.id)
ElMessage.success('删除成功')
loadCategories()
} catch {}
}
const handleSubmit = async () => {
if (!form.name.trim()) {
ElMessage.warning('请输入分类名称')
return
}
submitting.value = true
try {
if (form.id) {
await categoryApi.update({ id: form.id, name: form.name, parentId: form.parentId, sort: form.sort, icon: form.icon })
ElMessage.success('更新成功')
} else {
await categoryApi.create({ name: form.name, parentId: form.parentId, sort: form.sort, icon: form.icon })
ElMessage.success('添加成功')
}
dialogVisible.value = false
loadCategories()
} catch (error) {
ElMessage.error('操作失败')
} finally {
submitting.value = false
}
}
onMounted(() => loadCategories())
</script>
<style scoped lang="scss">
.category-page {
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
}
</style>