diff --git a/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue b/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue
index a479f0d..fdbfa76 100644
--- a/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue
+++ b/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue
@@ -130,6 +130,10 @@
@click.stop="toggleSelection(row.name)"
/>
+
+
{{ row[primaryLabel] || row.name }}
@@ -180,7 +184,7 @@
-
+
@@ -208,6 +214,11 @@
@click.stop="toggleSelection(row.name)"
/>
+
+
+
![]()
+
—
+
✓
@@ -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([])
+const pageMeta = ref({})
const linkTitleCache = ref>({})
const pageTypeConfigCache = ref>({})
const cacheAccessOrder = ref([]) // 记录访问顺序,用于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 // 保存完整的pageMeta,包含image_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,