pagetype列表页增加显示image_field字段图片

This commit is contained in:
jingrow 2025-11-01 16:41:51 +08:00
parent 3d34392946
commit 96d7d50c0b

View File

@ -130,6 +130,10 @@
@click.stop="toggleSelection(row.name)"
/>
</div>
<!-- 卡片图片 -->
<div v-if="imageFieldName && getImageUrl(row)" class="card-image">
<img :src="getImageUrl(row) || ''" :alt="row[primaryLabel] || row.name" />
</div>
<div class="card-content">
<div class="info" @click.stop="openDetail(row.name)">
<div class="title">{{ row[primaryLabel] || row.name }}</div>
@ -180,7 +184,7 @@
</div>
<!-- 列表视图 -->
<div v-else class="agent-list">
<div v-else class="agent-list" :class="{ 'has-image': imageFieldName }">
<div class="list-header">
<div class="col-checkbox">
<input
@ -190,6 +194,8 @@
@change="toggleSelectAll"
/>
</div>
<!-- 列表图片列 -->
<div v-if="imageFieldName" class="col-image">{{ t('Image') }}</div>
<div v-for="col in displayColumns" :key="col.key" :class="`col-${col.key}`">{{ t(col.title) }}</div>
<div class="col-actions">{{ t('Actions') }}</div>
</div>
@ -208,6 +214,11 @@
@click.stop="toggleSelection(row.name)"
/>
</div>
<!-- 列表图片 -->
<div v-if="imageFieldName" class="col-image" @click.stop="openDetail(row.name)">
<img v-if="getImageUrl(row)" :src="getImageUrl(row) || ''" :alt="row[primaryLabel] || row.name" />
<span v-else class="image-placeholder"></span>
</div>
<div v-for="col in displayColumns" :key="col.key" :class="`col-${col.key}`" @click.stop="openDetail(row.name)">
<template v-if="isBooleanField(col.key) && (row[col.key] === 1 || row[col.key] === 0 || typeof row[col.key] === 'boolean')">
<span v-if="row[col.key] === 1 || row[col.key] === true" class="boolean-true"></span>
@ -368,7 +379,21 @@ const cardBadges = computed(() => {
return keys.map((k: string) => ({ key: k }))
})
//
const imageFieldName = computed(() => {
return pageMeta.value?.image_field || ''
})
// URL
function getImageUrl(row: any): string | null {
if (!imageFieldName.value) return null
const imageValue = row[imageFieldName.value]
if (!imageValue || typeof imageValue !== 'string') return null
return imageValue.trim() || null
}
const metaFields = ref<any[]>([])
const pageMeta = ref<any>({})
const linkTitleCache = ref<Record<string, string>>({})
const pageTypeConfigCache = ref<Record<string, any>>({})
const cacheAccessOrder = ref<string[]>([]) // 访LRU
@ -497,7 +522,9 @@ async function loadMeta() {
try {
const url = `/api/data/PageType/${encodeURIComponent(entity.value)}`
const res = await axios.get(url, { headers: get_session_api_headers(), withCredentials: true })
metaFields.value = res.data?.data?.fields || []
const data = res.data?.data || {}
metaFields.value = data.fields || []
pageMeta.value = data // pageMetaimage_field
//
isSinglePage.value = await isSinglePageType(entity.value)
@ -635,7 +662,11 @@ async function loadData() {
try {
const listUrl = `/api/data/${encodeURIComponent(entity.value)}`
//
const fieldNames = displayColumns.value.map((c: any) => c.key).filter((k: string) => k && k !== 'actions')
let fieldNames = displayColumns.value.map((c: any) => c.key).filter((k: string) => k && k !== 'actions')
// image_field
if (imageFieldName.value && !fieldNames.includes(imageFieldName.value)) {
fieldNames.push(imageFieldName.value)
}
//
const params: any = {
@ -968,6 +999,37 @@ function formatDisplayValue(value: any, fieldName: string) {
height: 18px;
}
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
overflow: hidden;
background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.card-image::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.02) 100%);
pointer-events: none;
}
.card-image img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.agent-card:hover .card-image img {
transform: scale(1.08);
}
.card-content {
display: flex;
flex-direction: column;
@ -1134,9 +1196,17 @@ function formatDisplayValue(value: any, fieldName: string) {
width: 40px;
}
.agent-list.has-image .list-header {
grid-template-columns: 40px minmax(50px, 50px) repeat(auto-fit, minmax(120px, 1fr)) 140px;
}
.agent-list:not(.has-image) .list-header {
grid-template-columns: 40px repeat(auto-fit, minmax(120px, 1fr)) 140px;
}
.list-header {
display: grid;
grid-template-columns: 40px repeat(auto-fit, minmax(120px, 1fr)) 140px;
align-items: center;
gap: 16px;
padding: 16px 20px;
background: #f9fafb;
@ -1151,9 +1221,17 @@ function formatDisplayValue(value: any, fieldName: string) {
overflow-y: auto;
}
.agent-list.has-image .list-item {
grid-template-columns: 40px minmax(50px, 50px) repeat(auto-fit, minmax(120px, 1fr)) 140px;
}
.agent-list:not(.has-image) .list-item {
grid-template-columns: 40px repeat(auto-fit, minmax(120px, 1fr)) 140px;
}
.list-item {
display: grid;
grid-template-columns: 40px repeat(auto-fit, minmax(120px, 1fr)) 140px;
align-items: center;
gap: 16px;
padding: 16px 20px;
border-bottom: 1px solid #f3f4f6;
@ -1174,6 +1252,38 @@ function formatDisplayValue(value: any, fieldName: string) {
border-color: #3b82f6;
}
.col-image {
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
flex-shrink: 0;
}
.col-image img {
width: 50px;
height: 50px;
object-fit: cover;
object-position: center;
border-radius: 8px;
border: 1px solid #e5e7eb;
background: #f9fafb;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.list-item:hover .col-image img {
border-color: #3b82f6;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
transform: translateY(-1px);
}
.col-image .image-placeholder {
color: #9ca3af;
font-size: 14px;
}
.col-actions {
display: flex;
align-items: center;
@ -1219,6 +1329,14 @@ function formatDisplayValue(value: any, fieldName: string) {
font-size: 16px;
}
/* 列表列通用样式 - 确保垂直居中 */
.list-header > div:not(.col-checkbox),
.list-item > div:not(.col-checkbox):not(.col-actions) {
display: flex;
align-items: center;
min-height: 24px;
}
/* 通用截断样式 */
.col-title,
.col-subtitle,