Compare commits

...
2 Commits
Author SHA1 Message Date
wc24 f7ee862e0f feat(relation): 显示改查 refs 字典(替代逐行 _label)
配合 shrimp-server v0.6.7:list 响应带回 refs。
- ShrimpTable: 取 res.refs 并 provide('shrimpRefs')
- FieldRelation.display: inject refs,按 id 查名(单/多选),去掉 displayLabel 依赖
- ShrimpTable displayLabel 绑定去掉 relation(tab/textarea 仍走 _label)
relation 顶层显示行为不变,只是换了供给方式。
2026-06-29 20:19:56 +08:00
wc24 8ac1427015 feat(tab): 表格列显示后端 stringify 文本(displayLabel=${key}_label)
配合 shrimp-server v0.6.3:tab 字段本体端到端是对象(编辑回显正常),
表格展示改用后端算好的 ${key}_label 字符串,与 relation/textarea 的 _label 约定一致。
FieldTab.display 优先渲染 displayLabel,无则回退旧的对象标签逻辑。
2026-06-29 15:47:29 +08:00
4 changed files with 30 additions and 8 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "shrimp-admin", "name": "shrimp-admin",
"version": "0.3.0", "version": "0.3.2",
"private": true, "private": true,
"description": "Shrimp 框架前端 — 消费 ShrimpServer + ShrimpTable 契约的经典 Vue3 + Element Plus 实现", "description": "Shrimp 框架前端 — 消费 ShrimpServer + ShrimpTable 契约的经典 Vue3 + Element Plus 实现",
"main": "src/index.ts", "main": "src/index.ts",
+6 -2
View File
@@ -29,7 +29,7 @@
<slot :name="`column-${field.key}`" :row="row" :field="field"> <slot :name="`column-${field.key}`" :row="row" :field="field">
<component :is="vueAdmin.getFieldComponent(moduleKey, field.type, field.key)" :field="field" <component :is="vueAdmin.getFieldComponent(moduleKey, field.type, field.key)" :field="field"
:model-value="row[field.key]" mode="display" :model-value="row[field.key]" mode="display"
v-bind="field.type === 'relation' || field.type === 'textarea' ? { displayLabel: row[field.key + '_label'] } : {}" /> v-bind="field.type === 'textarea' || field.type === 'tab' ? { displayLabel: row[field.key + '_label'] } : {}" />
</slot> </slot>
</template> </template>
</el-table-column> </el-table-column>
@@ -78,7 +78,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, useSlots, onMounted, type Component } from 'vue' import { ref, computed, useSlots, onMounted, provide, type Component } from 'vue'
import { ITableCrudProto, IFieldConfig, IModuleInfo, IPageQuery } from '../IShrimpType' import { ITableCrudProto, IFieldConfig, IModuleInfo, IPageQuery } from '../IShrimpType'
import { vueAdmin } from '../VueAdmin' import { vueAdmin } from '../VueAdmin'
import { Plus, Delete as DeleteIcon, Download, Upload, Edit, VideoPlay, VideoPause, Operation } from '@element-plus/icons-vue' import { Plus, Delete as DeleteIcon, Download, Upload, Edit, VideoPlay, VideoPause, Operation } from '@element-plus/icons-vue'
@@ -214,6 +214,9 @@ const filterSlotNames = computed(() => Object.keys(slots).filter(name => name.st
const data = ref<any[]>([]) const data = ref<any[]>([])
const loading = ref(false) const loading = ref(false)
const total = ref(0) const total = ref(0)
// relation 显示名字典 { moduleKey: { id: 名 } },由 list 响应带回,provide 给表格里的字段组件查名
const refs = ref<Record<string, Record<string, any>>>({})
provide('shrimpRefs', refs)
// ---- 对话框状态 ---- // ---- 对话框状态 ----
@@ -501,6 +504,7 @@ async function refresh(query?: Record<string, any>) {
Object.assign(pageQuery, query) Object.assign(pageQuery, query)
const res = await api.list(pageQuery) const res = await api.list(pageQuery)
data.value = res.list || [] data.value = res.list || []
refs.value = res.refs || {}
total.value = res.total total.value = res.total
currentPage.value = res.page currentPage.value = res.page
currentPageSize.value = res.pageSize currentPageSize.value = res.pageSize
+17 -4
View File
@@ -1,7 +1,7 @@
<template> <template>
<template v-if="mode === 'display'"> <template v-if="mode === 'display'">
<template v-if="field.multiple && Array.isArray(displayLabel)">{{ displayLabel.filter(Boolean).join(', ') || '-' }}</template> <template v-if="field.multiple">{{ displayNames.join('、') || '-' }}</template>
<template v-else>{{ displayLabel || modelValue || '-' }}</template> <template v-else>{{ displayName || '-' }}</template>
</template> </template>
<el-select <el-select
v-else-if="mode === 'filter'" v-else-if="mode === 'filter'"
@@ -18,7 +18,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, computed } from 'vue' import { ref, inject, onMounted, computed, type Ref } from 'vue'
import { vueAdmin } from '../../VueAdmin' import { vueAdmin } from '../../VueAdmin'
import { ITableCrudProto } from '../../IShrimpType' import { ITableCrudProto } from '../../IShrimpType'
@@ -27,7 +27,6 @@ const props = defineProps<{
modelValue: any modelValue: any
mode: 'display' | 'edit' | 'filter' mode: 'display' | 'edit' | 'filter'
disabled?: boolean disabled?: boolean
displayLabel?: string | string[]
}>() }>()
defineEmits<{ defineEmits<{
@@ -37,6 +36,20 @@ defineEmits<{
const options = ref<{ label: string; value: string }[]>([]) const options = ref<{ label: string; value: string }[]>([])
const loading = ref(false) const loading = ref(false)
// 显示用:从 ShrimpTable provide 的 refs 字典按 id 查名(替代旧的 _label)
const refs = inject<Ref<Record<string, Record<string, any>>>>('shrimpRefs', ref<Record<string, Record<string, any>>>({}))
const dict = computed(() => refs.value?.[props.field.relation?.moduleKey] ?? {})
const displayName = computed(() => {
const id = props.modelValue
if (id == null) return ''
return dict.value[String(id)] ?? String(id)
})
const displayNames = computed(() =>
(Array.isArray(props.modelValue) ? props.modelValue : [])
.map((id: any) => dict.value[String(id)] ?? String(id))
.filter(Boolean)
)
/** 统一转为字符串值,解决 ObjectId 与 el-select 的匹配问题 */ /** 统一转为字符串值,解决 ObjectId 与 el-select 的匹配问题 */
const stringValue = computed(() => { const stringValue = computed(() => {
// filter 模式强制 multiple,始终返回数组 // filter 模式强制 multiple,始终返回数组
+6 -1
View File
@@ -1,6 +1,10 @@
<template> <template>
<template v-if="mode === 'display'"> <template v-if="mode === 'display'">
<span v-if="!modelValue || typeof modelValue !== 'object'">-</span> <!-- 表格列展示后端算好的 stringify 文本(displayLabel=${key}_label);本体仍是对象,供编辑/逻辑用 -->
<el-tooltip v-if="displayLabel" :content="String(displayLabel)" placement="top">
<span class="tab-display">{{ displayLabel }}</span>
</el-tooltip>
<span v-else-if="!modelValue || typeof modelValue !== 'object'">-</span>
<el-tooltip v-else :content="JSON.stringify(modelValue, null, 2)" placement="top"> <el-tooltip v-else :content="JSON.stringify(modelValue, null, 2)" placement="top">
<span class="tab-display">{{ displayText }}</span> <span class="tab-display">{{ displayText }}</span>
</el-tooltip> </el-tooltip>
@@ -35,6 +39,7 @@ const props = defineProps<{
modelValue: any modelValue: any
mode: 'display' | 'edit' | 'filter' mode: 'display' | 'edit' | 'filter'
disabled?: boolean disabled?: boolean
displayLabel?: string
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{