feat: 完善参数页面的开发

This commit is contained in:
valarchie 2023-07-20 14:53:37 +08:00
parent 5aa607744b
commit 51c1deab44
6 changed files with 328 additions and 229 deletions

3
.gitignore vendored
View File

@ -12,6 +12,9 @@ npm-debug.log*
.pnpm-debug.log
tests/**/coverage/
# 本机调试debug配置文件
.vscode/launch.json
# Editor directories and files
.idea
*.suo

76
src/api/system/config.ts Normal file
View File

@ -0,0 +1,76 @@
import { http } from "@/utils/http";
export interface ConfigQuery extends BasePageQuery {
/**
* key
*/
configKey?: string;
/**
*
*/
configName?: string;
/**
*
*/
isAllowChange?: string;
}
/**
* ConfigDTO,
*/
export interface ConfigDTO {
configId?: string;
configKey?: string;
configName?: string;
configOptions?: string[];
configValue?: string;
createTime?: Date;
isAllowChange?: string;
isAllowChangeStr?: string;
remark?: string;
}
/**
* ConfigUpdateCommand
*/
export interface UpdateConfigRequest {
configValue: string;
}
/** 获取配置列表 */
export const getConfigListApi = (params?: ConfigQuery) => {
return http.request<ResponseData<PageDTO<ConfigDTO>>>(
"get",
"/system/configs",
{
params
}
);
};
/** 获取配置信息 */
export const getConfigInfoApi = (configId: string) => {
return http.request<ResponseData<ConfigDTO>>(
"get",
`/system/config/${configId}`
);
};
/** 刷新配置缓存 */
export const updateConfigApi = (
configId: number,
data: UpdateConfigRequest
) => {
return http.request<ResponseData<PageDTO<ConfigDTO>>>(
"put",
`/system/config/${configId}`,
{
data
}
);
};
/** 刷新配置缓存 */
export const refreshConfigCacheApi = () => {
return http.request<ResponseData<void>>("delete", "/system/configs/cache");
};

View File

@ -0,0 +1,98 @@
<script setup lang="ts">
import { ref } from "vue";
import { useUserStoreHook } from "@/store/modules/user";
import { formRules } from "./utils/rule";
import { ConfigDTO } from "@/api/system/config";
interface FormProps<T> {
formInline: T;
}
/** TODO 有其他方式 来换掉这个props 父子组件传值吗? */
const props = withDefaults(defineProps<FormProps<ConfigDTO>>(), {
formInline: () => ({})
});
const formData = ref(props.formInline);
// TODO
const formRuleRef = ref();
function getFormRuleRef() {
return formRuleRef.value;
}
defineExpose({ getFormRuleRef });
</script>
<template>
<el-form
ref="formRuleRef"
:model="formData"
:rules="formRules"
label-width="82px"
>
<el-form-item label="参数名称" prop="configName">
<el-input
v-model="formData.configName"
clearable
placeholder="请输入参数名称"
:disabled="true"
/>
</el-form-item>
<el-form-item label="参数键名" prop="configKey">
<el-input
v-model="formData.configKey"
clearable
placeholder="请输入参数键名"
:disabled="true"
/>
</el-form-item>
<el-form-item label="参数值" prop="configValue">
<el-select
v-model="formData.configValue"
v-if="formData.configOptions.length > 0"
placeholder="请选择类型"
clearable
class="!w-[180px]"
>
<el-option
v-for="item in formData.configOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
<el-input
v-else
v-model="formData.configValue"
placeholder="请输入参数键值"
/>
</el-form-item>
<el-form-item label="允许修改" prop="isAllowChange">
<el-select
v-model="formData.isAllowChange"
placeholder="请选择"
clearable
class="!w-[180px]"
:disabled="true"
>
<el-option
v-for="dict in useUserStoreHook().dictionaryList['common.yesOrNo']"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
placeholder="请输入内容"
:disabled="true"
/>
</el-form-item>
</el-form>
</template>

View File

