tinymce在vue3中的用法以及文本流式输出

一、版本
"@tinymce/tinymce-vue": "4.0.5",
"tinymce": "5.10.2",
二、步骤

具体步骤可以参考tinymce在vue2中的用法中的步骤

三、在项目index.html-body中引入tinymcejs
<script src="tinymce/tinymce.min.js"></script>
四、tinymce引入自定义插件

在项目的public/tinymce文件夹下引入按需放置自定义插件文件,比如formatpainter和indent2em

在使用tinymce插件的vue中引入自定义插件

import '@PublicDir/tinymce/plugins/formatpainter';
import '@PublicDir/tinymce/plugins/indent2em';
五、具体代码

1、封装组件

把tinymce初始化以及文本流式输出封装成组件放到项目src/components文件夹下:

tinymce.vue

<template>
    <div class="tinymce-page">
        <Editor v-model="editorValue" class="tinymce-editor" :init="init" @focus="handleFocus">
        </Editor>
        <!-- AI生成modal  -->
        <FModal v-model:show="modalObj.showModal" :maskClosable="false" :closable="modalObj.state === '2'" :title="modalObj.title" vertical-center :width="860" contentClass="ai-modal-wrapper" @cancel="cancel">
            <div class="ai">
                <FScrollbar ref="scrollbarRef" :maxHeight="modalMaxHeight" style="width: 100%">
                    <div v-if="modalObj.state === '1'" class="ai-loading">
                        <EditOutlined />AI生成中
                    </div>
                    <FTooltip v-else mode="popover" trigger="click" placement="right-start" popperClass="popover-regular">
                        <div id="message" class="message">
                            <span v-html="modalObj.message"></span>
                            <LoadingOutlined v-if="modalObj.messageState === '2'" />
                        </div>
                        <template #content>
                            <div class="list">
                                <div class="title">编辑诉求或选择以下方式</div>
                                <div v-for="(item, index) in aiToolbarList"
                                     :key="index"
                                     class="item"
                                     :title="item.label"
                                     @click="handleClick(item.value)"
                                >
                                    <EditOutlined />{{item.label}}
                                </div>
                            </div>
                        </template>
                    </FTooltip>
                </FScrollbar>
            </div>
            <template #footer></template>
        </FModal>
    </div>
</template>
<script setup>
import {
    FInput, FMessage, FModal, FScrollbar,
} from '@fesjs/fes-design';
import {
    ref, defineEmits, defineProps, computed, nextTick, watch, onUnmounted,
} from 'vue';
import { EditOutlined, LoadingOutlined } from '@fesjs/fes-design/icon';
import { request } from '@fesjs/fes';
import tinymce from 'tinymce'; // tinymce默认hidden,不引入不显示
import Editor from '@tinymce/tinymce-vue';// 编辑器引入;
import 'tinymce/icons/default/icons';
import 'tinymce/themes/silver/theme';// 编辑器主题
import 'tinymce/plugins/contextmenu'; // 上下文菜单
import 'tinymce/plugins/table';
import 'tinymce/plugins/image';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/paste';
import '@PublicDir/tinymce/plugins/formatpainter';
import '@PublicDir/tinymce/plugins/indent2em';

const props = defineProps({
    value: {
        type: String,
        default: '',
    },
    disabled: {
        type: Boolean,
        default: false,
    },
    aiId: { // ai生成时所需ID
        type: String,
        default: '',
    },
    title: { // 文档标题
        type: String,
        default: '',
    },
    textTitle: { // 输入框标题,用来做校验错误信息提示
        type: String,
        default: '',
    },
    placeholder: { // placeholder
        type: String,
        default: '',
    },
});
const emits = defineEmits(['update:value', 'selectEditor']);

// 通过重写计算属性的set和get方法,将计算属性的结果绑定在文本框的model中
const editorValue = computed({
    get: () => props.value,
    set: (val) => {
        emits('update:value', val);
    },
});
const myEditor = ref(null);

const modalObj = ref({
    title: '',
    showModal: false,
    message: '',
    state: '1', // 1、AI生成中 2、生成成功
    messageState: '1', // 加载状态显示 1、生成成功或者未触发生成 2、文案生成中
});
const tinymceSelectText = ref('');// 文本框选中的文案

