This commit is contained in:
oneao committed 2026-03-26 17:25:23 +08:00
1 parent c7240ea7c9
commit 189a085353
26 files changed
+2650 -201

No files matched your search

@@ -100,8 +100,6 @@ async function onVcodeSuccess() {
res.data.token,
);
await permissionStore.init();
ElMessage.success("登录成功");
router.push("/");
@@ -8,12 +8,13 @@ import {
} from "@/request/api";
import { getAlignByFormat, formatValueByFormat } from "@/utils/columns";
import { updateViewTableColumns } from "@/utils/columns";
import { downloadZip } from "@/utils/file";
import { downloadZip, getAliyunFileBlob } from "@/utils/file";
import { formatToDate } from "@/utils/time";
import { ElLoading, ElMessage } from "element-plus";
import { saveAs } from "file-saver";
import JSZip from "jszip";
import { usePermissionStore } from "@/store/permission";
import { ExportVxeTableExcel, exportExcel } from "@/utils/excel";
const tableRef = ref();
@@ -56,9 +57,7 @@ const viewName = "v_container";
const showPreview = ref(false);
const previewIndex = ref(-1);
const previewImageList = computed(() => {
return tableData.value
.map((item) => item.b_container_img)
.filter((img) => img);
return tableData.value.map((item) => item.b_container_img).filter((img) => img);
});
const currentRow = ref({});
@@ -191,12 +190,9 @@ async function deleteRows(rows = []) {
const safeDeleteIds = deleteIds.map((id) => Number(id));
// 使用安全的 SQL 查询
const fileRes = await loadDataApi(
"bf_file",
`father IN (${safeDeleteIds.join(",")})`,
"",
["subid"],
);
const fileRes = await loadDataApi("bf_file", `father IN (${safeDeleteIds.join(",")})`, "", [
"subid",
]);
if (!fileRes.success) {
return;
@@ -272,11 +268,7 @@ async function handleDownloadOne(data) {
});
try {
const fileRes = await loadDataApi(
"bf_file",
`father = ${data.b_id}`,
"mx_xh asc",
);
const fileRes = await loadDataApi("bf_file", `father = ${data.b_id}`, "mx_xh asc");
if (!fileRes.success) {
ElMessage.error("获取附件失败");
@@ -284,11 +276,7 @@ async function handleDownloadOne(data) {
}
const downloadFiles = fileRes.data.map((item, index) => ({
mx_filename: buildFileName(
data.b_container_no,
index + 1,
item.mx_filename,
),
mx_filename: buildFileName(data.b_container_no, index + 1, item.mx_filename),
mx_mapfilename: item.mx_mapfilename,
}));
@@ -349,29 +337,19 @@ async function handleDownloadBatch() {
let total = 0;
let finished = 0;
// 👉 先统计总文件数(用于进度)
for (const item of records) {
const fileRes = await loadDataApi(
"bf_file",
`father = '${item.b_id}'`,
"",
);
const fileRes = await loadDataApi("bf_file", `father = '${item.b_id}'`, "");
if (fileRes.success) {
total += (fileRes.data || []).length;
}
}
// 👉 正式处理
for (const item of records) {
const folderName = `${item.b_container_no}_${formatToDate(item.b_inputdatetime)}`;
const folder = zip.folder(folderName);
const fileRes = await loadDataApi(
"bf_file",
`father = '${item.b_id}'`,
"mx_xh asc",
);
const fileRes = await loadDataApi("bf_file", `father = '${item.b_id}'`, "mx_xh asc");
if (!fileRes.success) {
ElMessage.error("获取附件失败");
@@ -384,12 +362,10 @@ async function handleDownloadBatch() {
const fileUrl = file.mx_mapfilename;
try {
const res = await fetch(fileUrl);
const blob = await res.blob();
const blob = getAliyunFileBlob(fileUrl);
const fileName =
buildFileName(item.b_container_no, index + 1, file.mx_filename) ||
"未命名文件";
buildFileName(item.b_container_no, index + 1, file.mx_filename) || "未命名文件";
folder.file(fileName, blob);
} catch (e) {
@@ -401,11 +377,9 @@ async function handleDownloadBatch() {
}
});
// 控制并发数
await runWithLimit(tasks, 3);
}
// 👉 生成 zip
loading.setText("正在生成压缩包...");
const content = await zip.generateAsync({ type: "blob" });
@@ -425,6 +399,110 @@ function buildFileName(containerNo, index, fileName) {
return `${containerNo}_${formattedIndex}.${fileName.split(".").pop()}`;
}
function testExport() {
const options = {
columns: [
{
label: "基础信息",
field: "basicInfo",
width: 120,
children: [
{
label: "姓名",
field: "name",
width: 120,
},
{
label: "年龄",
field: "age",
width: 80,
},
{
label: "头像",
field: "avatar",
width: 100,
type: "image",
imageWidth: 80,
imageHeight: 80,
},
],
},
{
label: "联系方式",
field: "contactInfo",
width: 160,
children: [
{
label: "电话",
field: "phone",
width: 120,
},
{
label: "邮箱",
field: "email",
width: 200,
},
],
},
{
label: "其他信息",
field: "otherInfo",
width: 100,
children: [
{
label: "地址信息",
field: "address",
width: 150,
children: [
{ label: "省份", field: "province", width: 80 },
{ label: "城市", field: "city", width: 80 },
],
},
{ label: "备注", field: "remark", width: 200 },
],
},
],
data: [
{
name: "张三",
age: 25,
phone: "13800138000",
email: "zhangsan@example.com",
province: "浙江",
city: "杭州",
remark: "测试数据1",
avatar:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAQAElEQVR4AezdDXojR7kFYHNXAisJ7ISshLASclcCWQnsBLpmxpnxjGVJ3fV/3jxWbMvqqjrvZ6IjexL+78VfBAgQIECAQJyAAhA3coEJECBAgMDLiwLgu4AAAQIECAQKKACBQxeZAAECBLIFSnoFoCi4ESBAgACBMAEFIGzg4hIgQIBAusDn/ArAZwd/J0CAAAECUQIKQNS4hSVAgACBdIHX/ArAq4T3BAgQIEAgSEABCBq2qAQIECCQLvA1vwLw1cJHBAgQIEAgRkABiBm1oAQIECCQLvBtfgXgWw0fEyBAgACBEAEFIGTQYhIgQIBAusDb/ArAWw+fESBAgACBCAEFIGLMQhIgQIBAusD3+RWA70V8ToAAAQIEAgQUgIAhi0iAAAEC6QI/5lcAfjRxDwECBAgQ2F5AAdh+xAISIECAQLrAe/kVgPdU3EeAAAECBDYXUAA2H7B4BAgQIJAu8H5+BeB9F/cSIECAAIGtBRSArccrHAECBAikC9zKrwDcknE/AQIECBDYWEAB2Hi4ohEgQIBAusDt/ArAbRtfIUCAAAEC2wooANuOVjACBAgQSBf4KL8C8JGOrxEgQIAAgU0FFIBNBysWAQIECKQLfJxfAfjYx1cJECBAgMCWAgrAlmMVigABAgTSBe7lVwDuCfk6AQIECBDYUEAB2HCoIhEgQIBAusD9/ArAfSOPIECAAAEC2wkoANuNVCACBAgQSBd4JL8C8IiSxxAgQIAAgc0EFIDNBioOAQIECKQLPJZfAXjMyaMIECBAgMBWAgrAVuMUhgABAgTSBR7NrwA8KuVxBAgQIEBgIwEFYKNhikKAAAEC6QKP51cAHrfySAIECBAgsI2AArDNKAUhQIAAgXSBZ/IrAM9oeSwBAgQIENhEQAHYZJBiECBAgEC6wHP5FYDnvDyaAAECBAhsIaAAbDFGIQgQIEAgXeDZ/ArAs2IeT4AAAQIENhBQADYYoggECBAgkC7wfH4F4HkzVxAgQIAAgeUFFIDlRygAAQIECKQLnMmvAJxRcw0BAgQIEFhcQAFYfICOT4AAAQLpAufyKwDn3FxFgAABAgSWFlAAlh6fwxMgQIBAusDZ/ArAWTnXESBAgACBhQUUgIWH5+gECBAgkC5wPr8CcN7OlQQIECBAYFkBBWDZ0Tk4AQIECKQLXMmvAFzRcy0BAgQIEFhUQAFYdHCOTYAAAQLpAtfyKwDX/FxNgAABAgSWFFAAlhybQxMgQIBAusDV/ArAVUHXEyBAgACBBQUUgAWH5sgECBAgkC5wPb8CcN3QCgQIECBAYDkBBWC5kTkwAQIECKQL1MivANRQtAYBAgQIEFhMQAFYbGCOS4AAAQLpAnXyKwB1HK1CgAABAgSWElAAlhqXwxIgQIBAukCt/ApALUnrECBAgACBhQQUgIWG5agECBAgkC5QL78CUM/SSgQIECBAYBkBBWCZUTkoAQIECKQL1MyvANTUtBYBAgQIEFhEQAFYZFCOSYAAAQLpAnXzKwB1Pa1GgAABAgSWEFAAlhiTQxIgQIBAukDt/ApAbVHrESBAgACBBQQUgAWG5IgECBAgkC5QP78CUN/UigQIECBAYHoBBWD6ETkgAQIECKQLtMivALRQtSYBAgQIEJhcQAGYfECOR4AAAQLpAm3yKwBtXK1KgAABAgSmFlAAph6PwxEgQIBAukCr/ApAK1nrEiBAgACBiQUUgImH42gECBAgkC7QLr8C0M7WygQIECBAYFoBBWDa0TgYAQIECKQLtMyvALTUtTYBAgQIEJhUQAGYdDCORYAAAQLpAm3zKwBtfa1OgAABAgSmFFAAphyLQxEgQIBAukDr/ApAa2HrEyBAgACBCQUUgAmH4kgECBAgkC7QPr8C0N7YDgQIECBAYDoBBWC6kTgQAQIECKQL9MivAPRQtgcBAgQIEJhMQAGYbCCOQ4AAAQLpAn3yKwB9nO1CgAABAgSmElAAphqHwxAgQIBAukCv/ApAL2n7ECBAgACBiQQUgImG4SgECBAgkC7QL78C0M/aTgQIECBAYBoBBWCaUTgIAQIECKQL9MyvAPTUthcBAgQIEJhEQAGYZBCOQYAAAQLpAn3zKwB9ve1GgAABAgSmEFAAphiDQxAgQIBAukDv/ApAb3H7ESBAgACBCQQUgAmG4AgECBAgkC7QP78C0N/cjgQIECBAYLiAAjB8BA5AgAABAukCI/IrACPU7UmAAAECBAYLKACDB2B7AgQIEEgXGJNfARjjblcCBAgQIDBUQAEYym9zAgQIEEgXGJVfARglb18CBAgQIDBQQAEYiG9rAgQIEEgXGJdfARhnb2cCBAgQIDBMQAEYRm9jAgQIEEgXGJlfARipb28CBAgQIDBIQAEYBG9bAgQIEEgXGJtfARjrb3cCBAgQIDBEQAEYwm5TAgQIEEgXGJ1fARg9AfsTIECAAIEBAgrAAHRbEiBAgEC6wPj8CsD4GTgBAQIECBDoLqAAdCe3IQECBAikC8yQXwGYYQrOQIAAAQIEOgsoAJ3BbUeAAAEC6QJz5FcA5piDUxAgQIAAga4CCkBXbpsRIECAQLrALPkVgFkm4RwECBAgQKCjgALQEdtWBAgQIJAuME9+BWCeWTgJAQIECBDoJqAAdKO2EQECBAikC8yUXwGYaRrOQoAAAQIEOgkoAJ2gbUOAAAEC6QJz5VcA5pqH0xAgQIAAgS4CCkAXZpsQIECAQLrAbPkVgNkm4jwECBAgQKCDgALQAdkWBAgQIJAuMF9+BWC+mTgRAQIECBBoLqAANCe2AQECBAikC8yYXwGYcSrORIAAAQIEGgsoAI2BLU+AAAEC6QJz5lcA5pyLUxEgQIAAgaYCCkBTXosTIECAQLrArPkVgFkn41wECBAgQKChgALQENfSBAgQIJAuMG9+BWDe2TgZAQIECBBoJqAANKO1MAECBAikC8ycXwGYeTrORoAAAQIEGgkoAI1gLUuAAAEC6QJz51cA5p6P0xEgQIAAgSYCCkATVosSIECAQLrA7PkVgNkn5HwECBAgQKCBgALQANWSBAgQIJAuMH9+BWD+GTkhAQIECBCoLqAAVCe1IAECBAikC6yQXwFYYUrOSIAAAQIEKgsoAJVBLUeAAAEC6QJr5FcA1piTUxIgQIAAgaoCCkBVTosRIECAQLrAKvkVgFUm5ZwECBAgQKCigAJQEdNSBAgQIJAusE5+BWCdWTkpAQIECBCoJqAAVKO0EAECBAikC6yUXwFYaVrOSoAAAQIEKgkoAJUgLUOAAAEC6QJr5VcA1pqX0xIgQIAAgSoCCkAVRosQIECAQLrAavkVgNUm5rwECBAgQKCCgAJQAdESBAgQIJAusF5+BWC9mTkxAQIECBC4LKAAXCa0AAECBAikC6yYXwFYcWrOTIAAAQIELgooABcBXU6AAAEC6QJr5lcA1pybUxMgQIAAgUsCCsAlPhcTIECAQLrAqvkVgFUn59wECBAgQOCCgAJwAc+lBAgQIJAusG5+BWDd2Tk5AQIECBA4LaAAnKZzIQECBAikC6ycXwFYeXrOToAAAQIETgooACfhXEaAAAEC6QJr51cA1p6f0xMgQIAAgVMCCsApNhcRIECAQLrA6vkVgDYT/Mex7H+Db389sq/yljynZ7P/+xhquf3zeF++x/92vP/zcVv57VmD5MeX2ZfbTvNf+Xv38tkVgMuEFiAQI/DHI2m5lSf9UvJ+OT4vTwblSaEUgnL/cZe3TQXK7MutzNn8X9afsgKw/gwlIDBaoDwplCeEUgZKESifjz6T/fsJlHmbfz/vajspANUoLUSAwCHw+kRQfj1wfOotTCBm/jvMVQHYYYoyEJhLoLwiLL8eKL8aKB/PdTqnaS1QZm7+rZUrrK8AVEC0BAEC7wqUJ4Lya4Hy/t0HuHNrgTL3Tee/x9wUgD3mKAWBWQVenwTKHxyb9YzO1U7A/NvZXl5ZAbhMaAECBO4IlCcBfzjwDtLGX95u/rvMSgHYZZJyEJhboDwJlB8Hz31Kp2slYP6tZC+sqwBcwHMpAQJPCZQngfKTgKcu8uBtBDaZ/zbzeFEA9pmlJARWECh/FqDcVjirM9YXKLMvt/orW/FpAQXgaTIXECBwQaC8CvTfCLgAuPily89/cf83x1cA3nD4hACBDgLlFWC5ddjKFhMKlNmX24RHyzqSApA1b2kJzCLgpwCzTGLMORad/xisVrsqAK1krUuAwEcC5UfBH33d1/YWMP8J5qsATDAERyAQKFCeAPwYOHDwXyIvOf8vZ9/mnQKwzSgFIbCcwE/LndiBawqYf03NE2spACfQXEKAQBUBPwGowrjsIovNf1nnmwdXAG7S+AIBAgQIENhXQAHYd7aSEZhdoPweePYzOl87gaXm345h3MoKwDh7OxMgQIAAgWECCsAwehsTiBfwCjD7W2Ch+e85KAVgz7lKRYAAAQIEPhRQAD7k8UUCBAgQSBfYNb8CsOtk5SJAgAABAh8IKAAf4PgSAQIECKQL7JtfAdh3tpIRIECAAIGbAgrATRpfIECAAIF0gZ3zKwA7T1c2AgQIECBwQ0ABuAHjbgIECBBIF9g7vwKw93ylI0CAAAEC7wooAO+yuJMAAQIE0gV2z68AtJnwz8eyfxh4+/XY29tjAiPnNMPejynt+6gZZjDyDPtOVrK7AgrAXSIPIECAAIE8gf0TKwD7z1hCAgQIECDwg4AC8AOJOwgQIEAgXSAhvwKQMGUZCRAgQIDAdwIKwHcgPiVAgACBdIGM/ApAxpylJECAAAECbwQUgDccPiFAgACBdIGU/ApAyqTlJECAAAEC3wgoAN9g+JAAAQIE0gVy8isAObOWlAABAgQI/C6gAPxO4QMCBAgQSBdIyq8AJE1bVgIECBAg8EVAAfgC4R0BAgQIpAtk5VcAsuYtLQECBAgQ+CSgAHLine truncated
},
{
name: "李四",
age: 30,
phone: "13900139000",
email: "lisi@example.com",
province: "江苏",
city: "南京",
remark: "测试数据2",
avatar:
"data:image/png;base64,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 truncated
},
],
footer: [
{
name: "总计",
age: 55,
phone: "",
email: "",
province: "",
city: "",
remark: "2条记录",
},
],
};
exportExcel(options);
}
</script>
<template>
@@ -534,7 +612,7 @@ function buildFileName(containerNo, index, fileName) {
<el-dropdown-item @click="updateViewTableColumns(tableRef)">
栏位保存
</el-dropdown-item>
<el-dropdown-item>导出Execl</el-dropdown-item>
<el-dropdown-item @click="ExportVxeTableExcel(tableRef)"> 导出Execl </el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -563,15 +641,10 @@ function buildFileName(containerNo, index, fileName) {
trigger: 'cell',
}"
height="100%"
show-overflow
show-header-overflow
>
<vxe-column
field="seq"
type="seq"
width="50"
align="center"
title=" "
fixed="left"
/>
<vxe-column field="seq" type="seq" width="50" align="center" title=" " fixed="left" />
<vxe-column type="checkbox" width="50" align="center" fixed="left" />
<vxe-column
v-for="column in columns"
@@ -592,12 +665,7 @@ function buildFileName(containerNo, index, fileName) {
/>
</template>
<template v-else>
{{
formatValueByFormat(
row[column.col_FieldName],
column.col_DisplayFormat,
)
}}
{{ formatValueByFormat(row[column.col_FieldName], column.col_DisplayFormat) }}
</template>
</template>
</vxe-column>
@@ -0,0 +1,333 @@
<script lang="js" setup>
import { updateViewTableColumns } from "@/utils/columns";
import { loadViewColumnsApi, loadPageDataApi } from "@/request/api";
import { ExportVxeTableExcel } from "@/utils/excel";
const viewName = "log_datas";
const showSearch = ref(true);
const tableRef = ref();
const columns = ref([]);
const tableData = ref([]);
const loading = reactive({
table: true,
});
const searchForm = ref({
startTime: "",
endTime: "",
datas: "",
option: "",
table: "",
});
const pagination = reactive({
page: 1,
size: 50,
total: 0,
});
const options = [
{
label: "INSERT",
value: "INSERT",
},
{
label: "UPDATE",
value: "UPDATE",
},
{
label: "DELETE",
value: "DELETE",
},
];
onMounted(async () => {
loading.table = true;
await loadTableColumns();
await loadTableDatas();
loading.table = false;
});
async function loadTableColumns() {
const columnRes = await loadViewColumnsApi(viewName);
if (columnRes.success) {
columns.value = columnRes.data;
}
}
async function loadTableDatas() {
loading.table = true;
let searchCondition = "";
if (searchForm.value.startTime) {
searchCondition += `b_inputdatetime >= '${searchForm.value.startTime}' AND `;
}
if (searchForm.value.endTime) {
searchCondition += `b_inputdatetime <= '${searchForm.value.endTime}' AND `;
}
if (searchCondition.endsWith(" AND ")) {
searchCondition = searchCondition.slice(0, -4);
}
await loadPageDataApi({
viewName,
page: pagination.page,
size: pagination.size,
searchCondition,
orderField: "b_inputdatetime DESC",
}).then((res) => {
if (res.success) {
tableData.value = res.data.rows;
pagination.total = res.data.total;
loading.table = false;
}
});
}
const handleSizeChange = (val) => {
pagination.size = val;
loadTableDatas();
};
const handleCurrentChange = (val) => {
pagination.page = val;
loadTableDatas();
};
function handleSearch() {
pagination.page = 1;
loadTableDatas();
}
function handleSearchReset() {
searchForm.value.startTime = "";
searchForm.value.endTime = "";
searchForm.value.datas = "";
searchForm.value.table = "";
searchForm.value.option = "";
}
</script>
<template>
<div class="g3-container">
<div v-if="showSearch" class="g3-search">
<G3Grid class="grid">
<G3GridGroup :column="2" :label-width="80">
<G3GridItem label="操作时间" :span="2">
<el-date-picker
type="datetime"
placeholder="开始日期"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
v-model="searchForm.startTime"
/>
<el-divider direction="vertical" />
<el-date-picker
type="datetime"
placeholder="结束日期"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
v-model="searchForm.endTime"
/>
</G3GridItem>
<G3GridItem label="操作内容" :span="2">
<el-input v-model="searchForm.datas" />
</G3GridItem>
<G3GridItem label="操作" :span="1">
<el-select v-model="searchForm.option" placeholder="">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</G3GridItem>
<G3GridItem label="操作表" :span="1">
<el-input v-model="searchForm.table" />
</G3GridItem>
</G3GridGroup>
</G3Grid>
<div class="search-extra">
<el-button type="primary">
<el-icon class="el-icon--left">
<G3Icon icon="lucide:search" @click="handleSearch" />
</el-icon>
搜索
</el-button>
<el-button>
<el-icon class="el-icon--left">
<G3Icon icon="lucide:refresh-ccw" @click="handleSearchReset" />
</el-icon>
重置
</el-button>
</div>
</div>
<div class="g3-content">
<div class="content-top">
<div />
<div>
<el-tooltip content="显示/关闭 搜索">
<el-button circle @click="showSearch = !showSearch">
<el-icon>
<G3Icon icon="lucide:search" />
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="刷新">
<el-button circle @click="loadTableDatas">
<el-icon>
<G3Icon icon="lucide:refresh-ccw" />
</el-icon>
</el-button>
</el-tooltip>
<el-dropdown>
<el-button circle style="margin-left: 12px">
<el-icon>
<G3Icon icon="lucide:ellipsis" />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="updateViewTableColumns(tableRef)">
栏位保存
</el-dropdown-item>
<el-dropdown-item @click="ExportVxeTableExcel(tableRef)">
导出Excel
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<div class="g3-table">
<vxe-table
ref="tableRef"
:loading="loading.table"
:data="tableData"
:column-config="{
resizable: true,
drag: true,
}"
:row-config="{
useKey: true,
isCurrent: true,
isHover: true,
}"
:filter-config="{
showIcon: false,
}"
:column-drag-config="{
showIcon: false,
trigger: 'cell',
}"
height="100%"
show-overflow
show-header-overflow
>
<vxe-column field="seq" type="seq" width="50" align="center" title=" " fixed="left" />
<vxe-column type="checkbox" width="50" align="center" fixed="left" />
<vxe-column
v-for="column in columns"
:key="column.col_FieldName"
:field="column.col_FieldName"
:title="column.col_Caption"
:width="column.col_Width"
align="center"
:params="column"
:sortable="true"
>
</vxe-column>
</vxe-table>
</div>
<div class="g3-pagination">
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.size"
:page-sizes="[50, 100, 200, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.g3-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.g3-search {
display: flex;
align-items: center;
padding: 10px;
background-color: #fff;
margin-bottom: 12px;
.grid {
width: 500px;
}
.search-extra {
display: flex;
margin-left: 24px;
}
}
.g3-content {
flex-grow: 1;
background-color: #fff;
padding: 10px;
min-height: 0;
display: flex;
flex-direction: column;
.content-top {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
::v-deep(.el-button) {
height: 32px !important;
}
}
.g3-table {
flex: 1;
min-height: 0;
}
.g3-pagination {
margin-top: 8px;
display: flex;
align-items: center;
justify-content: flex-end;
flex-shrink: 0;
min-height: 0;
}
}
</style>
@@ -0,0 +1,310 @@
<script lang="js" setup>
import { updateViewTableColumns } from "@/utils/columns";
import { loadViewColumnsApi, loadPageDataApi } from "@/request/api";
import { ExportVxeTableExcel } from "@/utils/excel";
const viewName = "b_login_log";
const showSearch = ref(true);
const tableRef = ref();
const columns = ref([]);
const tableData = ref([]);
const loading = reactive({
table: true,
});
const searchForm = ref({
startTime: "",
endTime: "",
userid: "",
});
const pagination = reactive({
page: 1,
size: 50,
total: 0,
});
onMounted(async () => {
loading.table = true;
await loadTableColumns();
await loadTableDatas();
loading.table = false;
});
async function loadTableColumns() {
const columnRes = await loadViewColumnsApi(viewName);
if (columnRes.success) {
columns.value = columnRes.data;
}
}
async function loadTableDatas() {
loading.table = true;
let searchCondition = "";
if (searchForm.value.startTime) {
searchCondition += `b_login_time >= '${searchForm.value.startTime}' AND `;
}
if (searchForm.value.endTime) {
searchCondition += `b_login_time <= '${searchForm.value.endTime}' AND `;
}
if (searchCondition.endsWith(" AND ")) {
searchCondition = searchCondition.slice(0, -4);
}
await loadPageDataApi({
viewName,
page: pagination.page,
size: pagination.size,
searchCondition,
orderField: "b_login_time DESC",
}).then((res) => {
if (res.success) {
tableData.value = res.data.rows;
pagination.total = res.data.total;
loading.table = false;
}
});
}
const handleSizeChange = (val) => {
pagination.size = val;
loadTableDatas();
};
const handleCurrentChange = (val) => {
pagination.page = val;
loadTableDatas();
};
function handleSearch() {
pagination.page = 1;
loadTableDatas();
}
function handleSearchReset() {
searchForm.value.startTime = "";
searchForm.value.endTime = "";
searchForm.value.userid = "";
}
</script>
<template>
<div class="g3-container">
<div v-if="showSearch" class="g3-search">
<G3Grid class="grid">
<G3GridGroup :column="3" :label-width="80">
<G3GridItem label="登录时间" :span="2">
<el-date-picker
type="datetime"
placeholder="开始日期"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
v-model="searchForm.startTime"
/>
<el-divider direction="vertical" />
<el-date-picker
type="datetime"
placeholder="结束日期"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
v-model="searchForm.endTime"
/>
</G3GridItem>
<G3GridItem label="用户账号" :span="1">
<!-- <el-input v-model="searchForm.datas" /> -->
<G3Select
v-model="searchForm.userid"
v-model:label="searchForm.userid"
search-condition="b_id <> 'g3soft'"
view-name="b_user"
value-key="b_id"
label-key="b_id"
keep-input
/>
</G3GridItem>
</G3GridGroup>
</G3Grid>
<div class="search-extra">
<el-button type="primary">
<el-icon class="el-icon--left">
<G3Icon icon="lucide:search" @click="handleSearch" />
</el-icon>
搜索
</el-button>
<el-button>
<el-icon class="el-icon--left">
<G3Icon icon="lucide:refresh-ccw" @click="handleSearchReset" />
</el-icon>
重置
</el-button>
</div>
</div>
<div class="g3-content">
<div class="content-top">
<div />
<div>
<el-tooltip content="显示/关闭 搜索">
<el-button circle @click="showSearch = !showSearch">
<el-icon>
<G3Icon icon="lucide:search" />
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="刷新">
<el-button circle @click="loadTableDatas">
<el-icon>
<G3Icon icon="lucide:refresh-ccw" />
</el-icon>
</el-button>
</el-tooltip>
<el-dropdown>
<el-button circle style="margin-left: 12px">
<el-icon>
<G3Icon icon="lucide:ellipsis" />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="updateViewTableColumns(tableRef)">
栏位保存
</el-dropdown-item>
<el-dropdown-item @click="ExportVxeTableExcel(tableRef)">
导出Execl
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<div class="g3-table">
<vxe-table
ref="tableRef"
:loading="loading.table"
:data="tableData"
:column-config="{
resizable: true,
drag: true,
}"
:row-config="{
useKey: true,
isCurrent: true,
isHover: true,
}"
:filter-config="{
showIcon: false,
}"
:column-drag-config="{
showIcon: false,
trigger: 'cell',
}"
height="100%"
show-overflow
show-header-overflow
>
<vxe-column field="seq" type="seq" width="50" align="center" title=" " fixed="left" />
<vxe-column type="checkbox" width="50" align="center" fixed="left" />
<vxe-column
v-for="column in columns"
:key="column.col_FieldName"
:field="column.col_FieldName"
:title="column.col_Caption"
:width="column.col_Width"
align="center"
:params="column"
:sortable="true"
>
</vxe-column>
</vxe-table>
</div>
<div class="g3-pagination">
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.size"
:page-sizes="[50, 100, 200, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.g3-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.g3-search {
display: flex;
align-items: center;
padding: 10px;
background-color: #fff;
margin-bottom: 12px;
.grid {
width: 600px;
}
.search-extra {
display: flex;
margin-left: 24px;
}
}
.g3-content {
flex-grow: 1;
background-color: #fff;
padding: 10px;
min-height: 0;
display: flex;
flex-direction: column;
.content-top {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
::v-deep(.el-button) {
height: 32px !important;
}
}
.g3-table {
flex: 1;
min-height: 0;
}
.g3-pagination {
margin-top: 8px;
display: flex;
align-items: center;
justify-content: flex-end;
flex-shrink: 0;
min-height: 0;
}
}
</style>