refactor: 整理代码组织

This commit is contained in:
chansee97 2025-08-27 00:24:52 +08:00
parent d54810ab02
commit 34f0dda267
48 changed files with 282 additions and 869 deletions

2
.env
View File

@ -11,7 +11,7 @@ VITE_ROUTE_MODE = web
VITE_ROUTE_LOAD_MODE = static
# 设置登陆后跳转地址
VITE_HOME_PATH = /dashboard/workbench
VITE_HOME_PATH = /home
# 本地存储前缀
VITE_STORAGE_PREFIX =

View File

@ -90,20 +90,18 @@
},
"route": {
"appRoot": "Home",
"home": "Overview",
"cardList": "Card list",
"draggableList": "Draggable list",
"commonList": "Common list",
"dashboard": "Dashboard",
"demo": "Function example",
"fetch": "Request example",
"list": "List",
"monitor": "Monitoring",
"multi": "Multi-level menu",
"multi2": "Multi-level menu subpage",
"multi2Detail": "Details page of multi-level menu",
"multi3": "multi-level menu",
"multi4": "Multi-level menu 3-1",
"workbench": "Workbench",
"QRCode": "QR code",
"about": "About",
"clipboard": "Clipboard",

View File

@ -119,9 +119,7 @@
},
"route": {
"appRoot": "首页",
"dashboard": "仪表盘",
"workbench": "工作台",
"monitor": "监控页",
"home": "概览",
"multi": "多级菜单演示",
"multi2": "多级菜单子页",
"multi2Detail": "多级菜单的详情页",

View File

@ -40,6 +40,10 @@ const naiveLocale = computed(() => {
})
const propOverrides = {
ProSearchForm: {
labelPlacement: 'left',
cols: 4,
},
ProModalForm: {
labelWidth: 120,
labelPlacement: 'left',

View File

@ -1,4 +1,4 @@
import { request } from '../http'
import { request } from '../utils/alova'
/* 示例列表接口 */
export function fetchUserList() {

7
src/api/index.ts Normal file
View File

@ -0,0 +1,7 @@
export * from './login'
export * from './demo'
export * from './test'
export * from './system/user'
export * from './system/menu'
export * from './system/role'
export * from './system/dict'

View File

@ -1,4 +1,4 @@
import { request } from '../http'
import { request } from '../utils/alova'
interface LoginParams {
userName: string

View File

@ -1,4 +1,4 @@
import { request } from '../../http'
import { request } from '../../utils/alova'
// 字典类型查询参数接口
interface DictTypeQueryParams {

View File

@ -1,4 +1,4 @@
import { request } from '../../http'
import { request } from '../../utils/alova'
/**
*

View File

@ -1,4 +1,4 @@
import { request } from '../../http'
import { request } from '../../utils/alova'
// 角色查询参数接口
interface RoleQueryParams {

View File

@ -1,4 +1,4 @@
import { request } from '../../http'
import { request } from '../../utils/alova'
// 用户查询参数接口
interface UserQueryParams {

View File

@ -1,4 +1,4 @@
import { blankInstance, request } from '../http'
import { blankInstance, request } from '@/utils/alova'
/* get方法测试 */
export function fetchGet(params?: any) {

14
src/modules/navie-pro.ts Normal file
View File

@ -0,0 +1,14 @@
import {
create,
ProInput,
ProSelect,
} from 'pro-naive-ui'
import type { App } from 'vue'
const proNaive = create({
components: [ProInput, ProSelect],
})
export function install(app: App) {
app.use(proNaive)
}

View File

@ -1,37 +1,14 @@
export const staticRoutes: AppRoute.RowRoute[] = [
{
name: 'dashboard',
path: '/dashboard',
title: '仪表盘',
requiresAuth: true,
name: 'home',
path: '/home',
title: '概览',
icon: 'icon-park-outline:analysis',
menuType: 'dir',
componentPath: null,
id: 1,
parentId: null,
},
{
name: 'workbench',
path: '/dashboard/workbench',
title: '工作台',
requiresAuth: true,
icon: 'icon-park-outline:alarm',
pinTab: true,
menuType: 'page',
componentPath: '/dashboard/workbench/index.vue',
id: 101,
parentId: 1,
},
{
name: 'monitor',
path: '/dashboard/monitor',
title: '监控页',
requiresAuth: true,
icon: 'icon-park-outline:anchor',
menuType: 'page',
componentPath: '/dashboard/monitor/index.vue',
id: 102,
parentId: 1,
component: '/build-in/home/index.vue',
id: 1,
parentId: 0,
},
{
name: 'multi',
@ -40,7 +17,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:list',
menuType: 'dir',
componentPath: null,
component: null,
id: 2,
parentId: null,
},
@ -51,7 +28,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:list',
menuType: 'page',
componentPath: '/demo/multi/multi-2/index.vue',
component: '/demo/multi/multi-2/index.vue',
id: 201,
parentId: 2,
},
@ -64,7 +41,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
hide: true,
activeMenu: '/multi/multi-2',
menuType: 'page',
componentPath: '/demo/multi/multi-2/detail/index.vue',
component: '/demo/multi/multi-2/detail/index.vue',
id: 20101,
parentId: 2,
},
@ -75,7 +52,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:list',
menuType: 'dir',
componentPath: null,
component: null,
id: 202,
parentId: 2,
},
@ -85,7 +62,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '多级菜单3-1',
requiresAuth: true,
icon: 'icon-park-outline:list',
componentPath: '/demo/multi/multi-3/multi-4/index.vue',
component: '/demo/multi/multi-3/multi-4/index.vue',
id: 20201,
parentId: 202,
},
@ -96,7 +73,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:list-two',
menuType: 'dir',
componentPath: null,
component: null,
id: 3,
parentId: null,
},
@ -106,7 +83,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '常用列表',
requiresAuth: true,
icon: 'icon-park-outline:list-view',
componentPath: '/demo/list/common-list/index.vue',
component: '/demo/list/common-list/index.vue',
id: 301,
parentId: 3,
},
@ -116,7 +93,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '卡片列表',
requiresAuth: true,
icon: 'icon-park-outline:view-grid-list',
componentPath: '/demo/list/card-list/index.vue',
component: '/demo/list/card-list/index.vue',
id: 302,
parentId: 3,
},
@ -126,7 +103,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '拖拽列表',
requiresAuth: true,
icon: 'icon-park-outline:menu-fold',
componentPath: '/demo/list/draggable-list/index.vue',
component: '/demo/list/draggable-list/index.vue',
id: 303,
parentId: 3,
},
@ -137,7 +114,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:application-one',
menuType: 'dir',
componentPath: null,
component: null,
id: 4,
parentId: null,
},
@ -147,7 +124,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '请求示例',
requiresAuth: true,
icon: 'icon-park-outline:international',
componentPath: '/demo/fetch/index.vue',
component: '/demo/fetch/index.vue',
id: 401,
parentId: 4,
},
@ -157,7 +134,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: 'ECharts',
requiresAuth: true,
icon: 'icon-park-outline:chart-proportion',
componentPath: '/demo/echarts/index.vue',
component: '/demo/echarts/index.vue',
id: 402,
parentId: 4,
},
@ -168,7 +145,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'carbon:map',
keepAlive: true,
componentPath: '/demo/map/index.vue',
component: '/demo/map/index.vue',
id: 403,
parentId: 4,
},
@ -179,7 +156,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:editor',
menuType: 'dir',
componentPath: null,
component: null,
id: 404,
parentId: 4,
},
@ -189,7 +166,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: 'MarkDown',
requiresAuth: true,
icon: 'ri:markdown-line',
componentPath: '/demo/editor/md/index.vue',
component: '/demo/editor/md/index.vue',
id: 40401,
parentId: 404,
},
@ -199,7 +176,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '富文本',
requiresAuth: true,
icon: 'icon-park-outline:edit-one',
componentPath: '/demo/editor/rich/index.vue',
component: '/demo/editor/rich/index.vue',
id: 40402,
parentId: 404,
},
@ -209,7 +186,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '剪贴板',
requiresAuth: true,
icon: 'icon-park-outline:clipboard',
componentPath: '/demo/clipboard/index.vue',
component: '/demo/clipboard/index.vue',
id: 405,
parentId: 4,
},
@ -219,7 +196,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '图标',
requiresAuth: true,
icon: 'local:cool',
componentPath: '/demo/icons/index.vue',
component: '/demo/icons/index.vue',
id: 406,
parentId: 4,
},
@ -229,7 +206,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '二维码',
requiresAuth: true,
icon: 'icon-park-outline:two-dimensional-code',
componentPath: '/demo/qr-code/index.vue',
component: '/demo/qr-code/index.vue',
id: 407,
parentId: 4,
},
@ -239,7 +216,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '省市区联动',
requiresAuth: true,
icon: 'icon-park-outline:add-subset',
componentPath: '/demo/cascader/index.vue',
component: '/demo/cascader/index.vue',
id: 408,
parentId: 4,
},
@ -249,7 +226,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '字典示例',
requiresAuth: true,
icon: 'icon-park-outline:book-one',
componentPath: '/demo/dict/index.vue',
component: '/demo/dict/index.vue',
id: 409,
parentId: 4,
},
@ -260,7 +237,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:file-doc',
menuType: 'dir',
componentPath: null,
component: null,
id: 5,
parentId: null,
},
@ -270,7 +247,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: 'Vue',
requiresAuth: true,
icon: 'logos:vue',
componentPath: '/demo/documents/vue/index.vue',
component: '/demo/documents/vue/index.vue',
id: 501,
parentId: 5,
},
@ -280,7 +257,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: 'Vite',
requiresAuth: true,
icon: 'logos:vitejs',
componentPath: '/demo/documents/vite/index.vue',
component: '/demo/documents/vite/index.vue',
id: 502,
parentId: 5,
},
@ -291,7 +268,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'logos:vueuse',
href: 'https://vueuse.org/guide/',
componentPath: 'null',
component: 'null',
id: 503,
parentId: 5,
},
@ -303,7 +280,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'local:logo',
href: 'https://nova-admin-docs.netlify.app/',
componentPath: '2333333',
component: '2333333',
id: 504,
parentId: 5,
},
@ -314,7 +291,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'local:logo',
href: '/public',
componentPath: 'null',
component: 'null',
id: 505,
parentId: 5,
},
@ -325,7 +302,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:people-safe',
menuType: 'dir',
componentPath: null,
component: null,
id: 6,
parentId: null,
},
@ -335,7 +312,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '权限示例',
requiresAuth: true,
icon: 'icon-park-outline:right-user',
componentPath: '/demo/permission/permission/index.vue',
component: '/demo/permission/permission/index.vue',
id: 601,
parentId: 6,
},
@ -348,7 +325,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
'super',
],
icon: 'icon-park-outline:wrong-user',
componentPath: '/demo/permission/just-super/index.vue',
component: '/demo/permission/just-super/index.vue',
id: 602,
parentId: 6,
},
@ -359,27 +336,27 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
icon: 'icon-park-outline:setting',
menuType: 'dir',
componentPath: null,
component: null,
id: 7,
parentId: null,
},
{
name: 'accountSetting',
path: '/setting/account',
path: '/setting/user',
title: '用户设置',
requiresAuth: true,
icon: 'icon-park-outline:every-user',
componentPath: '/setting/account/index.vue',
component: '/setting/user/index.vue',
id: 701,
parentId: 7,
},
{
name: 'dictionarySetting',
path: '/setting/dictionary',
path: '/setting/dict',
title: '字典设置',
requiresAuth: true,
icon: 'icon-park-outline:book-one',
componentPath: '/setting/dictionary/index.vue',
component: '/setting/dict/index.vue',
id: 702,
parentId: 7,
},
@ -389,7 +366,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '菜单设置',
requiresAuth: true,
icon: 'icon-park-outline:application-menu',
componentPath: '/setting/menu/index.vue',
component: '/setting/menu/index.vue',
id: 703,
parentId: 7,
},
@ -399,7 +376,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
title: '关于',
requiresAuth: true,
icon: 'icon-park-outline:info',
componentPath: '/demo/about/index.vue',
component: '/demo/about/index.vue',
id: 8,
parentId: null,
},
@ -410,7 +387,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
requiresAuth: true,
hide: true,
icon: 'carbon:user-avatar-filled-alt',
componentPath: '/build-in/user-center/index.vue',
component: '/build-in/user-center/index.vue',
id: 999,
parentId: null,
},