// const queryAIResult = sessionId => new Promise((resolve, reject) => {
//     request('/projectDoc/ai/write/result', { sessionId }, 'post')
//         .then((result) => {
//             if (result.status === 'PENDING') {
//                 reject('pending');
//             } else {
//                 resolve(result);
//             }
//         }).catch((error) => {
//             reject(error);
//         });
// });

// const queryAiResultCircle = (sessionId) => {
//     queryAIResult(sessionId).then((result) => {
//         if (result.status === 'FAIL') {
//             modalObj.value.message = '生成失败';
//         } else {
//             modalObj.value.message = result.output || '生成失败';
//         }
//         modalObj.value.state = '2';
//     }).catch((error) => {
//         setTimeout(() => queryAiResultCircle(sessionId), 5000);
//     });
// };

// const queryAiResultCircle = async (sessionId) => {
//     queryAIResult(sessionId).then((result) => {
//         if (result.status === 'FAIL') {
//             modalObj.value.message = '生成失败';
//         } else {
//             modalObj.value.message = result.output || '生成失败';
//         }
//         modalObj.value.state = '2';
//     }).catch((error) => {
//         setTimeout(() => queryAiResultCircle(sessionId), 5000);
//     });
//     const result = await request('/projectDoc/ai/write/body', {
//         sessionId,
//     }, {
//         method: 'post',
//     });
// };
const scrollbarRef = ref(null);
const modalMaxHeight = 800;
// EventSource文本流失输出
const eventSource = ref();
const setupEventSource = (sessionId) => {
    modalObj.value.message = '';
    eventSource.value = new EventSource(`${BASEURL}/projectDoc/ai/write/stream?sessionId=${sessionId}`); // 创建EventSource对象,参数为接口URL
    eventSource.value.addEventListener('message', (event) => {
        console.log('message:', event.data);
        modalObj.value.state = '2';
        modalObj.value.messageState = '2';
        modalObj.value.message = event.data;
        const messageDom = document.getElementById('message');
        // 文本框滚动条始终置于元素底部
        if (messageDom && messageDom.scrollHeight >= modalMaxHeight) {
            // console.log(messageDom.scrollHeight, scrollbarRef.value, scrollbarRef.value.height);
            scrollbarRef.value?.setScrollTop(messageDom.scrollHeight, 1000);
        }
    });
    eventSource.value.addEventListener('finish', (event) => {
        console.log('Finish:', event.data);
        modalObj.value.messageState = '1';
        eventSource.value.close(); // 关闭连接
    });
    eventSource.value.onerror = function (event) {
        console.error('EventSource failed.', event);
        modalObj.value.state = '2';
        modalObj.value.messageState = '1';
        modalObj.value.message = '生成失败';
        eventSource.value.close(); // 在发生错误时关闭连接
    };
};

const againAIId = ref(''); // 重新生成内容时的id
const againAIInput = ref(''); // 重新生成内容时的input
const getMessage = async ({
    id,
    input,
}) => {
    try {
        // 请求后台生成文案
        againAIId.value = id;
        againAIInput.value = input;
        const inputText = input.length > 4000 ? input.slice(input.length - 4000, input.length) : input;
        const result = await request('/projectDoc/ai/write/body', {
            id,
            input: inputText,
            title: props.title,
        }, {
            method: 'post',
        });
        const sessionId = result.sessionId;
        console.log(sessionId);
        setupEventSource(sessionId);
        // 每5s查询一次ai生成结果
        // queryAiResultCircle(sessionId);
    } catch (error) {
        console.log('getMessage error:', error);
        modalObj.value.message = '生成失败';
        modalObj.value.state = '2';
    }
};

// 上下文菜单items action
const tinymceAction = (modalTitle, text, aiId) => {
    if (!text) return FMessage.error('请选择文案');
    tinymceSelectText.value = text;
    modalObj.value.title = modalTitle;
    modalObj.value.showModal = true;
    getMessage({
        id: aiId,
        input: text,
    });
};
// fontsize获取
const getFontSize = (editor) => {
    const fontSize = editor.queryCommandValue('FontSize');
    return parseInt(fontSize.match(/(\S*)px/)[1]);
};