@ -1,24 +1,21 @@
<script setup lang="ts">
import { ref } from "vue";
import { useLoginLogHook } from "./utils/hook";
import { useHook } from "./utils/hook";
import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Delete from "@iconify-icons/ep/delete";
import EditPen from "@iconify-icons/ep/edit-pen";
import Search from "@iconify-icons/ep/search";
import Refresh from "@iconify-icons/ep/refresh";
import AddFill from "@iconify-icons/ri/add-circle-line";
import { useUserStoreHook } from "@/store/modules/user";
// TODO
import { CommonUtils } from "@/utils/common";
/** 组件name最好和菜单表中的router_name一致 */
/** !!!重要!!! 组件name最好和菜单表中的router_name一致, copy的时候记得更改这个名字*/
defineOptions({
name: "SystemOperationLog"
name: "SystemConfig"
});
const loginLogStatusList =
useUserStoreHook().dictionaryList["sysLoginLog.status"];
const yesOrNoList = useUserStoreHook().dictionaryList["common.yesOrNo"];
const tableRef = ref();
const searchFormRef = ref();
@ -28,16 +25,12 @@ const {
columns,
dataList,
pagination,
timeRange,
defaultSort,
multipleSelection,
onSearch,
resetForm,
exportAllExcel,
getLoginLogList,
handleDelete,
handleBulkDelete
} = useLoginLogHook();
openDialog,
handleRefresh,
getList
} = useHook();
</script>
<template>
@ -49,53 +42,38 @@ const {
:model="searchFormParams"
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
>
<el-form-item label="登录IP" prop="ipAddress">
<el-form-item label="参数名称:" prop="configName">
<el-input
v-model="searchFormParams.ipAddress"
placeholder="请输入IP地址"
v-model="searchFormParams.configName"
placeholder="请输入参数名称"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="用户名:" prop="username">
<el-form-item label="参数键名:" prop="configKey">
<el-input
v-model="searchFormParams.username"
placeholder="请选择用户名称"
v-model="searchFormParams.configKey"
placeholder="请输入参数键名"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="状态:" prop="status">
<el-form-item label="允许修改:" prop="isAllowChange">
<el-select
v-model="searchFormParams.status"
placeholder="请选择状态"
v-model="searchFormParams.isAllowChange"
placeholder="请选择"
clearable
class="!w-[180px]"
>
<el-option
v-for="dict in loginLogStatusList"
v-for="dict in yesOrNoList"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<label class="el-form-item__label is-required font-bold"
>登录时间</label
>
<!-- TODO 如何消除这个v-model的warning -->
<el-date-picker
class="!w-[240px]"
v-model="timeRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@ -115,24 +93,19 @@ const {
</el-form>
<!-- table bar 包裹 table -->
<PureTableBar title="登录日志列表" :columns="columns" @refresh="onSearch">
<PureTableBar title="通知列表" :columns="columns" @refresh="onSearch">
<!-- 表格操作栏 -->
<template #buttons>
<el-button
type="danger"
:icon="useRenderIcon(Delete)"
@click="handleBulkDelete(tableRef)"
type="warning"
:icon="useRenderIcon(AddFill)"
@click="handleRefresh()"
>
批量删除
刷新缓存
</el-button>
<el-button
type="primary"
@click="CommonUtils.exportExcel(columns, dataList, '登录日志列表')"
>单页导出</el-button
>
<el-button type="primary" @click="exportAllExcel">全部导出</el-button>
</template>
<template v-slot="{ size, dynamicColumns }">
<!-- TODO sort-change 有其他好的处理方式吗 -->
<pure-table
border
ref="tableRef"
@ -144,37 +117,26 @@ const {
adaptive
:data="dataList"
:columns="dynamicColumns"
:default-sort="defaultSort"
:pagination="pagination"
:paginationSmall="size === 'small' ? true : false"
:header-cell-style="{
background: 'var(--el-table-row-hover-bg-color)',
color: 'var(--el-text-color-primary)'
}"
@page-size-change="getLoginLogList"
@page-current-change="getLoginLogList"
@sort-change="getLoginLogList"
@selection-change="
rows => (multipleSelection = rows.map(item => item.logId))
"
@page-size-change="getList"
@page-current-change="getList"
>
<template #operation="{ row }">
<el-popconfirm
:title="`是否确认删除编号为${row.logId}的这条日志`"
@confirm="handleDelete(row)"
<el-button
class="reset-margin"
link
type="primary"
:size="size"
:icon="useRenderIcon(EditPen)"
@click="openDialog(row)"
>
<template #reference>
<el-button
class="reset-margin"
link
type="danger"
:size="size"
:icon="useRenderIcon(Delete)"
>
删除
</el-button>
</template>
</el-popconfirm>
修改
</el-button>
</template>
</pure-table>
</template>

View File

@ -1,26 +1,21 @@
import dayjs from "dayjs";
import editForm from "../form.vue";
import { message } from "@/utils/message";
import { ElMessageBox, Sort } from "element-plus";
import { addDialog } from "@/components/ReDialog";
import { type PaginationProps } from "@pureadmin/table";
import {
getLoginLogListApi,
deleteLoginLogApi,
exportLoginLogExcelApi,
LoginLogQuery
} from "@/api/system/log";
import { reactive, ref, onMounted, toRaw } from "vue";
import { useUserStoreHook } from "@/store/modules/user";
getConfigListApi,
getConfigInfoApi,
updateConfigApi,
refreshConfigCacheApi,
ConfigQuery,
ConfigDTO,
UpdateConfigRequest
} from "@/api/system/config";
import { reactive, ref, onMounted, h, toRaw } from "vue";
import { CommonUtils } from "@/utils/common";
import { PaginationProps } from "@pureadmin/table";
const loginLogStatusMap =
useUserStoreHook().dictionaryMap["sysLoginLog.status"];
export function useLoginLogHook() {
const defaultSort: Sort = {
prop: "loginTime",
order: "descending"
};
export function useHook() {
const pagination: PaginationProps = {
total: 0,
pageSize: 10,
@ -28,190 +23,150 @@ export function useLoginLogHook() {
background: true
};
const timeRange = ref([]);
const searchFormParams = reactive<LoginLogQuery>({
ipAddress: undefined,
username: undefined,
status: undefined,
beginTime: undefined,
endTime: undefined,
timeRangeColumn: defaultSort.prop
const searchFormParams = reactive<ConfigQuery>({
configKey: undefined,
configName: undefined,
isAllowChange: undefined
});
const formRef = ref();
const dataList = ref([]);
const pageLoading = ref(true);
const multipleSelection = ref([]);
const columns: TableColumnList = [
{
type: "selection",
align: "left"
label: "参数编号",
prop: "configId",
minWidth: 60
},
{
label: "日志编号",
prop: "logId",
label: "参数名称",
prop: "configName",
minWidth: 120
},
{
label: "参数键",
prop: "configKey",
minWidth: 120,
showOverflowTooltip: true
},
{
label: "参数值",
prop: "configValue",
minWidth: 150
},
{
label: "参数选项",
prop: "configOptions",
minWidth: 120
},
{
label: "允许更改",
prop: "isAllowChangeStr",
minWidth: 100
},
{
label: "用户名",
prop: "username",
label: "备注",
prop: "remark",
minWidth: 120,
sortable: "custom"
},
{
label: "IP地址",
prop: "ipAddress",
minWidth: 120
},
{
label: "登录地点",
prop: "loginLocation",
minWidth: 120
},
{
label: "操作系统",
prop: "operationSystem",
minWidth: 120
},
{
label: "浏览器",
prop: "browser",
minWidth: 120
},
{
label: "状态",
prop: "status",
minWidth: 120,
cellRenderer: ({ row, props }) => (
<el-tag
size={props.size}
type={loginLogStatusMap[row.status].cssTag}
effect="plain"
>
{loginLogStatusMap[row.status].label}
</el-tag>
)
},
{
label: "状态名",
prop: "statusStr",
minWidth: 120,
hide: true
},
{
label: "登录时间",
minWidth: 160,
prop: "loginTime",
sortable: "custom",
formatter: ({ loginTime }) =>
dayjs(loginTime).format("YYYY-MM-DD HH:mm:ss")
showOverflowTooltip: true
},
{
label: "操作",
fixed: "right",
width: 140,
width: 120,
slot: "operation"
}
];
async function onSearch() {
function onSearch() {
// 点击搜索的时候 需要重置分页
pagination.currentPage = 1;
getLoginLogList();
getList();
}
function resetForm(formEl, tableRef) {
if (!formEl) return;
// 清空查询参数
formEl.resetFields();
// 清空排序
searchFormParams.orderColumn = undefined;
searchFormParams.orderDirection = undefined;
// 清空时间查询 TODO 这块有点繁琐 有可以优化的地方吗?
// Form组件的resetFields方法无法清除datepicker里面的数据。
timeRange.value = [];
searchFormParams.beginTime = undefined;
searchFormParams.endTime = undefined;
tableRef.getTableRef().clearSort();
// 重置分页并查询
onSearch();
}
async function getLoginLogList(sort: Sort = defaultSort) {
pageLoading.value = true;
if (sort != null) {
CommonUtils.fillSortParams(searchFormParams, sort);
}
async function getList() {
CommonUtils.fillPaginationParams(searchFormParams, pagination);
CommonUtils.fillTimeRangeParams(searchFormParams, timeRange.value);
const { data } = await getLoginLogListApi(toRaw(searchFormParams)).finally(
pageLoading.value = true;
const { data } = await getConfigListApi(toRaw(searchFormParams)).finally(
() => {
pageLoading.value = false;
}
);
dataList.value = data.rows;
pagination.total = data.total;
}
async function exportAllExcel(sort: Sort = defaultSort) {
if (sort != null) {
CommonUtils.fillSortParams(searchFormParams, sort);
}
CommonUtils.fillPaginationParams(searchFormParams, pagination);
CommonUtils.fillTimeRangeParams(searchFormParams, timeRange.value);
exportLoginLogExcelApi(toRaw(searchFormParams), "登录日志.xls");
}
async function handleDelete(row) {
await deleteLoginLogApi([row.logId]).then(() => {
message(`您删除了操作编号为${row.logId}的这条数据`, {
async function handleRefresh() {
await refreshConfigCacheApi().then(() => {
message("刷新缓存成功", {
type: "success"
});
// 刷新列表
getLoginLogList();
getList();
});
}
async function handleBulkDelete(tableRef) {
if (multipleSelection.value.length === 0) {
message("请选择需要删除的数据", { type: "warning" });
return;
}
async function handleUpdate(curData, done) {
const request: UpdateConfigRequest = {
configValue: curData.configValue
};
console.log("curData");
console.log(curData);
ElMessageBox.confirm(
`确认要<strong>删除</strong>编号为<strong style='color:var(--el-color-primary)'>[ ${multipleSelection.value} ]</strong>的日志吗?`,
"系统提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
dangerouslyUseHTMLString: true,
draggable: true
}
)
.then(async () => {
await deleteLoginLogApi(multipleSelection.value).then(() => {
message(`您删除了日志编号为[ ${multipleSelection.value} ]的数据`, {
type: "success"
});
// 刷新列表
getLoginLogList();
});
})
.catch(() => {
message("取消删除", {
type: "info"
});
// 清空checkbox选择的数据
tableRef.getTableRef().clearSelection();
await updateConfigApi(curData.configId, request).then(() => {
message(`您成功修改了配置:${curData.configName}`, {
type: "success"
});
// 关闭弹框
done();
// 刷新列表
getList();
});
}
async function openDialog(row?: ConfigDTO) {
const { data } = await getConfigInfoApi(row.configId);
addDialog({
title: `修改配置`,
props: {
formInline: data
},
width: "40%",
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(editForm, { ref: formRef }),
beforeSure: (done, { options }) => {
const formRuleRef = formRef.value.getFormRuleRef();
const curData = options.props.formInline;
formRuleRef.validate(valid => {
if (valid) {
handleUpdate(curData, done);
}
});
}
});
}
onMounted(() => {
getLoginLogList();
getList();
});
return {
@ -220,15 +175,11 @@ export function useLoginLogHook() {
columns,
dataList,
pagination,
defaultSort,
timeRange,
multipleSelection,
getList,
onSearch,
exportAllExcel,
// exportExcel,
getLoginLogList,
resetForm,
handleDelete,
handleBulkDelete
handleRefresh,
openDialog
};
}

View File

@ -0,0 +1,9 @@
import { reactive } from "vue";
import type { FormRules } from "element-plus";
/** 自定义表单规则校验 */
export const formRules = reactive(<FormRules>{
configValue: [
{ required: true, message: "角色名称为必填项", trigger: "blur" }
]
});