View File

@ -1,7 +0,0 @@
export * from './api/login'
export * from './api/demo'
export * from './api/test'
export * from './api/system/user'
export * from './api/system/menu'
export * from './api/system/role'
export * from './api/system/dict'

View File

@ -1,16 +1,16 @@
import { router } from '@/router'
import { fetchLogin, fetchUserInfo } from '@/service'
import { fetchLogin, fetchUserInfo } from '@/api'
import { local } from '@/utils'
import { useRouteStore } from './router'
import { useTabStore } from './tab'
interface AuthStatus {
userInfo: Entity.User | null
userInfo: Entity.User | Record<string, any>
}
export const useAuthStore = defineStore('auth-store', {
state: (): AuthStatus => {
return {
userInfo: null,
userInfo: {},
}
},
getters: {

View File

@ -1,4 +1,4 @@
import { fetchDictList } from '@/service'
import { fetchDictList } from '@/api'
import { session } from '@/utils'
export const useDictStore = defineStore('dict-store', {

View File

@ -31,15 +31,14 @@ export function createRoutes(routes: AppRoute.RowRoute[]) {
// Generate routes, no need to import files for those with redirect
const modules = import.meta.glob('@/views/**/*.vue')
resultRouter = resultRouter.map((item: AppRoute.Route) => {
if (item.componentPath && !item.redirect)
item.component = modules[`/src/views${item.componentPath}`]
if (item.component && !item.redirect)
item.component = modules[`/src/views${item.component}`]
return item
})
// Generate route tree
resultRouter = arrayToTree(resultRouter, {
parentProperty: 'parentId',
customID: 'id',
}) as AppRoute.Route[]
const appRootRoute: RouteRecordRaw = {
@ -104,7 +103,6 @@ export function createMenus(userRoutes: AppRoute.RowRoute[]) {
// generate side menu
return arrayToTree(transformAuthRoutesToMenus(visibleMenus), {
parentProperty: 'parentId',
customID: 'id',
})
}

View File

@ -1,7 +1,7 @@
import type { MenuOption } from 'naive-ui'
import { router } from '@/router'
import { staticRoutes } from '@/router/routes.static'
import { fetchUserRoutes } from '@/service'
import { fetchUserRoutes } from '@/api'
import { $t } from '@/utils'
import { createMenus, createRoutes, generateCacheRoutes } from './helper'

View File

@ -5,7 +5,7 @@ namespace Entity {
type MenuType = 'directory' | 'page' | 'permission'
interface Menu {
menuId: number
id: number
/**
*
*/

View File

@ -39,7 +39,7 @@ declare namespace AppRoute {
/** 路由重定向 */
redirect?: string
/* 页面组件地址 */
componentPath?: string | null
component?: string | null
/* 路由id */
id: number
/* 父级路由id顶级页面为null */

View File

@ -1,4 +1,4 @@
import { fetchRefreshToken } from '@/service'
import { fetchRefreshToken } from '@/api'
import { useAuthStore } from '@/store'
import { local } from '@/utils'

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { FormInst } from 'naive-ui'
import { useAuthStore } from '@/store'
import { fetchCaptchaImage } from '@/service'
import { fetchCaptchaImage } from '@/api'
import { local } from '@/utils'
import { useBoolean } from '@/hooks'

View File

@ -1,130 +0,0 @@
<script setup lang="ts">
import { useEcharts } from '@/hooks'
import type { ECOption } from '@/hooks'
import { graphic } from 'echarts'
const lineOptions = ref<ECOption>({
tooltip: {
trigger: 'axis',
},
grid: {
left: '2%',
right: '2%',
bottom: '0%',
top: '0%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['10:00', '10:10', '10:10', '10:30', '10:40', '10:50'],
axisTick: {
show: false,
},
axisLine: {
show: false,
},
},
yAxis: {
type: 'value',
splitLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
},
series: [{
name: '2',
type: 'line',
z: 3,
showSymbol: false,
smoothMonotone: 'x',
lineStyle: {
width: 3,
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: 'rgba(59,102,246)', // 0%
}, {
offset: 1,
color: 'rgba(118,237,252)', // 100%
}],
},
shadowBlur: 4,
shadowColor: 'rgba(69,126,247,.2)',
shadowOffsetY: 4,
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: 'rgba(227,233,250,.9)', // 0%
}, {
offset: 1,
color: 'rgba(248,251,252,.3)', // 100%
}],
},
},
smooth: true,
data: [20, 56, 17, 40, 68, 42],
}, {
name: '1',
type: 'line',
showSymbol: false,
smoothMonotone: 'x',
lineStyle: {
width: 3,
color: new graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(255,84,108)',
}, {
offset: 1,
color: 'rgba(252,140,118)',
}], false),
shadowBlur: 4,
shadowColor: 'rgba(253,121,128,.2)',
shadowOffsetY: 4,
},
areaStyle: {
color: new graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(255,84,108,.15)',
}, {
offset: 1,
color: 'rgba(252,140,118,0)',
}], false),
},
smooth: true,
data: [20, 71, 8, 50, 57, 32],
}],
}) as Ref<ECOption>
useEcharts('lineRef', lineOptions)
</script>
<template>
<div
ref="lineRef"
class="h-400px"
/>
</template>
<style scoped>
</style>

