feat: finish curd table
This commit is contained in:
parent
d1dd58215d
commit
b59e47b5dd
@ -12,7 +12,7 @@ const posts = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Vue2&Vue3项目风格指南',
|
title: 'Vue2&Vue3项目风格指南',
|
||||||
author: '大脸怪',
|
author: 'Ronnie',
|
||||||
category: 'Vue',
|
category: 'Vue',
|
||||||
description: '总结的Vue2和Vue3的项目风格',
|
description: '总结的Vue2和Vue3的项目风格',
|
||||||
content: '### 1. 命名风格\n\n> 文件夹如果是由多个单词组成,应该始终是横线连接 ',
|
content: '### 1. 命名风格\n\n> 文件夹如果是由多个单词组成,应该始终是横线连接 ',
|
||||||
@ -93,4 +93,46 @@ export default [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
url: '/api/post',
|
||||||
|
method: 'post',
|
||||||
|
response: ({ body }) => {
|
||||||
|
return {
|
||||||
|
code: 0,
|
||||||
|
message: 'ok',
|
||||||
|
data: body,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: '/api/post/:id',
|
||||||
|
method: 'put',
|
||||||
|
response: ({ query, body }) => {
|
||||||
|
return {
|
||||||
|
code: 0,
|
||||||
|
message: 'ok',
|
||||||
|
data: {
|
||||||
|
id: query.id,
|
||||||
|
body,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: '/api/post/:id',
|
||||||
|
method: 'delete',
|
||||||
|
response: ({ query }) => {
|
||||||
|
if (!query.id) {
|
||||||
|
return { code: -1, message: '删除失败,id不能为空' }
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
code: 0,
|
||||||
|
message: 'ok',
|
||||||
|
data: {
|
||||||
|
id: query.id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<footer flex justify-end>
|
<footer flex justify-end>
|
||||||
<slot name="footer">
|
<slot name="footer">
|
||||||
<n-button @click="show = false">取消</n-button>
|
<n-button @click="show = false">取消</n-button>
|
||||||
<n-button ml-20 type="primary" @click="emit('onSave')">保存</n-button>
|
<n-button :loading="loading" ml-20 type="primary" @click="emit('onSave')">保存</n-button>
|
||||||
</slot>
|
</slot>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
@ -30,6 +30,10 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['update:visible', 'onSave'])
|
const emit = defineEmits(['update:visible', 'onSave'])
|
||||||
|
1
src/composables/index.js
Normal file
1
src/composables/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as useCRUD } from './useCRUD'
|
@ -1,5 +1,104 @@
|
|||||||
|
import { isNullOrWhitespace } from '../utils/is'
|
||||||
|
|
||||||
const ACTIONS = {
|
const ACTIONS = {
|
||||||
view: '查看',
|
view: '查看',
|
||||||
edit: '编辑',
|
edit: '编辑',
|
||||||
add: '新增',
|
add: '新增',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function ({ name, initForm = {}, doCreate, doDelete, doUpdate, refresh }) {
|
||||||
|
const modalVisible = ref(false)
|
||||||
|
const modalAction = ref('')
|
||||||
|
const modalTitle = computed(() => ACTIONS[modalAction.value] + name)
|
||||||
|
const modalLoading = ref(false)
|
||||||
|
const modalFormRef = ref(null)
|
||||||
|
const modalForm = ref({ ...initForm })
|
||||||
|
|
||||||
|
/** 新增 */
|
||||||
|
function handleAdd() {
|
||||||
|
modalAction.value = 'add'
|
||||||
|
modalVisible.value = true
|
||||||
|
modalForm.value = { ...initForm }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改 */
|
||||||
|
function handleEdit(row) {
|
||||||
|
modalAction.value = 'edit'
|
||||||
|
modalVisible.value = true
|
||||||
|
modalForm.value = { ...row }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查看 */
|
||||||
|
function handleView(row) {
|
||||||
|
modalAction.value = 'view'
|
||||||
|
modalVisible.value = true
|
||||||
|
modalForm.value = { ...row }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 保存 */
|
||||||
|
function handleSave() {
|
||||||
|
if (!['edit', 'add'].includes(modalAction.value)) {
|
||||||
|
modalVisible.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
modalFormRef.value?.validate(async (err) => {
|
||||||
|
if (err) return
|
||||||
|
const actions = {
|
||||||
|
add: {
|
||||||
|
api: () => doCreate(modalForm.value),
|
||||||
|
cb: () => $message.success('新增成功'),
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
api: () => doUpdate(modalForm.value),
|
||||||
|
cb: () => $message.success('编辑成功'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const action = actions[modalAction.value]
|
||||||
|
|
||||||
|
try {
|
||||||
|
modalLoading.value = true
|
||||||
|
const data = await action.api()
|
||||||
|
action.cb()
|
||||||
|
modalLoading.value = modalVisible.value = false
|
||||||
|
data && refresh(data)
|
||||||
|
} catch (error) {
|
||||||
|
$message.error('操作失败')
|
||||||
|
modalLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除 */
|
||||||
|
function handleDelete(id, confirmOptions) {
|
||||||
|
if (isNullOrWhitespace(id)) return
|
||||||
|
$dialog.confirm({
|
||||||
|
content: '确定删除?',
|
||||||
|
async confirm() {
|
||||||
|
try {
|
||||||
|
modalLoading.value = true
|
||||||
|
const data = await doDelete(id)
|
||||||
|
$message.success('删除成功')
|
||||||
|
modalLoading.value = false
|
||||||
|
refresh(data)
|
||||||
|
} catch (error) {
|
||||||
|
modalLoading.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...confirmOptions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modalVisible,
|
||||||
|
modalAction,
|
||||||
|
modalTitle,
|
||||||
|
modalLoading,
|
||||||
|
handleAdd,
|
||||||
|
handleDelete,
|
||||||
|
handleEdit,
|
||||||
|
handleView,
|
||||||
|
handleSave,
|
||||||
|
modalForm,
|
||||||
|
modalFormRef,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -35,7 +35,11 @@ export function reqReject(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function repResolve(response) {
|
export function repResolve(response) {
|
||||||
return response?.data
|
if (response?.data?.code !== 0) {
|
||||||
|
$message.error(response?.data?.message || '操作异常')
|
||||||
|
return Promise.reject(response?.data)
|
||||||
|
}
|
||||||
|
return Promise.resolve(response?.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function repReject(error) {
|
export function repReject(error) {
|
||||||
@ -67,5 +71,6 @@ export function repReject(error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.error(`【${code}】 ${error}`)
|
console.error(`【${code}】 ${error}`)
|
||||||
return Promise.resolve({ code, message, error })
|
$message.error(message || '操作异常')
|
||||||
|
return Promise.reject({ code, message, error })
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,7 @@ import request from '@/utils/http'
|
|||||||
export default {
|
export default {
|
||||||
getPosts: (params = {}) => request.get('posts', { params }),
|
getPosts: (params = {}) => request.get('posts', { params }),
|
||||||
getPostById: (id) => request.get(`/post/${id}`),
|
getPostById: (id) => request.get(`/post/${id}`),
|
||||||
savePost: (id, data = {}) => {
|
addPost: (data) => request.post('/post', data),
|
||||||
if (id) {
|
updatePost: (data) => request.put(`/post/${data.id}`, data),
|
||||||
return request.put(`/post/${id}`, data)
|
|
||||||
}
|
|
||||||
return request.post('/post', data)
|
|
||||||
},
|
|
||||||
deletePost: (id) => request.delete(`/post/${id}`),
|
deletePost: (id) => request.delete(`/post/${id}`),
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<CommonPage show-footer title="文章">
|
<CommonPage show-footer title="文章">
|
||||||
<template #action>
|
<template #action>
|
||||||
<n-button type="primary" @click="modalVisible = true">
|
<n-button type="primary" @click="handleAdd">
|
||||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" /> 新建文章
|
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" /> 新建文章
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
@ -10,19 +10,73 @@
|
|||||||
ref="$table"
|
ref="$table"
|
||||||
v-model:query-items="queryItems"
|
v-model:query-items="queryItems"
|
||||||
:extra-params="extraParams"
|
:extra-params="extraParams"
|
||||||
:scroll-x="1600"
|
:scroll-x="1200"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:get-data="api.getPosts"
|
:get-data="api.getPosts"
|
||||||
@on-checked="onChecked"
|
@on-checked="onChecked"
|
||||||
>
|
>
|
||||||
<template #queryBar>
|
<template #queryBar>
|
||||||
<QueryBarItem label="标题" :label-width="50">
|
<QueryBarItem label="标题" :label-width="50">
|
||||||
<n-input v-model:value="queryItems.title" type="text" placeholder="请输入标题" />
|
<n-input
|
||||||
|
v-model:value="queryItems.title"
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入标题"
|
||||||
|
@keydown.enter="$table?.handleSearch"
|
||||||
|
/>
|
||||||
</QueryBarItem>
|
</QueryBarItem>
|
||||||
</template>
|
</template>
|
||||||
</CrudTable>
|
</CrudTable>
|
||||||
<!-- 新增/编辑/查看 -->
|
<!-- 新增/编辑/查看 -->
|
||||||
<CrudModal v-model:visible="modalVisible" :title="modalTitle" @on-save="handleSave"> 内容 </CrudModal>
|
<CrudModal
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="modalTitle"
|
||||||
|
:loading="modalLoading"
|
||||||
|
:show-footer="modalAction !== 'view'"
|
||||||
|
@on-save="handleSave"
|
||||||
|
>
|
||||||
|
<n-form
|
||||||
|
ref="modalFormRef"
|
||||||
|
label-placement="left"
|
||||||
|
label-align="left"
|
||||||
|
:label-width="80"
|
||||||
|
:model="modalForm"
|
||||||
|
:disabled="modalAction === 'view'"
|
||||||
|
>
|
||||||
|
<n-form-item label="作者" path="author">
|
||||||
|
<n-input v-model:value="modalForm.author" disabled />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item
|
||||||
|
label="文章标题"
|
||||||
|
path="title"
|
||||||
|
:rule="{
|
||||||
|
required: true,
|
||||||
|
message: '请输入文章标题',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<n-input v-model:value="modalForm.title" placeholder="请输入文章标题" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item
|
||||||
|
label="文章内容"
|
||||||
|
path="content"
|
||||||
|
:rule="{
|
||||||
|
required: true,
|
||||||
|
message: '请输入文章内容',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<n-input
|
||||||
|
v-model:value="modalForm.content"
|
||||||
|
placeholder="请输入文章内容"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{
|
||||||
|
minRows: 3,
|
||||||
|
maxRows: 5,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
</CrudModal>
|
||||||
</CommonPage>
|
</CommonPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -30,29 +84,42 @@
|
|||||||
import { NButton, NSwitch } from 'naive-ui'
|
import { NButton, NSwitch } from 'naive-ui'
|
||||||
import { formatDateTime } from '@/utils'
|
import { formatDateTime } from '@/utils'
|
||||||
import { renderIcon } from '@/utils/icon'
|
import { renderIcon } from '@/utils/icon'
|
||||||
|
import { useCRUD } from '@/composables'
|
||||||
import api from './api'
|
import api from './api'
|
||||||
|
import { isNullOrUndef } from '@/utils/is'
|
||||||
|
|
||||||
const $table = ref(null)
|
const $table = ref(null)
|
||||||
/** queryBar参数 */
|
/** QueryBar筛选参数(可选) */
|
||||||
const queryItems = ref({})
|
const queryItems = ref({
|
||||||
/** 可选,用于补充参数 */
|
title: '',
|
||||||
|
})
|
||||||
|
/** 补充参数(可选) */
|
||||||
const extraParams = ref({})
|
const extraParams = ref({})
|
||||||
|
|
||||||
// 选中事件
|
onMounted(() => {
|
||||||
function onChecked(rowKeys) {
|
$table.value?.handleSearch()
|
||||||
if (rowKeys.length) $message.info(`选中${rowKeys.join(' ')}`)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ type: 'selection' },
|
{ type: 'selection', fixed: 'left' },
|
||||||
|
{
|
||||||
|
title: '发布',
|
||||||
|
key: 'isPublish',
|
||||||
|
width: 60,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'left',
|
||||||
|
render(row) {
|
||||||
|
return h(NSwitch, {
|
||||||
|
size: 'small',
|
||||||
|
rubberBand: false,
|
||||||
|
value: row['isPublish'],
|
||||||
|
loading: !!row.publishing,
|
||||||
|
onUpdateValue: () => handlePublish(row),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
{ title: '标题', key: 'title', width: 150, ellipsis: { tooltip: true } },
|
{ title: '标题', key: 'title', width: 150, ellipsis: { tooltip: true } },
|
||||||
{ title: '分类', key: 'category', width: 80, ellipsis: { tooltip: true } },
|
{ title: '分类', key: 'category', width: 80, ellipsis: { tooltip: true } },
|
||||||
{
|
|
||||||
title: '描述',
|
|
||||||
key: 'description',
|
|
||||||
width: 200,
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{ title: '创建人', key: 'author', width: 80 },
|
{ title: '创建人', key: 'author', width: 80 },
|
||||||
{
|
{
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
@ -70,42 +137,10 @@ const columns = [
|
|||||||
return h('span', formatDateTime(row['updateDate']))
|
return h('span', formatDateTime(row['updateDate']))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '推荐',
|
|
||||||
key: 'isRecommend',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
render(row) {
|
|
||||||
return h(NSwitch, {
|
|
||||||
size: 'small',
|
|
||||||
value: row['isRecommend'],
|
|
||||||
rubberBand: false,
|
|
||||||
loading: !!row.recommending,
|
|
||||||
onUpdateValue: () => handleRecommend(row),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '发布',
|
|
||||||
key: 'isPublish',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
render(row) {
|
|
||||||
return h(NSwitch, {
|
|
||||||
size: 'small',
|
|
||||||
rubberBand: false,
|
|
||||||
value: row['isPublish'],
|
|
||||||
loading: !!row.publishing,
|
|
||||||
onUpdateValue: () => handlePublish(row),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
width: 200,
|
width: 240,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
render(row) {
|
render(row) {
|
||||||
@ -115,17 +150,29 @@ const columns = [
|
|||||||
{
|
{
|
||||||
size: 'small',
|
size: 'small',
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
|
secondary: true,
|
||||||
|
onClick: () => handleView(row),
|
||||||
|
},
|
||||||
|
{ default: () => '查看', icon: renderIcon('majesticons:eye-line', { size: 14 }) }
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: 'primary',
|
||||||
|
style: 'margin-left: 15px;',
|
||||||
onClick: () => handleEdit(row),
|
onClick: () => handleEdit(row),
|
||||||
},
|
},
|
||||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 14 }) }
|
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 14 }) }
|
||||||
),
|
),
|
||||||
|
|
||||||
h(
|
h(
|
||||||
NButton,
|
NButton,
|
||||||
{
|
{
|
||||||
size: 'small',
|
size: 'small',
|
||||||
type: 'error',
|
type: 'error',
|
||||||
style: 'margin-left: 15px;',
|
style: 'margin-left: 15px;',
|
||||||
onClick: () => handleDelete(row),
|
onClick: () => handleDelete(row.id),
|
||||||
},
|
},
|
||||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 14 }) }
|
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 14 }) }
|
||||||
),
|
),
|
||||||
@ -134,34 +181,41 @@ const columns = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
onMounted(() => {
|
// 选中事件
|
||||||
$table.value?.handleSearch()
|
function onChecked(rowKeys) {
|
||||||
|
if (rowKeys.length) $message.info(`选中${rowKeys.join(' ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布
|
||||||
|
function handlePublish(row) {
|
||||||
|
if (isNullOrUndef(row.id)) return
|
||||||
|
|
||||||
|
row.publishing = true
|
||||||
|
setTimeout(() => {
|
||||||
|
row.isPublish = !row.isPublish
|
||||||
|
row.publishing = false
|
||||||
|
$message?.success(row.isPublish ? '已发布' : '已取消发布')
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalVisible,
|
||||||
|
modalAction,
|
||||||
|
modalTitle,
|
||||||
|
modalLoading,
|
||||||
|
handleAdd,
|
||||||
|
handleDelete,
|
||||||
|
handleEdit,
|
||||||
|
handleView,
|
||||||
|
handleSave,
|
||||||
|
modalForm,
|
||||||
|
modalFormRef,
|
||||||
|
} = useCRUD({
|
||||||
|
name: '文章',
|
||||||
|
initForm: { author: '大脸怪' },
|
||||||
|
doCreate: api.addPost,
|
||||||
|
doDelete: api.deletePost,
|
||||||
|
doUpdate: api.updatePost,
|
||||||
|
refresh: () => $table.value?.handleSearch(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const modalVisible = ref(false)
|
|
||||||
const modalTitle = ref('新增文章')
|
|
||||||
|
|
||||||
function handleDelete(row) {
|
|
||||||
if (row && row.id) {
|
|
||||||
$dialog.confirm({
|
|
||||||
content: '确定删除?',
|
|
||||||
confirm() {
|
|
||||||
$message.success('删除成功')
|
|
||||||
initTableData()
|
|
||||||
},
|
|
||||||
cancel() {
|
|
||||||
$message.success('已取消')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleEdit(row) {
|
|
||||||
modalTitle.value = '编辑文章'
|
|
||||||
modalVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSave() {
|
|
||||||
modalVisible.value = false
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user