feat: adding router

This commit is contained in:
wzp 2023-04-19 21:43:08 +08:00
parent 7760d510e8
commit 71384106a7
23 changed files with 947 additions and 1851 deletions

View File

@ -1,3 +1,4 @@
<div align="center">
<img alt="V3 Admin Vite Logo" width="120" height="120" src="./src/assets/layout/logo.png">
<h1>V3 Admin Vite</h1>

1796
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,5 @@
<script lang="ts" setup>
import { h } from "vue"
import { useTheme } from "@/hooks/useTheme"
import { ElNotification } from "element-plus"
import zhCn from "element-plus/lib/locale/lang/zh-cn"
const { initTheme } = useTheme()
@ -10,17 +8,6 @@ const { initTheme } = useTheme()
initTheme()
/** 将 Element Plus 的语言设置为中文 */
const locale = zhCn
ElNotification({
title: "Hello",
message: h(
"a",
{ style: "color: teal", target: "_blank", href: "https://github.com/un-pany/v3-admin-vite" },
"小项目获取 star 不易,如果你喜欢这个项目的话,欢迎点击这里支持一个 star !这是作者持续维护的唯一动力(小声:毕竟是免费的)"
),
duration: 0,
position: "bottom-right"
})
</script>
<template>

View File

@ -21,11 +21,11 @@ interface ILayoutSettings {
}
const layoutSettings: ILayoutSettings = {
showSettings: true,
showSettings: false,
showTagsView: true,
fixedHeader: true,
showSidebarLogo: true,
showNotify: true,
showNotify: false,
showThemeSwitch: true,
showScreenfull: true,
showGreyMode: false,

View File

@ -1,11 +0,0 @@
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M117.722 167.444C117.722 139.83 140.108 117.444 167.722 117.444V117.444C195.336 117.444 217.722 139.83 217.722 167.444V167.444C217.722 195.058 195.336 217.444 167.722 217.444V217.444C140.108 217.444 117.722 195.058 117.722 167.444V167.444Z"
fill="#fff" fill-opacity="0.6" />
<path
d="M117.722 52.5561C117.722 24.9419 140.108 2.55614 167.722 2.55614V2.55614C195.336 2.55614 217.722 24.9419 217.722 52.5561V97.5561C217.722 100.318 215.483 102.556 212.722 102.556H122.722C119.961 102.556 117.722 100.318 117.722 97.5561V52.5561Z"
fill="#fff" fill-opacity="0.3" />
<path
d="M102.278 167.444C102.278 195.058 79.8922 217.444 52.278 217.444V217.444C24.6637 217.444 2.27796 195.058 2.27796 167.444L2.27796 122.444C2.27796 119.682 4.51654 117.444 7.27796 117.444L97.278 117.444C100.039 117.444 102.278 119.682 102.278 122.444L102.278 167.444Z"
fill="#fff" />
</svg>

Before

Width:  |  Height:  |  Size: 996 B

View File

@ -57,165 +57,35 @@ export const constantRoutes: RouteRecordRaw[] = [
]
},
{
path: "/unocss",
path: "/order",
component: Layout,
redirect: "/unocss/index",
children: [
{
path: "index",
component: () => import("@/views/unocss/index.vue"),
name: "UnoCSS",
meta: {
title: "unocss",
svgIcon: "unocss"
}
}
]
},
{
path: "/link",
component: Layout,
children: [
{
path: "https://juejin.cn/post/7089377403717287972",
component: () => {},
name: "Link",
meta: {
title: "外链",
svgIcon: "link"
}
}
]
},
{
path: "/table",
component: Layout,
redirect: "/table/element-plus",
name: "Table",
redirect: "/order/",
name: "Order",
meta: {
title: "表格",
elIcon: "Grid"
},
children: [
{
path: "element-plus",
component: () => import("@/views/table/element-plus/index.vue"),
name: "ElementPlus",
meta: {
title: "Element Plus",
keepAlive: true
}
},
{
path: "vxe-table",
component: () => import("@/views/table/vxe-table/index.vue"),
name: "VxeTable",
meta: {
title: "Vxe Table",
keepAlive: true
}
}
]
},
{
path: "/menu",
component: Layout,
redirect: "/menu/menu1",
name: "Menu",
meta: {
title: "多级菜单",
svgIcon: "menu"
},
children: [
{
path: "menu1",
component: () => import("@/views/menu/menu1/index.vue"),
redirect: "/menu/menu1/menu1-1",
name: "Menu1",
meta: {
title: "menu1"
},
children: [
{
path: "menu1-1",
component: () => import("@/views/menu/menu1/menu1-1/index.vue"),
name: "Menu1-1",
meta: {
title: "menu1-1"
}
},
{
path: "menu1-2",
component: () => import("@/views/menu/menu1/menu1-2/index.vue"),
redirect: "/menu/menu1/menu1-2/menu1-2-1",
name: "Menu1-2",
meta: {
title: "menu1-2"
},
children: [
{
path: "menu1-2-1",
component: () => import("@/views/menu/menu1/menu1-2/menu1-2-1/index.vue"),
name: "Menu1-2-1",
meta: {
title: "menu1-2-1"
}
},
{
path: "menu1-2-2",
component: () => import("@/views/menu/menu1/menu1-2/menu1-2-2/index.vue"),
name: "Menu1-2-2",
meta: {
title: "menu1-2-2"
}
}
]
},
{
path: "menu1-3",
component: () => import("@/views/menu/menu1/menu1-3/index.vue"),
name: "Menu1-3",
meta: {
title: "menu1-3"
}
}
]
},
{
path: "menu2",
component: () => import("@/views/menu/menu2/index.vue"),
name: "Menu2",
meta: {
title: "menu2"
}
}
]
},
{
path: "/hook-demo",
component: Layout,
redirect: "/hook-demo/use-fetch-select",
name: "HookDemo",
meta: {
title: "hook 示例",
elIcon: "Menu",
title: "订单",
elIcon: "Files",
roles: ["admin", "editor"],
alwaysShow: true
},
children: [
{
path: "use-fetch-select",
component: () => import("@/views/hook-demo/use-fetch-select.vue"),
name: "UseFetchSelect",
path: "create",
component: () => import("@/views/order/create/index.vue"),
name: "CreateOrder",
meta: {
title: "useFetchSelect"
title: "创建订单",
roles: ["editor"],
elIcon: "FolderAdd"
}
},
{
path: "use-fullscreen-loading",
component: () => import("@/views/hook-demo/use-fullscreen-loading.vue"),
name: "UseFullscreenLoading",
path: "manager",
component: () => import("@/views/order/manager/index.vue"),
name: "ManagerOrder",
meta: {
title: "useFullscreenLoading"
title: "管理订单",
roles: ["admin"],
elIcon: "FolderChecked"
}
}
]

View File

@ -1,20 +0,0 @@
<script lang="ts" setup>
import { useFetchSelect } from "@/hooks/useFetchSelect"
import { getSelectDataApi } from "@/api/hook-demo/use-fetch-select"
const { loading, options, value } = useFetchSelect({
api: getSelectDataApi
})
</script>
<template>
<div class="app-container">
<h4>该示例是演示通过 hook 自动调用 api 后拿到 Select 组件需要的数据并传递给 Select 组件</h4>
<h5>Select 示例</h5>
<el-select :loading="loading" v-model="value" filterable>
<el-option v-for="(item, index) in options" v-bind="item" :key="index" placeholder="请选择" />
</el-select>
<h5>Select V2 示例如果数据量过多可以选择该组件</h5>
<el-select-v2 :loading="loading" v-model="value" :options="options" filterable placeholder="请选择" />
</div>
</template>

View File

@ -1,44 +0,0 @@
<script lang="ts" setup>
import { useFullscreenLoading } from "@/hooks/useFullscreenLoading"
import { getSuccessApi, getErrorApi } from "@/api/hook-demo/use-fullscreen-loading"
import { ElMessage } from "element-plus"
const svg = `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`
const options = {
text: "即将发生错误...",
background: "#F56C6C20",
svg,
svgViewBox: "-10, -10, 50, 50"
}
const querySuccess = async () => {
const res = await useFullscreenLoading(getSuccessApi)()
ElMessage.success(res.message)
}
const queryError = async () => {
try {
await useFullscreenLoading(getErrorApi, options)()
} catch (err: any) {
ElMessage.error(err.message)
}
}
</script>
<template>
<div class="app-container">
<h4>该示例是演示通过将要执行的函数传递给 hook hook 自动开启全屏 loading函数执行结束后自动关闭 loading</h4>
<el-button @click="querySuccess">查询成功</el-button>
<el-button @click="queryError">查询失败</el-button>
</div>
</template>

View File

@ -1,7 +0,0 @@
<template>
<div class="app-container">
<el-card header="menu 1">
<router-view />
</el-card>
</div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<div class="app-container">
<el-card> menu 1-1 </el-card>
</div>
</template>

View File

@ -1,7 +0,0 @@
<template>
<div class="app-container">
<el-card header="menu 1-2">
<router-view />
</el-card>
</div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<div class="app-container">
<el-card> menu 1-2-1 </el-card>
</div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<div class="app-container">
<el-card> menu 1-2-2 </el-card>
</div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<div class="app-container">
<el-card> menu 1-3 </el-card>
</div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<div class="app-container">
<el-card> menu 2 </el-card>
</div>
</template>

View File

@ -0,0 +1,7 @@
<template>
<div>OrderCreate</div>
</template>
<script lang="ts" setup></script>
<style scoped></style>

View File

@ -0,0 +1,5 @@
<template><div>OrderManger</div></template>
<script lang="ts" setup></script>
<style scoped></style>

View File

@ -1,12 +0,0 @@
<script lang="ts" setup>
import { useRoute, useRouter } from "vue-router"
const route = useRoute()
const router = useRouter()
router.replace({ path: "/" + route.params.path, query: route.query })
</script>
<template>
<div />
</template>

View File

@ -1,249 +0,0 @@
<script lang="ts" setup>
import { reactive, ref, watch } from "vue"
import { createTableDataApi, deleteTableDataApi, updateTableDataApi, getTableDataApi } from "@/api/table"
import { type IGetTableData } from "@/api/table/types/table"
import { type FormInstance, type FormRules, ElMessage, ElMessageBox } from "element-plus"
import { Search, Refresh, CirclePlus, Delete, Download, RefreshRight } from "@element-plus/icons-vue"
import { usePagination } from "@/hooks/usePagination"
defineOptions({
name: "ElementPlus"
})
const loading = ref<boolean>(false)
const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()
//#region
const dialogVisible = ref<boolean>(false)
const formRef = ref<FormInstance | null>(null)
const formData = reactive({
username: "",
password: ""
})
const formRules: FormRules = reactive({
username: [{ required: true, trigger: "blur", message: "请输入用户名" }],
password: [{ required: true, trigger: "blur", message: "请输入密码" }]
})
const handleCreate = () => {
formRef.value?.validate((valid: boolean) => {
if (valid) {
if (currentUpdateId.value === undefined) {
createTableDataApi({
username: formData.username,
password: formData.password
}).then(() => {
ElMessage.success("新增成功")
dialogVisible.value = false
getTableData()
})
} else {
updateTableDataApi({
id: currentUpdateId.value,
username: formData.username
}).then(() => {
ElMessage.success("修改成功")
dialogVisible.value = false
getTableData()
})
}
} else {
return false
}
})
}
const resetForm = () => {
currentUpdateId.value = undefined
formData.username = ""
formData.password = ""
}
//#endregion
//#region
const handleDelete = (row: IGetTableData) => {
ElMessageBox.confirm(`正在删除用户:${row.username},确认删除?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
deleteTableDataApi(row.id).then(() => {
ElMessage.success("删除成功")
getTableData()
})
})
}
//#endregion
//#region
const currentUpdateId = ref<undefined | string>(undefined)
const handleUpdate = (row: IGetTableData) => {
currentUpdateId.value = row.id
formData.username = row.username
dialogVisible.value = true
}
//#endregion
//#region
const tableData = ref<IGetTableData[]>([])
const searchFormRef = ref<FormInstance | null>(null)
const searchData = reactive({
username: "",
phone: ""
})
const getTableData = () => {
loading.value = true
getTableDataApi({
currentPage: paginationData.currentPage,
size: paginationData.pageSize,
username: searchData.username || undefined,
phone: searchData.phone || undefined
})
.then((res) => {
paginationData.total = res.data.total
tableData.value = res.data.list
})
.catch(() => {
tableData.value = []
})
.finally(() => {
loading.value = false
})
}
const handleSearch = () => {
if (paginationData.currentPage === 1) {
getTableData()
}
paginationData.currentPage = 1
}
const resetSearch = () => {
searchFormRef.value?.resetFields()
if (paginationData.currentPage === 1) {
getTableData()
}
paginationData.currentPage = 1
}
const handleRefresh = () => {
getTableData()
}
//#endregion
/** 监听分页参数的变化 */
watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
</script>
<template>
<div class="app-container">
<el-card v-loading="loading" shadow="never" class="search-wrapper">
<el-form ref="searchFormRef" :inline="true" :model="searchData">
<el-form-item prop="username" label="用户名">
<el-input v-model="searchData.username" placeholder="请输入" />
</el-form-item>
<el-form-item prop="phone" label="手机号">
<el-input v-model="searchData.phone" placeholder="请输入" />
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
<el-button :icon="Refresh" @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card v-loading="loading" shadow="never">
<div class="toolbar-wrapper">
<div>
<el-button type="primary" :icon="CirclePlus" @click="dialogVisible = true">新增用户</el-button>
<el-button type="danger" :icon="Delete">批量删除</el-button>
</div>
<div>
<el-tooltip content="下载">
<el-button type="primary" :icon="Download" circle />
</el-tooltip>
<el-tooltip content="刷新表格">
<el-button type="primary" :icon="RefreshRight" circle @click="handleRefresh" />
</el-tooltip>
</div>
</div>
<div class="table-wrapper">
<el-table :data="tableData">
<el-table-column type="selection" width="50" align="center" />
<el-table-column prop="username" label="用户名" align="center" />
<el-table-column prop="roles" label="角色" align="center">
<template #default="scope">
<el-tag v-if="scope.row.roles === 'admin'" effect="plain">admin</el-tag>
<el-tag v-else type="warning" effect="plain">{{ scope.row.roles }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="phone" label="手机号" align="center" />
<el-table-column prop="email" label="邮箱" align="center" />
<el-table-column prop="status" label="状态" align="center">
<template #default="scope">
<el-tag v-if="scope.row.status" type="success" effect="plain">启用</el-tag>
<el-tag v-else type="danger" effect="plain">禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center" />
<el-table-column fixed="right" label="操作" width="150" align="center">
<template #default="scope">
<el-button type="primary" text bg size="small" @click="handleUpdate(scope.row)">修改</el-button>
<el-button type="danger" text bg size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pager-wrapper">
<el-pagination
background
:layout="paginationData.layout"
:page-sizes="paginationData.pageSizes"
:total="paginationData.total"
:page-size="paginationData.pageSize"
:currentPage="paginationData.currentPage"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<!-- 新增/修改 -->
<el-dialog
v-model="dialogVisible"
:title="currentUpdateId === undefined ? '新增用户' : '修改用户'"
@close="resetForm"
width="30%"
>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" label-position="left">
<el-form-item prop="username" label="用户名">
<el-input v-model="formData.username" placeholder="请输入" />
</el-form-item>
<el-form-item prop="password" label="密码" v-if="currentUpdateId === undefined">
<el-input v-model="formData.password" placeholder="请输入" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleCreate">确认</el-button>
</template>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
.search-wrapper {
margin-bottom: 20px;
:deep(.el-card__body) {
padding-bottom: 2px;
}
}
.toolbar-wrapper {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.table-wrapper {
margin-bottom: 20px;
}
.pager-wrapper {
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -1,388 +0,0 @@
<script lang="ts" setup>
import { nextTick, reactive, ref } from "vue"
import { type ElMessageBoxOptions, ElMessageBox, ElMessage } from "element-plus"
import { deleteTableDataApi, getTableDataApi } from "@/api/table"
import { type GetTableResponseData } from "@/api/table/types/table"
import RoleColumnSolts from "./tsx/RoleColumnSolts"
import StatusColumnSolts from "./tsx/StatusColumnSolts"
import {
type VxeGridInstance,
type VxeGridProps,
type VxeModalInstance,
type VxeModalProps,
type VxeFormInstance,
type VxeFormProps,
type VxeGridPropTypes,
type VxeFormDefines
} from "vxe-table"
defineOptions({
name: "VxeTable"
})
//#region vxe-grid
interface IRowMeta {
id: string
username: string
roles: string
phone: string
email: string
status: boolean
createTime: string
/** vxe-table 自动添加上去的属性 */
_VXE_ID?: string
}
const xGridDom = ref<VxeGridInstance>()
const xGridOpt: VxeGridProps = reactive({
loading: true,
autoResize: true,
/** 分页配置项 */
pagerConfig: {
align: "right"
},
/** 表单配置项 */
formConfig: {
items: [
{
field: "username",
itemRender: {
name: "$input",
props: { placeholder: "用户名", clearable: true }
}
},
{
field: "phone",
itemRender: {
name: "$input",
props: { placeholder: "手机号", clearable: true }
}
},
{
itemRender: {
name: "$buttons",
children: [
{
props: { type: "submit", content: "查询", status: "primary" }
},
{
props: { type: "reset", content: "重置" }
}
]
}
}
]
},
/** 工具栏配置 */
toolbarConfig: {
refresh: true,
custom: true,
slots: { buttons: "toolbar-btns" }
},
/** 自定义列配置项 */
customConfig: {
/** 是否允许列选中 */
checkMethod: ({ column }) => !["username"].includes(column.field)
},
/** 列配置 */
columns: [
{
type: "checkbox",
width: "50px"
},
{
field: "username",
title: "用户名"
},
{
field: "roles",
title: "角色",
/** 自定义列与 type: "html" 的列一起使用,会产生错误,所以采用 TSX 实现 */
slots: RoleColumnSolts
},
{
field: "phone",
title: "手机号"
},
{
field: "email",
title: "邮箱"
},
{
field: "status",
title: "状态",
slots: StatusColumnSolts
},
{
field: "createTime",
title: "创建时间"
},
{
title: "操作",
width: "150px",
fixed: "right",
showOverflow: false,
slots: { default: "row-operate" }
}
],
/** 数据代理配置项(基于 Promise API */
proxyConfig: {
/** 启用动态序号代理 */
seq: true,
/** 是否代理表单 */
form: true,
/** 是否自动加载,默认为 true */
// autoLoad: false,
props: {
total: "total"
},
ajax: {
query: ({ page, form }: VxeGridPropTypes.ProxyAjaxQueryParams) => {
xGridOpt.loading = true
crudStore.clearTable()
return new Promise<any>((resolve: Function) => {
let total = 0
let result: IRowMeta[] = []
/** 加载数据 */
const callback = (res: GetTableResponseData) => {
if (res && res.data) {
const resData = res.data
//
if (Number.isInteger(resData.total)) {
total = resData.total
}
//
if (Array.isArray(resData.list)) {
result = resData.list
}
}
xGridOpt.loading = false
resolve({ total, result })
}
/** 接口需要的参数 */
const params = {
username: form.username || undefined,
phone: form.phone || undefined,
size: page.pageSize,
currentPage: page.currentPage
}
/** 调用接口 */
getTableDataApi(params).then(callback).catch(callback)
})
}
}
}
})
//#endregion
//#region vxe-modal
const xModalDom = ref<VxeModalInstance>()
const xModalOpt: VxeModalProps = reactive({
title: "",
showClose: true,
escClosable: true,
maskClosable: true,
beforeHideMethod: () => {
xFormDom.value?.clearValidate()
return Promise.resolve()
}
})
//#endregion
//#region vxe-form
const xFormDom = ref<VxeFormInstance>()
const xFormOpt = reactive<VxeFormProps>({
span: 24,
titleWidth: "100px",
loading: false,
/** 是否显示标题冒号 */
titleColon: false,
/** 表单数据 */
data: {
username: "",
password: ""
},
/** 项列表 */
items: [
{
field: "username",
title: "用户名",
itemRender: { name: "$input", props: { placeholder: "请输入" } }
},
{
field: "password",
title: "密码",
itemRender: { name: "$input", props: { placeholder: "请输入" } }
},
{
align: "right",
itemRender: {
name: "$buttons",
children: [
{ props: { content: "取消" }, events: { click: () => xModalDom.value?.close() } },
{
props: { type: "submit", content: "确定", status: "primary" },
events: { click: () => crudStore.onSubmitForm() }
}
]
}
}
],
/** 校验规则 */
rules: {
username: [
{
required: true,
validator: ({ itemValue }) => {
if (!itemValue) {
return new Error("请输入")
}
if (!itemValue.trim()) {
return new Error("空格无效")
}
}
}
],
password: [
{
required: true,
validator: ({ itemValue }) => {
if (!itemValue) {
return new Error("请输入")
}
if (!itemValue.trim()) {
return new Error("空格无效")
}
}
}
]
}
})
//#endregion
//#region CRUD
const crudStore = reactive({
/** 表单类型修改true 新增false */
isUpdate: true,
/** 加载表格数据 */
commitQuery: () => xGridDom.value?.commitProxy("query"),
/** 清空表格数据 */
clearTable: () => xGridDom.value?.reloadData([]),
/** 点击显示弹窗 */
onShowModal: (row?: IRowMeta) => {
if (row) {
crudStore.isUpdate = true
xModalOpt.title = "修改用户"
//
xFormOpt.data.username = row.username
} else {
crudStore.isUpdate = false
xModalOpt.title = "新增用户"
}
//
if (xFormOpt.items) {
if (xFormOpt.items[0]?.itemRender?.props) {
xFormOpt.items[0].itemRender.props.disabled = crudStore.isUpdate
}
}
xModalDom.value?.open()
nextTick(() => {
!crudStore.isUpdate && xFormDom.value?.reset()
xFormDom.value?.clearValidate()
})
},
/** 确定并保存 */
onSubmitForm: () => {
if (xFormOpt.loading) return
xFormDom.value?.validate((errMap?: VxeFormDefines.ValidateErrorMapParams) => {
if (errMap) return
xFormOpt.loading = true
const callback = (err?: any) => {
xFormOpt.loading = false
if (err) return
xModalDom.value?.close()
ElMessage.success("操作成功")
!crudStore.isUpdate && crudStore.afterInsert()
crudStore.commitQuery()
}
if (crudStore.isUpdate) {
//
setTimeout(() => callback(), 1000)
} else {
//
setTimeout(() => callback(), 1000)
}
})
},
/** 新增后是否跳入最后一页 */
afterInsert: () => {
const pager: VxeGridPropTypes.ProxyAjaxQueryPageParams = xGridDom.value?.getProxyInfo()?.pager
if (pager) {
const currTotal: number = pager.currentPage * pager.pageSize
if (currTotal === pager.total) {
++pager.currentPage
}
}
},
/** 删除 */
onDelete: (row: IRowMeta) => {
const tip = `确定 <strong style='color:red;'>删除</strong> 用户 <strong style='color:#409eff;'>${row.username}</strong> `
const config: ElMessageBoxOptions = {
type: "warning",
showClose: true,
closeOnClickModal: true,
closeOnPressEscape: true,
cancelButtonText: "取消",
confirmButtonText: "确定",
dangerouslyUseHTMLString: true
}
ElMessageBox.confirm(tip, "提示", config)
.then(() => {
deleteTableDataApi(row.id)
.then(() => {
ElMessage.success("删除成功")
crudStore.afterDelete()
crudStore.commitQuery()
})
.catch(() => 1)
})
.catch(() => 1)
},
/** 删除后是否返回上一页 */
afterDelete: () => {
const tableData: IRowMeta[] = xGridDom.value!.getData()
const pager: VxeGridPropTypes.ProxyAjaxQueryPageParams = xGridDom.value?.getProxyInfo()?.pager
if (pager && pager.currentPage > 1 && tableData.length === 1) {
--pager.currentPage
}
},
/** 更多自定义方法 */
moreFunc: () => {}
})
//#endregion
</script>
<template>
<div class="app-container">
<!-- 表格 -->
<vxe-grid ref="xGridDom" v-bind="xGridOpt">
<!-- 左侧按钮列表 -->
<template #toolbar-btns>
<vxe-button status="primary" icon="vxe-icon-add" @click="crudStore.onShowModal()">新增用户</vxe-button>
<vxe-button status="danger" icon="vxe-icon-delete">批量删除</vxe-button>
</template>
<!-- 操作 -->
<template #row-operate="{ row }">
<el-button link type="primary" @click="crudStore.onShowModal(row)">修改</el-button>
<el-button link type="danger" @click="crudStore.onDelete(row)">删除</el-button>
</template>
</vxe-grid>
<!-- 弹窗 -->
<vxe-modal ref="xModalDom" v-bind="xModalOpt">
<!-- 表单 -->
<vxe-form ref="xFormDom" v-bind="xFormOpt" />
</vxe-modal>
</div>
</template>
<style lang="scss" scoped></style>

View File

@ -1,11 +0,0 @@
import { type VxeColumnPropTypes } from "vxe-table/types/column"
const solts: VxeColumnPropTypes.Slots = {
default: ({ row, column }) => {
const cellValue = row[column.field]
const type = cellValue === "admin" ? "" : "warning"
return [<span class={`el-tag el-tag--${type} el-tag--plain`}>{cellValue}</span>]
}
}
export default solts

View File

@ -1,16 +0,0 @@
import { type VxeColumnPropTypes } from "vxe-table/types/column"
const solts: VxeColumnPropTypes.Slots = {
default: ({ row, column }) => {
const cellValue = row[column.field]
let type = "danger"
let value = "禁用"
if (cellValue) {
type = "success"
value = "启用"
}
return [<span class={`el-tag el-tag--${type} el-tag--plain`}>{value}</span>]
}
}
export default solts

View File

@ -1,16 +0,0 @@
<template>
<div h-full uno-padding-20>
<div h-full text-center flex select-none all:transition-400>
<div ma>
<div text-5xl fw100 animate-bounce-alt animate-count-infinite animate-1s>UnoCSS</div>
<div op30 dark:op60 text-lg fw300 m1>具有高性能且极具灵活性的即时原子化 CSS 引擎</div>
<div m2 flex justify-center text-lg op30 dark:op60 hover="op80" dark:hover="op80">
<a href="https://antfu.me/posts/reimagine-atomic-css-zh" target="_blank">推荐阅读重新构想原子化 CSS</a>
</div>
</div>
</div>
<div absolute bottom-5 right-0 left-0 text-center op30 dark:op60 fw300>
该页面是一个 UnoCSS 的使用案例其他页面依旧采用 Scss
</div>
</div>
</template>