mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2026-01-22 23:50:22 +08:00
Compare commits
2 Commits
2.x_vxe-ta
...
1e1704a12d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e1704a12d | ||
|
|
07754975da |
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@@ -20,12 +20,5 @@
|
|||||||
"json",
|
"json",
|
||||||
"jsonc"
|
"jsonc"
|
||||||
],
|
],
|
||||||
"common-intellisense.ui": [
|
"common-intellisense.ui": ["naiveUi2"]
|
||||||
"naiveUi2"
|
|
||||||
],
|
|
||||||
"workbench.colorCustomizations": {
|
|
||||||
"activityBar.background": "#233212",
|
|
||||||
"titleBar.activeBackground": "#314619",
|
|
||||||
"titleBar.activeForeground": "#F9FCF6"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ npx degit zclzone/vue-naive-admin
|
|||||||
- [naive-admin-go](https://github.com/ituserxxx/naive-admin-go): 一个 Go 后端服务,基于 gin、gorm、mysql、jwt和session,已对接 Vue Naive Admin 2.0。
|
- [naive-admin-go](https://github.com/ituserxxx/naive-admin-go): 一个 Go 后端服务,基于 gin、gorm、mysql、jwt和session,已对接 Vue Naive Admin 2.0。
|
||||||
- [isme-java](https://github.com/AllenDengMs/isme-java): 一个轻量且完成度高的Java后端服务,基于Springboot 3 + JDK21,层次结构严谨,注释齐全,避免过度封装,代码可读性度高,依赖最简化,上手成本低,已集成 账号管理、权限管理、API鉴权、消息国际化等功能。
|
- [isme-java](https://github.com/AllenDengMs/isme-java): 一个轻量且完成度高的Java后端服务,基于Springboot 3 + JDK21,层次结构严谨,注释齐全,避免过度封装,代码可读性度高,依赖最简化,上手成本低,已集成 账号管理、权限管理、API鉴权、消息国际化等功能。
|
||||||
|
|
||||||
## 联系作者 or 进交流群
|
## 入群交流
|
||||||
|
|
||||||
[https://www.isme.top/contact.html](https://www.isme.top/contact.html)
|
添加微信,拉你进群
|
||||||
|
|
||||||
|

|
||||||
|
|||||||
35
package.json
35
package.json
@@ -17,42 +17,39 @@
|
|||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"exceljs": "^4.4.0",
|
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"naive-ui": "^2.40.1",
|
"naive-ui": "^2.39.0",
|
||||||
"pinia": "^2.2.4",
|
"pinia": "^2.2.2",
|
||||||
"pinia-plugin-persistedstate": "^4.1.1",
|
"pinia-plugin-persistedstate": "^4.0.2",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.8",
|
||||||
"vue-echarts": "^7.0.3",
|
"vue-echarts": "^7.0.3",
|
||||||
"vue-router": "^4.4.5",
|
"vue-router": "^4.4.5",
|
||||||
"vxe-pc-ui": "4.2.5",
|
|
||||||
"vxe-table": "4.8.13",
|
|
||||||
"vxe-table-plugin-export-xlsx": "^4.0.4",
|
|
||||||
"xlsx": "^0.18.5"
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^3.8.0",
|
"@antfu/eslint-config": "^3.7.1",
|
||||||
"@iconify/json": "^2.2.262",
|
"@iconify/json": "^2.2.252",
|
||||||
"@unocss/eslint-config": "^0.63.6",
|
"@unocss/eslint-config": "^0.62.4",
|
||||||
"@unocss/eslint-plugin": "^0.63.6",
|
"@unocss/eslint-plugin": "^0.62.4",
|
||||||
"@unocss/preset-rem-to-px": "^0.63.6",
|
"@unocss/preset-rem-to-px": "^0.62.4",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
||||||
"eslint": "^9.13.0",
|
"eslint": "^9.11.1",
|
||||||
"eslint-plugin-format": "^0.1.2",
|
"eslint-plugin-format": "^0.1.2",
|
||||||
"esno": "^4.8.0",
|
"esno": "^4.7.0",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
"lint-staged": "^15.2.10",
|
"lint-staged": "^15.2.10",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
|
"sass": "^1.79.3",
|
||||||
"simple-git-hooks": "^2.11.1",
|
"simple-git-hooks": "^2.11.1",
|
||||||
"taze": "^0.17.2",
|
"taze": "^0.16.9",
|
||||||
"unocss": "^0.63.6",
|
"unocss": "^0.62.4",
|
||||||
"unplugin-auto-import": "^0.18.3",
|
"unplugin-auto-import": "^0.18.3",
|
||||||
"unplugin-vue-components": "^0.27.4",
|
"unplugin-vue-components": "^0.27.4",
|
||||||
"vite": "^5.4.9",
|
"vite": "^5.4.7",
|
||||||
"vite-plugin-router-warn": "^1.0.0",
|
"vite-plugin-router-warn": "^1.0.0",
|
||||||
"vite-plugin-vue-devtools": "^7.5.2"
|
"vite-plugin-vue-devtools": "^7.4.6"
|
||||||
},
|
},
|
||||||
"simple-git-hooks": {
|
"simple-git-hooks": {
|
||||||
"pre-commit": "pnpm lint-staged"
|
"pre-commit": "pnpm lint-staged"
|
||||||
|
|||||||
2125
pnpm-lock.yaml
generated
2125
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,8 @@
|
|||||||
<main :class="{ 'flex-1': full }" class="m-12">
|
<main :class="{ 'flex-1': full }" class="m-12">
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
<slot name="footer">
|
<slot v-if="$slots.footer" name="footer" />
|
||||||
<TheFooter v-if="showFooter" class="mb-12 mt-auto" />
|
<TheFooter v-else-if="showFooter" class="mb-12 mt-auto" />
|
||||||
</slot>
|
|
||||||
<n-back-top :bottom="20" />
|
<n-back-top :bottom="20" />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -16,18 +16,16 @@
|
|||||||
<slot v-if="$slots.header" name="header" />
|
<slot v-if="$slots.header" name="header" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<slot name="title-prefix">
|
<slot v-if="$slots['title-prefix']" name="title-prefix" />
|
||||||
<template v-if="back">
|
<template v-else-if="back">
|
||||||
<div
|
<div
|
||||||
class="mr-16 flex cursor-pointer items-center text-16 opacity-60 transition-all-300 hover:opacity-40"
|
class="mr-16 flex cursor-pointer items-center text-16 opacity-60 transition-all-300 hover:opacity-40"
|
||||||
@click="router.back()"
|
@click="router.back()"
|
||||||
>
|
>
|
||||||
<i class="i-material-symbols:arrow-left-alt" />
|
<i class="i-material-symbols:arrow-left-alt" />
|
||||||
<span class="ml-4">返回</span>
|
<span class="ml-4">返回</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</slot>
|
|
||||||
|
|
||||||
<div class="mr-12 h-16 w-4 rounded-l-2 bg-primary" />
|
<div class="mr-12 h-16 w-4 rounded-l-2 bg-primary" />
|
||||||
<h2 class="font-normal">
|
<h2 class="font-normal">
|
||||||
{{ title ?? route.meta?.title }}
|
{{ title ?? route.meta?.title }}
|
||||||
@@ -41,11 +39,10 @@
|
|||||||
<slot />
|
<slot />
|
||||||
</AppCard>
|
</AppCard>
|
||||||
|
|
||||||
<slot name="footer">
|
<slot v-if="$slots.footer" name="footer" />
|
||||||
<AppCard v-if="showFooter" class="flex-shrink-0 py-12">
|
<AppCard v-else-if="showFooter" class="flex-shrink-0 py-12">
|
||||||
<TheFooter />
|
<TheFooter />
|
||||||
</AppCard>
|
</AppCard>
|
||||||
</slot>
|
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,240 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
:style="isComputedHeight ? `height: calc(100% - ${autoHeight}px);` : `height: ${autoHeight}px`"
|
|
||||||
>
|
|
||||||
<vxe-grid
|
|
||||||
ref="gridRef"
|
|
||||||
:columns="vxeColumns"
|
|
||||||
:loading="loading"
|
|
||||||
:data="data"
|
|
||||||
height="auto"
|
|
||||||
:stripe="true"
|
|
||||||
:border="true"
|
|
||||||
size="mini"
|
|
||||||
:auto-resize="true"
|
|
||||||
:pager-config="pagerConfig"
|
|
||||||
:export-config="exportConfig"
|
|
||||||
:toolbar-config="vxeToolbarConfig"
|
|
||||||
:proxy-config="proxyConfig"
|
|
||||||
v-on="gridEvent"
|
|
||||||
>
|
|
||||||
<template #loading>
|
|
||||||
<n-spin :show="loading" />
|
|
||||||
</template>
|
|
||||||
<template #total>
|
|
||||||
<span class="font-size-14 line-height-28px">共 {{ pagerConfig.total }} 条数据</span>
|
|
||||||
</template>
|
|
||||||
<template v-for="(c, k) in slots" :key="k" #[c.key]="{ row }">
|
|
||||||
<template v-if="Array.isArray(c.render(row))">
|
|
||||||
<component :is="r" v-for="(r, k1) in c.render(row)" :key="k1" :row="row" :col="c" />
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<component :is="c.render(row)" :row="row" :col="c" />
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</vxe-grid>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { computed } from 'vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'MeVxeGrid' })
|
|
||||||
const props = defineProps({
|
|
||||||
/**
|
|
||||||
* @remote 当isComputedHeight为true时为除table外的高度 false时为table高度
|
|
||||||
*/
|
|
||||||
autoHeight: {
|
|
||||||
type: Number,
|
|
||||||
default: 300,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @isPagination 是否为自动计算高度
|
|
||||||
*/
|
|
||||||
isComputedHeight: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @remote table的列
|
|
||||||
*/
|
|
||||||
columns: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
|
|
||||||
pagination: {
|
|
||||||
type: [Boolean, Object],
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
exportConfig: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
toolbarConfig: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({ }),
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 是否显示复选框
|
|
||||||
*/
|
|
||||||
showCheck: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
rowKey: {
|
|
||||||
type: String, // rowKey
|
|
||||||
default: 'id',
|
|
||||||
},
|
|
||||||
|
|
||||||
exportMethod: {
|
|
||||||
type: Function,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
remoteMethod: {
|
|
||||||
type: Function,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const emits = defineEmits(['update:checked-row-keys', 'pageChanged'])
|
|
||||||
const isFirst = shallowRef(true)
|
|
||||||
const gridRef = ref(null)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.data,
|
|
||||||
(n) => {
|
|
||||||
isFirst.value = false
|
|
||||||
gridRef.value.reloadData(n)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const vxeToolbarConfig = computed(() => {
|
|
||||||
return {
|
|
||||||
size: 'mini',
|
|
||||||
className: 'c-toolbar',
|
|
||||||
perfect: true,
|
|
||||||
refresh: true,
|
|
||||||
export: true,
|
|
||||||
zoom: true,
|
|
||||||
custom: true,
|
|
||||||
...props.toolbarConfig,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const pagerConfig = computed(() => {
|
|
||||||
if (props.pagination === false) {
|
|
||||||
return {
|
|
||||||
enabled: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
currentPage: props.pagination.pageNo,
|
|
||||||
pageSize: props.pagination.pageSize,
|
|
||||||
total: props.pagination.itemCount || 0,
|
|
||||||
background: true,
|
|
||||||
size: 'small',
|
|
||||||
layouts: [
|
|
||||||
'Home',
|
|
||||||
'PrevPage',
|
|
||||||
'JumpNumber',
|
|
||||||
'NextPage',
|
|
||||||
'End',
|
|
||||||
'Sizes',
|
|
||||||
'FullJump',
|
|
||||||
// 'Total',
|
|
||||||
],
|
|
||||||
slots: {
|
|
||||||
right: 'total',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
/**
|
|
||||||
* 将naive 转为vxe-table可用的列
|
|
||||||
*/
|
|
||||||
const vxeColumns = computed(() => {
|
|
||||||
if (props.showCheck) {
|
|
||||||
return [{
|
|
||||||
type: 'checkbox',
|
|
||||||
resizable: false,
|
|
||||||
width: 45,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'left',
|
|
||||||
}, ...props.columns.map((item) => {
|
|
||||||
item.field = item.key
|
|
||||||
if (item.render) {
|
|
||||||
item.slots = { default: item.key }
|
|
||||||
}
|
|
||||||
return item
|
|
||||||
})]
|
|
||||||
}
|
|
||||||
return props.columns.map((item) => {
|
|
||||||
item.field = item.key
|
|
||||||
if (item.render) {
|
|
||||||
item.slots = { default: item.key }
|
|
||||||
}
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将naive render函数转换为vxeTable的slots
|
|
||||||
*/
|
|
||||||
const slots = computed(() => {
|
|
||||||
return props.columns.filter(item => item.render)
|
|
||||||
})
|
|
||||||
|
|
||||||
const gridEvent = {
|
|
||||||
checkboxAll() {
|
|
||||||
const $grid = gridRef.value
|
|
||||||
if ($grid) {
|
|
||||||
emits(
|
|
||||||
'update:checked-row-keys',
|
|
||||||
$grid.getCheckboxRecords().map(item => item[props.rowKey]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
checkboxChange() {
|
|
||||||
const $grid = gridRef.value
|
|
||||||
if ($grid) {
|
|
||||||
emits(
|
|
||||||
'update:checked-row-keys',
|
|
||||||
$grid.getCheckboxRecords().map(item => item[props.rowKey]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
pageChange(e) {
|
|
||||||
emits('pageChanged', e)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const proxyConfig = {
|
|
||||||
props: {
|
|
||||||
result: 'data.pageData',
|
|
||||||
total: 'data.total',
|
|
||||||
},
|
|
||||||
ajax: {
|
|
||||||
query: async ({ page }) => {
|
|
||||||
if (isFirst.value === true) {
|
|
||||||
return Promise.reject('')
|
|
||||||
}
|
|
||||||
props.remoteMethod(page.currentPage, page.pageSize)
|
|
||||||
},
|
|
||||||
queryAll: async () => {
|
|
||||||
const res = await props.exportMethod()
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -2,6 +2,5 @@ export { default as AppCard } from './AppCard.vue'
|
|||||||
export { default as AppPage } from './AppPage.vue'
|
export { default as AppPage } from './AppPage.vue'
|
||||||
export { default as CommonPage } from './CommonPage.vue'
|
export { default as CommonPage } from './CommonPage.vue'
|
||||||
export { default as LayoutSetting } from './LayoutSetting.vue'
|
export { default as LayoutSetting } from './LayoutSetting.vue'
|
||||||
export { default as MeVxeGrid } from './MeVxeGrid.vue'
|
|
||||||
export { default as TheFooter } from './TheFooter.vue'
|
export { default as TheFooter } from './TheFooter.vue'
|
||||||
export { default as ToggleTheme } from './ToggleTheme.vue'
|
export { default as ToggleTheme } from './ToggleTheme.vue'
|
||||||
|
|||||||
@@ -7,59 +7,38 @@
|
|||||||
--------------------------------->
|
--------------------------------->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="computedHeight === true ? 'h-100%' : ''">
|
<AppCard v-if="$slots.default" bordered bg="#fafafc dark:black" class="mb-30 min-h-60 rounded-4">
|
||||||
<div id="search_main">
|
<form class="flex justify-between p-16" @submit.prevent="handleSearch()">
|
||||||
<AppCard v-if="$slots.default" bordered bg="#fafafc dark:black" class="mb-30 min-h-60 rounded-4">
|
<n-scrollbar x-scrollable>
|
||||||
<form class="flex justify-between p-16" @submit.prevent="handleSearch()">
|
<n-space :wrap="!expand || isExpanded" :size="[32, 16]" class="p-10">
|
||||||
<n-scrollbar x-scrollable>
|
<slot />
|
||||||
<n-space :wrap="!expand || isExpanded" :size="[32, 16]" class="p-10">
|
</n-space>
|
||||||
<slot />
|
</n-scrollbar>
|
||||||
</n-space>
|
<div class="flex-shrink-0 p-10">
|
||||||
</n-scrollbar>
|
<n-button ghost type="primary" @click="handleReset">
|
||||||
<div class="flex-shrink-0 p-10">
|
<i class="i-fe:rotate-ccw mr-4" />
|
||||||
<n-button ghost type="primary" @click="handleReset">
|
重置
|
||||||
<i class="i-fe:rotate-ccw mr-4" />
|
</n-button>
|
||||||
重置
|
<n-button attr-type="submit" class="ml-20" type="primary">
|
||||||
</n-button>
|
<i class="i-fe:search mr-4" />
|
||||||
<n-button attr-type="submit" class="ml-20" type="primary">
|
搜索
|
||||||
<i class="i-fe:search mr-4" />
|
</n-button>
|
||||||
搜索
|
|
||||||
</n-button>
|
|
||||||
|
|
||||||
<template v-if="expand">
|
<template v-if="expand">
|
||||||
<n-button v-if="!isExpanded" type="primary" text @click="toggleExpand">
|
<n-button v-if="!isExpanded" type="primary" text @click="toggleExpand">
|
||||||
<i class="i-fe:chevrons-down ml-4" />
|
<i class="i-fe:chevrons-down ml-4" />
|
||||||
展开
|
展开
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button v-else text type="primary" @click="toggleExpand">
|
<n-button v-else text type="primary" @click="toggleExpand">
|
||||||
<i class="i-fe:chevrons-up ml-4" />
|
<i class="i-fe:chevrons-up ml-4" />
|
||||||
收起
|
收起
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
</div>
|
|
||||||
<MeVxeGrid
|
|
||||||
:remote="remote"
|
|
||||||
:loading="loading"
|
|
||||||
:scroll-x="scrollX"
|
|
||||||
:columns="columns"
|
|
||||||
:export-config="{ filename: exportName, modes: ['all', 'current'] }"
|
|
||||||
:toolbar-config="toolbarConfig"
|
|
||||||
:data="tableData"
|
|
||||||
:auto-height="computedHeight === true ? autoHeight : computedHeight"
|
|
||||||
:row-key="rowKey"
|
|
||||||
:is-computed-height="computedHeight === true"
|
|
||||||
:show-check="showCheck"
|
|
||||||
:pagination="isPagination ? pagination : false"
|
|
||||||
:remote-method="remoteMethod"
|
|
||||||
:export-method="exportMethod"
|
|
||||||
@update:checked-row-keys="onChecked"
|
|
||||||
@update:page="onPageChange"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- <NDataTable
|
<NDataTable
|
||||||
:remote="remote"
|
:remote="remote"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll-x="scrollX"
|
:scroll-x="scrollX"
|
||||||
@@ -69,39 +48,14 @@
|
|||||||
:pagination="isPagination ? pagination : false"
|
:pagination="isPagination ? pagination : false"
|
||||||
@update:checked-row-keys="onChecked"
|
@update:checked-row-keys="onChecked"
|
||||||
@update:page="onPageChange"
|
@update:page="onPageChange"
|
||||||
/> -->
|
/>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { NDataTable } from 'naive-ui'
|
||||||
import { utils, writeFile } from 'xlsx'
|
import { utils, writeFile } from 'xlsx'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
/**
|
|
||||||
* 是否自动计算高度,适用只有search和table
|
|
||||||
*/
|
|
||||||
computedHeight: {
|
|
||||||
type: [Boolean, Number],
|
|
||||||
default: () => true,
|
|
||||||
},
|
|
||||||
showCheck: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false,
|
|
||||||
},
|
|
||||||
toolbarConfig: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
export: true,
|
|
||||||
zoom: true,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @remote 导出数据表格名称
|
|
||||||
*/
|
|
||||||
exportName: {
|
|
||||||
type: String,
|
|
||||||
default: '导出数据',
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* @remote true: 后端分页 false: 前端分页
|
* @remote true: 后端分页 false: 前端分页
|
||||||
*/
|
*/
|
||||||
@@ -115,6 +69,13 @@ const props = defineProps({
|
|||||||
isPagination: {
|
isPagination: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @isShowSizePicker 是否显示每页条数选择
|
||||||
|
*/
|
||||||
|
isShowSizePicker: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
},
|
},
|
||||||
scrollX: {
|
scrollX: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@@ -151,18 +112,8 @@ const props = defineProps({
|
|||||||
/** 是否支持展开 */
|
/** 是否支持展开 */
|
||||||
expand: Boolean,
|
expand: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['update:queryItems', 'onChecked', 'onDataChange'])
|
const emit = defineEmits(['update:queryItems', 'onChecked', 'onDataChange'])
|
||||||
|
|
||||||
const autoHeight = ref(0)
|
|
||||||
|
|
||||||
window.onresize = () => {
|
|
||||||
autoHeight.value = document.getElementById('search_main').clientHeight + 24
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
autoHeight.value = document.getElementById('search_main').clientHeight + 24
|
|
||||||
})
|
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const initQuery = { ...props.queryItems }
|
const initQuery = { ...props.queryItems }
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
@@ -172,6 +123,16 @@ const pagination = reactive({
|
|||||||
prefix({ itemCount }) {
|
prefix({ itemCount }) {
|
||||||
return `共 ${itemCount} 条数据`
|
return `共 ${itemCount} 条数据`
|
||||||
},
|
},
|
||||||
|
pageSizes: [10, 20, 50, 100],
|
||||||
|
showSizePicker: props.isShowSizePicker,
|
||||||
|
onChange: (page) => {
|
||||||
|
pagination.page = page
|
||||||
|
},
|
||||||
|
onUpdatePageSize: (pageSize) => {
|
||||||
|
pagination.pageSize = pageSize
|
||||||
|
pagination.page = 1
|
||||||
|
handleQuery()
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否展开
|
// 是否展开
|
||||||
@@ -181,22 +142,6 @@ function toggleExpand() {
|
|||||||
isExpanded.value = !isExpanded.value
|
isExpanded.value = !isExpanded.value
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportMethod() {
|
|
||||||
const { data } = await props.getData({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 2000,
|
|
||||||
...props.queryItems,
|
|
||||||
})
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
function remoteMethod(page, pageSize) {
|
|
||||||
pagination.page = page
|
|
||||||
pagination.pageSize = pageSize
|
|
||||||
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleQuery() {
|
async function handleQuery() {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -215,7 +160,6 @@ async function handleQuery() {
|
|||||||
// 如果当前页数据为空,且总条数不为0,则返回上一页数据
|
// 如果当前页数据为空,且总条数不为0,则返回上一页数据
|
||||||
onPageChange(pagination.page - 1)
|
onPageChange(pagination.page - 1)
|
||||||
}
|
}
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -253,7 +197,9 @@ function onPageChange(currentPage) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function onChecked(rowKeys) {
|
function onChecked(rowKeys) {
|
||||||
emit('onChecked', rowKeys)
|
if (props.columns.some(item => item.type === 'selection')) {
|
||||||
|
emit('onChecked', rowKeys)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function handleExport(columns = props.columns, data = tableData.value) {
|
function handleExport(columns = props.columns, data = tableData.value) {
|
||||||
if (!data?.length)
|
if (!data?.length)
|
||||||
|
|||||||
@@ -26,22 +26,21 @@
|
|||||||
|
|
||||||
<!-- 底部按钮 -->
|
<!-- 底部按钮 -->
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<slot name="footer">
|
<slot v-if="$slots.footer" name="footer" />
|
||||||
<footer v-if="modalOptions.showFooter" class="flex justify-end">
|
<footer v-else-if="modalOptions.showFooter" class="flex justify-end">
|
||||||
<n-button v-if="modalOptions.showCancel" @click="handleCancel()">
|
<n-button v-if="modalOptions.showCancel" @click="handleCancel()">
|
||||||
{{ modalOptions.cancelText }}
|
{{ modalOptions.cancelText }}
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
v-if="modalOptions.showOk"
|
v-if="modalOptions.showOk"
|
||||||
type="primary"
|
type="primary"
|
||||||
:loading="modalOptions.okLoading"
|
:loading="modalOptions.okLoading"
|
||||||
class="ml-20"
|
class="ml-20"
|
||||||
@click="handleOk()"
|
@click="handleOk()"
|
||||||
>
|
>
|
||||||
{{ modalOptions.okText }}
|
{{ modalOptions.okText }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</footer>
|
</footer>
|
||||||
</slot>
|
|
||||||
</template>
|
</template>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ function handleMenuSelect(key, item) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style lang="scss">
|
||||||
.side-menu:not(.n-menu--collapsed) {
|
.side-menu:not(.n-menu--collapsed) {
|
||||||
.n-menu-item-content {
|
.n-menu-item-content {
|
||||||
&::before {
|
&::before {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ async function handleContextMenu(e, tagItem) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
:deep(.n-tabs) {
|
:deep(.n-tabs) {
|
||||||
.n-tabs-tab {
|
.n-tabs-tab {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
|
|||||||
20
src/main.js
20
src/main.js
@@ -8,35 +8,19 @@
|
|||||||
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||||
**********************************/
|
**********************************/
|
||||||
|
|
||||||
import ExcelJS from 'exceljs'
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
import { setupDirectives } from './directives'
|
import { setupDirectives } from './directives'
|
||||||
/**
|
|
||||||
* 引入vxe-table 和 vxe-pc-ui excel导出
|
|
||||||
*/
|
|
||||||
import VxeUI from 'vxe-pc-ui'
|
|
||||||
import VXETable from 'vxe-table'
|
|
||||||
import VXETablePluginExportXLSX from 'vxe-table-plugin-export-xlsx'
|
|
||||||
import { setupRouter } from './router'
|
import { setupRouter } from './router'
|
||||||
import { setupStore } from './store'
|
import { setupStore } from './store'
|
||||||
|
|
||||||
import { setupNaiveDiscreteApi } from './utils'
|
import { setupNaiveDiscreteApi } from './utils'
|
||||||
import 'vxe-pc-ui/lib/style.css'
|
import '@/styles/global.scss'
|
||||||
import 'vxe-table/lib/style.css'
|
|
||||||
import '@/styles/reset.css'
|
import '@/styles/reset.css'
|
||||||
import '@/styles/global.css'
|
|
||||||
import 'uno.css'
|
import 'uno.css'
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
VXETable.use(VXETablePluginExportXLSX, {
|
|
||||||
ExcelJS,
|
|
||||||
})
|
|
||||||
app.use(VxeUI).use(VXETable)
|
|
||||||
|
|
||||||
setupStore(app)
|
setupStore(app)
|
||||||
setupDirectives(app)
|
setupDirectives(app)
|
||||||
await setupRouter(app)
|
await setupRouter(app)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { defaultLayout, defaultPrimaryColor, naiveThemeOverrides } from '@/setti
|
|||||||
import { generate, getRgbStr } from '@arco-design/color'
|
import { generate, getRgbStr } from '@arco-design/color'
|
||||||
import { useDark } from '@vueuse/core'
|
import { useDark } from '@vueuse/core'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { VxeUI } from 'vxe-pc-ui'
|
|
||||||
|
|
||||||
export const useAppStore = defineStore('app', {
|
export const useAppStore = defineStore('app', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -41,18 +40,6 @@ export const useAppStore = defineStore('app', {
|
|||||||
list: true,
|
list: true,
|
||||||
dark: isDark,
|
dark: isDark,
|
||||||
})
|
})
|
||||||
VxeUI.setTheme(isDark ? 'dark' : 'light')
|
|
||||||
if (isDark) {
|
|
||||||
document.body.style.setProperty('--vxe-ui-font-color', '#fff')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
document.body.style.setProperty('--vxe-ui-font-color', '#000')
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.style.setProperty('--vxe-ui-font-primary-color', color)
|
|
||||||
document.body.style.setProperty('--vxe-ui-font-primary-lighten-color', color)
|
|
||||||
document.body.style.setProperty('--vxe-ui-font-primary-darken-color', color)
|
|
||||||
|
|
||||||
document.body.style.setProperty('--primary-color', getRgbStr(colors[5]))
|
document.body.style.setProperty('--primary-color', getRgbStr(colors[5]))
|
||||||
this.naiveThemeOverrides.common = Object.assign(this.naiveThemeOverrides.common || {}, {
|
this.naiveThemeOverrides.common = Object.assign(this.naiveThemeOverrides.common || {}, {
|
||||||
primaryColor: colors[5],
|
primaryColor: colors[5],
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ body {
|
|||||||
|
|
||||||
.fade-slide-enter-from {
|
.fade-slide-enter-from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-2%);
|
transform: translateX(-30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-slide-leave-to {
|
.fade-slide-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(2%);
|
transform: translateX(30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 自定义滚动条样式 */
|
/* 自定义滚动条样式 */
|
||||||
@@ -63,9 +63,10 @@ defineOptions({ name: 'ImgUpload' })
|
|||||||
const { copy, copied } = useClipboard()
|
const { copy, copied } = useClipboard()
|
||||||
|
|
||||||
const imgList = reactive([
|
const imgList = reactive([
|
||||||
{ url: 'https://img.isme.top/isme/67208863145ef.jpg' },
|
{ url: 'https://cdn.isme.top/images/5c23d52f880511ebb6edd017c2d2eca2.jpg' },
|
||||||
{ url: 'https://img.isme.top/isme/67208ab2a9de0.jpg' },
|
{ url: 'https://cdn.isme.top/images/5c23d52f880511ebb6edd017c2d2eca2.jpg' },
|
||||||
{ url: 'https://img.isme.top/isme/67208ab4c6596.jpg' },
|
{ url: 'https://cdn.isme.top/images/5c23d52f880511ebb6edd017c2d2eca2.jpg' },
|
||||||
|
{ url: 'https://cdn.isme.top/images/5c23d52f880511ebb6edd017c2d2eca2.jpg' },
|
||||||
])
|
])
|
||||||
|
|
||||||
watch(copied, (val) => {
|
watch(copied, (val) => {
|
||||||
|
|||||||
@@ -81,7 +81,6 @@
|
|||||||
:columns="btnsColumns"
|
:columns="btnsColumns"
|
||||||
:scroll-x="-1"
|
:scroll-x="-1"
|
||||||
:get-data="api.getButtons"
|
:get-data="api.getButtons"
|
||||||
:computed-height="300"
|
|
||||||
:query-items="{ parentId: currentMenu.id }"
|
:query-items="{ parentId: currentMenu.id }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
:scroll-x="1200"
|
:scroll-x="1200"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:get-data="api.getAllUsers"
|
:get-data="api.getAllUsers"
|
||||||
:show-check="true"
|
|
||||||
@on-checked="onChecked"
|
@on-checked="onChecked"
|
||||||
>
|
>
|
||||||
<MeQueryItem label="用户名" :label-width="50">
|
<MeQueryItem label="用户名" :label-width="50">
|
||||||
@@ -91,7 +90,7 @@ const genders = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
// { type: 'selection', fixed: 'left' },
|
{ type: 'selection', fixed: 'left' },
|
||||||
{
|
{
|
||||||
title: '头像',
|
title: '头像',
|
||||||
key: 'avatar',
|
key: 'avatar',
|
||||||
@@ -102,10 +101,11 @@ const columns = [
|
|||||||
src: avatar,
|
src: avatar,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{ title: '用户名', key: 'username', ellipsis: { tooltip: true } },
|
{ title: '用户名', key: 'username', width: 150, ellipsis: { tooltip: true } },
|
||||||
{
|
{
|
||||||
title: '角色',
|
title: '角色',
|
||||||
key: 'roles',
|
key: 'roles',
|
||||||
|
width: 200,
|
||||||
ellipsis: { tooltip: true },
|
ellipsis: { tooltip: true },
|
||||||
render: ({ roles }) => {
|
render: ({ roles }) => {
|
||||||
if (roles?.length) {
|
if (roles?.length) {
|
||||||
@@ -156,7 +156,7 @@ const columns = [
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
width: 120,
|
width: 100,
|
||||||
align: 'right',
|
align: 'right',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
hideInExcel: true,
|
hideInExcel: true,
|
||||||
|
|||||||
@@ -162,10 +162,11 @@ const columns = [
|
|||||||
src: avatar,
|
src: avatar,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{ title: '用户名', key: 'username', ellipsis: { tooltip: true } },
|
{ title: '用户名', key: 'username', width: 150, ellipsis: { tooltip: true } },
|
||||||
{
|
{
|
||||||
title: '角色',
|
title: '角色',
|
||||||
key: 'roles',
|
key: 'roles',
|
||||||
|
width: 200,
|
||||||
ellipsis: { tooltip: true },
|
ellipsis: { tooltip: true },
|
||||||
render: ({ roles }) => {
|
render: ({ roles }) => {
|
||||||
if (roles?.length) {
|
if (roles?.length) {
|
||||||
|
|||||||
Reference in New Issue
Block a user