const tinymceId = `tinymce${new Date().getTime()}`;
// 富文本框初始化
const init = {
    selector: `${tinymceId}`, // 富文本编辑器的id,
    placeholder: props.placeholder,
    language_url: 'tinymce/langs/zh_CN.js', // 引入语言包文件
    language: 'zh_CN', // 语言类型
    menubar: false,
    skin_url: 'tinymce/skins/ui/oxide', // 皮肤:浅色
    content_css: 'tinymce/skins/content/default/content.css',
    plugins: 'formatpainter image lists table indent2em contextmenu paste',
    toolbar: [
        'formatpainter | styleselect | fontselect | fontsizeselect | customInsert | fontSizeA+ fontSizeA- | bold italic underline strikethrough forecolor backcolor | numlist bullist | indent2em outdent indent | alignleft aligncenter alignright | lineheight blockquote | undo redo',
    ],
    toolbar_groups: {
        customInsert: {
            text: '插入',
            tooltip: 'insert',
            items: 'table image',
        },
    },
    fontsize_formats: '12px 14px 16px 18px 24px 36px 48px 56px 72px',
    font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;',
    contextmenu: 'title | textGeneration expandWordCount extractKeyPoints changeLanguage',
    setup(editor) {
        myEditor.value = editor;
        editor.ui.registry.addIcon('file', '<svg width="14" height="14" viewBox="0 0 1024 1024"><path d="M533.419 96.555a21.333 21.333 0 0 1 21.333 21.333v21.333a21.333 21.333 0 0 1-17.493 20.992l-3.84.342-372.907-.043v702.933h702.933V533.333A21.333 21.333 0 0 1 884.78 512h21.333a21.333 21.333 0 0 1 21.333 21.333v342.443c0 26.752-20.309 48.768-46.378 51.413l-5.291.256H148.224a51.67 51.67 0 0 1-51.413-46.378l-.256-5.291V148.224c0-26.752 20.309-48.768 46.378-51.413l5.291-.256h385.195zm263.296 3.84L917.376 221.14a21.333 21.333 0 0 1 0 30.123l-371.627 371.67a21.333 21.333 0 0 1-13.184 6.143l-132.693 12.075a21.333 21.333 0 0 1-23.21-23.168l12.074-132.736a21.333 21.333 0 0 1 6.144-13.141l371.627-371.67a21.333 21.333 0 0 1 30.208 0zm-15.062 75.392L451.072 506.368l-6.059 66.39 66.39-6.06 330.581-330.58-60.33-60.331z"></path></svg>');
        editor.ui.registry.addIcon('a-', '<svg width="20" height="20" viewBox="0 0 1024 1024" p-id="1183" width="200" height="200"><path d="M631.1 266.9h345.4v41H631.1zM368.5 150.5L57.2 855.2h73.2l101.3-236.8h346.7l98.9 236.8h78.1L444.2 150.5h-75.7zM559 572H251.3l54.9-127.6c35.4-82.2 65.9-156.6 96.4-241.7h4.9c31.7 85.1 61 159.5 97.7 241.7L559 572z" fill="#000" p-id="1184"></path></svg>');
        editor.ui.registry.addIcon('a+', '<svg width="20" height="20" viewBox="0 0 1024 1024" p-id="902" width="200" height="200"><path d="M631.1 266.9h152.2v41H631.1zM824.3 266.9h152.2v41H824.3zM783.3 307.8h41V460h-41zM783.3 114.6h41v152.2h-41z" fill=" #000" p-id="903"></path><path d="M783.3 266.9h41v41h-41zM368.5 150.5L57.2 855.2h73.2l101.3-236.8h346.7l98.9 236.8h78.1L444.2 150.5h-75.7zM559 572H251.3l54.9-127.6c35.4-82.2 65.9-156.6 96.4-241.7h4.9c31.7 85.1 61 159.5 97.7 241.7L559 572z" fill=" #000" p-id="904"></path></svg>');
        editor.ui.registry.addButton('fontSizeA+', {
            icon: 'a+',
            tooltip: '字号放大',
            onAction() {
                const num = getFontSize(editor);
                if (num === 72) return;
                editor.execCommand('fontSize', false, `${num + 2}px`);
            },
        });
        editor.ui.registry.addButton('fontSizeA-', {
            icon: 'a-',
            tooltip: '字号缩小',
            onAction() {
                const num = getFontSize(editor);
                if (num === 12) return;
                editor.execCommand('fontSize', false, `${num - 2}px`);
            },
        });
        editor.ui.registry.addMenuItem('title', {
            text: '请选择以下方式使用AI',
        });
        editor.ui.registry.addMenuItem('textGeneration', {
            text: '文本生成',
            context: 'tools',
            icon: 'file',
            onAction(e) {
                console.log(e, '文本生成', editor.selection.getContent({ format: 'text' }));
                const text = editor.selection.getContent({ format: 'text' });
                tinymceAction('文本生成', text, `${props.aiId}-SC`);
            },
        });
        editor.ui.registry.addMenuItem('expandWordCount', {
            text: '扩充字数',
            context: 'tools',
            icon: 'file',
            onAction() {
                console.log('扩充字数');
                const text = editor.selection.getContent({ format: 'text' });
                tinymceAction('扩充字数', text, `${props.aiId}-KC`);
            },
        });
        editor.ui.registry.addMenuItem('extractKeyPoints', {
            text: '提炼要点',
            context: 'tools',
            icon: 'file',
            onAction() {
                console.log('提炼要点');
                const text = editor.selection.getContent({ format: 'text' });
                tinymceAction('提炼要点', text, `${props.aiId}-TL`);
            },
        });
        editor.ui.registry.addMenuItem('changeLanguage', {
            text: '转换语言风格',
            context: 'tools',
            icon: 'file',
            onAction() {
                console.log('转换语言风格');
                const text = editor.selection.getContent({ format: 'text' });
                tinymceAction('转换语言风格', text, `${props.aiId}-ZH`);
            },
        });
    },
    indentation: '2em',
    branding: false,
    elementpath: false,
    width: '100%',
    min_height: 600,
    paste_preprocess(plugin, args) {
        // 复制粘贴去掉原有样式
        console.log(args.content, plugin);
        args.content = args.content.replace(/(\sface=".*"\scolor)/g, ' color');
    },
    file_picker_callback(callback, value, meta) {
        // 上传需要处理
        console.log(callback, value, meta);
        // 文件分类
        let filetypeAccept = '.pdf, .png';
        const apiFileType = '1';
        // 后端接收上传文件的地址
        let upurl = 'file/upload';
        // 为不同插件指定文件类型及后端地址
        switch (meta.filetype) {
            case 'image':
                filetypeAccept = '.jpg, .jpeg, .png, .gif';
                upurl = '/oacgs-bms/commonFile/uploadFile';
                // apiFileType = '1';
                break;
            case 'file':
            default:
        }
        // 模拟出一个input用于添加本地文件
        const input = document.createElement('input');
        input.setAttribute('type', 'file');
        input.setAttribute('accept', filetypeAccept);
        input.click();
        input.onchange = function () {
            const file = this.files[0];
            console.log(file.name);
            const xhr = new XMLHttpRequest();
            xhr.withCredentials = false;
            xhr.open('POST', upurl);
            xhr.onload = function () {
                if (xhr.status !== 200) {
                    FMessage.error(`HTTP Error: ${xhr.status}`);
                    return;
                }
                const json = JSON.parse(xhr.responseText);
                console.log('json', json);
                if (!json || json.code !== '0') {
                    FMessage.error(json?.msg || '上传失败');
                    return;
                }
                console.log('json', json?.result);
                const recordId = json?.result?.recordId;
                const fileUrl = `${window.location.origin}/oacgs-bms/commonFile/image/download?recordId=${recordId}`;
                console.log('fileUrl', fileUrl);
                if (meta.filetype === 'file') return callback(fileUrl || '', { title: file.name, text: file.name });
                callback(fileUrl || '', {});
            };
            const formData = new FormData();
            formData.append('file', file, file.name);
            formData.append('fileType', apiFileType);
            xhr.send(formData);
        };
    },
};

const aiToolbarList = [
    { label: '替换选择区域', value: 'replace' },
    { label: '添加到所选区域之后', value: 'append' },
    { label: '将生成内容扩充字数', value: 'continueExpandWordCount' },
    { label: '重新生成', value: 'regenerate' },
    { label: '放弃文本', value: 'cancel' },
];
const again = ({
    id, input,
}) => {
    modalObj.value.state = '1';
    modalObj.value.message = '';
    getMessage({ id, input });
};

const cancel = () => {
    modalObj.value.state = '1';
    modalObj.value.message = '';
    modalObj.value.showModal = false;
    if (!eventSource.value) return;
    eventSource.value.close();
};

// 生成文案点击选择items
const handleClick = (type) => {
    const selectText = myEditor.value.selection.getContent();
    switch (type) {
        case 'replace':
            // 替换选择区域
            myEditor.value.insertContent(modalObj.value.message);
            cancel();
            break;
        case 'append':
            // 添加到所选区域之后
            myEditor.value.selection.setContent(`${selectText}${modalObj.value.message}`);
            cancel();
            break;
        case 'continueExpandWordCount':
            // 将生成内容扩充字数
            console.log('将生成内容扩充字数');
            again({
                id: `${props.aiId}-KC`,
                input: modalObj.value.message,
            });
            break;
        case 'regenerate':
            // 重新生成
            console.log('重新生成');
            again({
                id: againAIId.value,
                input: againAIInput.value,
            });
            break;
        case 'cancel':
            // 放弃文本
            cancel();
            break;
        default:
            break;
    }
};

