Skip to content

Commit

Permalink
feat(categorys): 完成网站分类 RESTFUL 风格 API 接口和实现 CURD 操作
Browse files Browse the repository at this point in the history
  • Loading branch information
baiwumm committed Jun 14, 2024
1 parent 2ea51ca commit ad776f3
Show file tree
Hide file tree
Showing 11 changed files with 523 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/enum/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* @description: 响应状态码
*/
export enum RESPONSE_STATUS_CODE {
SUCCESS = 200, // 请求成功
FAIL = 400, // 请求失败
UNAUTHORIZED = 401, // 未授权
FORBIDDEN = 403, // 禁止访问
NOT_FOUND = 404, // 请求资源不存在
TIMEOUT = 408, // 请求超时
SERVER_ERROR = 500, // 服务器异常
SERVICE_UNAVAILABLE = 503, // 服务不可用
GATEWAY_TIMEOUT = 504 // 网关超时
}
102 changes: 102 additions & 0 deletions src/pages/admin/_components/categorys/components/EditModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<!--
* @Author: 白雾茫茫丶<baiwumm.com>
* @Date: 2024-06-05 10:47:28
* @LastEditors: 白雾茫茫丶<baiwumm.com>
* @LastEditTime: 2024-06-14 10:03:07
* @Description: 新增/编辑弹窗
-->
<template>
<el-dialog
:model-value="open"
:title="categoryId ? `编辑分类: ${name}` : '新增分类'"
width="500"
@close="handleClose"
>
<template #header>
{{ categoryId ? `编辑分类: ` : '新增分类' }}
<el-tag v-if="name" type="primary">
{{ name }}
</el-tag>
</template>
<template #footer>
<el-button type="primary" :loading="confirmLoading" @click="handleConfirm(ruleFormRef)">
确定
</el-button>
</template>
<el-form ref="ruleFormRef" :model="form" label-width="auto" :rules="rules">
<el-form-item label="分类名称" prop="name">
<el-input v-model="form.name" maxlength="12" show-word-limit type="text" />
</el-form-item>
<el-form-item label="分类描述" prop="desc">
<el-input v-model="form.desc" type="textarea" :rows="3" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="1" :max="99" :style="{ width: '100%' }" />
</el-form-item>
</el-form>
</el-dialog>
</template>
<script setup lang="ts">
import type { CategoryEdit } from '~/types'
import type { FormInstance, FormRules } from 'element-plus'
import { RESPONSE_STATUS_CODE } from '~/enum'

const open = ref(false) // 是否显示弹窗
const name = ref('') // 当前数据
const confirmLoading = ref(false)
const categoryId = ref()

const emit = defineEmits(['refresh'])

const form = reactive<CategoryEdit>({
name: '',
desc: undefined,
sort: 1
})

const ruleFormRef = ref<FormInstance>()
// 表单规则校验
const rules = reactive<FormRules<CategoryEdit>>({
name: [
{ required: true, message: '请输入分类名称', trigger: 'blur' },
{ min: 1, max: 12, message: '长度1-12个字符', trigger: 'blur' }
]
})

// 暴露方法
defineExpose({ open, name, form, categoryId })

// 关闭回调
const handleClose = () => {
ruleFormRef.value?.resetFields()
open.value = false
name.value = ''
categoryId.value = undefined
}

