feat: 添加角色导入导出;修改组件信息

This commit is contained in:
bunny 2025-04-26 08:57:23 +08:00
parent 962f1e1ab5
commit a461e0d1dd
60 changed files with 4476 additions and 186 deletions

View File

@ -1,9 +1,32 @@
{
"name": "bunny-admin-web",
"name": "bunny-auth-admin",
"version": "3.0.0",
"private": true,
"type": "module",
"license": "MIT",
"keywords": [
"bunny-auth-admin",
"element-plus",
"tailwindcss",
"typescript",
"pinia",
"vue3",
"vite",
"esm"
],
"homepage": "https://github.com/BunnyMaster",
"repository": {
"type": "git",
"url": "git+https://github.com/BunnyMaster/bunny-admin-web.git"
},
"bugs": {
"url": "https://github.com/BunnyMaster/bunny-admin-web/issues"
},
"author": {
"name": "BunnyMaster",
"email": "1319900154@qq.com",
"url": "https://github.com/BunnyMaster"
},
"scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=1024 vite",
"serve": "pnpm vite",

View File

@ -132,7 +132,7 @@ class PureHttp {
// 检查配置的响应类型是否为二进制类型('blob' 或 'arraybuffer', 如果是,直接返回响应对象
if ($config.responseType === 'blob' || $config.responseType === 'arraybuffer') {
return data;
return response;
}
// 登录过期,和异常处理

View File

@ -55,8 +55,8 @@ export const fetchLogout = (data?: object) => {
};
/** 获取用户信息,根据当前token获取 */
export const fetchGetUserinfo = () => {
return http.request<BaseResult<any>>('get', 'user/noManage/getUserinfo');
export const fetchUserinfo = () => {
return http.request<BaseResult<any>>('get', 'user/noManage/userinfo');
};
/** 用户信息---获取用户信息列表 */

View File

@ -9,8 +9,8 @@ export const fetchGetRoleList = (data: any) => {
};
/** 角色---获取所有角色 */
export const fetchGetAllRoles = () => {
return http.request<BaseResult<any>>('get', `role/noManage/getAllRoles`);
export const fetchAllRoles = () => {
return http.request<BaseResult<any>>('get', `role/noManage/allRoles`);
};
/** 角色---根据用户id获取所有角色 */
@ -18,6 +18,21 @@ export const fetchGetRoleListByUserId = (data) => {
return http.request<BaseResult<any>>('get', `userRole/getRoleListByUserId`, { params: data });
};
/** 角色---使用Excel导出导出角色列表 */
export const fetchExportByExcel = () => {
return http.request<BaseResult<any>>('get', `role/exportByExcel`, { responseType: 'blob' });
};
/* 角色---使用Excel更新角色列表 */
export const fetchUpdateRoleByFile = (data: any) => {
return http.request<BaseResult<any>>(
'put',
`/role/update/roleByFile`,
{ data },
{ headers: { 'Content-Type': 'multipart/form-data' } }
);
};
/** 角色---添加角色 */
export const fetchAddRole = (data: any) => {
return http.request<BaseResult<object>>('post', 'role/addRole', { data });

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,174 @@
<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue';
import { fetchMenuIconList } from '@/api/v1/menu/menuIcon';
import { FormProps } from './types';
import { $t } from '@/plugins/i18n';
const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({
icon: '',
}),
});
const innerForm = reactive({
datalist: [],
currentPage: 1,
pageSize: 30,
total: 100,
loading: false,
});
const form = ref(props.formInline);
/** 搜索和初始化 */
const onSearch = async () => {
innerForm.loading = true;
const { currentPage, pageSize } = innerForm;
//
const baseResult = await fetchMenuIconList({ currentPage, pageSize });
if (baseResult.code !== 200) return;
const data = baseResult.data;
//
innerForm.datalist = data.list;
innerForm.currentPage = data.pageNo;
innerForm.pageSize = data.pageSize;
innerForm.total = data.total;
innerForm.loading = false;
};
/**
* * 修改图标
* @param value
*/
const onChangeIcon = (value: any) => {
form.value.icon = value.iconCode;
};
/** 清除图标 */
const onClear = () => (form.value.icon = '');
/** 修改当前页 */
const onCurrentChange = async (value: number) => {
innerForm.currentPage = value;
await onSearch();
};
onMounted(() => {
onSearch();
});
</script>
<template>
<div class="selector">
<el-popover :popper-options="{ placement: 'auto' }" :width="350" popper-class="pure-popper" trigger="click">
<template #reference>
<div class="w-[60px] h-[32px] cursor-pointer flex justify-center items-center">
<el-text v-if="!form.icon" type="primary">{{ $t('select_icon') }}</el-text>
<IconifyIconOnline v-else :icon="form.icon" style="font-size: 32px" />
</div>
</template>
<ul class="flex flex-wrap px-2 ml-2 h-[210px]">
<li
v-for="(item, key) in innerForm.datalist"
:key="key"
:class="`icon-item p-2 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-[#e5e7eb] ${item.iconCode === form.icon ? 'current' : ''}`"
:title="item.iconName"
@click="onChangeIcon(item)"
>
<IconifyIconOnline :icon="item.iconCode" height="20px" width="20px" />
</li>
</ul>
<el-empty v-show="innerForm.datalist.length === 0" :image-size="60" description="图标不存在" />
<div class="w-full h-9 flex items-center overflow-auto border-t border-[#e5e7eb]">
<el-pagination
:current-page="innerForm.currentPage"
:page-size="innerForm.pageSize"
:pager-count="5"
:total="innerForm.total"
background
class="flex-auto ml-2"
hide-on-single-page
layout="pager"
size="small"
@current-change="onCurrentChange"
/>
<el-button bg class="justify-end mr-2 ml-2" size="small" text type="danger" @click="onClear">清空</el-button>
</div>
</el-popover>
<el-link
:title="$t('systemMenuIcon.officialWebsite')"
:underline="false"
href="https://icon-sets.iconify.design/"
target="_blank"
type="primary"
>
{{ $t('systemMenuIcon.officialWebsite') }}
</el-link>
</div>
</template>
<style lang="scss" scoped>
.current {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
transition: all 0.4s;
transform: scaleX(1.05);
}
.icon-item {
width: 38px;
height: 38px;
&:hover {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
transition: all 0.4s;
transform: scaleX(1.05);
}
}
:deep(.el-tabs__nav-next) {
font-size: 15px;
line-height: 32px;
box-shadow: -5px 0 5px -6px #ccc;
}
:deep(.el-tabs__nav-prev) {
font-size: 15px;
line-height: 32px;
box-shadow: 5px 0 5px -6px #ccc;
}
:deep(.el-input-group__append) {
padding: 0;
}
:deep(.el-tabs__item) {
height: 30px;
font-size: 12px;
font-weight: normal;
line-height: 30px;
}
:deep(.el-tabs__header),
:deep(.el-tabs__nav-wrap) {
position: static;
margin: 0;
box-shadow: 0 2px 5px rgb(0 0 0 / 6%);
}
:deep(.el-tabs__nav-wrap::after) {
height: 0;
}
:deep(.el-tabs__nav-wrap) {
padding: 0 24px;
}
:deep(.el-tabs__content) {
margin-top: 4px;
}
</style>

View File

@ -1,127 +1,184 @@
<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue';
import { fetchMenuIconList } from '@/api/v1/menu/menuIcon';
import { FormProps } from './types';
import { $t } from '@/plugins/i18n';
import { IconJson } from '@/components/ReIcon/data';
import { cloneDeep, isAllEmpty } from '@pureadmin/utils';
import { computed, CSSProperties, ref, watch } from 'vue';
import Search from '@iconify-icons/ri/search-eye-line';
const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({
icon: '',
}),
type ParameterCSSProperties = (item?: string) => CSSProperties | undefined;
defineOptions({
name: 'IconSelect',
});
const innerForm = reactive({
datalist: [],
currentPage: 1,
pageSize: 30,
total: 100,
loading: false,
const inputValue = defineModel({ type: String });
const iconList = ref(IconJson);
const icon = ref();
const currentActiveType = ref('ep:');
//
const copyIconList = cloneDeep(iconList.value);
const totalPage = ref(0);
// 35
const pageSize = ref(35);
const currentPage = ref(1);
//
const filterValue = ref('');
const tabsList = [
{
label: 'Element Plus',
name: 'ep:',
},
{
label: 'Remix Icon',
name: 'ri:',
},
{
label: 'Font Awesome 5 Solid',
name: 'fa-solid:',
},
];
const pageList = computed(
() =>
currentActiveType.value !== 'web' &&
copyIconList[currentActiveType.value]
.filter((i) => i.includes(filterValue.value))
.slice((currentPage.value - 1) * pageSize.value, currentPage.value * pageSize.value)
);
const iconItemStyle = computed((): ParameterCSSProperties => {
return (item) => {
if (inputValue.value === currentActiveType.value + item) {
return {
borderColor: 'var(--el-color-primary)',
color: 'var(--el-color-primary)',
};
}
};
});
const form = ref(props.formInline);
/** 搜索和初始化 */
const onSearch = async () => {
innerForm.loading = true;
const { currentPage, pageSize } = innerForm;
function setVal() {
currentActiveType.value = inputValue.value.substring(0, inputValue.value.indexOf(':') + 1);
icon.value = inputValue.value.substring(inputValue.value.indexOf(':') + 1);
}
//
const baseResult = await fetchMenuIconList({ currentPage, pageSize });
if (baseResult.code !== 200) return;
const data = baseResult.data;
function onBeforeEnter() {
if (isAllEmpty(icon.value)) return;
setVal();
//
const curIconIndex = copyIconList[currentActiveType.value].findIndex((i) => i === icon.value);
currentPage.value = Math.ceil((curIconIndex + 1) / pageSize.value);
}
//
innerForm.datalist = data.list;
innerForm.currentPage = data.pageNo;
innerForm.pageSize = data.pageSize;
innerForm.total = data.total;
innerForm.loading = false;
};
function onAfterLeave() {
filterValue.value = '';
}
/**
* * 修改图标
* @param value
*/
const onChangeIcon = (value: any) => {
form.value.icon = value.iconCode;
};
function handleClick({ props }) {
currentPage.value = 1;
currentActiveType.value = props.name;
}
/** 清除图标 */
const onClear = () => (form.value.icon = '');
function onChangeIcon(item) {
icon.value = item;
inputValue.value = currentActiveType.value + item;
}
/** 修改当前页 */
const onCurrentChange = async (value: number) => {
innerForm.currentPage = value;
await onSearch();
};
function onCurrentChange(page) {
currentPage.value = page;
}
onMounted(() => {
onSearch();
});
function onClear() {
icon.value = '';
inputValue.value = '';
}
watch(
() => pageList.value,
() =>
currentActiveType.value !== 'web' &&
(totalPage.value = copyIconList[currentActiveType.value].filter((i) => i.includes(filterValue.value)).length),
{ immediate: true }
);
watch(
() => inputValue.value,
(val) => val && setVal(),
{ immediate: true }
);
watch(
() => filterValue.value,
() => (currentPage.value = 1)
);
</script>
<template>
<div class="selector">
<el-popover :popper-options="{ placement: 'auto' }" :width="350" popper-class="pure-popper" trigger="click">
<template #reference>
<div class="w-[60px] h-[32px] cursor-pointer flex justify-center items-center">
<el-text v-if="!form.icon" type="primary">{{ $t('select_icon') }}</el-text>
<IconifyIconOnline v-else :icon="form.icon" style="font-size: 32px" />
</div>
</template>
<ul class="flex flex-wrap px-2 ml-2 h-[210px]">
<li
v-for="(item, key) in innerForm.datalist"
:key="key"
:class="`icon-item p-2 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-[#e5e7eb] ${item.iconCode === form.icon ? 'current' : ''}`"
:title="item.iconName"
@click="onChangeIcon(item)"
<el-input v-model="inputValue" disabled>
<template #append>
<el-popover
:popper-options="{
placement: 'auto',
}"
:width="350"
popper-class="pure-popper"
trigger="click"
@before-enter="onBeforeEnter"
@after-leave="onAfterLeave"
>
<IconifyIconOnline :icon="item.iconCode" height="20px" width="20px" />
</li>
</ul>
<el-empty v-show="innerForm.datalist.length === 0" :image-size="60" description="图标不存在" />
<template #reference>
<div class="w-[40px] h-[32px] cursor-pointer flex justify-center items-center">
<IconifyIconOffline v-if="!icon" :icon="Search" />
<IconifyIconOnline v-else :icon="inputValue" />
</div>
</template>
<div class="w-full h-9 flex items-center overflow-auto border-t border-[#e5e7eb]">
<el-pagination
:current-page="innerForm.currentPage"
:page-size="innerForm.pageSize"
:pager-count="5"
:total="innerForm.total"
background
class="flex-auto ml-2"
hide-on-single-page
layout="pager"
size="small"
@current-change="onCurrentChange"
/>
<el-button bg class="justify-end mr-2 ml-2" size="small" text type="danger" @click="onClear">清空</el-button>
</div>
</el-popover>
<el-link
:title="$t('systemMenuIcon.officialWebsite')"
:underline="false"
href="https://icon-sets.iconify.design/"
target="_blank"
type="primary"
>
{{ $t('systemMenuIcon.officialWebsite') }}
</el-link>
<el-input v-model="filterValue" class="px-2 pt-2" clearable placeholder="搜索图标" />
<el-tabs v-model="currentActiveType" @tab-click="handleClick">
<el-tab-pane v-for="(pane, index) in tabsList" :key="index" :label="pane.label" :name="pane.name">
<el-scrollbar height="220px">
<ul class="flex flex-wrap px-2 ml-2">
<li
v-for="(item, key) in pageList"
:key="key"
:style="iconItemStyle(item)"
:title="item"
class="icon-item p-2 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-[#e5e7eb]"
@click="onChangeIcon(item)"
>
<IconifyIconOnline :icon="currentActiveType + item" height="20px" width="20px" />
</li>
</ul>
<el-empty v-show="pageList.length === 0" :description="`${filterValue} 图标不存在`" :image-size="60" />
</el-scrollbar>
<div class="w-full h-9 flex items-center overflow-auto border-t border-[#e5e7eb]">
<el-pagination
:current-page="currentPage"
:page-size="pageSize"
:pager-count="5"
:total="totalPage"
background
class="flex-auto ml-2"
layout="pager"
size="small"
@current-change="onCurrentChange"
/>
<el-button bg class="justify-end mr-2 ml-2" size="small" text type="danger" @click="onClear">
清空
</el-button>
</div>
</el-tab-pane>
</el-tabs>
</el-popover>
</template>
</el-input>
</div>
</template>
<style lang="scss" scoped>
.current {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
transition: all 0.4s;
transform: scaleX(1.05);
}
.icon-item {
width: 38px;
height: 38px;
&:hover {
color: var(--el-color-primary);
border-color: var(--el-color-primary);

View File

@ -1,11 +1,12 @@
import { defineStore } from 'pinia';
import {
fetchAddRole,
fetchAllRoles,
fetchAssignPowersToRole,
fetchDeleteRole,
fetchGetAllRoles,
fetchGetRoleList,
fetchUpdateRole,
fetchUpdateRoleByFile,
} from '@/api/v1/system/role';
import { pageSizes } from '@/enums/baseConstant';
import { storeMessage } from '@/utils/message';
@ -58,8 +59,8 @@ export const useRoleStore = defineStore('roleStore', {
},
/** 获取所有角色 */
async getAllRoles() {
const result = await fetchGetAllRoles();
async allRoles() {
const result = await fetchAllRoles();
if (result.code !== 200) return;
this.allRoleList = result.data.map((role) => ({ key: role.id, label: role.description }));
@ -71,6 +72,12 @@ export const useRoleStore = defineStore('roleStore', {
return storeMessage(result);
},
/* 使用Excel更新角色列表 */
async updateRoleByFile(data: any) {
const result = await fetchUpdateRoleByFile(data);
return storeMessage(result);
},
/** 为角色分配权限 */
async assignPowersToRole(data: any) {
const result = await fetchAssignPowersToRole(data);

View File

@ -2,10 +2,10 @@ import { defineStore } from 'pinia';
import { resetRouter, router, routerArrays, storageLocal, store, type userType } from '../utils';
import {
fetchAssignRolesToUsers,
fetchGetUserinfo,
fetchLogin,
fetchLogout,
fetchPostEmailCode,
fetchUserinfo,
refreshTokenApi,
} from '@/api/v1/system/adminUser';
import { useMultiTagsStoreHook } from '../multiTags';
@ -86,7 +86,7 @@ export const useUserStore = defineStore({
/** 获取用户信息 */
async getUserinfo() {
const result = await fetchGetUserinfo();
const result = await fetchUserinfo();
if (result.code === 200) {
const data = result.data;
setToken(data);

View File

@ -62,6 +62,7 @@ export async function download(blob: any, filename: string) {
const result = await blobToJson(blob);
if (result) return;
blob = blob.data;
// 创建一个临时的 URL用于下载文件
const a = document.createElement('a');
const url = window.URL.createObjectURL(new Blob([blob]));
@ -117,8 +118,7 @@ export const downloadBlob = async (response: any, fileName: string) => {
const contentDisposition = response.headers['content-disposition'];
// let fileName = 'download.zip';
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename="?(.+)"/);
const fileNameMatch = contentDisposition.match(/filename=?(.+)/);
if (fileNameMatch && fileNameMatch[1]) {
fileName = fileNameMatch[1];
}
@ -144,7 +144,8 @@ export const downloadBlob = async (response: any, fileName: string) => {
*/
async function blobToJson(blob: any): Promise<any> {
try {
const text = await blob.text();
const text = await blob.data.text();
const json = JSON.parse(text);
if (json.code !== 200) {
message(json.message, { type: 'error' });

View File

@ -1,5 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import ReAuth from '@/components/ReAuth/src/auth';
import { useRenderIcon } from '@/components/ReIcon/src/hooks';
import { selectUserinfo } from '@/components/Table/Userinfo/columns';
import PureTableBar from '@/components/TableBar/src/bar';
import { $t } from '@/plugins/i18n';
import { hasAuth } from '@/router/utils';
import { useEmailTemplateStore } from '@/store/configuration/emailTemplate';
import {
auth,
columns,
@ -11,19 +17,15 @@ import {
selectRows,
viewTemplate,
} from '@/views/configuration/email-template/utils';
import PureTableBar from '@/components/TableBar/src/bar';
import AddFill from '@iconify-icons/ri/add-circle-line';
import PureTable from '@pureadmin/table';
import Delete from '@iconify-icons/ep/delete';
import EditPen from '@iconify-icons/ep/edit-pen';
import Refresh from '@iconify-icons/ep/refresh';
import { selectUserinfo } from '@/components/Table/Userinfo/columns';
import { $t } from '@/plugins/i18n';
import { useEmailTemplateStore } from '@/store/configuration/emailTemplate';
import { useRenderIcon } from '@/components/ReIcon/src/hooks';
import { hasAuth } from '@/router/utils';
import View from '@iconify-icons/ep/view';
import ReAuth from '@/components/ReAuth/src/auth';
import AddFill from '@iconify-icons/ri/add-circle-line';
import PureTable from '@pureadmin/table';
import { onMounted, ref } from 'vue';
defineOptions({ name: 'EmailTemplate' });
const tableRef = ref();
const formRef = ref();

View File

@ -35,7 +35,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, minWidth: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', minWidth: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', minWidth: 130 },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
// 添加规则

View File

@ -28,6 +28,8 @@ import { hasAuth } from '@/router/utils';
import { enabledOrNotStatus } from '@/enums/baseConstant';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'EmailUsersConfiguration' });
const tableRef = ref();
const formRef = ref();
const emailUsersStore = useEmailUsersStore();

View File

@ -23,7 +23,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, minWidth: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', minWidth: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', minWidth: 130 },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
// 添加规则

View File

@ -24,6 +24,7 @@ import MenuIconSelectIconName from '@/views/configuration/menu-icon/components/m
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'MenuIconConfiguration' });
const tableRef = ref();
const formRef = ref();
const menuIconStore = useMenuIconStore();

View File

@ -13,7 +13,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', width: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', width: 130 },
{ label: $t('table.operation'), fixed: 'right', width: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', width: 210, slot: 'operation' },
];
// 添加规则

View File

@ -8,6 +8,8 @@ import { userI18nTypeStore } from '@/store/i18n/i18nType';
import { usePublicHooks } from '@/views/hooks';
import { hasAuth } from '@/router/utils';
defineOptions({ name: 'WebConfiguration' });
const ruleFormRef = ref<FormInstance>();
const i18nTypeStore = userI18nTypeStore();
//

View File

@ -76,6 +76,6 @@ defineExpose({ formRef });
</el-form-item>
<!-- 更新提示 -->
<el-text type="danger">{{ $t('update_i18n_tip') }}</el-text>
<el-text type="danger">{{ $t('update_tip') }}</el-text>
</el-form>
</template>

View File

@ -27,6 +27,8 @@ import Download from '@iconify-icons/ep/download';
import Upload from '@iconify-icons/ri/upload-line';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'I18n' });
const tableRef = ref();
const pageFormRef = ref();
const i18nStore = userI18nStore();
@ -134,7 +136,7 @@ onMounted(() => {
<!-- 下载多语言配置 -->
<el-dropdown v-if="hasAuth(auth.update)" class="mr-1" type="primary">
<el-button :icon="useRenderIcon(Upload)" plain type="primary">{{ $t('file_update') }}</el-button>
<el-button :icon="useRenderIcon(Upload)" plain type="primary">{{ $t('file_import') }}</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="updateI18nSetting('json')">{{ $t('use_json_update') }}</el-dropdown-item>

View File

@ -1,7 +1,7 @@
// 多语言表格列字段
import { reactive } from 'vue';
import type { FormRules } from 'element-plus';
import { $t } from '@/plugins/i18n';
import type { FormRules } from 'element-plus';
import { reactive } from 'vue';
export const columns: TableColumnList = [
{ type: 'selection', align: 'left' },
@ -13,7 +13,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', width: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', width: 130 },
{ label: $t('table.operation'), fixed: 'right', width: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', width: 210, slot: 'operation' },
];
// 添加多语言表单规则

View File

@ -14,6 +14,8 @@ import { selectUserinfo } from '@/components/Table/Userinfo/columns';
import { $t } from '@/plugins/i18n';
import { hasAuth } from '@/router/utils';
defineOptions({ name: 'I18nType' });
const tableRef = ref();
const formRef = ref();
const i18nTypeStore = userI18nTypeStore();

View File

@ -12,7 +12,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime' },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', width: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', width: 130 },
{ label: $t('table.operation'), fixed: 'right', width: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', width: 210, slot: 'operation' },
];
// 添加规则

View File

@ -6,6 +6,8 @@ import { formState, settingLR } from '@/views/message-manger/message-editing/uti
import MarkdownEditor from '@/views/message-manger/message-editing/components/markdown-editor.vue';
import { onMounted } from 'vue';
defineOptions({ name: 'MessageEditer' });
/** 退出提醒 */
const exitAlter = () => {
window.addEventListener('beforeunload', function (e) {

View File

@ -24,6 +24,8 @@ import { Message } from '@element-plus/icons-vue';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'MessageReceived' });
const tableRef = ref();
const formRef = ref();
const messageReceivedStore = useMessageReceivedStore();

View File

@ -25,6 +25,8 @@ import { useMessageTypeStore } from '@/store/message/messageType';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'MessageSender' });
const tableRef = ref();
const formRef = ref();
const messageTypeStore = useMessageTypeStore();

View File

@ -39,7 +39,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, minWidth: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', minWidth: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', minWidth: 130 },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
// 添加规则

View File

@ -26,6 +26,8 @@ import { usePublicHooks } from '@/views/hooks';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'MessageType' });
const tableRef = ref();
const formRef = ref();
const messageTypeStore = useMessageTypeStore();

View File

@ -18,7 +18,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, width: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', width: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', width: 130 },
{ label: $t('table.operation'), fixed: 'right', width: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', width: 210, slot: 'operation' },
];
// 添加规则

View File

@ -2,6 +2,8 @@
import { onMounted, ref } from 'vue';
import { fetchSystemCaches } from '@/api/v1/actuator';
defineOptions({ name: 'SystemCaches' });
const caches = ref([]);
const onSearch = async () => {
let result = await fetchSystemCaches();
@ -16,7 +18,7 @@ onMounted(() => {
<template>
<el-descriptions :column="2" border direction="vertical" title="系统已缓存内容">
<el-descriptions-item v-for="cache in caches" :key="cache.key" :label="cache.key" style="overflow: auto">
<el-descriptions-item v-for="cache in caches" :key="cache.key" :label="cache.key">
{{ cache.value }}
</el-descriptions-item>
</el-descriptions>

View File

@ -25,6 +25,8 @@ import 'vue-json-pretty/lib/styles.css';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'SchedulerExecuteLog' });
const tableRef = ref();
const formRef = ref();
const quartzExecuteLogStore = useQuartzExecuteLogStore();

View File

@ -23,7 +23,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, minWidth: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', minWidth: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', minWidth: 130 },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
// 添加规则
export const rules = reactive({});

View File

@ -4,6 +4,8 @@ import { info, loading, svg } from '@/views/monitor/server/utils';
import SystemServer from '@/views/monitor/server/components/system-server.vue';
import SystemCpu from '@/views/monitor/server/components/system-cpu.vue';
import SystemJvmCpu from '@/views/monitor/server/components/system-jvm-cpu.vue';
defineOptions({ name: 'MonitorServer' });
</script>
<template>

View File

@ -22,6 +22,8 @@ import { FormInstance } from 'element-plus';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'UserLoginLog' });
const tableRef = ref();
const formRef = ref();
const userLoginLogStore = useUserLoginLogStore();

View File

@ -1,5 +1,5 @@
import { reactive } from 'vue';
import { $t } from '@/plugins/i18n';
import { reactive } from 'vue';
// 表格列
export const columns: TableColumnList = [
@ -23,7 +23,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, minWidth: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', minWidth: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', minWidth: 130 },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
// 添加规则

View File

@ -24,6 +24,8 @@ import { FormInstance } from 'element-plus';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'SchedulerGroup' });
const tableRef = ref();
const formRef = ref();
const schedulersGroupStore = useSchedulersGroupStore();

View File

@ -13,7 +13,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, width: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', width: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', width: 130 },
{ label: $t('table.operation'), fixed: 'right', width: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', width: 210, slot: 'operation' },
];
// 添加规则

View File

@ -24,6 +24,8 @@ import { FormInstance } from 'element-plus';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'SchedulerTask' });
const tableRef = ref();
const formRef = ref();
const schedulersStore = useSchedulersStore();

View File

@ -18,7 +18,7 @@ export const columns: TableColumnList = [
{ label: $t('schedulers_triggerState'), prop: 'triggerState' },
// corn表达式
{ label: $t('schedulers_cronExpression'), prop: 'cronExpression' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
// 添加规则

View File

@ -26,7 +26,7 @@ const getRoleListByUserId = async () => {
};
onMounted(() => {
roleStore.getAllRoles();
roleStore.allRoles();
getRoleListByUserId();
});

View File

@ -42,6 +42,8 @@ import { usePublicHooks } from '@/views/hooks';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'AdminUserManger' });
const adminUserStore = useAdminUserStore();
const deptStore = useDeptStore();
//

View File

@ -25,6 +25,7 @@ import { FormInstance } from 'element-plus';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'DeptManger' });
const tableRef = ref();
const formRef = ref();
const deptStore = useDeptStore();

