mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-01-06 15:14:29 +08:00
feat: 修改 layout 布局,增加 demo 页面
This commit is contained in:
parent
c4ab7c158d
commit
44eb1aa0de
46 changed files with 1968 additions and 745 deletions
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = {
|
||||
// 一行最多 80 字符
|
||||
printWidth: 80,
|
||||
// 一行最多 120 字符
|
||||
printWidth: 120,
|
||||
// 使用 4 个空格缩进
|
||||
tabWidth: 4,
|
||||
// 不使用 tab 缩进,而使用空格
|
||||
|
|
|
|||
1975
frontend/package-lock.json
generated
1975
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -26,11 +26,14 @@
|
|||
"echarts": "^5.3.0",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"element-plus": "^2.2.6",
|
||||
"fit2cloud-ui-plus": "^0.0.1-beta.12",
|
||||
"js-md5": "^0.7.3",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.12",
|
||||
"pinia-plugin-persistedstate": "^1.6.1",
|
||||
"qs": "^6.10.3",
|
||||
"sass-loader": "^13.0.2",
|
||||
"unplugin-vue-define-options": "^0.7.3",
|
||||
"vue": "^3.2.25",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.12",
|
||||
|
|
|
|||
|
|
@ -11,17 +11,22 @@ export interface ResultData<T = any> extends Result {
|
|||
|
||||
// * 分页响应参数
|
||||
export interface ResPage<T> {
|
||||
datalist: T[];
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
items: T[];
|
||||
total: number;
|
||||
code: number;
|
||||
msg?: '';
|
||||
}
|
||||
|
||||
// * 分页请求参数
|
||||
export interface ReqPage {
|
||||
pageNum: number;
|
||||
currentPage: number;
|
||||
pageSize: number;
|
||||
}
|
||||
export interface CommonModel {
|
||||
ID: number;
|
||||
CreatedAt: string;
|
||||
UpdatedAt: string;
|
||||
}
|
||||
|
||||
// * 登录模块
|
||||
export namespace Login {
|
||||
|
|
@ -36,41 +41,6 @@ export namespace Login {
|
|||
[propName: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
// * 用户管理模块
|
||||
export namespace User {
|
||||
export interface ReqGetUserParams extends ReqPage {
|
||||
username: string;
|
||||
gender: number;
|
||||
idCard: string;
|
||||
email: string;
|
||||
address: string;
|
||||
createTime: string[];
|
||||
status: number;
|
||||
}
|
||||
export interface ResUserList {
|
||||
id: string;
|
||||
username: string;
|
||||
gender: string;
|
||||
age: number;
|
||||
idCard: string;
|
||||
email: string;
|
||||
address: string;
|
||||
createTime: string;
|
||||
status: number;
|
||||
avatar: string;
|
||||
children?: ResUserList[];
|
||||
}
|
||||
export interface ResStatus {
|
||||
userLabel: string;
|
||||
userValue: number;
|
||||
}
|
||||
export interface ResGender {
|
||||
genderLabel: string;
|
||||
genderValue: number;
|
||||
}
|
||||
}
|
||||
|
||||
// * 文件上传模块
|
||||
export namespace Upload {
|
||||
export interface ResFileUrl {
|
||||
|
|
|
|||
17
frontend/src/api/interface/user.ts
Normal file
17
frontend/src/api/interface/user.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { CommonModel, ReqPage } from '.';
|
||||
|
||||
export namespace User {
|
||||
export interface User extends CommonModel {
|
||||
username: string;
|
||||
email: string;
|
||||
}
|
||||
export interface UserCreate {
|
||||
username: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface ReqGetUserParams extends ReqPage {
|
||||
username?: string;
|
||||
email?: string;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +1,65 @@
|
|||
import { ResPage, User } from '@/api/interface/index';
|
||||
import { PORT1 } from '@/api/config/servicePort';
|
||||
|
||||
import http from '@/api';
|
||||
import { User } from '../interface/user';
|
||||
import UserDataList from '@/assets/json/user.json';
|
||||
|
||||
/**
|
||||
* @name 用户管理模块
|
||||
*/
|
||||
// * 获取用户列表
|
||||
export const getUserList = (params: User.ReqGetUserParams) => {
|
||||
return http.post<ResPage<User.ResUserList>>(PORT1 + `/user/list`, params);
|
||||
console.log(params);
|
||||
|
||||
return UserDataList;
|
||||
// return http.post<ResPage<User.User>>(`/users/list`, params);
|
||||
};
|
||||
|
||||
// * 新增用户
|
||||
export const addUser = (params: { id: string }) => {
|
||||
return http.post(PORT1 + `/user/add`, params);
|
||||
export const addUser = (params: User.UserCreate) => {
|
||||
return http.post(`/users/add`, params);
|
||||
};
|
||||
|
||||
// * 批量添加用户
|
||||
export const BatchAddUser = (params: FormData) => {
|
||||
return http.post(PORT1 + `/user/import`, params);
|
||||
export const getUserById = (id: string) => {
|
||||
return http.get(`/users/detail/${id}`);
|
||||
};
|
||||
// // * 批量添加用户
|
||||
// export const BatchAddUser = (params: FormData) => {
|
||||
// return http.post(`/users/import`, params);
|
||||
// };
|
||||
|
||||
// * 编辑用户
|
||||
export const editUser = (params: { id: string }) => {
|
||||
return http.post(PORT1 + `/user/edit`, params);
|
||||
export const editUser = (params: User.User) => {
|
||||
return http.post(`/users/edit`, params);
|
||||
};
|
||||
|
||||
// * 删除用户
|
||||
export const deleteUser = (params: { id: string[] }) => {
|
||||
return http.post(PORT1 + `/user/delete`, params);
|
||||
// * 批量删除用户
|
||||
export const deleteUser = (params: { ids: number[] }) => {
|
||||
return http.post(`/users/delete`, params);
|
||||
};
|
||||
|
||||
// * 切换用户状态
|
||||
export const changeUserStatus = (params: { id: string; status: number }) => {
|
||||
return http.post(PORT1 + `/user/change`, params);
|
||||
};
|
||||
// export const changeUserStatus = (params: { id: string; status: number }) => {
|
||||
// return http.post(`/users/change`, params);
|
||||
|
||||
// * 重置用户密码
|
||||
export const resetUserPassWord = (params: { id: string }) => {
|
||||
return http.post(PORT1 + `/user/rest_password`, params);
|
||||
};
|
||||
// };
|
||||
|
||||
// * 导出用户数据
|
||||
export const exportUserInfo = (params: User.ReqGetUserParams) => {
|
||||
return http.post<BlobPart>(PORT1 + `/user/export`, params, {
|
||||
responseType: 'blob',
|
||||
});
|
||||
};
|
||||
// // * 重置用户密码
|
||||
// export const resetUserPassWord = (params: { id: string }) => {
|
||||
// return http.post(`/users/rest_password`, params);
|
||||
// };
|
||||
|
||||
// * 获取用户状态
|
||||
export const getUserStatus = () => {
|
||||
return http.get<User.ResStatus>(PORT1 + `/user/status`);
|
||||
};
|
||||
// // * 导出用户数据
|
||||
// export const exportUserInfo = (params: User.ReqGetUserParams) => {
|
||||
// return http.post<BlobPart>(`/user/export`, params, {
|
||||
// responseType: 'blob',
|
||||
// });
|
||||
// };
|
||||
|
||||
// * 获取用户性别字典
|
||||
export const getUserGender = () => {
|
||||
return http.get<User.ResGender>(PORT1 + `/user/gender`);
|
||||
};
|
||||
// // * 获取用户状态
|
||||
// export const getUserStatus = () => {
|
||||
// return http.get<User.ResStatus>(`/user/status`);
|
||||
// };
|
||||
|
||||
// // * 获取用户性别字典
|
||||
// export const getUserGender = () => {
|
||||
// return http.get<User.ResGender>(`/user/gender`);
|
||||
// };
|
||||
|
|
|
|||
19
frontend/src/assets/json/user.json
Normal file
19
frontend/src/assets/json/user.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"code": 200,
|
||||
"items": [
|
||||
{
|
||||
"ID": 11232,
|
||||
"name": "admin",
|
||||
"email": "admin@fit2cloud.com",
|
||||
"createdAt": "2022-08-10T00:00:20+08:00"
|
||||
},
|
||||
{
|
||||
"ID": 11222232,
|
||||
"name": "admin2",
|
||||
"email": "admin2@fit2cloud.com",
|
||||
"createdAt": "2022-08-10T00:00:20+08:00"
|
||||
}
|
||||
],
|
||||
"total": 100,
|
||||
"msg": ""
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="footer flx-center">
|
||||
<a href="http://www.spicyboy.cn/" target="_blank">
|
||||
2022 © 1Panel By 飞致云.
|
||||
|
|
@ -21,7 +21,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import CollapseIcon from './components/CollapseIcon.vue';
|
||||
19
frontend/src/components/app-layout/index.vue
Normal file
19
frontend/src/components/app-layout/index.vue
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<Layout>
|
||||
<template #menu>
|
||||
<Menu></Menu>
|
||||
</template>
|
||||
<template #header>
|
||||
<Header></Header>
|
||||
</template>
|
||||
<template #footer>
|
||||
<Footer></Footer>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Layout from '@/layout/index.vue';
|
||||
import Header from './header/index.vue';
|
||||
import Footer from './footer/index.vue';
|
||||
import Menu from './menu/index.vue';
|
||||
</script>
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
<div
|
||||
class="menu"
|
||||
:style="{ width: isCollapse ? '65px' : '220px' }"
|
||||
v-loading="loading"
|
||||
element-loading-text="Loading..."
|
||||
:element-loading-spinner="loadingSvg"
|
||||
element-loading-svg-view-box="-10, -10, 50, 50"
|
||||
39
frontend/src/components/back-button/index.vue
Normal file
39
frontend/src/components/back-button/index.vue
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<template>
|
||||
<el-icon class="back-button" @click="jump">
|
||||
<Back />
|
||||
</el-icon>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
path: String,
|
||||
name: String,
|
||||
to: Object,
|
||||
});
|
||||
function jump() {
|
||||
const { path, name, to } = props;
|
||||
if (path) {
|
||||
router.push(path);
|
||||
}
|
||||
if (name) {
|
||||
router.push({ name: name });
|
||||
}
|
||||
if (to) {
|
||||
router.push(to);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.back-button {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
font-weight: 600;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
93
frontend/src/components/complex-table/index.vue
Normal file
93
frontend/src/components/complex-table/index.vue
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<div class="complex-table">
|
||||
<div class="complex-table__header" v-if="$slots.header || header">
|
||||
<slot name="header">{{ header }}</slot>
|
||||
</div>
|
||||
<div v-if="$slots.toolbar && !searchConfig" style="margin-bottom: 10px">
|
||||
<slot name="toolbar"></slot>
|
||||
</div>
|
||||
|
||||
<template v-if="searchConfig">
|
||||
<fu-filter-bar v-bind="searchConfig" @exec="search">
|
||||
<template #tl>
|
||||
<slot name="toolbar"></slot>
|
||||
</template>
|
||||
<template #default>
|
||||
<slot name="complex"></slot>
|
||||
</template>
|
||||
<template #buttons>
|
||||
<slot name="buttons"></slot>
|
||||
</template>
|
||||
</fu-filter-bar>
|
||||
</template>
|
||||
|
||||
<div class="complex-table__body">
|
||||
<fu-table v-bind="$attrs" @selection-change="handleSelectionChange">
|
||||
<slot></slot>
|
||||
</fu-table>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="complex-table__pagination"
|
||||
v-if="$slots.pagination || paginationConfig"
|
||||
>
|
||||
<slot name="pagination">
|
||||
<fu-table-pagination
|
||||
v-model:current-page="paginationConfig.currentPage"
|
||||
v-model:page-size="paginationConfig.pageSize"
|
||||
v-bind="paginationConfig"
|
||||
@change="search"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
defineOptions({ name: 'ComplexTable' }); // 组件名
|
||||
defineProps({
|
||||
header: String,
|
||||
searchConfig: Object,
|
||||
paginationConfig: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['search', 'update:selects']);
|
||||
const condition = ref({});
|
||||
function search(conditions: any, e: any) {
|
||||
if (conditions) {
|
||||
condition.value = conditions;
|
||||
}
|
||||
emit('search', condition.value, e);
|
||||
}
|
||||
|
||||
function handleSelectionChange(row: any) {
|
||||
emit('update:selects', row);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use '@/styles/mixins.scss' as *;
|
||||
|
||||
.complex-table {
|
||||
.complex-table__header {
|
||||
@include flex-row(flex-start, center);
|
||||
line-height: 60px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.complex-table__toolbar {
|
||||
@include flex-row(space-between, center);
|
||||
|
||||
.fu-search-bar {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.complex-table__pagination {
|
||||
margin-top: 20px;
|
||||
@include flex-row(flex-end);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,32 +1,37 @@
|
|||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { HandleData } from './interface';
|
||||
import i18n from '@/lang';
|
||||
|
||||
/**
|
||||
* @description 操作单条数据信息(二次确认【删除、禁用、启用、重置密码】)
|
||||
* @description 删除操作使用
|
||||
* @param {Function} api 操作数据接口的api方法(必传)
|
||||
* @param {Object} params 携带的操作数据参数 {id,params}(必传)
|
||||
* @param {String} message 提示信息(必传)
|
||||
* @param {String} confirmType icon类型(不必传,默认为 warning)
|
||||
* @return Promise
|
||||
*/
|
||||
export const useHandleData = <P = any, R = any>(
|
||||
export const useDeleteData = <P = any, R = any>(
|
||||
api: (params: P) => Promise<R>,
|
||||
params: Parameters<typeof api>[0],
|
||||
message: string,
|
||||
confirmType: HandleData.MessageType = 'warning',
|
||||
confirmType: HandleData.MessageType = 'error',
|
||||
) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ElMessageBox.confirm(`是否${message}?`, '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: confirmType,
|
||||
draggable: true,
|
||||
}).then(async () => {
|
||||
ElMessageBox.confirm(
|
||||
i18n.global.t(`${message}`) + '?',
|
||||
i18n.global.t('commons.msg.title'),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: confirmType,
|
||||
draggable: true,
|
||||
},
|
||||
).then(async () => {
|
||||
const res = await api(params);
|
||||
if (!res) return reject(false);
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: `${message}成功!`,
|
||||
message: i18n.global.t('commons.msg.deleteSuccss'),
|
||||
});
|
||||
resolve(true);
|
||||
});
|
||||
|
|
@ -1,6 +1,27 @@
|
|||
export default {
|
||||
commons: {
|
||||
button: {
|
||||
create: 'Create',
|
||||
delete: 'Delete',
|
||||
edit: 'Edit',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
},
|
||||
table: {
|
||||
name: 'Name',
|
||||
createdAt: 'Creation Time',
|
||||
updatedAt: 'Update Time',
|
||||
operate: 'Operations',
|
||||
},
|
||||
msg: {
|
||||
delete: 'This operation cannot be rolled back. Do you want to continue',
|
||||
title: 'Delete',
|
||||
deleteSuccess: 'Delete Success',
|
||||
},
|
||||
},
|
||||
menu: {
|
||||
home: 'Dashboard',
|
||||
demo: 'Demo',
|
||||
},
|
||||
home: {
|
||||
welcome: 'Welcome',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,27 @@
|
|||
export default {
|
||||
commons: {
|
||||
button: {
|
||||
create: '创建',
|
||||
delete: '删除',
|
||||
edit: '编辑',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
},
|
||||
table: {
|
||||
name: '名称',
|
||||
createdAt: '创建时间',
|
||||
updatedAt: '更新时间',
|
||||
operate: '操作',
|
||||
},
|
||||
msg: {
|
||||
delete: '此操作不可回滚,是否继续',
|
||||
title: '删除',
|
||||
deleteSuccess: '删除成功',
|
||||
},
|
||||
},
|
||||
menu: {
|
||||
home: '概览',
|
||||
demo: '样例',
|
||||
},
|
||||
|
||||
home: {
|
||||
|
|
|
|||
52
frontend/src/layout/LayoutContent.vue
Normal file
52
frontend/src/layout/LayoutContent.vue
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<div class="main-box">
|
||||
<div class="content-container__header" v-if="slots.header || header">
|
||||
<slot name="header">
|
||||
<back-button
|
||||
:path="backPath"
|
||||
:name="backName"
|
||||
:to="backTo"
|
||||
v-if="showBack"
|
||||
></back-button>
|
||||
{{ header }}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="content-container__toolbar" v-if="slots.toolbar">
|
||||
<slot name="toolbar"></slot>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue';
|
||||
import BackButton from '@/components/back-button/index.vue';
|
||||
defineOptions({ name: 'LayoutContent' }); // 组件名
|
||||
const slots = useSlots();
|
||||
const prop = defineProps({
|
||||
header: String,
|
||||
backPath: String,
|
||||
backName: String,
|
||||
backTo: Object,
|
||||
});
|
||||
|
||||
const showBack = computed(() => {
|
||||
const { backPath, backName, backTo } = prop;
|
||||
return backPath || backName || backTo;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use '@/styles/mixins.scss' as *;
|
||||
|
||||
.content-container__header {
|
||||
font-weight: 700;
|
||||
padding: 5px 0 25px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.content-container__toolbar {
|
||||
@include flex-row(space-between, center);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
3
frontend/src/layout/LayoutFooter.vue
Normal file
3
frontend/src/layout/LayoutFooter.vue
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<slot></slot>
|
||||
</template>
|
||||
3
frontend/src/layout/LayoutHeader.vue
Normal file
3
frontend/src/layout/LayoutHeader.vue
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<slot></slot>
|
||||
</template>
|
||||
3
frontend/src/layout/LayoutMenu.vue
Normal file
3
frontend/src/layout/LayoutMenu.vue
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<slot></slot>
|
||||
</template>
|
||||
13
frontend/src/layout/LayoutView.vue
Normal file
13
frontend/src/layout/LayoutView.vue
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition appear name="fade-transform" mode="out-in">
|
||||
<keep-alive :include="cacheRouter">
|
||||
<component :is="Component" :key="route.path"></component>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import cacheRouter from '@/routers/cacheRouter';
|
||||
</script>
|
||||
|
|
@ -23,12 +23,12 @@
|
|||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
overflow-x: hidden !important;
|
||||
background-color: #ffffff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
// box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
&::-webkit-scrollbar {
|
||||
background-color: white;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,25 @@
|
|||
<template>
|
||||
<el-container>
|
||||
<el-aside>
|
||||
<Menu></Menu>
|
||||
<Menu>
|
||||
<slot name="menu"></slot>
|
||||
</Menu>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<el-header>
|
||||
<Header></Header>
|
||||
<Header>
|
||||
<slot name="header"></slot>
|
||||
</Header>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<section class="main-box">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition appear name="fade-transform" mode="out-in">
|
||||
<keep-alive :include="cacheRouter">
|
||||
<component
|
||||
:is="Component"
|
||||
:key="route.path"
|
||||
></component>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</section>
|
||||
<Content>
|
||||
<View></View>
|
||||
</Content>
|
||||
</el-main>
|
||||
<el-footer v-if="themeConfig.footer">
|
||||
<Footer></Footer>
|
||||
<Footer>
|
||||
<slot name="footer"></slot>
|
||||
</Footer>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
|
|
@ -30,10 +27,11 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import Menu from './Menu/index.vue';
|
||||
import Header from './Header/index.vue';
|
||||
import Footer from './Footer/index.vue';
|
||||
import cacheRouter from '@/routers/cacheRouter';
|
||||
import Menu from './LayoutMenu.vue';
|
||||
import Header from './LayoutHeader.vue';
|
||||
import Footer from './LayoutFooter.vue';
|
||||
import View from './LayoutView.vue';
|
||||
import Content from './LayoutContent.vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
const globalStore = GlobalStore();
|
||||
const themeConfig = computed(() => globalStore.themeConfig);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import '@/styles/common.scss';
|
|||
import '@/assets/iconfont/iconfont.scss';
|
||||
import '@/assets/fonts/font.scss';
|
||||
import ElementPlus from 'element-plus';
|
||||
import Fit2CloudPlus from 'fit2cloud-ui-plus';
|
||||
import * as Icons from '@element-plus/icons-vue';
|
||||
import 'element-plus/dist/index.css';
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||
|
|
@ -26,4 +27,5 @@ app.use(router)
|
|||
.use(pinia)
|
||||
.use(directives)
|
||||
.use(ElementPlus)
|
||||
.use(Fit2CloudPlus)
|
||||
.mount('#app');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
/**
|
||||
* @description: default layout
|
||||
*/
|
||||
export const Layout = () => import('@/layout/index.vue');
|
||||
// export const Layout = () => import('@/layout/index.vue');
|
||||
export const Layout = () => import('@/components/app-layout/index.vue');
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
// import { Layout } from '@/routers/constant';
|
||||
// const homeRouter = {
|
||||
// sort: 1,
|
||||
// path: '/',
|
||||
// component: Layout,
|
||||
// redirect: '/home/index',
|
||||
// meta: {
|
||||
// keepAlive: true,
|
||||
// requiresAuth: true,
|
||||
// title: '首页',
|
||||
// key: 'home',
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/home/index',
|
||||
// name: 'home',
|
||||
// component: () => import('@/views/home/index.vue'),
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
// export default homeRouter;
|
||||
26
frontend/src/routers/modules/demo.ts
Normal file
26
frontend/src/routers/modules/demo.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { Layout } from '@/routers/constant';
|
||||
|
||||
// demo
|
||||
const demoRouter = {
|
||||
sort: 1,
|
||||
path: '/demos',
|
||||
component: Layout,
|
||||
redirect: '/demos/table',
|
||||
meta: {
|
||||
title: 'menu.demo',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/demos/table',
|
||||
name: 'table',
|
||||
component: () => import('@/views/demos/table/index.vue'),
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
requiresAuth: true,
|
||||
key: 'table',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default demoRouter;
|
||||
15
frontend/src/styles/mixins.scss
Normal file
15
frontend/src/styles/mixins.scss
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@mixin flex-row($justify: flex-start, $align: stretch) {
|
||||
display: flex;
|
||||
@if $justify != flex-start {
|
||||
justify-content: $justify;
|
||||
}
|
||||
@if $align != stretch {
|
||||
align-items: $align;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin variant($color, $background-color, $border-color) {
|
||||
color: $color;
|
||||
background-color: $background-color;
|
||||
border-color: $border-color;
|
||||
}
|
||||
|
|
@ -205,3 +205,26 @@ export function filterEnum(
|
|||
if (type == 'tag') return filterData?.tagType ? filterData.tagType : '';
|
||||
return filterData ? filterData[label] : '--';
|
||||
}
|
||||
/**
|
||||
* 对日期进行格式化,默认yyyy-MM-dd HH:mm:ss
|
||||
* @param dataStr 要格式化的日期
|
||||
* @return String
|
||||
*/
|
||||
|
||||
export function dateFromat(row: number, col: number, dataStr: any) {
|
||||
const date = new Date(dataStr);
|
||||
const y = date.getFullYear();
|
||||
let m: string | number = date.getMonth() + 1;
|
||||
m = m < 10 ? `0${String(m)}` : m;
|
||||
let d: string | number = date.getDate();
|
||||
d = d < 10 ? `0${String(d)}` : d;
|
||||
let h: string | number = date.getHours();
|
||||
h = h < 10 ? `0${String(h)}` : h;
|
||||
let minute: string | number = date.getMinutes();
|
||||
minute = minute < 10 ? `0${String(minute)}` : minute;
|
||||
let second: string | number = date.getSeconds();
|
||||
second = second < 10 ? `0${String(second)}` : second;
|
||||
return `${String(y)}-${String(m)}-${String(d)} ${String(h)}:${String(
|
||||
minute,
|
||||
)}:${String(second)}`;
|
||||
}
|
||||
|
|
|
|||
121
frontend/src/views/demos/table/index.vue
Normal file
121
frontend/src/views/demos/table/index.vue
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<LayoutContent :header="'样例'">
|
||||
<ComplexTable
|
||||
:pagination-config="paginationConfig"
|
||||
v-model:selects="selects"
|
||||
:data="data"
|
||||
@search="search"
|
||||
>
|
||||
<template #toolbar>
|
||||
<el-button type="primary">{{
|
||||
$t('commons.button.create')
|
||||
}}</el-button>
|
||||
<el-button type="primary" plain>{{ '其他操作' }}</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="selects.length === 0"
|
||||
@click="batchDelete"
|
||||
>{{ $t('commons.button.delete') }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column label="ID" min-width="100" prop="ID" fix />
|
||||
<el-table-column
|
||||
:label="$t('commons.table.name')"
|
||||
min-width="100"
|
||||
prop="name"
|
||||
fix
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<fu-input-rw-switch v-model="row.name" size="mini" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Email" min-width="100" prop="email" />
|
||||
<el-table-column
|
||||
prop="createdAt"
|
||||
:label="$t('commons.table.createdAt')"
|
||||
:formatter="dateFromat"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
/>
|
||||
<fu-table-operations
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
</LayoutContent>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import LayoutContent from '@/layout/LayoutContent.vue';
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import { dateFromat } from '@/utils/util';
|
||||
import { User } from '@/api/interface/user';
|
||||
import { deleteUser, getUserList } from '@/api/modules/user';
|
||||
import { onMounted, reactive, ref } from '@vue/runtime-core';
|
||||
import { useDeleteData } from '@/hooks/useDeleteData';
|
||||
import i18n from '@/lang';
|
||||
const data = ref();
|
||||
const selects = ref<any>([]);
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
});
|
||||
const buttons = [
|
||||
{
|
||||
label: '编辑',
|
||||
click: edit,
|
||||
},
|
||||
// {
|
||||
// label: '执行',
|
||||
// click: buttonClick,
|
||||
// },
|
||||
// {
|
||||
// label: '删除',
|
||||
// type: 'danger',
|
||||
// click: buttonClick,
|
||||
// },
|
||||
// {
|
||||
// label: '复制',
|
||||
// click: buttonClick,
|
||||
// },
|
||||
// {
|
||||
// label: '定时任务',
|
||||
// click: buttonClick,
|
||||
// },
|
||||
];
|
||||
|
||||
// function select(row: any) {
|
||||
// console.log(row);
|
||||
// selects.value.push(row.ID);
|
||||
// console.log(selects);
|
||||
// }
|
||||
function edit(row: User.User) {
|
||||
console.log(row);
|
||||
}
|
||||
|
||||
const batchDelete = async () => {
|
||||
let ids: Array<number> = [];
|
||||
selects.value.forEach((item: User.User) => {
|
||||
ids.push(item.ID);
|
||||
});
|
||||
await useDeleteData(
|
||||
deleteUser,
|
||||
{ ids: ids },
|
||||
i18n.global.t('commons.msg.delete'),
|
||||
);
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
const { currentPage, pageSize } = paginationConfig;
|
||||
const res = getUserList({ currentPage, pageSize });
|
||||
data.value = res.items;
|
||||
paginationConfig.total = res.total;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
</script>
|
||||
4
frontend/src/views/demos/table/operate/index.vue
Normal file
4
frontend/src/views/demos/table/operate/index.vue
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
|
|
@ -107,7 +107,7 @@ import { ref, reactive } from 'vue';
|
|||
import { ElMessage } from 'element-plus';
|
||||
import { User } from '@/api/interface';
|
||||
import { ColumnProps } from '@/components/ProTable/interface';
|
||||
import { useHandleData } from '@/hooks/useHandleData';
|
||||
import { useHandleData } from '@/hooks/useDeleteData';
|
||||
import { useDownload } from '@/hooks/useDownload';
|
||||
import ProTable from '@/components/ProTable/index.vue';
|
||||
import ImportExcel from '@/components/ImportExcel/index.vue';
|
||||
|
|
|
|||
|
|
@ -268,9 +268,9 @@ import { genderType } from '@/utils/serviceDict';
|
|||
import { defaultFormat } from '@/utils/util';
|
||||
import { User } from '@/api/interface';
|
||||
import { useDownload } from '@/hooks/useDownload';
|
||||
import { useHandleData } from '@/hooks/useHandleData';
|
||||
import { useHandleData } from '@/hooks/useDeleteData';
|
||||
import { useSelection } from '@/hooks/useSelection';
|
||||
import { useAuthButtons } from '@/hooks/useAuthButtons';
|
||||
// import { useAuthButtons } from '@/hooks/useAuthButtons';
|
||||
import { useTable } from '@/hooks/useTable';
|
||||
import ImportExcel from '@/components/ImportExcel/index.vue';
|
||||
import UserDrawer from '@/views/proTable/components/UserDrawer.vue';
|
||||
|
|
@ -325,8 +325,15 @@ const { isSelected, selectedListIds, selectionChange, getRowKeys } =
|
|||
useSelection();
|
||||
|
||||
// 页面按钮权限
|
||||
const { BUTTONS } = useAuthButtons();
|
||||
// const { BUTTONS } = useAuthButtons();
|
||||
|
||||
const BUTTONS = {
|
||||
status: true,
|
||||
view: true,
|
||||
reset: true,
|
||||
edit: true,
|
||||
delete: true,
|
||||
};
|
||||
// 设置搜索表单默认参数
|
||||
searchInitParam.value = {
|
||||
createTime: ['2022-04-05 00:00:00', '2022-05-10 23:59:59'],
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import importToCDN from 'vite-plugin-cdn-import';
|
|||
// import AutoImport from "unplugin-auto-import/vite";
|
||||
// import Components from "unplugin-vue-components/vite";
|
||||
// import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
||||
import DefineOptions from 'unplugin-vue-define-options/vite';
|
||||
|
||||
// @see: https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||
|
|
@ -31,7 +32,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: `@import "@/styles/var.scss";`,
|
||||
additionalData: `@use "@/styles/var.scss" as *;`,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -62,6 +63,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||
},
|
||||
},
|
||||
}),
|
||||
DefineOptions(),
|
||||
// * EsLint 报错信息显示在浏览器界面上
|
||||
eslintPlugin(),
|
||||
// * vite 可以使用 jsx/tsx 语法
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue