<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="utf-8">
|
<title>BasRgvErrLog 管理</title>
|
<meta name="renderer" content="webkit">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<link rel="stylesheet" href="../../static/vue/element/element.css">
|
<link rel="stylesheet" href="../../static/css/cool.css">
|
<style>
|
:root {
|
--card-bg: rgba(255, 255, 255, 0.94);
|
--card-border: rgba(216, 226, 238, 0.95);
|
--text-main: #243447;
|
}
|
|
[v-cloak] {
|
display: none;
|
}
|
|
html,
|
body {
|
margin: 0;
|
min-height: 100%;
|
color: var(--text-main);
|
font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
|
background:
|
radial-gradient(1000px 420px at 0% -10%, rgba(44, 107, 193, 0.12), transparent 56%),
|
radial-gradient(900px 400px at 100% 0%, rgba(28, 150, 126, 0.10), transparent 58%),
|
linear-gradient(180deg, #f2f6fb 0%, #f8fafc 100%);
|
}
|
|
.page-shell {
|
max-width: 1700px;
|
margin: 0 auto;
|
padding: 14px;
|
box-sizing: border-box;
|
}
|
|
.card-shell {
|
position: relative;
|
border-radius: 24px;
|
border: 1px solid var(--card-border);
|
background:
|
radial-gradient(760px 220px at -8% 0%, rgba(43, 117, 196, 0.05), transparent 55%),
|
radial-gradient(680px 200px at 108% 10%, rgba(24, 150, 129, 0.05), transparent 58%),
|
var(--card-bg);
|
box-shadow: 0 16px 32px rgba(44, 67, 96, 0.08);
|
overflow: hidden;
|
}
|
|
.card-body {
|
position: relative;
|
z-index: 1;
|
}
|
|
.list-toolbar {
|
padding: 12px 16px 10px;
|
border-bottom: 1px solid rgba(222, 230, 239, 0.92);
|
}
|
|
.toolbar-main {
|
display: flex;
|
align-items: flex-start;
|
justify-content: space-between;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
|
.toolbar-left {
|
flex: 1 1 960px;
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
|
.toolbar-search {
|
flex: 1 1 auto;
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
|
.toolbar-search-item {
|
flex: 0 0 152px;
|
min-width: 152px;
|
}
|
|
.toolbar-search-item.keyword {
|
flex: 0 0 220px;
|
min-width: 220px;
|
}
|
|
.toolbar-query-actions,
|
.toolbar-ops {
|
display: flex;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
|
.toolbar-ops {
|
justify-content: flex-end;
|
}
|
|
.list-toolbar .el-input__inner,
|
.list-toolbar .el-range-editor.el-input__inner,
|
.advanced-panel .el-input__inner,
|
.advanced-panel .el-range-editor.el-input__inner {
|
height: 32px;
|
line-height: 32px;
|
}
|
|
.list-toolbar .el-range-editor.el-input__inner,
|
.advanced-panel .el-range-editor.el-input__inner {
|
align-items: center;
|
}
|
|
.list-toolbar .el-input__icon,
|
.advanced-panel .el-input__icon {
|
line-height: 32px;
|
}
|
|
.list-toolbar .el-range-editor .el-range__icon,
|
.list-toolbar .el-range-editor .el-range-separator,
|
.list-toolbar .el-range-editor .el-range__close-icon,
|
.advanced-panel .el-range-editor .el-range__icon,
|
.advanced-panel .el-range-editor .el-range-separator,
|
.advanced-panel .el-range-editor .el-range__close-icon {
|
display: inline-flex;
|
align-items: center;
|
justify-content: center;
|
height: 100%;
|
line-height: 1;
|
}
|
|
.list-toolbar .el-button,
|
.advanced-panel .el-button {
|
padding: 8px 12px;
|
border-radius: 8px;
|
}
|
|
.advanced-panel {
|
padding: 10px 16px 12px;
|
border-bottom: 1px solid rgba(222, 230, 239, 0.92);
|
background: rgba(248, 251, 254, 0.78);
|
}
|
|
.advanced-grid {
|
display: grid;
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
gap: 8px;
|
}
|
|
.advanced-item {
|
min-width: 0;
|
}
|
|
.advanced-item.span-2 {
|
grid-column: span 2;
|
}
|
|
.table-wrap {
|
padding: 10px 16px;
|
}
|
|
.table-shell {
|
border-radius: 20px;
|
overflow: hidden;
|
border: 1px solid rgba(217, 227, 238, 0.98);
|
background: rgba(255, 255, 255, 0.95);
|
}
|
|
.table-shell .el-table {
|
border-radius: 20px;
|
overflow: hidden;
|
}
|
|
.table-shell .el-table th {
|
background: #f7fafc;
|
color: #53677d;
|
font-weight: 700;
|
}
|
|
.payload-cell {
|
display: inline-block;
|
max-width: 280px;
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
|
.mono {
|
font-family: Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
}
|
|
.pager-bar {
|
padding: 0 16px 16px;
|
display: flex;
|
align-items: center;
|
justify-content: flex-end;
|
}
|
|
.column-popover {
|
max-width: 320px;
|
}
|
|
.column-popover-head {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
gap: 12px;
|
margin-bottom: 10px;
|
font-size: 13px;
|
font-weight: 700;
|
color: var(--text-main);
|
}
|
|
.column-list {
|
display: grid;
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
gap: 8px 10px;
|
max-height: 280px;
|
overflow: auto;
|
padding-right: 4px;
|
}
|
|
.dialog-panel .el-dialog {
|
border-radius: 24px;
|
overflow: hidden;
|
}
|
|
.dialog-panel .el-dialog__header {
|
padding: 22px 24px 12px;
|
background: linear-gradient(180deg, #f8fbff 0%, #f3f7fb 100%);
|
border-bottom: 1px solid rgba(224, 232, 241, 0.92);
|
}
|
|
.dialog-panel .el-dialog__title {
|
font-weight: 700;
|
color: var(--text-main);
|
}
|
|
.dialog-panel .el-dialog__body {
|
padding: 18px 24px 8px;
|
}
|
|
.dialog-footer {
|
display: flex;
|
justify-content: flex-end;
|
gap: 10px;
|
}
|
|
@media (max-width: 1520px) {
|
.advanced-grid {
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
}
|
}
|
|
@media (max-width: 1280px) {
|
.advanced-grid {
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
}
|
}
|
|
@media (max-width: 960px) {
|
.toolbar-left {
|
flex-basis: 100%;
|
}
|
|
.advanced-grid {
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
}
|
|
.advanced-item.span-2 {
|
grid-column: span 2;
|
}
|
}
|
|
@media (max-width: 720px) {
|
.page-shell {
|
padding: 12px;
|
}
|
|
.toolbar-search-item,
|
.toolbar-search-item.keyword {
|
min-width: 100%;
|
flex-basis: 100%;
|
}
|
|
.toolbar-query-actions,
|
.toolbar-ops {
|
width: 100%;
|
}
|
|
.advanced-grid {
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
}
|
|
.advanced-item.span-2 {
|
grid-column: span 2;
|
}
|
}
|
|
@media (max-width: 560px) {
|
.advanced-grid {
|
grid-template-columns: 1fr;
|
}
|
|
.advanced-item.span-2 {
|
grid-column: auto;
|
}
|
|
.list-toolbar,
|
.advanced-panel,
|
.table-wrap,
|
.pager-bar {
|
padding-left: 14px;
|
padding-right: 14px;
|
}
|
|
.column-list {
|
grid-template-columns: 1fr;
|
}
|
}
|
</style>
|
</head>
|
<body>
|
<div id="app" class="page-shell" v-cloak>
|
<section class="card-shell list-card">
|
<div class="card-body">
|
<div class="list-toolbar">
|
<div class="toolbar-main">
|
<div class="toolbar-left">
|
<div class="toolbar-search">
|
<div class="toolbar-search-item keyword">
|
<el-input
|
v-model.trim="searchForm.condition"
|
size="small"
|
clearable
|
placeholder="请输入"
|
@keyup.enter.native="handleSearch">
|
</el-input>
|
</div>
|
<div
|
v-for="field in quickSearchableFields"
|
:key="'quick-' + field.field"
|
class="toolbar-search-item">
|
<el-select
|
v-if="field.kind === 'enum'"
|
v-model="searchForm[field.field]"
|
size="small"
|
clearable
|
:placeholder="field.label"
|
style="width: 100%;">
|
<el-option
|
v-for="option in field.enumOptions"
|
:key="'quick-' + field.field + '-' + option.rawValue"
|
:label="option.label"
|
:value="normalizeOptionValue(field, option.rawValue)">
|
</el-option>
|
</el-select>
|
<el-autocomplete
|
v-else-if="field.kind === 'foreign'"
|
v-model="searchDisplay[field.field]"
|
size="small"
|
:fetch-suggestions="getSuggestionFetcher(field)"
|
:placeholder="field.label"
|
style="width: 100%;"
|
@select="handleSearchForeignSelect(field, $event)"
|
@input="handleSearchForeignInput(field)">
|
<template slot-scope="{ item }">
|
<div class="mono">{{ item.value }}</div>
|
<div style="font-size:12px;color:#8a98ac;">ID: {{ item.id }}</div>
|
</template>
|
</el-autocomplete>
|
<el-select
|
v-else-if="field.kind === 'checkbox'"
|
v-model="searchForm[field.field]"
|
size="small"
|
clearable
|
:placeholder="field.label"
|
style="width: 100%;">
|
<el-option label="是" :value="normalizeOptionValue(field, field.checkboxActiveRaw)"></el-option>
|
<el-option label="否" :value="normalizeOptionValue(field, field.checkboxInactiveRaw)"></el-option>
|
</el-select>
|
<el-input
|
v-else
|
v-model.trim="searchForm[field.field]"
|
size="small"
|
clearable
|
:placeholder="field.label"
|
@keyup.enter.native="handleSearch">
|
</el-input>
|
</div>
|
</div>
|
<div class="toolbar-query-actions">
|
<el-button size="small" type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
|
<el-button size="small" icon="el-icon-refresh-left" @click="handleReset">重置</el-button>
|
<el-button
|
v-if="hasAdvancedFilters"
|
size="small"
|
plain
|
:icon="advancedFiltersVisible ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
|
@click="toggleAdvancedFilters">
|
{{ advancedFiltersVisible ? '收起' : '筛选' }}
|
</el-button>
|
</div>
|
</div>
|
<div class="toolbar-ops">
|
<el-button size="small" type="primary" plain icon="el-icon-plus" @click="openCreateDialog">新增</el-button>
|
<el-button size="small" type="danger" plain icon="el-icon-delete" :disabled="selection.length === 0" @click="removeSelection">删除</el-button>
|
<el-popover
|
placement="bottom"
|
width="320"
|
trigger="click"
|
popper-class="column-popover">
|
<div class="column-popover-head">
|
<span>列设置</span>
|
<div>
|
<el-button type="text" @click="selectAllColumns">全选</el-button>
|
<el-button type="text" @click="resetColumns">重置</el-button>
|
</div>
|
</div>
|
<div class="column-list">
|
<el-checkbox
|
v-for="field in allColumns"
|
:key="'column-' + field.field"
|
:value="isColumnVisible(field.field)"
|
@change="toggleColumn(field.field, $event)">
|
{{ field.label }}
|
</el-checkbox>
|
</div>
|
<el-button slot="reference" size="small" plain icon="el-icon-setting">列设置</el-button>
|
</el-popover>
|
<el-button size="small" plain icon="el-icon-download" :loading="exporting" @click="exportRows">导出</el-button>
|
</div>
|
</div>
|
</div>
|
|
<el-collapse-transition>
|
<div v-show="advancedFiltersVisible && hasAdvancedFilters" class="advanced-panel">
|
<div class="advanced-grid">
|
<div
|
v-for="field in advancedSearchableFields"
|
:key="'advanced-' + field.field"
|
:class="['advanced-item', field.kind === 'date' ? 'span-2' : '']">
|
<el-date-picker
|
v-if="field.kind === 'date'"
|
v-model="searchForm[field.field]"
|
size="small"
|
type="datetimerange"
|
unlink-panels
|
range-separator="至"
|
:start-placeholder="field.label + '开始'"
|
:end-placeholder="field.label + '结束'"
|
value-format="yyyy-MM-dd HH:mm:ss"
|
style="width: 100%;">
|
</el-date-picker>
|
<el-select
|
v-else-if="field.kind === 'enum'"
|
v-model="searchForm[field.field]"
|
size="small"
|
clearable
|
:placeholder="field.label"
|
style="width: 100%;">
|
<el-option
|
v-for="option in field.enumOptions"
|
:key="'advanced-' + field.field + '-' + option.rawValue"
|
:label="option.label"
|
:value="normalizeOptionValue(field, option.rawValue)">
|
</el-option>
|
</el-select>
|
<el-autocomplete
|
v-else-if="field.kind === 'foreign'"
|
v-model="searchDisplay[field.field]"
|
size="small"
|
:fetch-suggestions="getSuggestionFetcher(field)"
|
:placeholder="field.label"
|
style="width: 100%;"
|
@select="handleSearchForeignSelect(field, $event)"
|
@input="handleSearchForeignInput(field)">
|
<template slot-scope="{ item }">
|
<div class="mono">{{ item.value }}</div>
|
<div style="font-size:12px;color:#8a98ac;">ID: {{ item.id }}</div>
|
</template>
|
</el-autocomplete>
|
<el-select
|
v-else-if="field.kind === 'checkbox'"
|
v-model="searchForm[field.field]"
|
size="small"
|
clearable
|
:placeholder="field.label"
|
style="width: 100%;">
|
<el-option label="是" :value="normalizeOptionValue(field, field.checkboxActiveRaw)"></el-option>
|
<el-option label="否" :value="normalizeOptionValue(field, field.checkboxInactiveRaw)"></el-option>
|
</el-select>
|
<el-input
|
v-else
|
v-model.trim="searchForm[field.field]"
|
size="small"
|
clearable
|
:placeholder="field.label"
|
@keyup.enter.native="handleSearch">
|
</el-input>
|
</div>
|
</div>
|
</div>
|
</el-collapse-transition>
|
|
<div class="table-wrap">
|
<div class="table-shell">
|
<el-table
|
ref="dataTable"
|
:key="'table-' + visibleColumnKeys.join('|')"
|
v-loading="loading"
|
:data="tableData"
|
border
|
stripe
|
:height="tableHeight"
|
@selection-change="handleSelectionChange"
|
@sort-change="handleSortChange">
|
<el-table-column type="selection" width="52" align="center"></el-table-column>
|
<el-table-column
|
v-for="field in visibleColumns"
|
:key="field.field"
|
:prop="field.field"
|
:label="field.label"
|
:width="field.primaryKey ? 90 : null"
|
:min-width="field.primaryKey ? null : field.minWidth"
|
:sortable="isSortableField(field) ? 'custom' : false"
|
:show-overflow-tooltip="field.kind !== 'image'"
|
align="center">
|
<template slot-scope="scope">
|
<el-image
|
v-if="field.kind === 'image' && getTableValue(scope.row, field)"
|
:src="getTableValue(scope.row, field)"
|
fit="cover"
|
style="width: 48px; height: 48px; border-radius: 10px;">
|
</el-image>
|
<el-tag v-else-if="field.kind === 'enum'" size="mini" type="success">
|
{{ valueOrDash(getTableValue(scope.row, field)) }}
|
</el-tag>
|
<el-tag v-else-if="field.kind === 'checkbox'" size="mini" :type="isCheckboxChecked(scope.row, field) ? 'success' : 'info'">
|
{{ isCheckboxChecked(scope.row, field) ? '是' : '否' }}
|
</el-tag>
|
<span v-else-if="field.textarea" class="payload-cell mono" :title="stringValue(getTableValue(scope.row, field))">
|
{{ valueOrDash(getTableValue(scope.row, field)) }}
|
</span>
|
<span v-else>{{ valueOrDash(getTableValue(scope.row, field)) }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="160" fixed="right" align="center">
|
<template slot-scope="scope">
|
<el-button type="text" @click="openEditDialog(scope.row)">修改</el-button>
|
<el-button type="text" style="color:#f56c6c;" @click="removeRows([scope.row[primaryKeyField]])">删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
</div>
|
|
<div class="pager-bar">
|
<el-pagination
|
small
|
background
|
layout="total, sizes, prev, pager, next, jumper"
|
:current-page="page.curr"
|
:page-size="page.limit"
|
:page-sizes="[15, 30, 50, 100, 200, 500]"
|
:total="page.total"
|
@current-change="handleCurrentChange"
|
@size-change="handleSizeChange">
|
</el-pagination>
|
</div>
|
</div>
|
</section>
|
|
<el-dialog
|
class="dialog-panel"
|
:title="dialog.mode === 'create' ? '新增 BasRgvErrLog' : '修改 BasRgvErrLog'"
|
:visible.sync="dialog.visible"
|
width="760px"
|
:close-on-click-modal="false">
|
<el-form
|
ref="dialogForm"
|
:model="dialogForm"
|
:rules="dialogRules"
|
label-width="110px"
|
size="small">
|
<el-row :gutter="16">
|
<el-col
|
v-for="field in editableFields"
|
:key="'dialog-' + field.field"
|
:span="field.textarea || field.kind === 'image' ? 24 : 12">
|
<el-form-item :label="field.label" :prop="field.field">
|
<el-date-picker
|
v-if="field.kind === 'date'"
|
v-model="dialogForm[field.field]"
|
type="datetime"
|
value-format="yyyy-MM-dd HH:mm:ss"
|
:placeholder="'请选择' + field.label"
|
style="width: 100%;">
|
</el-date-picker>
|
<el-select
|
v-else-if="field.kind === 'enum'"
|
v-model="dialogForm[field.field]"
|
clearable
|
:placeholder="'请选择' + field.label"
|
style="width: 100%;">
|
<el-option
|
v-for="option in field.enumOptions"
|
:key="'dialog-' + field.field + '-' + option.rawValue"
|
:label="option.label"
|
:value="normalizeOptionValue(field, option.rawValue)">
|
</el-option>
|
</el-select>
|
<el-autocomplete
|
v-else-if="field.kind === 'foreign'"
|
v-model="dialogDisplay[field.field]"
|
:fetch-suggestions="getSuggestionFetcher(field)"
|
:placeholder="'请输入' + field.label"
|
style="width: 100%;"
|
@select="handleForeignSelect(field, $event)"
|
@input="handleForeignInput(field)">
|
<template slot-scope="{ item }">
|
<div class="mono">{{ item.value }}</div>
|
<div style="font-size:12px;color:#8a98ac;">ID: {{ item.id }}</div>
|
</template>
|
</el-autocomplete>
|
<el-switch
|
v-else-if="field.kind === 'checkbox'"
|
v-model="dialogForm[field.field]"
|
:active-value="normalizeOptionValue(field, field.checkboxActiveRaw)"
|
:inactive-value="normalizeOptionValue(field, field.checkboxInactiveRaw)"
|
active-color="#13ce66"
|
inactive-color="#c0c4cc">
|
</el-switch>
|
<el-input
|
v-else-if="field.textarea"
|
v-model.trim="dialogForm[field.field]"
|
type="textarea"
|
:rows="3"
|
:placeholder="'请输入' + field.label">
|
</el-input>
|
<el-input
|
v-else
|
v-model.trim="dialogForm[field.field]"
|
:placeholder="'请输入' + field.label">
|
</el-input>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
<div slot="footer" class="dialog-footer">
|
<el-button @click="dialog.visible = false">取消</el-button>
|
<el-button type="primary" :loading="dialog.submitting" @click="submitDialog">保存</el-button>
|
</div>
|
</el-dialog>
|
</div>
|
|
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
|
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
|
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
|
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
|
<script type="text/javascript" src="../../static/js/basRgvErrLog/basRgvErrLog.js?v=20260310" charset="utf-8"></script>
|
</body>
|
</html>
|