feat: 新增物模型查看组件并优化产品详情页面
- 新增 MonacoEditor 组件用于代码编辑 - 新增 TSLViewer 组件用于查看物模型 - 更新 BasicInfo、DeviceAccess 和 Metadata 组件的显示逻辑 - 调整 MetadataTable 组件的列宽 - 在 package.json 中添加 monaco-editor 依赖 - 在 vite.config.mts 中添加 monaco-editor 插件配置
This commit is contained in:
parent
0c0a1994b2
commit
60e3f4fc73
|
@ -50,9 +50,11 @@
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"monaco-editor": "^0.52.2",
|
||||||
"pinia": "catalog:",
|
"pinia": "catalog:",
|
||||||
"tinymce": "^7.3.0",
|
"tinymce": "^7.3.0",
|
||||||
"unplugin-vue-components": "^0.27.3",
|
"unplugin-vue-components": "^0.27.3",
|
||||||
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
"vue": "catalog:",
|
"vue": "catalog:",
|
||||||
"vue-router": "catalog:",
|
"vue-router": "catalog:",
|
||||||
"vue3-colorpicker": "^2.3.0"
|
"vue3-colorpicker": "^2.3.0"
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref, watch, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: { type: [String, Number], default: '' },
|
||||||
|
theme: { type: String, default: 'vs-dark' },
|
||||||
|
language: { type: String, default: 'json' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
|
const dom = ref();
|
||||||
|
|
||||||
|
let instance;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const _model = monaco.editor.createModel(props.modelValue, props.language);
|
||||||
|
|
||||||
|
instance = monaco.editor.create(dom.value, {
|
||||||
|
model: _model,
|
||||||
|
tabSize: 2,
|
||||||
|
automaticLayout: true,
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
theme: props.theme, // 主题色: vs(默认高亮), vs-dark(黑色), hc-black(高亮黑色)
|
||||||
|
formatOnPaste: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
instance.onDidChangeModelContent(() => {
|
||||||
|
const value = instance.getValue();
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码格式化
|
||||||
|
*/
|
||||||
|
const editorFormat = () => {
|
||||||
|
if (!instance) return;
|
||||||
|
instance.getAction('editor.action.formatDocument')?.run();
|
||||||
|
};
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
editorFormat();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 光标位置插入内容
|
||||||
|
* @param {string} val
|
||||||
|
*/
|
||||||
|
const insert = (val) => {
|
||||||
|
if (!instance) return;
|
||||||
|
const position = instance.getPosition();
|
||||||
|
instance.executeEdits(instance.getValue(), [
|
||||||
|
{
|
||||||
|
range: new monaco.Range(
|
||||||
|
position?.lineNumber,
|
||||||
|
position?.column,
|
||||||
|
position?.lineNumber,
|
||||||
|
position?.column,
|
||||||
|
),
|
||||||
|
text: val,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
if (!instance) return;
|
||||||
|
// setValue之前获取光标位置
|
||||||
|
const position = instance.getPosition();
|
||||||
|
// setValue之后光标位置改变
|
||||||
|
instance.setValue(val);
|
||||||
|
// 设置光标位置为setValue之前的位置
|
||||||
|
instance.setPosition(position);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
editorFormat,
|
||||||
|
insert,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="editor" ref="dom"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.editor {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -114,13 +114,13 @@ const productParams = computed(() =>
|
||||||
)?.label
|
)?.label
|
||||||
}}
|
}}
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
<DescriptionsItem label="接入方式">
|
<DescriptionsItem label="接入网关">
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
@click="handleAccessConfig"
|
@click="handleAccessConfig"
|
||||||
v-access:code="['device:product:edit']"
|
v-access:code="['device:product:edit']"
|
||||||
>
|
>
|
||||||
{{ productInfo.provider || '配置接入方式' }}
|
{{ productInfo.gatewayName || '配置接入网关' }}
|
||||||
</Button>
|
</Button>
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
<DescriptionsItem label="创建时间">
|
<DescriptionsItem label="创建时间">
|
||||||
|
|
|
@ -86,10 +86,10 @@ const tableColumns = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Markdown转HTML
|
// Markdown转HTML
|
||||||
const markdownToHtml = computed(() => {
|
// const markdownToHtml = computed(() => {
|
||||||
// 这里可以使用markdown-it等库转换markdown为HTML
|
// // 这里可以使用markdown-it等库转换markdown为HTML
|
||||||
return accessInfo.value.document || '';
|
// return accessInfo.value.document || '';
|
||||||
});
|
// });
|
||||||
|
|
||||||
// 选择接入方式
|
// 选择接入方式
|
||||||
const handleSelectAccess = () => {
|
const handleSelectAccess = () => {
|
||||||
|
@ -141,7 +141,8 @@ const handleSave = async () => {
|
||||||
saveLoading.value = true;
|
saveLoading.value = true;
|
||||||
await productUpdateById(props.productInfo.id, {
|
await productUpdateById(props.productInfo.id, {
|
||||||
id: props.productInfo.id,
|
id: props.productInfo.id,
|
||||||
provider: accessInfo.value.id,
|
provider: accessInfo.value.provider,
|
||||||
|
gatewayId: accessInfo.value.id,
|
||||||
storePolicy: selectedStorePolicy.value,
|
storePolicy: selectedStorePolicy.value,
|
||||||
protocolConf: JSON.stringify(formData),
|
protocolConf: JSON.stringify(formData),
|
||||||
});
|
});
|
||||||
|
@ -165,9 +166,9 @@ const loadStorePolicy = async () => {
|
||||||
|
|
||||||
// 加载接入信息
|
// 加载接入信息
|
||||||
const loadAccessInfo = async () => {
|
const loadAccessInfo = async () => {
|
||||||
if (props.productInfo.provider) {
|
if (props.productInfo.gatewayId) {
|
||||||
// 这里调用API加载接入信息
|
// 这里调用API加载接入信息
|
||||||
const res = await gatewayInfo(props.productInfo.provider);
|
const res = await gatewayInfo(props.productInfo.gatewayId);
|
||||||
accessInfo.value = res;
|
accessInfo.value = res;
|
||||||
|
|
||||||
// 模拟数据
|
// 模拟数据
|
||||||
|
@ -214,7 +215,7 @@ onMounted(() => {
|
||||||
<a-button type="link" @click="handleSelectAccess">选择</a-button>
|
<a-button type="link" @click="handleSelectAccess">选择</a-button>
|
||||||
设备接入网关,用以提供设备接入能力
|
设备接入网关,用以提供设备接入能力
|
||||||
</span>
|
</span>
|
||||||
<span v-else>请联系管理员配置产品接入方式</span>
|
<span v-else>请联系管理员配置产品接入网关</span>
|
||||||
</template>
|
</template>
|
||||||
</Empty>
|
</Empty>
|
||||||
</div>
|
</div>
|
||||||
|
@ -226,7 +227,7 @@ onMounted(() => {
|
||||||
<!-- 接入方式 -->
|
<!-- 接入方式 -->
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h4>接入方式</h4>
|
<h4>接入网关</h4>
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -247,13 +248,14 @@ onMounted(() => {
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h4>消息协议</h4>
|
<h4>消息协议</h4>
|
||||||
<Tooltip title="此配置来自于产品接入方式所选择的协议">
|
<Tooltip title="此配置来自于产品接入网关所选择的协议">
|
||||||
<QuestionCircleOutlined />
|
<QuestionCircleOutlined />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-content">
|
<div class="section-content">
|
||||||
<p>{{ accessInfo.protocolName }}</p>
|
<p>{{ accessInfo.protocolName }}</p>
|
||||||
<div v-if="accessInfo.document" v-html="markdownToHtml"></div>
|
<!-- <div v-if="accessInfo.document" v-html="markdownToHtml"></div>-->
|
||||||
|
<div>{{markdownToHtml}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -311,7 +313,7 @@ onMounted(() => {
|
||||||
>
|
>
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h4>{{ config.name }}</h4>
|
<h4>{{ config.name }}</h4>
|
||||||
<Tooltip title="此配置来自于产品接入方式所选择的协议">
|
<Tooltip title="此配置来自于产品接入网关所选择的协议">
|
||||||
<QuestionCircleOutlined />
|
<QuestionCircleOutlined />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -453,7 +455,7 @@ onMounted(() => {
|
||||||
<!-- 选择接入方式抽屉 -->
|
<!-- 选择接入方式抽屉 -->
|
||||||
<Modal
|
<Modal
|
||||||
:open="accessModalVisible"
|
:open="accessModalVisible"
|
||||||
title="选择接入方式"
|
title="选择设备接入网关"
|
||||||
width="1000px"
|
width="1000px"
|
||||||
centered
|
centered
|
||||||
@cancel="handleAccessModalClose"
|
@cancel="handleAccessModalClose"
|
||||||
|
|
|
@ -307,7 +307,7 @@ watch(
|
||||||
<Space>
|
<Space>
|
||||||
<Button @click="handleDrawerClose">取消</Button>
|
<Button @click="handleDrawerClose">取消</Button>
|
||||||
<Button type="primary" @click="handleSave" :loading="saveLoading">
|
<Button type="primary" @click="handleSave" :loading="saveLoading">
|
||||||
保存
|
确认
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -26,9 +26,17 @@ const emit = defineEmits<{
|
||||||
refresh: [];
|
refresh: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const defaultMetadata = {
|
||||||
|
properties: [],
|
||||||
|
functions: [],
|
||||||
|
events: [],
|
||||||
|
propertyGroups: [], // 新增属性分组
|
||||||
|
};
|
||||||
|
|
||||||
const activeTab = ref('properties');
|
const activeTab = ref('properties');
|
||||||
const importVisible = ref(false);
|
const importVisible = ref(false);
|
||||||
const tslVisible = ref(false);
|
const tslVisible = ref(false);
|
||||||
|
const tslMetadata = ref<any>();
|
||||||
const showReset = ref(false);
|
const showReset = ref(false);
|
||||||
|
|
||||||
// 物模型编辑状态
|
// 物模型编辑状态
|
||||||
|
@ -71,6 +79,7 @@ const handleImport = () => {
|
||||||
|
|
||||||
// 查看TSL
|
// 查看TSL
|
||||||
const handleViewTSL = () => {
|
const handleViewTSL = () => {
|
||||||
|
tslMetadata.value = JSON.parse(JSON.stringify(currentMetadata.value));
|
||||||
tslVisible.value = true;
|
tslVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,6 +97,7 @@ const handleImportClose = () => {
|
||||||
|
|
||||||
// 关闭TSL抽屉
|
// 关闭TSL抽屉
|
||||||
const handleTSLClose = () => {
|
const handleTSLClose = () => {
|
||||||
|
tslMetadata.value = JSON.parse(JSON.stringify(defaultMetadata));
|
||||||
tslVisible.value = false;
|
tslVisible.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -220,7 +230,7 @@ const handleSave = async () => {
|
||||||
metadata: JSON.stringify(metadata),
|
metadata: JSON.stringify(metadata),
|
||||||
});
|
});
|
||||||
|
|
||||||
message.success('保存成功');
|
// message.success('保存成功');
|
||||||
// 更新原始数据
|
// 更新原始数据
|
||||||
originalMetadata.value = JSON.parse(JSON.stringify(metadata));
|
originalMetadata.value = JSON.parse(JSON.stringify(metadata));
|
||||||
metadataChanged.value = false;
|
metadataChanged.value = false;
|
||||||
|
@ -320,7 +330,7 @@ loadMetadata();
|
||||||
@click="handleViewTSL"
|
@click="handleViewTSL"
|
||||||
v-access:code="['device:product:edit']"
|
v-access:code="['device:product:edit']"
|
||||||
>
|
>
|
||||||
物模型TSL
|
物模型
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
|
@ -379,11 +389,15 @@ loadMetadata();
|
||||||
<!-- TSL查看抽屉 -->
|
<!-- TSL查看抽屉 -->
|
||||||
<Drawer
|
<Drawer
|
||||||
v-model:open="tslVisible"
|
v-model:open="tslVisible"
|
||||||
title="物模型TSL"
|
title="物模型"
|
||||||
width="800px"
|
width="800px"
|
||||||
@close="handleTSLClose"
|
@close="handleTSLClose"
|
||||||
>
|
>
|
||||||
<TSLViewer :product-id="productInfo.id" />
|
<TSLViewer
|
||||||
|
:product-id="productInfo.id"
|
||||||
|
:product-info="productInfo"
|
||||||
|
:metadata="tslMetadata"
|
||||||
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -91,7 +91,7 @@ const columns = computed(() => {
|
||||||
dataIndex: 'dataType',
|
dataIndex: 'dataType',
|
||||||
key: 'dataType',
|
key: 'dataType',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 100,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '读写类型',
|
title: '读写类型',
|
||||||
|
|
|
@ -431,7 +431,7 @@ watch(
|
||||||
<Space>
|
<Space>
|
||||||
<Button @click="handleDrawerClose">取消</Button>
|
<Button @click="handleDrawerClose">取消</Button>
|
||||||
<Button type="primary" @click="handleSave" :loading="saveLoading">
|
<Button type="primary" @click="handleSave" :loading="saveLoading">
|
||||||
保存
|
确认
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -257,7 +257,7 @@ const handleClose = () => {
|
||||||
<Space>
|
<Space>
|
||||||
<Button @click="handleClose">取消</Button>
|
<Button @click="handleClose">取消</Button>
|
||||||
<Button type="primary" :loading="saveLoading" @click="handleSave">
|
<Button type="primary" :loading="saveLoading" @click="handleSave">
|
||||||
确定
|
确认
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CopyOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
import { message, Space } from 'ant-design-vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
import MonacoEditor from '#/components/MonacoEditor/index.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
metadata: object;
|
||||||
|
productId: string;
|
||||||
|
productInfo: object;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
properties: [],
|
||||||
|
functions: [],
|
||||||
|
events: [],
|
||||||
|
propertyGroups: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 导出组件
|
||||||
|
defineExpose({});
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const exportLoading = ref(false);
|
||||||
|
const exportFormat = ref('json');
|
||||||
|
const exportFileName = ref('');
|
||||||
|
|
||||||
|
const tslJson = ref({
|
||||||
|
properties: [],
|
||||||
|
functions: [],
|
||||||
|
events: [],
|
||||||
|
propertyGroups: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载TSL数据
|
||||||
|
const loadTSLData = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
// tslJson.value = JSON.stringify(mockData, null, 2);
|
||||||
|
tslJson.value = JSON.stringify(props.metadata || mockData, null, 2);
|
||||||
|
} catch {
|
||||||
|
message.error('加载物模型数据失败');
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新
|
||||||
|
const handleRefresh = () => {
|
||||||
|
loadTSLData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const handleExport = () => {
|
||||||
|
const time = dayjs().format('YYYY-MM-DD_HH:mm:ss');
|
||||||
|
exportFileName.value = `${props.productInfo.productName}_物模型_${time}`;
|
||||||
|
handleExportConfirm();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 复制
|
||||||
|
const handleCopy = async () => {
|
||||||
|
try {
|
||||||
|
const text = tslJson.value;
|
||||||
|
// await navigator.clipboard.writeText(text);
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
await navigator.clipboard.writeText(text); // 现代 API
|
||||||
|
} else {
|
||||||
|
// 降级方案
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = text;
|
||||||
|
document.body.append(textarea);
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
textarea.remove();
|
||||||
|
}
|
||||||
|
message.success('复制成功');
|
||||||
|
} catch {
|
||||||
|
message.error('复制失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认导出
|
||||||
|
const handleExportConfirm = async () => {
|
||||||
|
try {
|
||||||
|
exportLoading.value = true;
|
||||||
|
|
||||||
|
const blob = new Blob([tslJson.value], { type: 'text/plain' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = `${exportFileName.value}.${exportFormat.value}`;
|
||||||
|
document.body.append(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
message.success('导出成功');
|
||||||
|
} catch {
|
||||||
|
message.error('导出失败');
|
||||||
|
} finally {
|
||||||
|
exportLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听metadata变化
|
||||||
|
watch(
|
||||||
|
() => props.metadata,
|
||||||
|
() => {
|
||||||
|
loadTSLData();
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadTSLData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="tsl-viewer">
|
||||||
|
<div class="viewer-header">
|
||||||
|
<Space>
|
||||||
|
<a-button @click="handleRefresh" :loading="loading">
|
||||||
|
<template #icon>
|
||||||
|
<ReloadOutlined />
|
||||||
|
</template>
|
||||||
|
刷新
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="handleCopy">
|
||||||
|
<template #icon>
|
||||||
|
<CopyOutlined />
|
||||||
|
</template>
|
||||||
|
复制
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="handleExport" type="primary">
|
||||||
|
<template #icon>
|
||||||
|
<DownloadOutlined />
|
||||||
|
</template>
|
||||||
|
导出
|
||||||
|
</a-button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
<div class="viewer-content-tips">
|
||||||
|
物模型是对设备在云端的功能描述,包括设备的属性、服务、事件和属性分组,物联网平台通过定义一种物的描述语言来描述物模型,称之为
|
||||||
|
TSL(即 Thing Specification Language),采用 JSON 格式,您可以根据 TSL
|
||||||
|
组装上报设备的数据。您可以导出完整物模型,用于云端应用开发。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="viewer-content">
|
||||||
|
<div class="viewer-toolbar">
|
||||||
|
<MonacoEditor
|
||||||
|
v-model="tslJson"
|
||||||
|
lang="javascript"
|
||||||
|
style="height: 100%"
|
||||||
|
theme="vs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tsl-viewer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.viewer-header {
|
||||||
|
padding-bottom: 12px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-content-tips {
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.viewer-toolbar {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-viewer {
|
||||||
|
.code-textarea {
|
||||||
|
font-family: Monaco, Menlo, 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
border: 1px solid #e1e4e8;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3,12 +3,14 @@ import { defineConfig } from '@vben/vite-config';
|
||||||
// 自行取消注释来启用按需导入功能
|
// 自行取消注释来启用按需导入功能
|
||||||
// import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
|
// import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
|
||||||
// import Components from 'unplugin-vue-components/vite';
|
// import Components from 'unplugin-vue-components/vite';
|
||||||
|
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
|
||||||
|
|
||||||
export default defineConfig(async () => {
|
export default defineConfig(async () => {
|
||||||
return {
|
return {
|
||||||
application: {},
|
application: {},
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
(monacoEditorPlugin as any).default({}),
|
||||||
// Components({
|
// Components({
|
||||||
// dirs: [], // 默认会导入src/components目录下所有组件 不需要
|
// dirs: [], // 默认会导入src/components目录下所有组件 不需要
|
||||||
// dts: './types/components.d.ts', // 输出类型文件
|
// dts: './types/components.d.ts', // 输出类型文件
|
||||||
|
|
Loading…
Reference in New Issue