260720
This commit is contained in:
@@ -0,0 +1,739 @@
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="960"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="modalTitle"
|
||||
:body-style="{ paddingBottom: '20px' }"
|
||||
okText="关闭"
|
||||
@update:visible="updateVisible"
|
||||
@ok="updateVisible(false)"
|
||||
>
|
||||
<a-spin :spinning="loading">
|
||||
<a-empty
|
||||
v-if="!loading && !descriptionItems.length"
|
||||
:description="emptyDescription"
|
||||
/>
|
||||
<a-descriptions v-else bordered :column="2" size="middle">
|
||||
<a-descriptions-item
|
||||
v-for="item in descriptionItems"
|
||||
:key="item.key"
|
||||
:label="item.label"
|
||||
:span="item.span"
|
||||
>
|
||||
<template v-if="isFileField(item.key)">
|
||||
<div
|
||||
v-for="(file, index) in normalizeFileEntries(item.value)"
|
||||
:key="`${item.key}-file-${index}`"
|
||||
class="file-entry"
|
||||
>
|
||||
<a :href="file.url" target="_blank" rel="noreferrer">
|
||||
{{ file.name }}
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<a-image
|
||||
v-else-if="isImageField(item.key) && typeof item.value === 'string'"
|
||||
:src="item.value"
|
||||
:width="120"
|
||||
/>
|
||||
<template v-else-if="Array.isArray(item.value)">
|
||||
<div
|
||||
v-for="(entry, index) in item.value"
|
||||
:key="`${item.key}-${index}`"
|
||||
class="array-entry"
|
||||
>
|
||||
<div class="array-entry-index">第{{ index + 1 }}项</div>
|
||||
<div
|
||||
v-for="(line, lineIndex) in formatArrayEntry(entry)"
|
||||
:key="`${item.key}-${index}-${lineIndex}`"
|
||||
class="array-entry-line"
|
||||
>
|
||||
{{ line }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="isPlainObject(item.value)">
|
||||
<div
|
||||
v-for="(line, index) in formatObjectLines(item.value)"
|
||||
:key="`${item.key}-obj-${index}`"
|
||||
class="array-entry-line"
|
||||
>
|
||||
{{ line }}
|
||||
</div>
|
||||
</template>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
v-else-if="isRichTextField(item.key)"
|
||||
class="rich-text-content markdown-body"
|
||||
v-html="formatRichText(item.value)"
|
||||
></div>
|
||||
<div v-else :class="{ 'multiline-text': isWideField(item.key) }">
|
||||
{{ formatDisplayValue(item.key, item.value) }}
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-spin>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { toDateString } from 'ele-admin-pro';
|
||||
import 'github-markdown-css/github-markdown-light.css';
|
||||
import { getManuscript } from '@/api/cms/manuscript';
|
||||
import { getQmgcForm } from '@/api/gxmu/qmgc-form';
|
||||
import { getSjqnForm } from '@/api/gxmu/sjqn-form';
|
||||
import { getSjtbzbsjForm } from '@/api/gxmu/sjtbzbsj-form';
|
||||
import { getTyglForm } from '@/api/gxmu/tygl-form';
|
||||
import { getTzbcyForm } from '@/api/gxmu/tzbcy-form';
|
||||
import { getWshqtwForm } from '@/api/gxmu/wshqtw-form';
|
||||
import { getWshqtzbForm } from '@/api/gxmu/wshqtzb-form';
|
||||
import { getWxxzxForm } from '@/api/gxmu/wxxzx-form';
|
||||
import { getYxgqtdgbForm } from '@/api/gxmu/yxgqtdgb-form';
|
||||
import { getYxgqtyForm } from '@/api/gxmu/yxgqty-form';
|
||||
|
||||
interface DescriptionItem {
|
||||
key: string;
|
||||
label: string;
|
||||
value: unknown;
|
||||
span: number;
|
||||
}
|
||||
|
||||
interface ModuleConfig {
|
||||
title: string;
|
||||
fetcher: (id: number) => Promise<Record<string, unknown>>;
|
||||
labels: Record<string, string>;
|
||||
}
|
||||
|
||||
const COMMON_LABELS: Record<string, string> = {
|
||||
year: '年份',
|
||||
id: 'ID',
|
||||
title: '标题',
|
||||
content: '内容',
|
||||
cover: '封面',
|
||||
createTime: '创建时间',
|
||||
updateTime: '更新时间',
|
||||
status: '状态',
|
||||
sortNumber: '排序',
|
||||
provinceCity: '省市',
|
||||
schoolName: '学校名称',
|
||||
projectName: '项目名称',
|
||||
projectType: '项目类型',
|
||||
projectGroup: '项目分组',
|
||||
publicProjectType: '公开展示项目类型',
|
||||
publicProjectGroup: '公开展示项目分组',
|
||||
leader: '负责人',
|
||||
phone: '联系电话',
|
||||
teamMembers: '团队成员',
|
||||
advisors: '指导老师',
|
||||
projectBrief: '项目简介',
|
||||
socialValue: '社会价值',
|
||||
practiceProcess: '实践过程',
|
||||
innovationMeaning: '创新意义',
|
||||
developmentProspect: '发展前景',
|
||||
teamCooperation: '团队协作情况',
|
||||
projectMaterials: '项目佐证材料',
|
||||
otherProofs: '其他证明材料',
|
||||
projectSummary: '项目总结',
|
||||
teamIntro: '团队介绍',
|
||||
teamSlogan: '团队口号',
|
||||
practiceLog: '实践日志',
|
||||
name: '姓名',
|
||||
gender: '性别',
|
||||
nation: '民族',
|
||||
photo: '电子版照片',
|
||||
birthMonth: '出生年月',
|
||||
politics: '政治面貌',
|
||||
nativePlace: '籍贯',
|
||||
wechat: '微信号',
|
||||
email: '邮箱',
|
||||
qq: 'QQ',
|
||||
idCardNo: '身份证号',
|
||||
hobby: '兴趣爱好',
|
||||
willingToWorkInGuangxi: '是否愿意留桂工作',
|
||||
schoolInfo: '学校信息',
|
||||
leaguePosition: '团内职务',
|
||||
resume: '个人简历',
|
||||
awards: '获得奖励',
|
||||
academicPerformance: '学习成绩',
|
||||
secondaryLeagueOpinion: '二级团组织意见',
|
||||
secondaryLeagueOpinionDate: '二级团组织意见日期',
|
||||
secondaryPartyOpinion: '二级党组织意见',
|
||||
secondaryPartyOpinionDate: '二级党组织意见日期',
|
||||
schoolLeagueOpinion: '学校团组织意见',
|
||||
schoolLeagueOpinionDate: '学校团组织意见日期',
|
||||
applyType: '申报类别',
|
||||
education: '学历',
|
||||
position: '职务',
|
||||
unit: '工作单位',
|
||||
experience: '主要经历',
|
||||
mainStory: '主要事迹',
|
||||
leagueOpinion: '团组织意见',
|
||||
partyOpinion: '党组织意见',
|
||||
schoolOpinion: '学校意见',
|
||||
collegeClass: '学院班级',
|
||||
branch: '团支部名称',
|
||||
eduEval: '教育评议情况',
|
||||
serialNo: '序号',
|
||||
archiveInCurrentOrg: '团籍是否在本组织',
|
||||
joinMonth: '入团时间',
|
||||
memberRecords: '团员记录',
|
||||
growthArchives: '成长档案',
|
||||
orgName: '组织名称',
|
||||
memberTotal: '团员总数',
|
||||
memberDeveloped2024: '上一年度发展团员数',
|
||||
smartSystemLogin: '智慧团建系统录入情况',
|
||||
committeeCount: '团委委员数',
|
||||
fulltimeCadreCount: '专职团干部数',
|
||||
parttimeCadreCount: '兼职团干部数',
|
||||
lastElectionTime: '最近一次换届时间',
|
||||
feeReceivable2024: '上一年度应收团费',
|
||||
feeReceived2024: '上一年度实收团费',
|
||||
feePayable2024: '上一年度应缴团费',
|
||||
feePaid2024: '上一年度实缴团费',
|
||||
branchCount: '团支部数量',
|
||||
standardizedWork2024: '上一年度标准化建设开展情况',
|
||||
recommendActivist2024: '上一年度推优入党人数',
|
||||
activistConfirmed: '入党积极分子确定情况',
|
||||
recommendDevTarget2024: '上一年度推荐发展对象人数',
|
||||
devTargetConfirmed: '发展对象确定情况',
|
||||
honorsFiveYears: '近五年获奖情况',
|
||||
workSummaryThreeYears: '近三年工作总结',
|
||||
branchName: '团支部名称',
|
||||
secondOrg: '所属二级党组织',
|
||||
secretary: '团支部书记',
|
||||
contact: '联系方式',
|
||||
establishTime: '成立时间',
|
||||
branchCommitteeMeetingCount: '支委会次数',
|
||||
branchMemberMeetingCount: '支部大会次数',
|
||||
eduEvalDone: '教育评议是否完成',
|
||||
annualRegDone: '年度团籍注册是否完成',
|
||||
classCount: '班级数量',
|
||||
smartSystem100: '智慧团建系统覆盖率',
|
||||
topicName: '课题名称',
|
||||
collegeGradeClass: '学院年级班级',
|
||||
advisor: '指导老师',
|
||||
topicType: '课题类别',
|
||||
guidanceTeachers: '指导教师组',
|
||||
applicants: '申请人信息',
|
||||
budgets: '经费预算',
|
||||
rationale: '立项依据',
|
||||
teacherOpinion: '指导教师意见',
|
||||
teacherSign: '指导教师签名',
|
||||
reviewOpinion: '评审意见',
|
||||
reviewScore: '评审成绩',
|
||||
reviewLeaderSign: '评审负责人签名',
|
||||
collegeOpinion: '学院意见',
|
||||
promiseSigner: '承诺签字人',
|
||||
promiseSignature: '承诺书签名',
|
||||
reviewCollege: '评审学院',
|
||||
reviewReporter: '汇报人',
|
||||
reviewDate: '评审日期',
|
||||
reviewMembers: '评审成员',
|
||||
fundingBudgetFile: '经费预算表',
|
||||
fundingAllocationFile: '经费划拨明细表',
|
||||
midtermRemark: '中期检查说明',
|
||||
midtermMaterials: '中期检查材料',
|
||||
finalRemark: '结题说明',
|
||||
finalMaterials: '结题材料',
|
||||
archiveAwards: '获奖情况',
|
||||
archiveProofs: '佐证材料',
|
||||
archiveRecords: '成果归档',
|
||||
grade: '年级',
|
||||
className: '班级',
|
||||
major: '专业',
|
||||
role: '角色',
|
||||
item: '项目',
|
||||
amount: '金额',
|
||||
reason: '原因',
|
||||
dept: '部门',
|
||||
sign: '签名',
|
||||
department: '部门',
|
||||
remark: '备注',
|
||||
type: '类型',
|
||||
recordTime: '时间',
|
||||
archiveTime: '时间',
|
||||
identity: '身份',
|
||||
organization: '所在组织',
|
||||
memberNo: '团员编号',
|
||||
currentDutyTime: '现任职务时间',
|
||||
cadreYears: '从事团学工作年限',
|
||||
assessment2024: '上一年度考核情况',
|
||||
volunteerRegTime: '志愿汇注册时间',
|
||||
cadreExperience: '团学工作经历',
|
||||
joinTime: '入团时间',
|
||||
collegeMajorClass: '学院专业班级',
|
||||
eduEval2024: '上一年度教育评议情况',
|
||||
smartSystem: '智慧团建系统录入情况',
|
||||
totalVolunteerHours: '累计志愿服务时长',
|
||||
volunteerHours2024: '上一年度志愿服务时长',
|
||||
duty: '职务',
|
||||
college: '学院',
|
||||
gradeMajor: '年级专业',
|
||||
birthDate: '出生日期',
|
||||
branchOrganizationName: '所属团支部'
|
||||
};
|
||||
|
||||
const MODULE_CONFIGS: Record<string, ModuleConfig> = {
|
||||
cms_manuscript: {
|
||||
title: '稿件',
|
||||
fetcher: (id) => getManuscript(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_sjqn: {
|
||||
title: '十佳青年岗位能手',
|
||||
fetcher: (id) => getSjqnForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_sjtbzbsj: {
|
||||
title: '十佳团支部书记',
|
||||
fetcher: (id) => getSjtbzbsjForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_wshqtw: {
|
||||
title: '五四红旗团委',
|
||||
fetcher: (id) => getWshqtwForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_wshqtzb: {
|
||||
title: '五四红旗团支部',
|
||||
fetcher: (id) => getWshqtzbForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_yxgqtdgb: {
|
||||
title: '优秀共青团干部',
|
||||
fetcher: (id) => getYxgqtdgbForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_yxgqty: {
|
||||
title: '优秀共青团员',
|
||||
fetcher: (id) => getYxgqtyForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_tzbcy_form: {
|
||||
title: '挑战杯',
|
||||
fetcher: (id) => getTzbcyForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_qmgc_form: {
|
||||
title: '青马工程',
|
||||
fetcher: (id) => getQmgcForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_wxxzx_project: {
|
||||
title: '未来学术之星',
|
||||
fetcher: (id) => getWxxzxForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS,
|
||||
title: '职称'
|
||||
}
|
||||
},
|
||||
gxmu_tygl_form: {
|
||||
title: '团员管理',
|
||||
fetcher: (id) => getTyglForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
},
|
||||
gxmu_tygl: {
|
||||
title: '团员管理',
|
||||
fetcher: (id) => getTyglForm(id) as Promise<Record<string, unknown>>,
|
||||
labels: {
|
||||
...COMMON_LABELS
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const HIDDEN_FIELDS = new Set([
|
||||
'reviewList',
|
||||
'deleted',
|
||||
'tenantId',
|
||||
'userId',
|
||||
'branchOrganizationId'
|
||||
]);
|
||||
const IMAGE_FIELDS = new Set(['photo', 'cover', 'promiseSignature']);
|
||||
const RICH_TEXT_FIELDS = new Set(['content']);
|
||||
const DATE_TIME_FIELDS = new Set(['createTime', 'updateTime']);
|
||||
const FILE_FIELDS = new Set([
|
||||
'fundingBudgetFile',
|
||||
'fundingAllocationFile',
|
||||
'midtermMaterials',
|
||||
'finalMaterials',
|
||||
'archiveProofs'
|
||||
]);
|
||||
const WIDE_FIELDS = new Set([
|
||||
'projectBrief',
|
||||
'socialValue',
|
||||
'practiceProcess',
|
||||
'innovationMeaning',
|
||||
'developmentProspect',
|
||||
'teamCooperation',
|
||||
'projectMaterials',
|
||||
'otherProofs',
|
||||
'projectSummary',
|
||||
'teamIntro',
|
||||
'practiceLog',
|
||||
'resume',
|
||||
'awards',
|
||||
'academicPerformance',
|
||||
'secondaryLeagueOpinion',
|
||||
'secondaryPartyOpinion',
|
||||
'schoolLeagueOpinion',
|
||||
'experience',
|
||||
'mainStory',
|
||||
'leagueOpinion',
|
||||
'partyOpinion',
|
||||
'schoolOpinion',
|
||||
'workSummaryThreeYears',
|
||||
'honorsFiveYears',
|
||||
'rationale',
|
||||
'teacherOpinion',
|
||||
'reviewOpinion',
|
||||
'collegeOpinion',
|
||||
'cadreExperience',
|
||||
'midtermRemark',
|
||||
'finalRemark',
|
||||
'archiveAwards'
|
||||
]);
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
module?: string;
|
||||
moduleName?: string;
|
||||
pk?: number;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
const maxable = ref(true);
|
||||
const detail = ref<Record<string, unknown>>({});
|
||||
const loadFailed = ref(false);
|
||||
|
||||
const currentConfig = computed(() => {
|
||||
return props.module ? MODULE_CONFIGS[props.module] : undefined;
|
||||
});
|
||||
|
||||
const modalTitle = computed(() => {
|
||||
return `${props.moduleName || currentConfig.value?.title || '数据'}详情`;
|
||||
});
|
||||
|
||||
const emptyDescription = computed(() => {
|
||||
if (loadFailed.value) {
|
||||
return '数据加载失败';
|
||||
}
|
||||
if (!currentConfig.value) {
|
||||
return '暂不支持查看该模块数据';
|
||||
}
|
||||
return '暂无可展示数据';
|
||||
});
|
||||
|
||||
const getFieldLabel = (key: string) => {
|
||||
const label = currentConfig.value?.labels[key];
|
||||
if (label) {
|
||||
return label;
|
||||
}
|
||||
return key
|
||||
.replace(/([A-Z])/g, ' $1')
|
||||
.replace(/^./, (text) => text.toUpperCase())
|
||||
.trim();
|
||||
};
|
||||
|
||||
const isPlainObject = (value: unknown): value is Record<string, unknown> => {
|
||||
return Object.prototype.toString.call(value) === '[object Object]';
|
||||
};
|
||||
|
||||
const hasVisibleValue = (value: unknown) => {
|
||||
if (value === null || value === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.trim().length > 0;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length > 0;
|
||||
}
|
||||
if (isPlainObject(value)) {
|
||||
return Object.values(value).some((item) => hasVisibleValue(item));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const isWideField = (key: string) => {
|
||||
return WIDE_FIELDS.has(key);
|
||||
};
|
||||
|
||||
const isImageField = (key: string) => {
|
||||
return IMAGE_FIELDS.has(key);
|
||||
};
|
||||
|
||||
const isFileField = (key: string) => {
|
||||
return FILE_FIELDS.has(key);
|
||||
};
|
||||
|
||||
const isRichTextField = (key: string) => {
|
||||
return props.module === 'cms_manuscript' && RICH_TEXT_FIELDS.has(key);
|
||||
};
|
||||
|
||||
const getFileName = (url?: string) => {
|
||||
if (!url) {
|
||||
return '未命名文件';
|
||||
}
|
||||
const cleanUrl = url.split('?')[0];
|
||||
return decodeURIComponent(cleanUrl.split('/').pop() || cleanUrl);
|
||||
};
|
||||
|
||||
const normalizeFileEntries = (value: unknown) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value
|
||||
.map((item) => {
|
||||
if (typeof item === 'string' && item.trim()) {
|
||||
return {
|
||||
name: getFileName(item),
|
||||
url: item
|
||||
};
|
||||
}
|
||||
if (
|
||||
isPlainObject(item) &&
|
||||
typeof item.url === 'string' &&
|
||||
item.url.trim()
|
||||
) {
|
||||
return {
|
||||
name:
|
||||
(typeof item.name === 'string' && item.name.trim()) ||
|
||||
getFileName(item.url),
|
||||
url: item.url
|
||||
};
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter((item): item is { name: string; url: string } => !!item);
|
||||
}
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
return [
|
||||
{
|
||||
name: getFileName(value),
|
||||
url: value
|
||||
}
|
||||
];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const normalizeDetailValue = (key: string, value: unknown) => {
|
||||
if (!isFileField(key) || typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
const text = value.trim();
|
||||
if (!text) {
|
||||
return value;
|
||||
}
|
||||
if (key === 'fundingBudgetFile' || key === 'fundingAllocationFile') {
|
||||
return text;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
return Array.isArray(parsed) ? parsed : text;
|
||||
} catch (e) {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const formatSimpleValue = (value: unknown) => {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-';
|
||||
}
|
||||
return String(value);
|
||||
};
|
||||
|
||||
const formatStatusValue = (value: unknown) => {
|
||||
if (props.module === 'cms_manuscript') {
|
||||
if (Number(value) === 0) {
|
||||
return '待审核';
|
||||
}
|
||||
if (Number(value) === 1) {
|
||||
return '显示';
|
||||
}
|
||||
}
|
||||
return formatSimpleValue(value);
|
||||
};
|
||||
|
||||
const formatDateTimeValue = (value: unknown) => {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-';
|
||||
}
|
||||
return toDateString(String(value), 'yyyy-MM-dd HH:mm:ss');
|
||||
};
|
||||
|
||||
const formatDisplayValue = (key: string, value: unknown) => {
|
||||
if (DATE_TIME_FIELDS.has(key)) {
|
||||
return formatDateTimeValue(value);
|
||||
}
|
||||
if (key === 'status') {
|
||||
return formatStatusValue(value);
|
||||
}
|
||||
return formatSimpleValue(value);
|
||||
};
|
||||
|
||||
const formatRichText = (value: unknown) => {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '<p>-</p>';
|
||||
}
|
||||
return String(value);
|
||||
};
|
||||
|
||||
const formatObjectLines = (value: Record<string, unknown>) => {
|
||||
return Object.keys(value)
|
||||
.filter((key) => !HIDDEN_FIELDS.has(key) && hasVisibleValue(value[key]))
|
||||
.map((key) => `${getFieldLabel(key)}:${formatDisplayValue(key, value[key])}`);
|
||||
};
|
||||
|
||||
const formatArrayEntry = (entry: unknown) => {
|
||||
if (isPlainObject(entry)) {
|
||||
const lines = formatObjectLines(entry);
|
||||
return lines.length ? lines : ['-'];
|
||||
}
|
||||
return [formatSimpleValue(entry)];
|
||||
};
|
||||
|
||||
const descriptionItems = computed<DescriptionItem[]>(() => {
|
||||
const source = detail.value || {};
|
||||
const orderedKeys = Object.keys(currentConfig.value?.labels || {}).filter(
|
||||
(key) => !HIDDEN_FIELDS.has(key) && hasVisibleValue(source[key])
|
||||
);
|
||||
const extraKeys = Object.keys(source).filter(
|
||||
(key) =>
|
||||
!HIDDEN_FIELDS.has(key) &&
|
||||
hasVisibleValue(source[key]) &&
|
||||
!orderedKeys.includes(key)
|
||||
);
|
||||
|
||||
return [...orderedKeys, ...extraKeys].map((key) => ({
|
||||
key,
|
||||
label: getFieldLabel(key),
|
||||
value: normalizeDetailValue(key, source[key]),
|
||||
span:
|
||||
isWideField(key) ||
|
||||
isFileField(key) ||
|
||||
isImageField(key) ||
|
||||
Array.isArray(normalizeDetailValue(key, source[key])) ||
|
||||
isPlainObject(normalizeDetailValue(key, source[key]))
|
||||
? 2
|
||||
: 1
|
||||
}));
|
||||
});
|
||||
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
const loadDetail = async () => {
|
||||
if (!props.visible) {
|
||||
return;
|
||||
}
|
||||
detail.value = {};
|
||||
loadFailed.value = false;
|
||||
|
||||
if (!props.pk || !currentConfig.value) {
|
||||
if (props.module && !currentConfig.value) {
|
||||
loadFailed.value = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
detail.value = (await currentConfig.value.fetcher(props.pk)) || {};
|
||||
} catch (e: any) {
|
||||
loadFailed.value = true;
|
||||
message.error(e.message || '数据加载失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [props.visible, props.module, props.pk],
|
||||
([visible]) => {
|
||||
if (visible) {
|
||||
loadDetail();
|
||||
} else {
|
||||
detail.value = {};
|
||||
loadFailed.value = false;
|
||||
loading.value = false;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.markdown-body) {
|
||||
background: #fff;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
}
|
||||
|
||||
.multiline-text {
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.rich-text-content {
|
||||
min-height: 32px;
|
||||
line-height: 1.8;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.rich-text-content:deep(p:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.array-entry {
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.array-entry + .array-entry {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.array-entry-index {
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
.array-entry-line {
|
||||
line-height: 1.8;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.file-entry + .file-entry {
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user