View File

@ -1,107 +0,0 @@
<script setup lang="ts">
import { useEcharts } from '@/hooks'
import type { ECOption } from '@/hooks'
import { graphic } from 'echarts'
const chartData = [
{ name: '1', value: 300 },
{ name: '2', value: 400 },
{ name: '3', value: 452 },
{ name: '4', value: 770 },
{ name: '5', value: 650 },
{ name: '6', value: 256 },
{ name: '7', value: 350 },
{ name: '8', value: 422 },
{ name: '9', value: 235 },
{ name: '10', value: 658 },
{ name: '11', value: 600 },
{ name: '12', value: 400 },
{ name: '13', value: 523 },
{ name: '14', value: 482 },
{ name: '15', value: 423 },
]
const xData = chartData.map(v => v.name)
const sData = chartData.map(v => v.value)
const option = ref<ECOption>({
tooltip: {
trigger: 'axis',
},
grid: {
left: '2%',
right: '2%',
bottom: '0%',
top: '0%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: xData,
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: 'rgba(151,151,151,0.5)',
type: 'dashed',
},
},
axisLabel: {
margin: 10,
color: '#666',
fontSize: 14,
},
},
yAxis: {
type: 'value',
splitLine: {
lineStyle: {
color: 'rgba(151,151,151,0.5)',
type: 'dashed',
},
},
axisLine: {
lineStyle: {
color: 'rgba(151,151,151,0.5)',
type: 'dashed',
},
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
},
series: [{
type: 'bar',
barWidth: '20px',
data: sData,
itemStyle: {
color: new graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#00BD89', // 0%
}, {
offset: 1,
color: '#C9F9E1', // 100%
}], false),
},
}],
}) as Ref<ECOption>
useEcharts('lineRef', option)
</script>
<template>
<div
ref="lineRef"
class="h-400px"
/>
</template>
<style scoped>
</style>