View File

@ -1,5 +1,5 @@
import { reactive } from 'vue';
import { $t } from '@/plugins/i18n';
import { reactive } from 'vue';
// 表格列
export const columns: TableColumnList = [
@ -15,7 +15,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, minWidth: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', minWidth: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', minWidth: 130 },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
// 添加规则

View File

@ -27,6 +27,8 @@ import { FormInstance } from 'element-plus';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'FileManger' });
const tableRef = ref();
const formRef = ref();
const filesStore = useFilesStore();

View File

@ -17,7 +17,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, minWidth: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', minWidth: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', minWidth: 130 },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
// 添加规则

View File

@ -1,7 +1,6 @@
<script lang="ts" setup>
import ReAnimateSelector from '@/components/ReAnimateSelector/src/index.vue';
import ReCol from '@/components/ReCol';
import IconSelect from '@/components/ReIcon/src/Select.vue';
import Segmented from '@/components/ReSegmented';
import {
fixedTagOptions,
@ -17,6 +16,7 @@ import { FormProps, formRules } from '@/views/system/menu/utils';
import { onMounted, ref } from 'vue';
import { userMenuStore } from '@/store/system/menu';
import { useRoleStore } from '@/store/system/role';
import NetWorkIcon from '@/components/ReIcon/src/NetWorkIcon.vue';
const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({
@ -66,7 +66,7 @@ const getRoleListByRouterId = async () => {
onMounted(() => {
//
roleStore.getAllRoles();
roleStore.allRoles();
// id
getRoleListByRouterId();
@ -150,7 +150,8 @@ defineExpose({ menuFormRef: formRef });
<!-- 菜单图标 -->
<re-col :sm="24" :value="12" :xs="24">
<el-form-item label="菜单图标" prop="icon">
<IconSelect :form-inline="form" class="w-full" />
<!--<IconSelect v-show="true" :form-inline="form" class="w-full" />-->
<NetWorkIcon :form-inline="form" class="w-full" />
</el-form-item>
</re-col>

View File

@ -26,24 +26,20 @@ import { FormInstance } from 'element-plus';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'MenuManger' });
const menuStore = userMenuStore();
const routerStore = userMenuStore();
const formRef = ref();
/**
* 表单重置
* @param formEl
*/
/* 表单重置 */
const resetForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
await onSearch();
};
/**
* * 选择多行
* @param rows
*/
/* 选择多行 */
const onSelectionChange = (rows: Array<any>) => {
selectIds.value = rows.map((row: any) => row.id);
};

View File

@ -104,7 +104,7 @@ export const columns: TableColumnList = [
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, minWidth: 160 },
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', minWidth: 130 },
{ label: $t('table.updateUser'), prop: 'updateUser', slot: 'updateUser', minWidth: 130 },
{ label: $t('table.operation'), fixed: 'right', minWidth: 160, slot: 'operation' },
{ label: $t('table.operation'), fixed: 'right', minWidth: 210, slot: 'operation' },
];
/** 自定义表单规则校验 */

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { FormInstance } from 'element-plus';
import { FormProps, powerCascadeProps, rules } from '@/views/system/power/utils';
import { FormProps, powerCascadeProps, rules } from '@/views/system/permission/utils';
import { $t } from '@/plugins/i18n';
import { handleTree } from '@pureadmin/utils';
import { usePowerStore } from '@/store/system/power';

View File

@ -10,7 +10,7 @@ import {
onUpdate,
onUpdateBatchParent,
powerIds,
} from '@/views/system/power/utils';
} from '@/views/system/permission/utils';
import PureTableBar from '@/components/TableBar/src/bar';
import AddFill from '@iconify-icons/ri/add-circle-line';
import PureTable from '@pureadmin/table';
@ -26,6 +26,8 @@ import { FormInstance } from 'element-plus';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
defineOptions({ name: 'PermissionManger' });
const tableRef = ref();
const formRef = ref();
const powerStore = usePowerStore();

View File

@ -1,12 +1,12 @@
import { addDialog } from '@/components/ReDialog/index';
import PowerDialog from '@/views/system/power/components/power-dialog.vue';
import PowerDialog from '@/views/system/permission/components/power-dialog.vue';
import { usePowerStore } from '@/store/system/power';
import { h, reactive, ref } from 'vue';
import { message, messageBox } from '@/utils/message';
import type { FormItemProps } from '@/views/system/power/utils/types';
import type { FormItemProps } from '@/views/system/permission/utils/types';
import { $t } from '@/plugins/i18n';
import { handleTree } from '@pureadmin/utils';
import { powerCascadeProps } from '@/views/system/power/utils/columns';
import { powerCascadeProps } from '@/views/system/permission/utils/columns';
import { ElCascader, ElForm, ElFormItem } from 'element-plus';
import DeleteBatchDialog from '@/components/Table/DeleteBatchDialog.vue';

View File

@ -13,7 +13,7 @@ import Check from '@iconify-icons/ep/check';
import { computed, nextTick, onMounted, ref, watch } from 'vue';
import { delay, getKeyList, handleTree, subBefore, useResizeObserver } from '@pureadmin/utils';
import { usePowerStore } from '@/store/system/power';
import { powerCascadeProps } from '@/views/system/power/utils';
import { powerCascadeProps } from '@/views/system/permission/utils';
import { useRoleStore } from '@/store/system/role';
const powerStore = usePowerStore();

View File

@ -0,0 +1,62 @@
<script lang="ts" setup>
import { FormInstance, type FormRules, genFileId, type UploadProps, type UploadRawFile } from 'element-plus';
import { $t } from '@/plugins/i18n';
import { reactive, ref } from 'vue';
import { UploadFilled } from '@element-plus/icons-vue';
interface Props {
form: {
file: any;
};
}
const rules = reactive<FormRules>({
file: [{ required: true, message: `${$t('select')}${$t('files')}`, trigger: 'blur' }],
});
const props = withDefaults(defineProps<Props>(), {
form: () => ({
file: undefined,
}),
});
const formRef = ref<FormInstance>();
const form = ref(props.form);
const uploadRef = ref();
const handleExceed: UploadProps['onExceed'] = (files) => {
uploadRef.value!.clearFiles();
const file = files[0] as UploadRawFile;
file.uid = genFileId();
uploadRef.value!.handleStart(file);
};
defineExpose({ formRef });
</script>
<template>
<el-form ref="formRef" :model="form" :rules="rules" isDefault-icon>
<el-form-item :label="$t('files')" prop="file">
<el-upload
ref="uploadRef"
v-model:file-list="form.file"
:autoUpload="false"
:limit="1"
:on-exceed="handleExceed"
class="w-full mt-2"
drag
>
<el-icon class="el-icon--upload">
<UploadFilled />
</el-icon>
<div class="el-upload__text">
<em>{{ `${$t('drop_file_here')} / ${$t('click_to_upload')}` }}</em>
</div>
</el-upload>
</el-form-item>
<!-- 更新提示 -->
<el-text type="danger">{{ $t('update_tip') }}</el-text>
</el-form>
</template>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { onMounted } from 'vue';
import { h, onMounted } from 'vue';
import {
auth,
columns,
@ -30,44 +30,76 @@ import Menu from '@iconify-icons/ep/menu';
import AssignPowersToRole from '@/views/system/role/components/assign-powers-to-role.vue';
import { hasAuth } from '@/router/utils';
import ReAuth from '@/components/ReAuth/src/auth';
import { fetchExportByExcel } from '@/api/v1/system/role';
import { downloadBlob } from '@/utils/sso';
import Download from '@iconify-icons/ep/download';
import Upload from '@iconify-icons/ri/upload-line';
import { addDialog } from '@/components/ReDialog/index';
import FileUpdateRoleDialog from '@/views/system/role/components/file-update-role-dialog.vue';
import { FormInstance } from 'element-plus';
defineOptions({ name: 'RoleManger' });
const roleStore = useRoleStore();
/**
* * 当前页改变时
*/
/* 当前页改变时 */
const onCurrentPageChange = async (value: number) => {
roleStore.pagination.currentPage = value;
await onSearch();
};
/**
* * 当分页发生变化
* @param value
*/
/* 当分页发生变化 */
const onPageSizeChange = async (value: number) => {
roleStore.pagination.pageSize = value;
await onSearch();
};
/**
* * 选择多行
* @param rows
*/
/* 选择多行 */
const onSelectionChange = (rows: Array<any>) => {
deleteIds.value = rows.map((row: any) => row.id);
};
/**
* 重置表单
* @param formEl
*/
const resetForm = async (formEl) => {
/* 重置表单 */
const resetForm = async (formEl: FormInstance) => {
if (!formEl) return;
formEl.resetFields();
await onSearch();
};
/* 使用Excel导出导出角色列表 */
const downloadRoleExcel = async () => {
const result = await fetchExportByExcel();
downloadBlob(result, 'role.zip');
};
/* 使用文件更新角色 */
const onUpdateByFile = (row: any) => {
addDialog({
title: `${$t('modify')}${$t('role')}`,
width: '30%',
props: { form: { file: undefined } },
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(FileUpdateRoleDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.form;
formRef.value.formRef.validate(async (valid: any) => {
if (!valid) return;
// data
const data = { file: form.file[0].raw };
//
const result = await roleStore.updateRoleByFile(data);
if (!result) return;
done();
await onSearch();
});
},
});
};
onMounted(() => {
onSearch();
});
@ -122,6 +154,27 @@ onMounted(() => {
@refresh="onSearch"
>
<template #buttons>
<!-- 下载Excel配置 -->
<el-button
v-if="hasAuth(auth.downloadRole)"
:icon="useRenderIcon(Download)"
plain
type="primary"
@click="downloadRoleExcel"
>
{{ $t('download_configuration') }}
</el-button>
<!-- 文件更新 -->
<el-button
v-if="hasAuth(auth.update)"
:icon="useRenderIcon(Upload)"
plain
type="primary"
@click="onUpdateByFile"
>
{{ $t('file_import') }}
</el-button>
<el-button v-if="hasAuth(auth.add)" :icon="useRenderIcon(AddFill)" plain type="primary" @click="onAdd">
{{ $t('addNew') }}
</el-button>

View File

@ -9,4 +9,6 @@ export const auth = {
deleted: ['role::deleteRole'],
// 为角色分配权限
assignPowersToRole: ['rolePower::assignPowersToRole'],
// 下载角色配置
downloadRole: ['role::downloadRole'],
};

View File

@ -1,5 +1,5 @@
import { computed, reactive } from 'vue';
import { $t } from '@/plugins/i18n';
import { computed, reactive } from 'vue';
// 表格列
export const columns: TableColumnList = [

View File

@ -58,10 +58,7 @@ export function onAdd() {
});
}
/**
* *
* @param row
*/
/* 更新角色 */
export function onUpdate(row: any) {
addDialog({
title: `${$t('modify')}${$t('role')}`,