const handleFocus = (event) => {
    // console.log('focus in', event)
    emits('selectEditor', props.aiId);
};

const openContextMenu = () => {
    if (!myEditor.value.selection.getContent().length) {
        myEditor.value.execCommand('selectAll');
    }
    console.log(myEditor.value.selection.getContent());
    myEditor.value.fire('contextMenu');
};

const validateLength = () => myEditor.value.getContent({ format: 'text' }).length < 10001;
// console.log(myEditor.value.getContent({format:'text'}), myEditor.value.getContent({format:'text'}).length)

watch(() => props.disabled, async () => {
    // console.log(props.disabled, myEditor.value)
    await nextTick();
    if (myEditor.value) {
        // console.log(myEditor.value);
        // 由于tinymce 5.x使用的setMode方式来控制文本框的disable态,查阅源码发现该函数内有异步事件,在连续调用时会出现后面的被前面覆盖的问题,故使用setTimeout解决该问题 (tinymce 5.10.2 Mode.ts line37-setMode)
        setTimeout(() => myEditor.value.setMode(props.disabled ? 'readonly' : 'design'), 300);
    }
}, { immediate: true });

// eslint-disable-next-line no-undef
defineExpose({
    openContextMenu, validateLength, textTitle: props.textTitle,
});

onUnmounted(() => {
    if (!eventSource.value) return;
    eventSource.value.close();
});
</script>

<style lang="less" >
.ai {
    .ai-loading {
        display: flex;
        align-items: center;
        .fes-design-icon {
            margin-right: 6px;
        }
    }
}
.message {
    cursor: pointer;
    line-height: 2;
    .fes-design-icon {
        line-height: 2;
        margin-left: 6px;
    }
}
.popover-regular {
    padding: 0!important;
    border-radius: 2px !important;
    overflow: hidden;
    border: 1px solid #ccc;
    .list {
        padding: 4px 0;
        .title,
        .item {
            display: flex;
            align-items: center;
            line-height: 32px;
            font-size: 14px;
            padding:  0 16px;
            color: #222f3e;
        }
        .title {
            border-bottom: 1px solid #ccc;
            padding-left: 20px;
            margin-bottom: 4px;
        }
        .item {
            cursor: pointer;
            .fes-design-icon {
                width: 24px;
                margin-right: 8px;
            }
            &:hover {
                background: #eee;
            }
        }
    }
    .fes-popper-arrow {
        display: none;
    }
}
.tox .tox-collection--list .tox-collection__item--active {
    background-color: #eee !important;
}
.tox-collection__item {
    cursor: pointer;
}
.tox .tox-collection--list .tox-collection__group:first-child {
    .tox-collection__item--active {
        background-color: transparent !important;
    }
    .tox-collection__item {
        cursor: default;
        &:hover {
            background: transparent;
        }
        .tox-collection__item-icon {
            display: none;
        }
    }
}
.ai-modal-wrapper {
    padding: 16px 0 !important;
    .fes-modal-header {
        margin: 16px;
    }
    .fes-modal-body {
        .fes-scrollbar {
            padding: 0 16px;
        }
    }
}

