完成系统日志的请求
This commit is contained in:
parent
fb5ad57dc1
commit
d5667573fa
37
src/api/logs/index.js
Normal file
37
src/api/logs/index.js
Normal file
@ -0,0 +1,37 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取系统日志列表
|
||||
* @param {Object} params - 查询参数
|
||||
* @param {Array} [params.dateRange] - 时间范围
|
||||
* @param {string} [params.type] - 日志类型
|
||||
* @param {string} [params.user] - 操作人
|
||||
* @param {string} [params.status] - 状态
|
||||
* @returns {Promise} 返回日志列表数据
|
||||
*/
|
||||
export function getLogList(params) {
|
||||
return request.get('/api/logs')
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出日志
|
||||
* @returns {Promise} 返回二进制文件流
|
||||
*/
|
||||
export function exportLogs() {
|
||||
return request.get('/api/logs/export', {
|
||||
responseType: 'arraybuffer',
|
||||
headers: {
|
||||
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期日志
|
||||
* @param {Object} params - 清理参数
|
||||
* @param {number} [params.days=30] - 保留最近多少天的日志,默认30天
|
||||
* @returns {Promise} 返回清理结果
|
||||
*/
|
||||
export function cleanupLogs(params = { days: 30 }) {
|
||||
return request.post('/api/logs/cleanup', params)
|
||||
}
|
||||
0
src/api/roles/index.js
Normal file
0
src/api/roles/index.js
Normal file
@ -36,30 +36,35 @@ service.interceptors.request.use(
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
const res = response.data
|
||||
// 如果是二进制数据,直接返回
|
||||
if (response.config.responseType === 'arraybuffer' || response.config.responseType === 'blob') {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
const res = response.data;
|
||||
|
||||
// 如果响应成功
|
||||
if (res.success) {
|
||||
return res
|
||||
return res;
|
||||
}
|
||||
|
||||
// 处理特定错误码
|
||||
switch (res.code) {
|
||||
case 401:
|
||||
handleUnauthorized()
|
||||
break
|
||||
handleUnauthorized();
|
||||
break;
|
||||
case 403:
|
||||
ElMessage.error('没有权限访问该资源')
|
||||
break
|
||||
ElMessage.error('没有权限访问该资源');
|
||||
break;
|
||||
case 500:
|
||||
console.error('服务器错误详情:', res)
|
||||
ElMessage.error(res.message || '服务器错误,请稍后重试')
|
||||
break
|
||||
console.error('服务器错误详情:', res);
|
||||
ElMessage.error(res.message || '服务器错误,请稍后重试');
|
||||
break;
|
||||
default:
|
||||
ElMessage.error(res.message || '请求失败')
|
||||
ElMessage.error(res.message || '请求失败');
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(res.message || '请求失败'))
|
||||
return Promise.reject(new Error(res.message || '请求失败'));
|
||||
},
|
||||
(error) => {
|
||||
console.error('响应错误详情:', {
|
||||
@ -73,36 +78,36 @@ service.interceptors.response.use(
|
||||
headers: error.config?.headers,
|
||||
params: error.config?.params
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// 处理网络错误
|
||||
if (!error.response) {
|
||||
ElMessage.error('网络错误,请检查您的网络连接')
|
||||
return Promise.reject(error)
|
||||
ElMessage.error('网络错误,请检查您的网络连接');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// 处理HTTP状态码错误
|
||||
const status = error.response.status
|
||||
const status = error.response.status;
|
||||
switch (status) {
|
||||
case 401:
|
||||
handleUnauthorized()
|
||||
break
|
||||
handleUnauthorized();
|
||||
break;
|
||||
case 403:
|
||||
ElMessage.error('没有权限访问该资源')
|
||||
break
|
||||
ElMessage.error('没有权限访问该资源');
|
||||
break;
|
||||
case 404:
|
||||
ElMessage.error('请求的资源不存在')
|
||||
break
|
||||
ElMessage.error('请求的资源不存在');
|
||||
break;
|
||||
case 500:
|
||||
ElMessage.error('服务器错误,请稍后重试')
|
||||
break
|
||||
ElMessage.error('服务器错误,请稍后重试');
|
||||
break;
|
||||
default:
|
||||
ElMessage.error(`请求失败:${error.message}`)
|
||||
ElMessage.error(`请求失败:${error.message}`);
|
||||
}
|
||||
|
||||
return Promise.reject(error)
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// 处理未授权情况
|
||||
const handleUnauthorized = () => {
|
||||
|
||||
40
src/utils/sort.js
Normal file
40
src/utils/sort.js
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 对数组按照指定字段进行排序
|
||||
* @param {Array} array - 要排序的数组
|
||||
* @param {string} field - 排序字段
|
||||
* @param {string} [order='asc'] - 排序方式:'asc' 升序,'desc' 降序
|
||||
* @returns {Array} 排序后的新数组
|
||||
*/
|
||||
export function sortArrayByField(array, field, order = 'asc') {
|
||||
if (!Array.isArray(array) || array.length === 0) {
|
||||
return array;
|
||||
}
|
||||
|
||||
const sortedArray = [...array].sort((a, b) => {
|
||||
let valueA = a[field];
|
||||
let valueB = b[field];
|
||||
|
||||
// 处理日期类型
|
||||
if (field.includes('time') || field.includes('date') || field.includes('at')) {
|
||||
valueA = new Date(valueA).getTime();
|
||||
valueB = new Date(valueB).getTime();
|
||||
}
|
||||
// 处理数字类型
|
||||
else if (typeof valueA === 'number' && typeof valueB === 'number') {
|
||||
return order === 'asc' ? valueA - valueB : valueB - valueA;
|
||||
}
|
||||
// 处理字符串类型
|
||||
else {
|
||||
valueA = String(valueA).toLowerCase();
|
||||
valueB = String(valueB).toLowerCase();
|
||||
}
|
||||
|
||||
if (order === 'asc') {
|
||||
return valueA > valueB ? 1 : -1;
|
||||
} else {
|
||||
return valueA < valueB ? 1 : -1;
|
||||
}
|
||||
});
|
||||
|
||||
return sortedArray;
|
||||
}
|
||||
@ -237,7 +237,7 @@ const icons = {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "./styles/variables" as v;
|
||||
@use "../../../styles/variables.scss" as *;
|
||||
|
||||
.data-container {
|
||||
.mb-20 {
|
||||
|
||||
@ -599,7 +599,7 @@ onUnmounted(() => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "./styles/variables" as v;
|
||||
@use "../../../styles/variables.scss" as *;
|
||||
|
||||
.device-container {
|
||||
.group-card {
|
||||
|
||||
@ -1,63 +1,170 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ref, onMounted, computed, watch } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useSystemLogStore } from '../../../stores/systemLog';
|
||||
import { markRaw } from 'vue';
|
||||
import { Download, Delete } from "@element-plus/icons-vue";
|
||||
import { getLogList, exportLogs, cleanupLogs } from '@/api/logs';
|
||||
import { formatDateTime } from "@/utils/format";
|
||||
import { sortArrayByField } from "@/utils/sort";
|
||||
|
||||
const systemLogStore = useSystemLogStore();
|
||||
|
||||
// 日志类型选项
|
||||
const logTypes = [
|
||||
{ label: "用户操作", value: "用户操作" },
|
||||
{ label: "系统配置", value: "系统配置" },
|
||||
{ label: "数据操作", value: "数据操作" },
|
||||
{ label: "异常警告", value: "异常警告" },
|
||||
{ label: "GET", value: "GET" },
|
||||
{ label: "POST", value: "POST" },
|
||||
{ label: "PUT", value: "PUT" },
|
||||
{ label: "DELETE", value: "DELETE" }
|
||||
];
|
||||
|
||||
// 状态选项
|
||||
const statusOptions = [
|
||||
{ label: "成功", value: "成功" },
|
||||
{ label: "失败", value: "失败" },
|
||||
{ label: "警告", value: "警告" },
|
||||
{ label: "成功", value: 1 },
|
||||
{ label: "失败", value: 0 }
|
||||
];
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusType = (status) => {
|
||||
switch (status) {
|
||||
case "成功":
|
||||
return "success";
|
||||
case "失败":
|
||||
return "danger";
|
||||
case "警告":
|
||||
return "warning";
|
||||
default:
|
||||
return "info";
|
||||
}
|
||||
};
|
||||
|
||||
// 导出日志
|
||||
const handleExport = () => {
|
||||
console.log("导出日志:", searchForm.value);
|
||||
};
|
||||
|
||||
// 清空日志
|
||||
const handleClear = () => {
|
||||
systemLogStore.clearLogs();
|
||||
ElMessage.success('日志已清空');
|
||||
};
|
||||
|
||||
// 搜索条件
|
||||
const searchForm = ref({
|
||||
dateRange: [],
|
||||
type: "",
|
||||
user: "",
|
||||
status: "",
|
||||
status: ""
|
||||
});
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref([]);
|
||||
const allData = ref([]); // 保存所有数据
|
||||
const loading = ref(false);
|
||||
|
||||
// 分页配置
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(10);
|
||||
const total = ref(0);
|
||||
|
||||
// 获取日志列表
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getLogList();
|
||||
|
||||
if (res.success) {
|
||||
// 直接使用原始数据,不进行状态转换
|
||||
allData.value = res.data?.list || [];
|
||||
updateTableData();
|
||||
} else {
|
||||
ElMessage.error(res.message || '获取日志列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取日志列表错误:', error);
|
||||
ElMessage.error('获取日志列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 更新表格数据
|
||||
const updateTableData = () => {
|
||||
tableData.value = paginatedData.value;
|
||||
total.value = filteredData.value.length;
|
||||
};
|
||||
|
||||
// 导出日志
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await exportLogs();
|
||||
|
||||
// 检查是否是错误响应(可能是JSON格式的错误信息)
|
||||
if (res instanceof ArrayBuffer && res.byteLength < 1000) { // 如果响应太小,可能是错误信息
|
||||
const text = new TextDecoder().decode(res);
|
||||
try {
|
||||
const errorData = JSON.parse(text);
|
||||
throw new Error(errorData.message || '导出失败');
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
// 如果不是JSON,继续处理为文件
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理文件下载
|
||||
const blob = new Blob([res], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
});
|
||||
|
||||
// 检查blob是否为空
|
||||
if (blob.size === 0) {
|
||||
throw new Error('导出的文件为空');
|
||||
}
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.style.display = 'none';
|
||||
link.href = url;
|
||||
link.download = `智慧湿地管理平台系统日志_${formatDateTime(new Date(), 'YYYY年MM月DD日')}.xlsx`;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error) {
|
||||
console.error('导出日志错误:', error);
|
||||
ElMessage.error(error.message || '导出失败,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 清空日志
|
||||
const handleClearLogs = () => {
|
||||
ElMessageBox.confirm(
|
||||
`<p>此操作将清理30天前的所有日志记录,请确认:</p>
|
||||
<ul style="margin: 10px 0; padding-left: 20px;">
|
||||
<li>系统将保留最近30天的日志记录</li>
|
||||
<li>被清理的日志无法恢复</li>
|
||||
<li>建议在清理前先导出日志备份</li>
|
||||
</ul>`,
|
||||
'清理确认',
|
||||
{
|
||||
confirmButtonText: '确定清理',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
dangerouslyUseHTMLString: true
|
||||
}
|
||||
).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await cleanupLogs({ days: 30 });
|
||||
ElMessage.success('日志清理成功');
|
||||
getList(); // 刷新列表
|
||||
} catch (error) {
|
||||
console.error('清理日志错误:', error);
|
||||
const errorMsg = error.response?.data?.message || '清理日志失败,请稍后重试';
|
||||
ElMessage.error(errorMsg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
// 用户取消操作
|
||||
ElMessage.info('已取消清理操作');
|
||||
});
|
||||
};
|
||||
|
||||
// 处理搜索框清空
|
||||
const handleSearchClear = (field) => {
|
||||
searchForm.value[field] = field === 'dateRange' ? [] : '';
|
||||
handleSearch(); // 自动触发搜索
|
||||
};
|
||||
|
||||
// 监听搜索条件变化
|
||||
watch([searchForm, currentPage, pageSize], () => {
|
||||
updateTableData();
|
||||
}, { deep: true });
|
||||
|
||||
// 重置搜索
|
||||
const resetSearch = () => {
|
||||
@ -65,14 +172,81 @@ const resetSearch = () => {
|
||||
dateRange: [],
|
||||
type: "",
|
||||
user: "",
|
||||
status: "",
|
||||
status: ""
|
||||
};
|
||||
currentPage.value = 1; // 重置页码
|
||||
updateTableData();
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1; // 重置页码
|
||||
updateTableData();
|
||||
};
|
||||
|
||||
// 监听分页变化
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
updateTableData();
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val;
|
||||
updateTableData();
|
||||
};
|
||||
|
||||
// 页面加载时获取列表
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
const icons = {
|
||||
Download: markRaw(Download),
|
||||
Delete: markRaw(Delete)
|
||||
};
|
||||
|
||||
// 过滤后的数据
|
||||
const filteredData = computed(() => {
|
||||
let result = [...allData.value];
|
||||
|
||||
// 时间范围过滤
|
||||
if (searchForm.value.dateRange?.length === 2) {
|
||||
const startTime = new Date(searchForm.value.dateRange[0]).getTime();
|
||||
const endTime = new Date(searchForm.value.dateRange[1]).getTime();
|
||||
result = result.filter(item => {
|
||||
const itemTime = new Date(item.created_at).getTime();
|
||||
return itemTime >= startTime && itemTime <= endTime;
|
||||
});
|
||||
}
|
||||
|
||||
// 请求方法过滤
|
||||
if (searchForm.value.type) {
|
||||
result = result.filter(item => item.request_method === searchForm.value.type);
|
||||
}
|
||||
|
||||
// 操作人过滤
|
||||
if (searchForm.value.user) {
|
||||
const keyword = searchForm.value.user.toLowerCase();
|
||||
result = result.filter(item =>
|
||||
item.user_id?.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
}
|
||||
|
||||
// 状态过滤
|
||||
if (searchForm.value.status !== '') {
|
||||
result = result.filter(item => item.status === searchForm.value.status);
|
||||
}
|
||||
|
||||
// 按创建时间正序排序
|
||||
return sortArrayByField(result, 'created_at', 'asc');
|
||||
});
|
||||
|
||||
// 分页后的数据
|
||||
const paginatedData = computed(() => {
|
||||
const start = (currentPage.value - 1) * pageSize.value;
|
||||
const end = start + pageSize.value;
|
||||
return filteredData.value.slice(start, end);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -86,7 +260,7 @@ const icons = {
|
||||
<el-icon><component :is="icons.Download" /></el-icon>
|
||||
导出日志
|
||||
</el-button>
|
||||
<el-button type="danger" @click="handleClear">
|
||||
<el-button type="danger" @click="handleClearLogs">
|
||||
<el-icon><component :is="icons.Delete" /></el-icon>
|
||||
清空日志
|
||||
</el-button>
|
||||
@ -104,10 +278,16 @@ const icons = {
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
@clear="handleSearchClear('dateRange')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="日志类型">
|
||||
<el-select v-model="searchForm.type" placeholder="请选择" clearable>
|
||||
<el-select
|
||||
v-model="searchForm.type"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
@clear="handleSearchClear('type')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in logTypes"
|
||||
:key="item.value"
|
||||
@ -117,10 +297,20 @@ const icons = {
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="操作人">
|
||||
<el-input v-model="searchForm.user" placeholder="请输入" clearable />
|
||||
<el-input
|
||||
v-model="searchForm.user"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
@clear="handleSearchClear('user')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="请选择" clearable>
|
||||
<el-select
|
||||
v-model="searchForm.status"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
@clear="handleSearchClear('status')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in statusOptions"
|
||||
:key="item.value"
|
||||
@ -130,32 +320,54 @@ const icons = {
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary">搜索</el-button>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 日志表格 -->
|
||||
<el-table :data="systemLogStore.getLogs" style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="type" label="日志类型" width="120" />
|
||||
<el-table-column prop="user" label="操作人" width="120" />
|
||||
<el-table-column prop="action" label="操作" width="120" />
|
||||
<el-table-column prop="ip" label="IP地址" width="140" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="80" align="center"/>
|
||||
<el-table-column prop="request_method" label="请求方法" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getStatusType(row.status)" size="small">
|
||||
{{ row.status }}
|
||||
<el-tag
|
||||
:type="row.request_method === 'GET' ? 'success' :
|
||||
row.request_method === 'POST' ? 'primary' :
|
||||
row.request_method === 'PUT' ? 'warning' :
|
||||
row.request_method === 'DELETE' ? 'danger' : 'info'"
|
||||
size="small"
|
||||
>
|
||||
{{ row.request_method }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="detail"
|
||||
label="详细信息"
|
||||
min-width="200"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="createTime" label="操作时间" width="180" />
|
||||
<el-table-column prop="action" label="操作行为" width="200" align="center"/>
|
||||
<el-table-column prop="resource_type" label="资源类型" width="120" align="center"/>
|
||||
<el-table-column prop="request_url" label="请求URL" min-width="180" show-overflow-tooltip align="center"/>
|
||||
<el-table-column prop="ip_address" label="IP地址" width="140" align="center"/>
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'" size="small">
|
||||
{{ row.status === 1 ? '成功' : '失败' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="error_message" label="错误信息" min-width="200" show-overflow-tooltip align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.status === 0" class="error-message" style="color: #f56c6c">
|
||||
{{ row.error_message || '未知错误' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="操作时间" width="180" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.created_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页器 -->
|
||||
@ -163,9 +375,12 @@ const icons = {
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="systemLogStore.getLogs.length"
|
||||
:background="true"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
@ -173,7 +388,7 @@ const icons = {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "./styles/variables" as v;
|
||||
@use "../../../styles/variables.scss" as *;
|
||||
|
||||
.logs-container {
|
||||
.card-header {
|
||||
@ -198,12 +413,23 @@ const icons = {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
:deep(.el-input),
|
||||
:deep(.el-select) {
|
||||
width: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
:deep(.el-pagination) {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-card) {
|
||||
|
||||
@ -236,7 +236,7 @@ const handleDelete = (row) => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "./styles/variables" as v;
|
||||
@use "../../../styles/variables.scss" as *;
|
||||
|
||||
.permission-container {
|
||||
.el-card {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user