<template>
|
<slot>
|
<el-button v-if="showBtn" @click="open()">{{ btnText }}</el-button>
|
</slot>
|
|
<cl-dialog
|
v-model="visible"
|
:height="height"
|
:width="width"
|
:title="title"
|
scrollbar
|
append-to-body
|
>
|
<div class="cl-editor-preview">
|
<el-tabs v-if="list.length > 1" v-model="active" type="card" @tab-change="onTabChange">
|
<el-tab-pane
|
v-for="(item, index) in list"
|
:key="index"
|
:label="item.name"
|
:name="index"
|
:lazy="index != 0"
|
/>
|
</el-tabs>
|
|
<div class="cl-editor-preview__container">
|
<slot name="prepend"></slot>
|
|
<cl-editor
|
:ref="setRefs('editor')"
|
:key="active"
|
v-bind="editConfig"
|
v-model="content"
|
:name="`cl-editor-${name}`"
|
height="100%"
|
preview
|
v-if="name"
|
/>
|
|
<div class="content" v-else>{{ content }}</div>
|
|
<slot name="append"></slot>
|
</div>
|
</div>
|
|
<template #footer>
|
<el-button @click="close">{{ $t('关闭') }}</el-button>
|
<el-button v-if="isCopy" type="success" @click="toCopy">{{ $t('复制') }}</el-button>
|
</template>
|
</cl-dialog>
|
</template>
|
|
<script lang="ts" setup>
|
defineOptions({
|
name: 'cl-editor-preview'
|
});
|
|
import { useClipboard } from '@vueuse/core';
|
import { ElMessage } from 'element-plus';
|
import { isObject, isString } from 'lodash-es';
|
import { nextTick, ref, computed, type PropType } from 'vue';
|
import { useCool } from '/@/cool';
|
import { CrudProps } from '/#/crud';
|
import { useI18n } from 'vue-i18n';
|
|
interface TabItem {
|
name: string;
|
data: string;
|
language: string;
|
}
|
|
const props = defineProps({
|
...CrudProps,
|
modelValue: String,
|
title: String,
|
name: String,
|
text: String,
|
type: {
|
type: String as PropType<'code' | 'text'>,
|
default: 'text'
|
},
|
showBtn: {
|
type: Boolean,
|
default: true
|
},
|
height: {
|
type: String,
|
default: '60vh'
|
},
|
width: {
|
type: String,
|
default: '60%'
|
},
|
formatter: Function,
|
// 多个内容展示
|
tabs: Array as PropType<TabItem[]>,
|
// 组件参数
|
props: Object
|
});
|
|
const { refs, setRefs } = useCool();
|
const { copy } = useClipboard();
|
const { t } = useI18n();
|
|
// 是否可见
|
const visible = ref(false);
|
|
// 内容
|
const content = ref('');
|
|
// 语言
|
const language = ref();
|
|
// 激活的标签
|
const active = ref(0);
|
|
// 列表
|
const list = ref<TabItem[]>([]);
|
|
// 是否代码预览
|
const isCode = computed(() => {
|
return props.type == 'code';
|
});
|
|
// 是否可以复制
|
const isCopy = computed(() => isCode);
|
|
// 编辑器配置
|
const editConfig = computed(() => {
|
return {
|
language: language.value,
|
...props.props
|
};
|
});
|
|
// 标题
|
const title = computed(() => {
|
return props.title || (isCode.value ? t('代码预览') : t('文本预览'));
|
});
|
|
// 按钮
|
const btnText = computed(() => {
|
return props.text || t('点击查看');
|
});
|
|
// 打开
|
async function open(data?: string | TabItem[]) {
|
if (!data) {
|
data = props.modelValue;
|
}
|
|
if (props.formatter) {
|
data = props.formatter(data);
|
}
|
|
if (props.tabs) {
|
list.value = props.tabs;
|
onTabChange(0);
|
} else {
|
setContent(data);
|
}
|
|
visible.value = true;
|
}
|
|
// 设置内容
|
function setContent(val: any) {
|
if (isString(val)) {
|
content.value = val;
|
} else if (isObject(val)) {
|
content.value = JSON.stringify(val, null, 4);
|
}
|
}
|
|
// 切换
|
async function onTabChange(index: any) {
|
const item = list.value[index];
|
|
// 设置语言
|
language.value = item.language;
|
|
// 设置
|
setContent(item.data);
|
|
await nextTick();
|
|
// 格式化代码
|
if (isCode.value) {
|
refs.editor?.formatCode?.();
|
}
|
}
|
|
// 关闭
|
function close() {
|
visible.value = false;
|
}
|
|
// 复制
|
function toCopy() {
|
copy(content.value);
|
ElMessage.success(t('复制成功'));
|
}
|
|
defineExpose({
|
open,
|
close
|
});
|
</script>
|
|
<style lang="scss" scoped>
|
.cl-editor-preview {
|
display: flex;
|
flex-direction: column;
|
height: 100%;
|
|
:deep(img) {
|
max-width: 100%;
|
}
|
|
&__container {
|
flex: 1;
|
|
.content {
|
white-space: pre-wrap;
|
background-color: var(--el-fill-color-light);
|
border-radius: 8px;
|
padding: 10px;
|
}
|
}
|
}
|
</style>
|