</style>

代码中的@PublicDir需要在alias设置

alias: {
    '@': path.resolve(__dirname, 'src'),
    '@PublicDir': path.resolve(__dirname, 'public')
},

说明: fesjs是基于最新 Vue3 + webpack5 的前端应用框架

2、调用组件

<template>
    <TEditor v-model:value="editorContent" />
</template>
<script setup>
import {ref} from 'vue';
import TEditor from '@/components/TEditor.vue';
const editorContent = ref('');
</script>

tinymce中文文档地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/417870.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch概述(七)---Optim

torch.optim是一个实现多种优化算法的包;很多常用的方法已经被支持;接口丰富;容易整合更为复杂的算法; 如何使用一个优化器 为了使用torch.optim包功能;用户必须构建一个优化器对象;该优化器将保持当前的参数状态且基于计算的梯度更新参数; 构建优化器 要构建一个优化器;必…

【一个上下拉且松手回弹的自定义ScrollView】

文章目录 UserDefineScrollView举例使用activity_main.xmlMainActivity.java文件运行效果下拉前下拉后上拉 普通的scrollView下拉到顶部时就不动了&#xff0c;而如qq设置界面中的布局&#xff0c;下拉到顶端时还能下拉一段距离。本文介绍一个自定义scrollView就可以实现这样的…

遥感、航拍、影像等用于深度学习的数据集集合

遥感图像的纹理特征异常繁杂&#xff0c;地貌类型多变&#xff0c;人工提取往往存在特征提取困难和特征提取不准确的问题&#xff0c;同时&#xff0c;在这个过程中还会耗费海量的人力物力。随着计算力的突破、数据洪流的暴发和算法的不断创新&#xff0c;在具有鲜明“大数据”…

嵌入式中14 个超级牛的免费开源小工具

Homebrew for macOS 地址&#xff1a;https://brew.sh Mac 上非常好用的包管理工具&#xff0c;很多常见的安装都可以通过 brew install app 或者 brew cask install app 直接安装&#xff0c;类似 apt-get 。 Oh My Zsh 地址&#xff1a;https://github.com/robbyrussell…

Machine Vision Technology:Lecture2 Linear filtering

Machine Vision Technology&#xff1a;Lecture2 Linear filtering Types of ImagesImage denoising图像去噪Defining convolution卷积的定义Key properties卷积的关键属性卷积的其它属性Annoying details卷积练习Sharpening锐化Gaussian KernelNoise噪声 分类Gaussian noise高…

江科大stm32学习笔记——【5-2】对射式红外传感器计次旋转编码计次

一.对射式红外传感器计次 1.原理 2.硬件连接 3.程序 CountSensor.c: #include "stm32f10x.h" // Device header #include "Delay.h"uint16_t CountSensor_Count;void CountSensor_Init(void) {//配置RCC时钟&#xff1a;RCC_APB2Perip…

改进YOLO系列 | YOLOv5/v7 引入通用高效层聚合网络 GELAN | YOLOv9 新模块

今天的深度学习方法专注于如何设计最合适的目标函数,以使模型的预测结果最接近真实情况。同时,必须设计一个合适的架构,以便为预测提供足够的信息。现有方法忽视了一个事实,即当输入数据经过逐层特征提取和空间转换时,会丢失大量信息。本文将深入探讨数据通过深度网络传输…

DH秘钥交换算法

1 应用 关于加密&#xff0c;对称加密和非对称加密各有优劣&#xff0c;最佳方案是先使用非对称加密实现秘钥交换&#xff0c;后面再利用协商的结果作为对称加密的秘钥&#xff0c;具体可以参考 《嵌入式算法6---AES加密/解密算法》、《嵌入式算法18---RSA非对称加密算法》。 …

TikTok运营应该使用什么IP?网络问题大全

想要迈过TikTok新手门槛&#xff0c;首先必须要学习的就是网络问题。很多人开始做TikTok账号或者TikTok小店时&#xff0c;都会遇到一些先前没有遇到的词汇和概念&#xff0c;比如原生IP&#xff0c;独享IP&#xff0c;甚至专线&#xff0c;那么一个IP可以做几个账号呢&#xf…

