740 lines
21 KiB
Vue
740 lines
21 KiB
Vue
<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>
|