View File

@ -1,60 +0,0 @@
<script setup lang="ts">
import { useEcharts } from '@/hooks'
import type { ECOption } from '@/hooks'
const option = ref<ECOption>({
tooltip: {
trigger: 'item',
formatter: '{b} : {d}%',
},
legend: {
orient: 'horizontal',
top: 30,
padding: 5,
itemWidth: 20,
itemHeight: 12,
textStyle: {
color: '#777',
},
},
series: [{
type: 'pie',
radius: ['45%', '60%'],
center: ['50%', '50%'],
label: {
show: true,
formatter: '{b} : {d}%',
color: '#777',
},
labelLine: {
show: true,
length2: 10,
},
data: [
{
value: 335,
name: '直接访问',
},
{
value: 77,
name: 'Bilibili',
},
{
value: 82,
name: '知乎',
},
{
value: 421,
name: '小红书',
},
],
}],
}) as Ref<ECOption>
useEcharts('lineRef', option)
</script>
<template>
<div ref="lineRef" class="h-400px" />
</template>
<style scoped></style>

View File

@ -1,258 +0,0 @@
<script setup lang="ts">
import { useAppStore } from '@/store'
import Chart from './components/chart.vue'
import Chart2 from './components/chart2.vue'
import Chart3 from './components/chart3.vue'
const appStore = useAppStore()
const tableData = [
{
id: 0,
name: '商品名称1',
start: '2022-02-02',
end: '2022-02-02',
prograss: '100',
status: '已完成',
},
{
id: 0,
name: '商品名称2',
start: '2022-02-02',
end: '2022-02-02',
prograss: '50',
status: '交易中',
},
{
id: 0,
name: '商品名称3',
start: '2022-02-02',
end: '2022-02-02',
prograss: '100',
status: '已完成',
},
]
</script>
<template>
<n-grid
:x-gap="16"
:y-gap="16"
:cols="12"
item-responsive
responsive="screen"
>
<!-- 统计卡片 - 移动端每行2个桌面端每行4个 -->
<n-gi span="6 m:3">
<n-card>
<n-space
justify="space-between"
align="center"
>
<n-statistic label="访问量">
<n-number-animation
:from="0"
:to="12039"
show-separator
/>
</n-statistic>
<n-icon
color="#de4307"
size="42"
>
<icon-park-outline-chart-histogram />
</n-icon>
</n-space>
<template #footer>
<n-space justify="space-between">
<span>累计访问数</span>
<span><n-number-animation
:from="0"
:to="322039"
show-separator
/></span>
</n-space>
</template>
</n-card>
</n-gi>
<n-gi span="6 m:3">
<n-card>
<n-space
justify="space-between"
align="center"
>
<n-statistic label="下载量">
<n-number-animation
:from="0"
:to="12039"
show-separator
/>
</n-statistic>
<n-icon
color="#ffb549"
size="42"
>
<icon-park-outline-chart-graph />
</n-icon>
</n-space>
<template #footer>
<n-space justify="space-between">
<span>累计下载量</span>
<span><n-number-animation
:from="0"
:to="322039"
show-separator
/></span>
</n-space>
</template>
</n-card>
</n-gi>
<n-gi span="6 m:3">
<n-card>
<n-space
justify="space-between"
align="center"
>
<n-statistic label="浏览量">
<n-number-animation
:from="0"
:to="12039"
show-separator
/>
</n-statistic>
<n-icon
color="#1687a7"
size="42"
>
<icon-park-outline-average />
</n-icon>
</n-space>
<template #footer>
<n-space justify="space-between">
<span>累计浏览量</span>
<span><n-number-animation
:from="0"
:to="322039"
show-separator
/></span>
</n-space>
</template>
</n-card>
</n-gi>
<n-gi span="6 m:3">
<n-card>
<n-space
justify="space-between"
align="center"
>
<n-statistic label="注册量">
<n-number-animation
:from="0"
:to="12039"
show-separator
/>
</n-statistic>
<n-icon
color="#42218E"
size="42"
>
<icon-park-outline-chart-pie />
</n-icon>
</n-space>
<template #footer>
<n-space justify="space-between">
<span>累计注册量</span>
<span><n-number-animation
:from="0"
:to="322039"
show-separator
/></span>
</n-space>
</template>
</n-card>
</n-gi>
<!-- 图表区域 - 全宽显示 -->
<n-gi :span="12">
<n-card content-style="padding: 0;">
<n-tabs
type="line"
size="large"
:tabs-padding="20"
pane-style="padding: 20px;"
>
<n-tab-pane name="流量趋势">
<Chart />
</n-tab-pane>
<n-tab-pane name="访问量趋势">
<Chart2 />
</n-tab-pane>
</n-tabs>
</n-card>
</n-gi>
<!-- 访问来源 - 移动端全宽桌面端1/3 -->
<n-gi span="12 m:4">
<n-card
title="访问来源"
:segmented="{
content: true,
}"
>
<Chart3 />
</n-card>
</n-gi>
<!-- 成交记录 - 移动端全宽桌面端2/3 -->
<n-gi span="12 m:8">
<n-card
title="成交记录"
:segmented="{
content: true,
}"
>
<template #header-extra>
<n-button
type="primary"
quaternary
>
更多
</n-button>
</template>
<n-table
:bordered="false"
:single-line="false"
:scroll-x="appStore.isMobile ? 600 : undefined"
>
<thead>
<tr>
<th>交易名称</th>
<th>开始时间</th>
<th>结束时间</th>
<th>进度</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr
v-for="item in tableData"
:key="item.id"
>
<td>{{ item.name }}</td>
<td>{{ item.start }}</td>
<td>{{ item.end }}</td>
<td>{{ item.prograss }}%</td>
<td>
<n-tag
:bordered="false"
type="info"
>
{{ item.status }}
</n-tag>
</td>
</tr>
</tbody>
</n-table>
</n-card>
</n-gi>
</n-grid>
</template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { fetchDictList } from '@/service'
import { fetchDictList } from '@/api'
import { useDictStore } from '@/store'
const { dict } = useDictStore()

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import {
fetchDelete,
} from '@/service'
} from '@/api'
const emit = defineEmits<{
update: [data: any] //

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import {
getBlob,
} from '@/service'
} from '@/api'
const emit = defineEmits<{
update: [data: any]

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import {
downloadFile,
} from '@/service'
} from '@/api'
import { useRequest } from 'alova/client'
import { normalizeSizeUnits } from '@/utils'

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import {
FailedRequest,
} from '@/service'
} from '@/api'
const emit = defineEmits<{
update: [data: any] //

View File

@ -1,164 +0,0 @@
<script setup lang="tsx">
import type { FormInst } from 'naive-ui'
import { useBoolean } from '@/hooks'
import { deleteUser, getUserList, updateUser } from '@/service'
import { createUserColumns } from './columns'
import TableModal from './components/TableModal.vue'
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
const initialModel = {
condition_1: '',
condition_2: '',
}
const model = ref({ ...initialModel })
function handleResetSearch() {
model.value = { ...initialModel }
}
const formRef = ref<FormInst | null>()
const modalRef = ref()
async function delteteUser(id: number) {
try {
await deleteUser(id)
window.$message.success('用户删除成功')
getUserPageList() //
}
catch {
}
}
// columns
const columns = createUserColumns({
onEdit: row => modalRef.value?.openModal('edit', row),
onDelete: delteteUser,
onStatusChange: handleUpdateDisabled,
})
const count = ref(0)
const listData = ref<Entity.User[]>([])
async function handleUpdateDisabled(value: 0 | 1, id: number) {
try {
// 使updateUser
await updateUser(id, { userStatus: value })
const index = listData.value.findIndex(item => item.userId === id)
if (index > -1) {
listData.value[index].userStatus = value
}
window.$message.success('用户状态更新成功')
}
catch {
}
}
async function getUserPageList() {
startLoading()
try {
const res = await getUserList({
pageNum: 1,
pageSize: 20,
username: model.value.condition_1,
gender: model.value.condition_2 as any,
})
listData.value = res.data.list
count.value = res.data.total
}
finally {
endLoading()
}
}
onMounted(() => {
getUserPageList()
})
function changePage(page: number, size: number) {
window.$message.success(`分页器:${page},${size}`)
}
const treeData = ref([
{
id: '1',
label: '安徽总公司',
children: [
{
id: '2',
label: '合肥分公司',
children: [
{
id: '4',
label: '财务部门',
},
{
id: '5',
label: '采购部门',
},
],
},
{
id: '3',
label: '芜湖分公司',
},
],
},
])
</script>
<template>
<n-flex>
<n-card class="w-70">
<n-tree
block-line
:data="treeData"
key-field="id"
/>
</n-card>
<NSpace vertical class="flex-1">
<n-card>
<n-form ref="formRef" :model="model" label-placement="left" inline :show-feedback="false">
<n-flex>
<n-form-item label="姓名" path="condition_1">
<n-input v-model:value="model.condition_1" placeholder="请输入" />
</n-form-item>
<n-form-item label="性别" path="condition_2">
<n-input v-model:value="model.condition_2" placeholder="请输入" />
</n-form-item>
<n-flex class="ml-auto">
<NButton type="primary" @click="getUserPageList">
<template #icon>
<icon-park-outline-search />
</template>
搜索
</NButton>
<NButton strong secondary @click="handleResetSearch">
<template #icon>
<icon-park-outline-redo />
</template>
重置
</NButton>
</n-flex>
</n-flex>
</n-form>
</n-card>
<n-card class="flex-1">
<template #header>
<NButton type="primary" @click="modalRef.openModal('add')">
<template #icon>
<icon-park-outline-add-one />
</template>
新建用户
</NButton>
</template>
<NSpace vertical>
<n-data-table :columns="columns" :data="listData" :loading="loading" />
<Pagination :count="count" @change="changePage" />
</NSpace>
<TableModal ref="modalRef" modal-name="用户" @success="getUserPageList" />
</n-card>
</NSpace>
</n-flex>
</template>

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { FormRules } from 'naive-ui'
import { useBoolean } from '@/hooks'
import { createDictData, createDictType, updateDictData, updateDictType } from '@/service'
import { createDictData, createDictType, updateDictData, updateDictType } from '@/api'
interface Props {
modalName?: string

View File

@ -1,6 +1,6 @@
<script setup lang="tsx">
import { useBoolean } from '@/hooks'
import { deleteDictData, deleteDictType, getDictDataByType, getDictTypeList } from '@/service'
import { deleteDictData, deleteDictType, getDictDataByType, getDictTypeList } from '@/api'
import { createDictDataColumns, createDictTypeColumns } from './columns'
import DictModal from './components/DictModal.vue'

View File

@ -95,7 +95,7 @@ export function createMenuColumns(actions: MenuColumnActions): DataTableColumns<
>
</NButton>
<NPopconfirm onPositiveClick={() => onDelete(row.menuId)}>
<NPopconfirm onPositiveClick={() => onDelete(row.id)}>
{{
default: () => '确认删除',
trigger: () => <NButton text type="error"></NButton>,

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { useBoolean } from '@/hooks'
import { createMenu, getMenuById, selectMenuTree, updateMenu } from '@/service'
import { createMenu, getMenuById, selectMenuTree, updateMenu } from '@/api'
import { createProModalForm } from 'pro-naive-ui'
import DirectoryForm from './DirectoryForm.vue'
import PageForm from './PageForm.vue'
@ -72,14 +72,14 @@ async function openModal(type: ModalType = 'add', data?: Partial<Entity.Menu>) {
const handlers = {
async add() {
// menuId
if (data?.menuId) {
modalForm.values.value.parentId = data.menuId
if (data?.id) {
modalForm.values.value.parentId = data.id
}
},
async edit() {
if (!data?.menuId)
if (!data?.id)
return
const response = await getMenuById(data.menuId)
const response = await getMenuById(data.id)
modalForm.values.value = response.data
},
}
@ -99,7 +99,7 @@ async function submitModal(filedValues: Partial<Entity.Menu>) {
},
async edit() {
try {
await updateMenu(modalForm.values.value.menuId!, filedValues)
await updateMenu(modalForm.values.value.id!, filedValues)
window.$message.success('菜单更新成功')
}
catch (error) {
@ -157,10 +157,7 @@ defineExpose({
title="标题"
path="title"
/>
<component
:is="currentFormComponent"
:tree-data="treeData"
/>
<component :is="currentFormComponent" />
<pro-textarea
title="备注"
path="remark"

View File

@ -1,12 +1,21 @@
<script setup lang="ts">
import { useBoolean } from '@/hooks'
import { deleteMenu, getMenuList } from '@/service'
import { deleteMenu, getMenuList } from '@/api'
import { createMenuColumns } from './columns'
import MenuModal from './components/MenuModal.vue'
import arrayToTree from 'array-to-tree'
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
const menuModalRef = ref()
// columns
const columns = createMenuColumns({
onEdit: row => menuModalRef.value.openModal('edit', row),
onDelete: deleteData,
onAdd: row => menuModalRef.value.openModal('add', row),
})
async function deleteData(id: number) {
try {
await deleteMenu(id)
@ -18,17 +27,6 @@ async function deleteData(id: number) {
}
}
const menuModalRef = ref()
// columns
const columns = createMenuColumns({
onEdit: row => menuModalRef.value.openModal('edit', row),
onDelete: deleteData,
onAdd: row => menuModalRef.value.openModal('add', row),
})
const tableData = ref<Entity.Menu[]>([])
//
function sortMenuTree(menus: Entity.Menu[]): Entity.Menu[] {
// sort
@ -45,13 +43,13 @@ function sortMenuTree(menus: Entity.Menu[]): Entity.Menu[] {
}))
}
const tableData = ref<Entity.Menu[]>([])
async function getAllRoutes() {
startLoading()
try {
const { data } = await getMenuList()
const treeData = arrayToTree(data, {
parentProperty: 'parentId',
customID: 'menuId',
})
// sort

View File

@ -1,6 +1,36 @@
import type { DataTableColumns } from 'naive-ui'
import { NButton, NPopconfirm, NSpace, NSwitch, NTag } from 'naive-ui'
import CopyText from '@/components/custom/CopyText.vue'
import type { ProSearchFormColumns } from 'pro-naive-ui'
import { renderProCopyableText, renderProDateText } from 'pro-naive-ui'
export const searchColumns: ProSearchFormColumns<Entity.User> = [
{
title: '用户名',
path: 'username',
},
{
title: '手机号',
path: 'phone',
},
{
title: '状态',
path: 'userStatus',
field: 'select',
fieldProps: {
options: [
{
label: '启用',
value: 1,
},
{
label: '禁用',
value: 0,
},
],
},
},
]
// 用户管理columns配置函数
interface UserColumnActions {
@ -11,12 +41,6 @@ interface UserColumnActions {
export function createUserColumns(actions: UserColumnActions): DataTableColumns<Entity.User> {
return [
{
title: 'ID',
align: 'center',
key: 'userId',
width: 80,
},
{
title: '用户名',
align: 'center',
@ -53,9 +77,7 @@ export function createUserColumns(actions: UserColumnActions): DataTableColumns<
title: '手机号',
align: 'center',
key: 'phone',
render: (row) => {
return row.phone ? <CopyText value={row.phone} /> : '-'
},
render: row => renderProCopyableText(row.phone),
},
{
title: '状态',
@ -65,8 +87,8 @@ export function createUserColumns(actions: UserColumnActions): DataTableColumns<
return (
<NSwitch
value={row.userStatus}
checked-value={1}
unchecked-value={0}
checked-value={0}
unchecked-value={1}
onUpdateValue={(value: 0 | 1) =>
actions.onStatusChange(value, row.userId)}
>
@ -79,9 +101,9 @@ export function createUserColumns(actions: UserColumnActions): DataTableColumns<
title: '创建时间',
align: 'center',
key: 'createTime',
render: (row) => {
return row.createTime ? new Date(row.createTime).toLocaleDateString() : '-'
},
render: row => renderProDateText(row.createTime, {
pattern: 'datetime',
}),
},
{
title: '操作',
@ -91,7 +113,7 @@ export function createUserColumns(actions: UserColumnActions): DataTableColumns<
return (
<NSpace justify="center">
<NButton
size="small"
text
onClick={() => actions.onEdit(row)}
>
@ -99,7 +121,7 @@ export function createUserColumns(actions: UserColumnActions): DataTableColumns<
<NPopconfirm onPositiveClick={() => actions.onDelete(row.userId)}>
{{
default: () => '确认删除',
trigger: () => <NButton size="small" type="error"></NButton>,
trigger: () => <NButton text type="error"></NButton>,
}}
</NPopconfirm>
</NSpace>

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { useBoolean } from '@/hooks'
import { createUser, fetchRoleList, updateUser } from '@/service'
import { createUser, fetchRoleList, updateUser } from '@/api'
interface Props {
modalName?: string

View File

@ -0,0 +1,126 @@
<script setup lang="tsx">
import { createProSearchForm, useNDataTable } from 'pro-naive-ui'
import { deleteUser, getUserList } from '@/api'
import { createUserColumns, searchColumns } from './columns'
import TableModal from './components/TableModal.vue'
const searchForm = createProSearchForm({
initialValues: {
},
})
const {
table: {
tableProps,
},
search: {
proSearchFormProps,
},
refresh,
} = useNDataTable(getUserPage, {
form: searchForm,
})
const modalRef = ref()
async function delteteUser(id: number) {
try {
await deleteUser(id)
window.$message.success('用户删除成功')
refresh() //
}
catch {
}
}
const tablecolumns = createUserColumns({
onEdit: row => modalRef.value?.openModal('edit', row),
onDelete: delteteUser,
onStatusChange: () => {},
})
async function getUserPage({ curent, pageSize }, formData) {
try {
const { data } = await getUserList({
...formData,
pageNum: curent,
pageSize,
})
return {
list: data.list,
total: data.total,
}
}
catch {
return {
list: [],
total: 0,
}
}
}
const treeData = ref([
{
id: '1',
label: '安徽总公司',
children: [
{
id: '2',
label: '合肥分公司',
children: [
{
id: '4',
label: '财务部门',
},
{
id: '5',
label: '采购部门',
},
],
},
{
id: '3',
label: '芜湖分公司',
},
],
},
])
</script>
<template>
<n-flex>
<n-card class="w-70">
<n-tree
block-line
:data="treeData"
key-field="id"
/>
</n-card>
<n-space vertical class="flex-1">
<n-card>
<pro-search-form
:form="searchForm"
:columns="searchColumns"
v-bind="proSearchFormProps"
:collapse-button-props="false"
/>
</n-card>
<pro-data-table
:columns="tablecolumns"
v-bind="tableProps"
>
<template #title>
<n-button type="primary" @click="modalRef.openModal('add')">
<template #icon>
<icon-park-outline-plus />
</template>
新建用户
</n-button>
</template>
</pro-data-table>
</n-space>
<TableModal ref="modalRef" modal-name="用户" @success="refresh" />
</n-flex>
</template>