多人同时导出 Excel 干崩服务器?我们来实现一个排队导出功能!

考虑到数据库数据日渐增多&#xff0c;导出会有全量数据的导出&#xff0c;多人同时导出可以会对服务性能造成影响&#xff0c;导出涉及到mysql查询的io操作&#xff0c;还涉及文件输入、输出流的io操作&#xff0c;所以对服务器的性能会影响的比较大&#xff1b; 结合以上原因…

CPD点云配准

一、CPD点云配准 Python 这是github上一位大佬写的Python包&#xff0c;链接&#xff1a;neka-nat/probreg: Python package for point cloud registration using probabilistic model (Coherent Point Drift, GMMReg, SVR, GMMTree, FilterReg, Bayesian CPD) (github.com)你…

深入理解与应用工厂方法模式

文章目录 一、模式概述**二、适用场景****三、模式原理与实现****四、采用工厂方法模式的原因****五、优缺点分析****六、与抽象工厂模式的比较**总结 一、模式概述 ​ 工厂方法模式是一种经典的设计模式&#xff0c;它遵循面向对象的设计原则&#xff0c;特别是“开闭原则”&…

EasyX的使用(详解版)

EasyX的基础概念&#xff1a; 图形化——EasyX的安装-CSDN博客 创建图形化窗口 #include<graphics.h> #include<conio.h> int main() {//创建绘图窗口&#xff0c;大小为100x100像素。//更改为大窗口&#xff0c;像素增大&#xff1b;更改为小窗口&#xff0c;像素…

华为数通方向HCIP-DataCom H12-821题库(单选题:481-500)

第481题 以下关于基于SD-WAN思想的EVPN互联方案的描述,错误的是哪一项? A、通过部署独立的控制面,将网络转发和控制进行了分离,从而实现了网络控制的集中化 B、通过对WAN网络抽象和建模,将上层网络业务和底层网络具体实现架构进行解耦,从而实现网络自动化 C、通过集中的…

上位机图像处理和嵌入式模块部署(当前机器视觉新形态)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 过去的机器视觉处理&#xff0c;大部分都是集中在上位机、或者是服务器领域&#xff0c;这种形式维持了很长的时间。这种业务形态下&#xff0c;无…

javaee教程郑阿奇课后答案,三年经验月薪50k我是怎么做到的

个人背景 如标题所示&#xff0c;我的个人背景非常简单&#xff0c;Java开发经验1年半&#xff0c;学历普通&#xff0c;2本本科毕业&#xff0c;毕业后出来就一直在Crud&#xff0c;在公司每天重复的工作对我的技术提升并没有什么帮助&#xff0c;但小镇出来的我也深知自我努…

这一步一步爬的伤痕累累

一、网安专业名词解释 ① CTF CTF&#xff08;Capture The Flag&#xff09;中文一般译作夺旗赛&#xff0c;在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会&#xff0c;以代替之前黑客们通过互相发起真实攻击进…

数据结构-----再谈String,字符串常量池,String对象的创建、intern方法的作用

文章目录 1.字符串常量池1.1. 创建对象的思考2.2. 字符串常量池(StringTable)1.3. 再谈String对象创建1.4. intern方法 1.字符串常量池 1.1. 创建对象的思考 下面两种创建String对象的方式相同吗&#xff1f; public static void main(String[] args) {String s1 "hel…

Jmeter系列(5)线程数到底能设置多大

疑惑 一台设备的线程数到底可以设置多大&#xff1f; 线程数设置 经过一番搜索找到了这样的答案&#xff1a; Linux下&#xff0c;2g的 java内存&#xff0c;1m 的栈空间&#xff0c;最大启动线程数2000线程数建议不超过1000jmeter 能启动多少线程&#xff0c;由你的堆内存…

Decision Transformer

DT个人理解 emmm, 这里的Transformer 就和最近接触到的whisper一样,比起传统Transformer,自己还设计了针对特殊情况的tokens。比如whisper里对SOT,起始时间,语言种类等都指定了特殊tokens去做Decoder的输入和输出。 DT这里的作为输入的Tokens由RL里喜闻乐见的历史数据:…