// 确定回调
const handleConfirm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
confirmLoading.value = true
await $fetch('/api/categorys', {
method: categoryId.value ? 'put' : 'post',
body: Object.assign(form, { id: categoryId.value })
})
.then(({ code, msg }) => {
if (code === RESPONSE_STATUS_CODE.SUCCESS) {
ElMessage.success('操作成功')
handleClose()
emit('refresh')
} else {
ElMessage.error(msg)
}
})
.finally(() => {
confirmLoading.value = false
})
}
})
}
</script>
60 changes: 60 additions & 0 deletions src/pages/admin/_components/categorys/components/TableTemplate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<template>
<el-space direction="vertical" fill class="w-full">
<el-table v-loading="pending" :data="data?.list || []" border stripe table-layou="auto">
<el-table-column type="index" label="#" width="50" align="center" />
<el-table-column prop="name" label="分类名称" align="center" show-overflow-tooltip>
<template #default="{ row }">
<el-tag type="primary">{{ row.name }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="desc" label="分类描述" align="center" show-overflow-tooltip>
<template #default="{ row }">
<div>{{ row.desc || '--' }}</div>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" align="center" sortable />
<el-table-column prop="created_at" label="创建时间" align="center" width="180" sortable>
<template #default="{ row }">
<div>{{ formatDateTime(row.created_at) }}</div>
</template>
</el-table-column>
<el-table-column prop="updated_at" label="更新时间" align="center" width="180" sortable>
<template #default="{ row }">
<div>{{ formatDateTime(row.updated_at) }}</div>
</template>
</el-table-column>
<el-table-column label="操作" width="140" align="center" fixed="right">
<template #default="{ row }">
<el-button size="small" @click="emit('handleEdit', row)"> 编辑 </el-button>
<el-button size="small" type="danger" @click="emit('handleDelete', row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-row justify="end">
<el-pagination
small
background
layout="total,prev, pager, next"
:total="data?.total || 0"
:page-size="pageSize"
@change="(currentPage: number, page: number) => emit('handleChangePage', currentPage, page)"
/>
</el-row>
</el-space>
</template>
<script setup lang="ts">
import { formatDateTime } from '@/utils'
import type { PageResponse, CategoryList } from '~/types'
// 父组件传递参数
defineProps<{
pending: boolean // 数据加载 loading
data?: PageResponse<CategoryList> // 表格数据
pageSize: number // 分页
}>()
// 父组件方法
const emit = defineEmits(['handleEdit', 'handleDelete', 'handleChangePage'])
</script>
112 changes: 112 additions & 0 deletions src/pages/admin/_components/categorys/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<template>
<el-space direction="vertical" alignment="start" fill class="w-full">
<el-space wrap>
<el-input v-model="name" style="width: 240px" placeholder="输入关键词搜索" />
<el-button type="primary" :loading="pending" @click="handleSearch"> 查询 </el-button>
<el-button type="primary" @click="handleAdd"> 新增 </el-button>
</el-space>
<!-- 表格列表 -->
<table-template
:pending="pending"
:data="data?.data"
:page-size="pageSize"
@handle-edit="handleEdit"
@handle-delete="handleDelete"
@handle-change-page="handleChangePage"
/>
<!-- 新增/编辑弹窗 -->
<edit-modal ref="modalRef" @refresh="refresh" />
</el-space>
</template>
<script setup lang="ts">
import type { PageResponse, CategoryList, Response } from '~/types'
import { RESPONSE_STATUS_CODE } from '~/enum'
import { ElMessage, ElMessageBox } from 'element-plus'
import EditModal from './components/EditModal.vue'
import TableTemplate from './components/TableTemplate.vue'
// 请求参数
const current = ref(1) // 当前页
const pageSize = ref(5) // 每页条数
const name = ref('') // 分类名称
const modalRef = ref<InstanceType<typeof EditModal>>()
// 请求列表
const { data, pending, refresh } = await useFetch<Response<PageResponse<CategoryList>>>(
'/api/categorys',
{
query: { current, pageSize, name },
watch: [current, pageSize],
// 处理响应数据
onResponse: ({ response }) => {
const { code, message, msg } = response._data
if (code !== RESPONSE_STATUS_CODE.SUCCESS) {
ElMessage.error(msg || message)
}
}
}
)
// 改变分页时回调
const handleChangePage = (currentPage: number, page: number) => {
current.value = currentPage
pageSize.value = page
}
// 查询回调
const handleSearch = () => {
current.value = 1
}
// 新增回调
const handleAdd = () => {
if (modalRef.value) {
modalRef.value.open = true
}
}
// 编辑回调
const handleEdit = (row: CategoryList) => {
if (modalRef.value) {
modalRef.value.open = true
modalRef.value.name = row.name
modalRef.value.categoryId = row.id
Object.assign(modalRef.value.form, {
name: row.name,
desc: row.desc,
sort: row.sort
})
}
}
// 删除回调
const handleDelete = (row: CategoryList) => {
ElMessageBox.confirm('确认要删除当前数据吗?', '温馨提示', {
type: 'warning',
beforeClose: async (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true
await $fetch('/api/categorys', {
method: 'delete',
body: { id: row.id }
})
.then(({ code, msg }) => {
if (code === RESPONSE_STATUS_CODE.SUCCESS) {
ElMessage.success('删除成功')
refresh()
} else {
ElMessage.error(msg)
}
done()
})
.finally(() => {
instance.confirmButtonLoading = false
})
} else {
done()
}
}
})
}
</script>
17 changes: 17 additions & 0 deletions src/pages/admin/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<template>
<el-tabs type="card">
<el-tab-pane>
<template #label>
<div class="flex items-center justify-center gap-1">
<Icon name="ri:list-indefinite" class="h-5 w-5" />
<span>网站分类</span>
</div>
</template>
<Categorys />
</el-tab-pane>
</el-tabs>
</template>

<script lang="ts" setup>
import Categorys from './_components/categorys/index.vue'
</script>
40 changes: 40 additions & 0 deletions src/server/api/categorys/index.delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* @Author: 白雾茫茫丶<baiwumm.com>
* @Date: 2024-06-13 13:39:14
* @LastEditors: 白雾茫茫丶<baiwumm.com>
* @LastEditTime: 2024-06-13 13:43:03
* @Description: 删除网站分类
*/
import type { Response, CategoryEdit, CategoryList } from '~/types'
import { serverSupabaseClient } from '#supabase/server'
import { RESPONSE_STATUS_CODE } from '~/enum'

export default defineEventHandler(async (event): Promise<Response<CategoryList[]>> => {
const client = await serverSupabaseClient<CategoryList>(event)
// 得到请求体
const { id }: CategoryEdit = await readBody(event)

if (!id) {
return {
code: RESPONSE_STATUS_CODE.FAIL,
msg: 'id不能为空!'
}
}

// 插入数据
const { error } = await client.from('categorys').delete().eq('id', id)

// 判断请求结果
if (error) {
throw createError({
statusCode: RESPONSE_STATUS_CODE.FAIL,
statusMessage: error.message
})
}

// 请求成功
return {
code: RESPONSE_STATUS_CODE.SUCCESS,
msg: '请求成功'
}
})
Loading

0 comments on commit ad776f3

Please sign in to comment.