#
Junjie
15 小时以前 2d2fd991826837d7189cc488aee6f309a6f5e216
src/main/resources/templates/Html.txt
@@ -1,70 +1,669 @@
<!DOCTYPE html>
<html lang="en">
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title></title>
    <title>@{ENTITYNAME} 管理</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/layui/css/layui.css" media="all">
    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
    <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 class="layui-fluid">
    <div class="layui-card">
        <div class="layui-card-body">
            <div class="layui-form toolbar" id="search-box">
                <div class="layui-form-item">
@{HTMLSEARCHCONTENT}
                    <div class="layui-inline">&emsp;
                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
                            <i class="layui-icon">&#xe615;</i>搜索
                        </button>
                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
                            <i class="layui-icon">&#xe666;</i>重置
                        </button>
<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>
            <table class="layui-hide" id="@{SIMPLEENTITYNAME}" lay-filter="@{SIMPLEENTITYNAME}"></table>
            <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"
                        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>
    </div>
    </section>
    <el-dialog
        class="dialog-panel"
        :title="dialog.mode === 'create' ? '新增 @{ENTITYNAME}' : '修改 @{ENTITYNAME}'"
        :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/html" id="toolbar">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">新增</button>
        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">删除</button>
        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">导出</button>
    </div>
</script>
<script type="text/html" id="operate">
    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">修改</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">删除</a>
</script>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/cool.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/@{SIMPLEENTITYNAME}/@{SIMPLEENTITYNAME}.js" charset="utf-8"></script>
</body>
<!-- 表单弹窗 -->
<script type="text/html" id="editDialog">
    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
        <div class="layui-row">
            <div class="layui-col-md12">
@{HTMLDIALOGCONTENT}
             </div>
        </div>
        <hr class="layui-bg-gray">
        <div class="layui-form-item text-right">
            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">保存</button>
            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
        </div>
    </form>
</script>
</html>