align(生成记录·像素复刻样办): RecordsHistoryPage 重写为自绘复刻 admin panel-history(旧版 form-box/panel-box/原生控件/table-scroll/thumb 缩略图/旧式分页/大图 modal CSS);脚本沿用现有 API,测试断言适配自绘结构
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
/** 历史生成记录页(module 13 task 259 对齐 admin panel-history):指定用户下拉 + 起止时间筛选、类型中文、≤3 缩略图、大图预览、空态。 */
|
||||
/** 生成记录页 · 像素复刻旧版 admin.html panel-history(自绘:form-box 筛选 + panel-box 表格 + 旧式分页 + thumb 缩略图)。
|
||||
* script 逻辑沿用现有 Vue 实现(查询/重置/翻页/预览/清理数据)。 */
|
||||
import { formatDateTime } from '@/utils/datetime'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { fetchHistoryList, fetchHistoryUserOptions, clearHistory } from './history-api.ts'
|
||||
@@ -21,9 +22,18 @@ const filter = reactive({ userId: '', timeStart: '', timeEnd: '' })
|
||||
|
||||
const previewVisible = ref(false)
|
||||
const previewItem = ref<HistoryRecordItem | null>(null)
|
||||
const jumpPage = ref('')
|
||||
|
||||
const hasFilter = computed(() => Boolean(filter.userId || filter.timeStart || filter.timeEnd))
|
||||
const emptyText = computed(() => historyEmptyText({ hasFilter: hasFilter.value, total: total.value }))
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize)))
|
||||
|
||||
/** datetime-local 原生值(YYYY-MM-DDTHH:mm) 转后端兼容格式。 */
|
||||
function toApiTime(value: string): string | undefined {
|
||||
const t = (value || '').trim()
|
||||
if (!t) return undefined
|
||||
return `${t.replace('T', ' ')}:00`
|
||||
}
|
||||
|
||||
/** 预览图顺序:长图在前 + 结果图去重(对齐 admin 缩略图 [long,...result])。 */
|
||||
function previewUrls(item: HistoryRecordItem): string[] {
|
||||
@@ -48,8 +58,8 @@ async function load() {
|
||||
page: page.value,
|
||||
pageSize,
|
||||
userId: filter.userId ? Number(filter.userId) : undefined,
|
||||
timeStart: filter.timeStart || undefined,
|
||||
timeEnd: filter.timeEnd || undefined,
|
||||
timeStart: toApiTime(filter.timeStart),
|
||||
timeEnd: toApiTime(filter.timeEnd),
|
||||
})
|
||||
rows.value = result.items
|
||||
total.value = result.total
|
||||
@@ -90,10 +100,20 @@ function retry() {
|
||||
}
|
||||
|
||||
function changePage(next: number) {
|
||||
if (next < 1 || next > totalPages.value) return
|
||||
page.value = next
|
||||
load()
|
||||
}
|
||||
|
||||
function goJump() {
|
||||
const n = Number.parseInt(jumpPage.value, 10)
|
||||
if (Number.isNaN(n)) {
|
||||
ElMessage.warning('请输入页码')
|
||||
return
|
||||
}
|
||||
changePage(n)
|
||||
}
|
||||
|
||||
function openPreview(item: HistoryRecordItem) {
|
||||
previewItem.value = item
|
||||
previewVisible.value = true
|
||||
@@ -101,7 +121,7 @@ function openPreview(item: HistoryRecordItem) {
|
||||
|
||||
const clearing = ref(false)
|
||||
|
||||
/** 清空全部生成记录:二次确认后调后端 DELETE /api/admin/history。 */
|
||||
/** 清空全部生成记录:二次确认后调后端 DELETE /api/admin/history(新版增强,保留)。 */
|
||||
async function clearAll() {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定清空全部生成记录吗?该操作会删除所有历史数据,无法恢复。', '清理生成记录', {
|
||||
@@ -132,143 +152,485 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-stack">
|
||||
<div class="page-heading">
|
||||
<div>
|
||||
<h2>生成记录</h2>
|
||||
<p>查看各用户在后台生成的历史记录与结果图。</p>
|
||||
<div class="history-view">
|
||||
<section class="form-box">
|
||||
<h3>筛选条件</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="min-width: 150px">
|
||||
<label>指定用户</label>
|
||||
<select v-model="filter.userId" :disabled="userLoading">
|
||||
<option value="">全部用户</option>
|
||||
<option v-for="u in userOptions" :key="u.id" :value="String(u.id)">{{ u.username }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" style="min-width: 176px">
|
||||
<label>开始时间</label>
|
||||
<input v-model="filter.timeStart" type="datetime-local" />
|
||||
</div>
|
||||
<div class="form-group" style="min-width: 176px">
|
||||
<label>结束时间</label>
|
||||
<input v-model="filter.timeEnd" type="datetime-local" />
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="btn" type="button" @click="apply">查询</button>
|
||||
<button class="btn btn-secondary" type="button" @click="reset">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel-box">
|
||||
<div class="panel-head">
|
||||
<h3>生成记录</h3>
|
||||
<button class="btn btn-danger" type="button" :disabled="clearing" :aria-busy="clearing" @click="clearAll">
|
||||
{{ clearing ? '清理中...' : '清理数据' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="errorText" class="msg err">
|
||||
<span>加载失败: {{ errorText }}</span>
|
||||
<button class="btn btn-sm btn-secondary" type="button" @click="retry">重试</button>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll history-table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 70px">ID</th>
|
||||
<th style="width: 130px">用户</th>
|
||||
<th style="width: 160px">类型</th>
|
||||
<th style="width: 170px">创建时间</th>
|
||||
<th>结果预览</th>
|
||||
<th style="width: 96px">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-if="rows.length">
|
||||
<tr v-for="h in rows" :key="h.id">
|
||||
<td>{{ h.id }}</td>
|
||||
<td>{{ h.username || '-' }}</td>
|
||||
<td>{{ historyPanelTypeLabel(h.panelType) }}</td>
|
||||
<td>{{ formatDateTime(h.createdAt) }}</td>
|
||||
<td>
|
||||
<div class="thumb-wrap">
|
||||
<img
|
||||
v-for="url in previewUrls(h).slice(0, 3)"
|
||||
:key="url"
|
||||
class="thumb"
|
||||
:src="url"
|
||||
alt=""
|
||||
title="点击查看大图"
|
||||
@click="openPreview(h)"
|
||||
/>
|
||||
<span v-if="!previewUrls(h).length" class="empty-mini">暂无图片</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm" type="button" @click="openPreview(h)">查看大图</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<tr v-else-if="loading">
|
||||
<td colspan="6" class="empty-tip">加载中...</td>
|
||||
</tr>
|
||||
<tr v-else>
|
||||
<td colspan="6" class="empty-tip">{{ emptyText }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="pagination">
|
||||
<span class="page-total">共 {{ total }} 条</span>
|
||||
<button type="button" :disabled="page <= 1" @click="changePage(page - 1)">上一页</button>
|
||||
<span class="page-cur">第 {{ page }} / {{ totalPages }} 页</span>
|
||||
<button type="button" :disabled="page >= totalPages" @click="changePage(page + 1)">下一页</button>
|
||||
<span class="page-jump">
|
||||
跳至
|
||||
<input v-model="jumpPage" type="text" inputmode="numeric" @keyup.enter="goJump" />
|
||||
页
|
||||
<button type="button" @click="goJump">跳转</button>
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div v-if="previewVisible" class="modal-mask" @click.self="previewVisible = false">
|
||||
<div class="modal">
|
||||
<div class="modal-head">
|
||||
<h3 v-if="previewItem">记录 #{{ previewItem.id }} 结果图</h3>
|
||||
<button class="modal-close" type="button" aria-label="关闭" @click="previewVisible = false">×</button>
|
||||
</div>
|
||||
<div v-if="previewItem" class="preview-meta">
|
||||
<span>{{ previewItem.username || '-' }}</span>
|
||||
<span>·</span>
|
||||
<span>{{ historyPanelTypeLabel(previewItem.panelType) }}</span>
|
||||
<span>·</span>
|
||||
<span>{{ formatDateTime(previewItem.createdAt) }}</span>
|
||||
</div>
|
||||
<div v-if="previewItem && previewUrls(previewItem).length" class="preview-grid">
|
||||
<a
|
||||
v-for="url in previewUrls(previewItem)"
|
||||
:key="url"
|
||||
:href="url"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
title="新标签打开原图"
|
||||
>
|
||||
<img :src="url" alt="" />
|
||||
</a>
|
||||
</div>
|
||||
<div v-else class="modal-empty">无结果图片</div>
|
||||
<div class="modal-foot">
|
||||
<button class="btn btn-secondary" type="button" @click="previewVisible = false">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<div class="filter-grid">
|
||||
<div class="f-item">
|
||||
<label>指定用户</label>
|
||||
<el-select v-model="filter.userId" clearable filterable placeholder="全部用户" style="width: 100%" :loading="userLoading">
|
||||
<el-option label="全部用户" value="" />
|
||||
<el-option v-for="u in userOptions" :key="u.id" :label="u.username" :value="String(u.id)" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="f-item">
|
||||
<label>开始时间</label>
|
||||
<el-date-picker
|
||||
v-model="filter.timeStart"
|
||||
type="datetime"
|
||||
placeholder="开始时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
<div class="f-item">
|
||||
<label>结束时间</label>
|
||||
<el-date-picker
|
||||
v-model="filter.timeEnd"
|
||||
type="datetime"
|
||||
placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
<div class="f-item btn-row">
|
||||
<el-button type="primary" @click="apply">查询</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<div class="table-toolbar">
|
||||
<el-button type="danger" plain :loading="clearing" @click="clearAll">清理数据</el-button>
|
||||
</div>
|
||||
<el-alert
|
||||
v-if="errorText"
|
||||
:title="errorText"
|
||||
type="error"
|
||||
show-icon
|
||||
:closable="false"
|
||||
style="margin-bottom: 12px"
|
||||
>
|
||||
<template #default>
|
||||
<el-button link type="primary" @click="retry">重试</el-button>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-table v-loading="loading" :data="rows" stripe :empty-text="emptyText">
|
||||
<el-table-column prop="id" label="ID" min-width="90" />
|
||||
<el-table-column prop="username" label="用户" min-width="140" />
|
||||
<el-table-column label="类型" min-width="150">
|
||||
<template #default="{ row }">
|
||||
{{ historyPanelTypeLabel((row as HistoryRecordItem).panelType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createdAt" label="创建时间" min-width="180">
|
||||
<template #default="{ row }">{{ formatDateTime((row as HistoryRecordItem).createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结果预览" min-width="220">
|
||||
<template #default="{ row }">
|
||||
<div v-if="previewUrls(row as HistoryRecordItem).length" class="thumbs">
|
||||
<el-image
|
||||
v-for="(url, index) in previewUrls(row as HistoryRecordItem).slice(0, 3)"
|
||||
:key="url"
|
||||
:src="url"
|
||||
:preview-src-list="previewUrls(row as HistoryRecordItem)"
|
||||
:initial-index="index"
|
||||
preview-teleported
|
||||
fit="cover"
|
||||
class="thumb"
|
||||
/>
|
||||
<span v-if="previewUrls(row as HistoryRecordItem).length > 3" class="dim">+{{ previewUrls(row as HistoryRecordItem).length - 3 }}</span>
|
||||
</div>
|
||||
<span v-else class="dim">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="110" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" size="small" @click="openPreview(row as HistoryRecordItem)">查看大图</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="table-footer">
|
||||
<span>共 <b>{{ total.toLocaleString() }}</b> 条</span>
|
||||
<el-pagination background layout="prev, pager, next, jumper" :total="total" :page-size="pageSize" :current-page="page" @current-change="changePage" />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="previewVisible" :title="previewItem ? `记录 #${previewItem.id} 结果图` : '结果图'" width="820px" top="6vh">
|
||||
<div v-if="previewItem" class="preview-meta">
|
||||
<el-tag size="small">{{ previewItem.username }}</el-tag>
|
||||
<span>{{ historyPanelTypeLabel(previewItem.panelType) }}</span>
|
||||
<span>{{ formatDateTime(previewItem.createdAt) }}</span>
|
||||
</div>
|
||||
<div v-if="previewItem && previewUrls(previewItem).length" class="preview-grid">
|
||||
<el-image
|
||||
v-for="(url, index) in previewUrls(previewItem)"
|
||||
:key="url"
|
||||
:src="url"
|
||||
:preview-src-list="previewUrls(previewItem)"
|
||||
:initial-index="index"
|
||||
preview-teleported
|
||||
fit="contain"
|
||||
class="preview-img"
|
||||
/>
|
||||
</div>
|
||||
<el-empty v-else description="无结果图片" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.filter-card { margin-bottom: 14px; }
|
||||
.filter-grid { display: flex; flex-wrap: wrap; gap: 12px 18px; }
|
||||
.f-item { display: flex; flex-direction: column; gap: 6px; width: 190px; }
|
||||
.f-item label { color: var(--el-text-color-secondary); font-size: 12px; }
|
||||
.f-item.btn-row { flex-direction: row; align-items: flex-end; gap: 8px; width: auto; }
|
||||
.thumbs { display: flex; align-items: center; gap: 6px; }
|
||||
.thumb { width: 56px; height: 56px; border-radius: 4px; border: 1px solid var(--el-border-color-lighter); flex: none; }
|
||||
.table-footer { display: flex; justify-content: space-between; align-items: center; padding-top: 14px; }
|
||||
.table-footer span { color: var(--el-text-color-secondary); font-size: 12.5px; }
|
||||
.table-footer b { color: var(--el-text-color-primary); }
|
||||
.dim { color: var(--el-text-color-secondary); font-size: 12px; }
|
||||
.preview-meta { display: flex; align-items: center; gap: 12px; color: var(--el-text-color-secondary); font-size: 12px; margin-bottom: 12px; }
|
||||
.preview-grid { display: flex; flex-direction: column; gap: 12px; }
|
||||
.preview-img { width: 100%; border: 1px solid var(--el-border-color-lighter); border-radius: 6px; }
|
||||
/* 像素复刻旧版 admin.html 蓝白末层样式:form-box/panel-box 双卡 + table-scroll 表格 + 旧式分页 + thumb 缩略图。 */
|
||||
.history-view {
|
||||
font-family: inherit;
|
||||
color: var(--c-text, #24384d);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
.form-box,
|
||||
.panel-box {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding: 20px 22px 24px;
|
||||
border: 1px solid #d8e3ee;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(145deg, #ffffff, #f9fbfd);
|
||||
box-shadow: 0 1px 2px rgba(39, 67, 94, 0.04), 0 14px 30px -24px rgba(39, 67, 94, 0.28);
|
||||
}
|
||||
h3 {
|
||||
margin: 0 0 16px;
|
||||
font-size: 15px;
|
||||
font-weight: 650;
|
||||
color: #24384d;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
.panel-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
.panel-head h3 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.form-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
gap: 14px 18px;
|
||||
}
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
}
|
||||
.form-group label {
|
||||
color: #5b6f83;
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.form-group input,
|
||||
.form-group select {
|
||||
min-width: 0;
|
||||
min-height: 42px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #cbd9e6;
|
||||
border-radius: 9px;
|
||||
background: #f8fbfd;
|
||||
color: #24384d;
|
||||
font-size: 13.5px;
|
||||
font-family: inherit;
|
||||
color-scheme: light;
|
||||
outline: none;
|
||||
transition: border-color 160ms ease, box-shadow 160ms ease, background 160ms ease;
|
||||
}
|
||||
.form-group input:hover,
|
||||
.form-group select:hover {
|
||||
border-color: #9fb7cd;
|
||||
}
|
||||
.form-group input:focus,
|
||||
.form-group select:focus {
|
||||
background: #ffffff;
|
||||
border-color: #5f85ad;
|
||||
box-shadow: 0 0 0 3px rgba(95, 133, 173, 0.16);
|
||||
}
|
||||
.form-actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
min-height: 42px;
|
||||
padding: 9px 18px;
|
||||
border: 1px solid #4f78a5;
|
||||
border-radius: 9px;
|
||||
background: linear-gradient(135deg, #5f85ad, #4f78a5);
|
||||
color: #ffffff;
|
||||
font-family: inherit;
|
||||
font-size: 13.5px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 8px 18px -14px rgba(79, 120, 165, 0.72);
|
||||
transition: background 160ms ease, box-shadow 160ms ease, transform 120ms ease;
|
||||
}
|
||||
.btn:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, #7094ba, #5d83ac);
|
||||
box-shadow: 0 11px 22px -14px rgba(79, 120, 165, 0.8);
|
||||
}
|
||||
.btn:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.btn-secondary {
|
||||
background: #ffffff;
|
||||
color: #5b6f83;
|
||||
border-color: #c7d7e5;
|
||||
}
|
||||
.btn-secondary:hover:not(:disabled) {
|
||||
color: #2f5d8b;
|
||||
border-color: #95b1cb;
|
||||
background: #edf5fb;
|
||||
}
|
||||
.btn-danger {
|
||||
background: linear-gradient(135deg, #c06d77, #b35f6a);
|
||||
border-color: #b35f6a;
|
||||
}
|
||||
.btn-danger:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, #cb7c84, #b96570);
|
||||
}
|
||||
.btn-sm {
|
||||
min-height: 36px;
|
||||
padding: 7px 12px;
|
||||
}
|
||||
.msg {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid;
|
||||
border-radius: 9px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.msg.err {
|
||||
color: #91474f;
|
||||
background: #f8ebeb;
|
||||
border-color: #e4c2c5;
|
||||
}
|
||||
.table-scroll {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
overflow-x: auto;
|
||||
border: 1px solid #dbe5ee;
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
}
|
||||
.table-scroll table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.history-table-scroll > table {
|
||||
min-width: 900px;
|
||||
}
|
||||
.table-scroll th,
|
||||
.table-scroll td {
|
||||
padding: 10px 12px;
|
||||
text-align: left;
|
||||
font-size: 13.5px;
|
||||
line-height: 1.5;
|
||||
border-bottom: 1px solid #e0e8ef;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.table-scroll th {
|
||||
background: #edf4fa;
|
||||
color: #4e6479;
|
||||
border-bottom-color: #d5e1eb;
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.table-scroll tbody tr:hover td {
|
||||
background: #f1f7fb;
|
||||
}
|
||||
.table-scroll tbody tr:last-child td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.empty-tip {
|
||||
padding: 44px 24px;
|
||||
text-align: center;
|
||||
color: #8293a5;
|
||||
font-size: 13.5px;
|
||||
}
|
||||
.thumb-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
.thumb {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
object-fit: cover;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #d8e3ee;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
.empty-mini {
|
||||
color: #8293a5;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.pagination {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 18px;
|
||||
color: #5b6f83;
|
||||
font-size: 13px;
|
||||
}
|
||||
.pagination button {
|
||||
min-height: 30px;
|
||||
padding: 4px 12px;
|
||||
border: 1px solid #c7d7e5;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
color: #5b6f83;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pagination button:hover:not(:disabled) {
|
||||
background: #edf5fb;
|
||||
border-color: #95b1cb;
|
||||
color: #2f5d8b;
|
||||
}
|
||||
.pagination button:disabled {
|
||||
background: #eef3f7;
|
||||
color: #9baaba;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.page-total {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.page-jump {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.page-jump input {
|
||||
width: 56px;
|
||||
min-height: 30px;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #cbd9e6;
|
||||
border-radius: 8px;
|
||||
background: #f8fbfd;
|
||||
color: #24384d;
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
}
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 3000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
background: rgba(45, 66, 86, 0.36);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.modal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: min(880px, 100%);
|
||||
max-height: 84vh;
|
||||
padding: 20px 22px;
|
||||
border: 1px solid #c7d7e5;
|
||||
border-radius: 14px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 22px 60px rgba(39, 67, 94, 0.2);
|
||||
}
|
||||
.modal-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.modal-head h3 {
|
||||
margin: 0;
|
||||
}
|
||||
.modal-close {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 9px;
|
||||
background: none;
|
||||
color: #5b6f83;
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.modal-close:hover {
|
||||
border-color: #cbd9e6;
|
||||
background: #eef4fa;
|
||||
color: #24384d;
|
||||
}
|
||||
.preview-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 12px 0 10px;
|
||||
color: #5b6f83;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.preview-grid {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
border: 1px solid #dbe5ee;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
background: #f8fbfd;
|
||||
}
|
||||
.preview-grid a {
|
||||
display: block;
|
||||
}
|
||||
.preview-grid img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #d8e3ee;
|
||||
background: #fff;
|
||||
}
|
||||
.modal-empty {
|
||||
padding: 48px 20px;
|
||||
text-align: center;
|
||||
color: #8293a5;
|
||||
}
|
||||
.modal-foot {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user