feat: 批量动态表单业务组件&卡片组件&代码编辑器组件&其他组件调整
This commit is contained in:
parent
dfea1f83f9
commit
9dc0ca96f8
|
@ -7,6 +7,7 @@ import configArcoStyleImportPlugin from './plugin/arcoStyleImport';
|
||||||
import configArcoResolverPlugin from './plugin/arcoResolver';
|
import configArcoResolverPlugin from './plugin/arcoResolver';
|
||||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||||
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
|
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
|
||||||
|
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -22,6 +23,7 @@ export default defineConfig({
|
||||||
// 指定symbolId格式
|
// 指定symbolId格式
|
||||||
symbolId: 'icon-[name]',
|
symbolId: 'icon-[name]',
|
||||||
}),
|
}),
|
||||||
|
monacoEditorPlugin({}),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: [
|
alias: [
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
<template>
|
||||||
|
<a-form ref="formRef" :model="form" layout="vertical">
|
||||||
|
<div class="mb-[16px] overflow-y-auto rounded-[4px] bg-[var(--color-fill-1)] p-[12px]">
|
||||||
|
<a-scrollbar class="overflow-y-auto" :style="{ 'max-height': props.maxHeight }">
|
||||||
|
<div class="flex flex-wrap items-start justify-between gap-[8px]">
|
||||||
|
<template v-for="(order, i) of form.list" :key="`form-item-${order}`">
|
||||||
|
<div class="flex w-full items-start justify-between gap-[8px]">
|
||||||
|
<a-form-item
|
||||||
|
v-for="item of props.models"
|
||||||
|
:key="`${item.filed}${order}`"
|
||||||
|
:field="`${item.filed}${order}`"
|
||||||
|
:class="i > 0 ? 'hidden-item' : 'mb-0 flex-1'"
|
||||||
|
:label="i === 0 && item.label ? t(item.label) : ''"
|
||||||
|
:rules="item.rules"
|
||||||
|
asterisk-position="end"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-if="item.type === 'input'"
|
||||||
|
v-model="form[`${item.filed}${order}`]"
|
||||||
|
class="mb-[4px] flex-1"
|
||||||
|
:placeholder="t(item.placeholder || '')"
|
||||||
|
:max-length="item.maxLength || 250"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
<a-input-number
|
||||||
|
v-if="item.type === 'inputNumber'"
|
||||||
|
v-model="form[`${item.filed}${order}`]"
|
||||||
|
class="mb-[4px] flex-1"
|
||||||
|
:placeholder="t(item.placeholder || '')"
|
||||||
|
:min="item.min"
|
||||||
|
:max="item.max || 9999999"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<div
|
||||||
|
v-show="form.list.length > 1"
|
||||||
|
:class="[
|
||||||
|
'flex',
|
||||||
|
'h-full',
|
||||||
|
'w-[32px]',
|
||||||
|
'cursor-pointer',
|
||||||
|
'items-center',
|
||||||
|
'justify-center',
|
||||||
|
'text-[var(--color-text-brand)]',
|
||||||
|
i === 0 ? 'mt-[36px]' : 'mt-[5px]',
|
||||||
|
]"
|
||||||
|
@click="removeField(order, i)"
|
||||||
|
>
|
||||||
|
<icon-minus-circle />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</a-scrollbar>
|
||||||
|
<div v-if="props.formMode === 'create'" class="w-full">
|
||||||
|
<a-button class="px-0" type="text" @click="addField">
|
||||||
|
<template #icon>
|
||||||
|
<icon-plus class="text-[14px]" />
|
||||||
|
</template>
|
||||||
|
{{ t(props.addText) }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watchEffect } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import type { ValidatedError, FormInstance } from '@arco-design/web-vue';
|
||||||
|
import type { FormItemModel, FormMode, ValueType } from './types';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
models: FormItemModel[];
|
||||||
|
formMode: FormMode;
|
||||||
|
addText: string;
|
||||||
|
maxHeight?: string;
|
||||||
|
valueType?: ValueType;
|
||||||
|
delimiter?: string; // 当valueType为 string 类型时的分隔符,默认为英文逗号,
|
||||||
|
defaultVals?: Record<string, string | string[] | number[]>; // 当外层是编辑状态时,可传入已填充的数据
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
valueType: 'Array',
|
||||||
|
delimiter: ',',
|
||||||
|
maxHeight: '30vh',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultForm = {
|
||||||
|
list: [0],
|
||||||
|
};
|
||||||
|
const form = ref<Record<string, any>>({ ...defaultForm });
|
||||||
|
const formRef = ref<FormInstance | null>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监测defaultVals和models的变化
|
||||||
|
* 初始化时通过models创建初始化表单
|
||||||
|
* 若defaultVals变化,则说明当前是填充模式,将清空之前的表单项,填充传入的数据(一般是表单编辑的时候)
|
||||||
|
*/
|
||||||
|
watchEffect(() => {
|
||||||
|
props.models.forEach((e) => {
|
||||||
|
form.value[`${e.filed}0`] = e.type === 'inputNumber' ? null : '';
|
||||||
|
});
|
||||||
|
if (props.defaultVals) {
|
||||||
|
// 重置表单,因为组件初始化后可能输入过值或创建过表单项
|
||||||
|
form.value = { list: [0] };
|
||||||
|
// 取出defaultVals的表单 filed
|
||||||
|
const arr = Object.keys(props.defaultVals);
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
const filed = arr[i];
|
||||||
|
// 取出当前 filed 的默认值
|
||||||
|
const dVals = props.defaultVals[filed];
|
||||||
|
// 判断默认值为数组还是字符串,字符串需要根据传入的分隔符delimiter分割
|
||||||
|
const vals = Array.isArray(dVals) ? dVals : dVals.split(`${props.delimiter}`);
|
||||||
|
// 遍历当前 filed 的默认值数组,填充至表单对象
|
||||||
|
vals.forEach((val, order) => {
|
||||||
|
form.value[`${filed}${order}`] = val;
|
||||||
|
if (i === 0 && order > 0) {
|
||||||
|
// 行数只需要遍历一次字段的值数组长度即可
|
||||||
|
form.value.list.push(order);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getFormResult() {
|
||||||
|
const res: Record<string, any> = {};
|
||||||
|
props.models.forEach((e) => {
|
||||||
|
res[e.filed] = [];
|
||||||
|
});
|
||||||
|
form.value.list.forEach((e: number) => {
|
||||||
|
props.models.forEach((m) => {
|
||||||
|
res[m.filed].push(form.value[`${m.filed}${e}`]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发表单校验
|
||||||
|
* @param cb 校验通过后执行回调
|
||||||
|
* @param isSubmit 是否需要将表单值拼接后传入回调函数
|
||||||
|
*/
|
||||||
|
function formValidate(cb: (res?: Record<string, string[] | string>) => void, isSubmit = true) {
|
||||||
|
formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
|
||||||
|
if (errors) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof cb === 'function') {
|
||||||
|
if (isSubmit) {
|
||||||
|
const res = getFormResult();
|
||||||
|
cb(props.valueType === 'Array' ? res : res.join(','));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加表单项
|
||||||
|
*/
|
||||||
|
function addField() {
|
||||||
|
formValidate(() => {
|
||||||
|
const lastIndex = form.value.list.length - 1;
|
||||||
|
const lastOrder = form.value.list[lastIndex] + 1;
|
||||||
|
form.value.list.push(lastOrder); // 序号自增,不会因为删除而重复
|
||||||
|
props.models.forEach((e) => {
|
||||||
|
form.value[`${e.filed}${lastOrder}`] = e.type === 'inputNumber' ? null : '';
|
||||||
|
});
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除表单项
|
||||||
|
* @param index 表单项的序号
|
||||||
|
* @param i 表单项对应 list 的下标
|
||||||
|
*/
|
||||||
|
function removeField(index: number, i: number) {
|
||||||
|
props.models.forEach((e) => {
|
||||||
|
delete form.value[`${e.filed}${index}`];
|
||||||
|
});
|
||||||
|
form.value.list.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
formValidate,
|
||||||
|
getFormResult,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { FieldRule } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
export type FormItemType = 'input' | 'select' | 'inputNumber';
|
||||||
|
export type FormMode = 'create' | 'edit';
|
||||||
|
export type ValueType = 'Array' | 'string';
|
||||||
|
|
||||||
|
export interface FormItemModel {
|
||||||
|
filed: string;
|
||||||
|
type: FormItemType;
|
||||||
|
rules?: FieldRule[];
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
maxLength?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const _default: import('vue').DefineComponent<
|
||||||
|
{
|
||||||
|
models: FormItemModel[];
|
||||||
|
formMode: FormMode;
|
||||||
|
addText: string;
|
||||||
|
maxHeight?: string;
|
||||||
|
valueType?: ValueType;
|
||||||
|
delimiter?: string; // 当valueType为 string 类型时的分隔符,默认为英文逗号,
|
||||||
|
defaultVals?: Record<string, string[] | string>; // 当外层是编辑状态时,可传入已填充的数据
|
||||||
|
},
|
||||||
|
unknown,
|
||||||
|
import('vue').ComponentOptionsMixin,
|
||||||
|
import('vue').ComponentOptionsMixin,
|
||||||
|
{
|
||||||
|
formValidate: (cb: (res?: Record<string, string[] | string>) => void, isSubmit = true) => void;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export declare type MsBatchFormInstance = InstanceType<typeof _default>;
|
|
@ -4,19 +4,24 @@
|
||||||
<div class="back-btn" @click="back"><icon-arrow-left /></div>
|
<div class="back-btn" @click="back"><icon-arrow-left /></div>
|
||||||
<div class="text-[var(--color-text-000)]">{{ props.title }}</div>
|
<div class="text-[var(--color-text-000)]">{{ props.title }}</div>
|
||||||
</div>
|
</div>
|
||||||
<a-divider />
|
<a-divider class="my-[16px]" />
|
||||||
<a-scrollbar class="mt-[16px]" style="overflow-y: auto; height: calc(100vh - 264px)">
|
<a-scrollbar class="mt-[16px]" style="overflow-y: auto; height: calc(100vh - 256px)">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</a-scrollbar>
|
</a-scrollbar>
|
||||||
<div
|
<div
|
||||||
v-if="!hideFooter"
|
v-if="!hideFooter"
|
||||||
class="m-[0_-24px_-24px] flex justify-end gap-[16px] p-[24px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]"
|
class="relative z-10 m-[0_-24px_-24px] flex justify-end gap-[16px] p-[24px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]"
|
||||||
>
|
>
|
||||||
<a-button type="secondary" @click="back">{{ t('mscard.defaultCancelText') }}</a-button>
|
<div class="ml-0 mr-auto">
|
||||||
<a-button v-if="!props.hideContinue" type="secondary" @click="emit('saveAndContinue')">
|
<slot name="footerLeft"></slot>
|
||||||
{{ t('mscard.defaultSaveAndContinueText') }}
|
</div>
|
||||||
</a-button>
|
<slot name="footerRight">
|
||||||
<a-button type="primary" @click="emit('save')">{{ t('mscard.defaultConfirm') }}</a-button>
|
<a-button type="secondary" @click="back">{{ t('mscard.defaultCancelText') }}</a-button>
|
||||||
|
<a-button v-if="!props.hideContinue" type="secondary" @click="emit('saveAndContinue')">
|
||||||
|
{{ t('mscard.defaultSaveAndContinueText') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" @click="emit('save')">{{ t('mscard.defaultConfirm') }}</a-button>
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -64,7 +69,6 @@
|
||||||
background: linear-gradient(90deg, rgb(var(--primary-9)) 3.36%, #ffffff 100%);
|
background: linear-gradient(90deg, rgb(var(--primary-9)) 3.36%, #ffffff 100%);
|
||||||
box-shadow: 0 0 7px rgb(15 0 78 / 9%);
|
box-shadow: 0 0 7px rgb(15 0 78 / 9%);
|
||||||
.arco-icon {
|
.arco-icon {
|
||||||
font-size: 20px !important;
|
|
||||||
color: rgb(var(--primary-5));
|
color: rgb(var(--primary-5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
<template>
|
||||||
|
<div ref="fullRef" class="rounded-[4px] bg-[var(--color-fill-1)] p-[12px]">
|
||||||
|
<div class="mb-[12px] flex justify-between pr-[12px]">
|
||||||
|
<slot name="title">
|
||||||
|
<span class="font-medium">{{ title }}</span>
|
||||||
|
</slot>
|
||||||
|
<div class="w-[96px] cursor-pointer text-right !text-[var(--color-text-4)]" @click="toggle">
|
||||||
|
<MsIcon v-if="isFullscreen" type="icon-icon_minify_outlined" />
|
||||||
|
<MsIcon v-else type="icon-icon_magnify_outlined" />
|
||||||
|
{{ t('msCodeEditor.fullScreen') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="codeEditBox" :class="['ms-code-editor', isFullscreen ? 'ms-code-editor-full-screen' : '']"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
|
import { editorProps, CustomeTheme } from './types';
|
||||||
|
import './userWorker';
|
||||||
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
|
import { useFullscreen } from '@vueuse/core';
|
||||||
|
import MsCodeEditorTheme from './themes';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'MonacoEditor',
|
||||||
|
props: editorProps,
|
||||||
|
emits: ['update:modelValue', 'change', 'editorMounted'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { t } = useI18n();
|
||||||
|
let editor: monaco.editor.IStandaloneCodeEditor;
|
||||||
|
const codeEditBox = ref();
|
||||||
|
const fullRef = ref<HTMLElement | null>();
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
// 注册自定义主题
|
||||||
|
if (MsCodeEditorTheme[props.theme as CustomeTheme]) {
|
||||||
|
monaco.editor.defineTheme(props.theme, MsCodeEditorTheme[props.theme as CustomeTheme]);
|
||||||
|
}
|
||||||
|
editor = monaco.editor.create(codeEditBox.value, {
|
||||||
|
value: props.modelValue,
|
||||||
|
automaticLayout: true,
|
||||||
|
...props,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听值的变化
|
||||||
|
editor.onDidBlurEditorText(() => {
|
||||||
|
const value = editor.getValue(); // 给父组件实时返回最新文本
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
emit('change', value);
|
||||||
|
});
|
||||||
|
|
||||||
|
emit('editorMounted', editor);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setEditBoxBg = () => {
|
||||||
|
const codeBgEl = document.querySelector('.monaco-editor-background');
|
||||||
|
if (codeBgEl) {
|
||||||
|
// 获取计算后的样式对象
|
||||||
|
const computedStyle = window.getComputedStyle(codeBgEl);
|
||||||
|
|
||||||
|
// 获取背景颜色
|
||||||
|
const { backgroundColor } = computedStyle;
|
||||||
|
codeEditBox.value.style.backgroundColor = backgroundColor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { isFullscreen, toggle } = useFullscreen(fullRef);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newValue) => {
|
||||||
|
if (editor) {
|
||||||
|
const value = editor.getValue();
|
||||||
|
if (newValue !== value) {
|
||||||
|
editor.setValue(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.options,
|
||||||
|
(newValue) => {
|
||||||
|
editor.updateOptions(newValue);
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => props.language,
|
||||||
|
// (newValue) => {
|
||||||
|
// monaco.editor.setModelLanguage(editor.getModel()!, newValue);
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
editor.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
init();
|
||||||
|
setEditBoxBg();
|
||||||
|
});
|
||||||
|
|
||||||
|
return { codeEditBox, fullRef, isFullscreen, toggle, t };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ms-code-editor {
|
||||||
|
@apply z-10;
|
||||||
|
|
||||||
|
padding: 16px 0;
|
||||||
|
width: v-bind(width);
|
||||||
|
height: v-bind(height);
|
||||||
|
&[data-mode-id='plaintext'] {
|
||||||
|
:deep(.mtk1) {
|
||||||
|
color: rgb(var(--primary-5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ms-code-editor-full-screen {
|
||||||
|
height: calc(100vh - 66px);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default {
|
||||||
|
'msCodeEditor.fullScreen': 'FullScreen',
|
||||||
|
};
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default {
|
||||||
|
'msCodeEditor.fullScreen': '全屏',
|
||||||
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { rgbToHex } from '@/utils';
|
||||||
|
import { primaryVars } from '@/hooks/useThemeVars';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
base: 'vs',
|
||||||
|
inherit: false,
|
||||||
|
rules: [],
|
||||||
|
colors: {
|
||||||
|
'editorLineNumber.foreground': rgbToHex(primaryVars.P2),
|
||||||
|
'editorLineNumber.activeForeground': rgbToHex(primaryVars.P4),
|
||||||
|
'editorCursor.background': rgbToHex(primaryVars.P5),
|
||||||
|
'editorCursor.foreground': rgbToHex(primaryVars.P5),
|
||||||
|
'editor.wordHighlightBackground': rgbToHex(primaryVars.P1),
|
||||||
|
'editor.selectionBackground': rgbToHex(primaryVars.P2),
|
||||||
|
'editor.lineHighlightBorder': rgbToHex(primaryVars.P1),
|
||||||
|
'editor.lineHighlightBackground': rgbToHex(primaryVars.P1),
|
||||||
|
'editor.rangeHighlightBackground': rgbToHex(primaryVars.P1),
|
||||||
|
'editor.findMatchBackground': rgbToHex(primaryVars.P2),
|
||||||
|
'editor.findMatchHighlightBackground': rgbToHex(primaryVars.P9),
|
||||||
|
'editor.findRangeHighlightBackground': rgbToHex(primaryVars.P5),
|
||||||
|
'scrollbarSlider.activeBackground': rgbToHex(primaryVars.P4),
|
||||||
|
'scrollbarSlider.background': rgbToHex(primaryVars.P2),
|
||||||
|
'scrollbarSlider.hoverBackground': rgbToHex(primaryVars.P3),
|
||||||
|
'scrollbar.shadow': rgbToHex(primaryVars.P2),
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
import MSText from './MS-text';
|
||||||
|
|
||||||
|
import type { CustomeTheme } from '../types';
|
||||||
|
|
||||||
|
const MsCodeEditorThemes: Record<CustomeTheme, any> = {
|
||||||
|
'MS-text': MSText,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MsCodeEditorThemes;
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
export type CustomeTheme = 'MS-text';
|
||||||
|
export type Theme = 'vs' | 'hc-black' | 'vs-dark' | CustomeTheme;
|
||||||
|
export type FoldingStrategy = 'auto' | 'indentation';
|
||||||
|
export type RenderLineHighlight = 'all' | 'line' | 'none' | 'gutter';
|
||||||
|
export type Language =
|
||||||
|
| 'plaintext'
|
||||||
|
| 'javascript'
|
||||||
|
| 'typescript'
|
||||||
|
| 'css'
|
||||||
|
| 'less'
|
||||||
|
| 'sass'
|
||||||
|
| 'html'
|
||||||
|
| 'sql'
|
||||||
|
| 'json'
|
||||||
|
| 'java'
|
||||||
|
| 'python'
|
||||||
|
| 'xml'
|
||||||
|
| 'yaml'
|
||||||
|
| 'shell';
|
||||||
|
export interface Options {
|
||||||
|
automaticLayout: boolean; // 自适应布局
|
||||||
|
foldingStrategy: FoldingStrategy; // 折叠方式 auto | indentation
|
||||||
|
renderLineHighlight: RenderLineHighlight; // 行亮
|
||||||
|
selectOnLineNumbers: boolean; // 显示行号
|
||||||
|
minimap: {
|
||||||
|
// 关闭小地图
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
readOnly: boolean; // 只读
|
||||||
|
fontSize: number; // 字体大小
|
||||||
|
scrollBeyondLastLine: boolean; // 取消代码后面一大段空白
|
||||||
|
overviewRulerBorder: boolean; // 不要滚动条的边框
|
||||||
|
}
|
||||||
|
|
||||||
|
export const editorProps = {
|
||||||
|
modelValue: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: [String, Number] as PropType<string | number>,
|
||||||
|
default: '100%',
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: [String, Number] as PropType<string | number>,
|
||||||
|
default: '50vh',
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
type: String as PropType<Language>,
|
||||||
|
default: 'plaintext',
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String as PropType<Theme>,
|
||||||
|
validator(value: string): boolean {
|
||||||
|
return ['vs', 'hc-black', 'vs-dark', 'MS-text'].includes(value);
|
||||||
|
},
|
||||||
|
default: 'vs-dark',
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Object as PropType<Options>,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
automaticLayout: true,
|
||||||
|
foldingStrategy: 'indentation',
|
||||||
|
renderLineHighlight: 'all',
|
||||||
|
selectOnLineNumbers: true,
|
||||||
|
minimap: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
readOnly: false,
|
||||||
|
fontSize: 16,
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
overviewRulerBorder: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,33 @@
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
||||||
|
// import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
|
||||||
|
// import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
|
||||||
|
// import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
|
||||||
|
// import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line no-restricted-globals
|
||||||
|
self.MonacoEnvironment = {
|
||||||
|
async getWorker(_: any, label: string) {
|
||||||
|
if (label === 'json') {
|
||||||
|
const JsonWorker = ((await import('monaco-editor/esm/vs/language/json/json.worker?worker')) as any).default;
|
||||||
|
return new JsonWorker();
|
||||||
|
}
|
||||||
|
if (label === 'css' || label === 'scss' || label === 'less') {
|
||||||
|
const CssWorker = ((await import('monaco-editor/esm/vs/language/css/css.worker?worker')) as any).default;
|
||||||
|
return new CssWorker();
|
||||||
|
}
|
||||||
|
if (label === 'html' || label === 'handlebars' || label === 'razor') {
|
||||||
|
const HtmlWorker = ((await import('monaco-editor/esm/vs/language/html/html.worker?worker')) as any).default;
|
||||||
|
return new HtmlWorker();
|
||||||
|
}
|
||||||
|
if (label === 'typescript' || label === 'javascript') {
|
||||||
|
const TsWorker = ((await import('monaco-editor/esm/vs/language/typescript/ts.worker?worker')) as any).default;
|
||||||
|
|
||||||
|
return new TsWorker();
|
||||||
|
}
|
||||||
|
return new EditorWorker();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
|
|
@ -18,21 +18,24 @@
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<slot>
|
<slot>
|
||||||
<MsDescription :descriptions="props.descriptions"></MsDescription>
|
<MsDescription v-if="props.descriptions?.length > 0" :descriptions="props.descriptions"></MsDescription>
|
||||||
</slot>
|
</slot>
|
||||||
</a-drawer>
|
</a-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch, defineAsyncComponent } from 'vue';
|
||||||
import MsDescription, { Description } from '@/components/pure/ms-description/index.vue';
|
import type { Description } from '@/components/pure/ms-description/index.vue';
|
||||||
|
|
||||||
|
// 懒加载描述组件
|
||||||
|
const MsDescription = defineAsyncComponent(() => import('@/components/pure/ms-description/index.vue'));
|
||||||
|
|
||||||
interface DrawerProps {
|
interface DrawerProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
title: string | undefined;
|
title: string | undefined;
|
||||||
titleTag?: string;
|
titleTag?: string;
|
||||||
titleTagColor?: string;
|
titleTagColor?: string;
|
||||||
descriptions: Description[];
|
descriptions?: Description[];
|
||||||
footer?: boolean;
|
footer?: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
@ -75,8 +78,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.arco-drawer-footer {
|
.arco-drawer-footer {
|
||||||
@apply text-left;
|
|
||||||
|
|
||||||
border-bottom: 1px solid var(--color-text-n8);
|
border-bottom: 1px solid var(--color-text-n8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<icon-font
|
<icon-font :type="props.type" :size="props.size || 14" />
|
||||||
:type="props.type"
|
|
||||||
:size="props.size || 14"
|
|
||||||
:class="props.color ? `text-[${props.color}]` : 'text-[var(--color-text-4)]'"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -15,7 +11,6 @@
|
||||||
size?: string | number;
|
size?: string | number;
|
||||||
rotate?: number;
|
rotate?: number;
|
||||||
spin?: boolean;
|
spin?: boolean;
|
||||||
color?: string;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const IconFont = Icon.addFromIconFontCn({
|
const IconFont = Icon.addFromIconFontCn({
|
||||||
|
|
|
@ -24,10 +24,12 @@
|
||||||
type UploadProps = Partial<{
|
type UploadProps = Partial<{
|
||||||
mainText: string;
|
mainText: string;
|
||||||
subText: string;
|
subText: string;
|
||||||
|
class: string;
|
||||||
multiple: boolean;
|
multiple: boolean;
|
||||||
limit: number;
|
limit: number;
|
||||||
imagePreview: boolean;
|
imagePreview: boolean;
|
||||||
showFileList: boolean;
|
showFileList: boolean;
|
||||||
|
[key: string]: any;
|
||||||
}> & {
|
}> & {
|
||||||
accept: UploadType;
|
accept: UploadType;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue