Skip to content

安装库

Shell
pnpm install clipboard highlight.js
pnpm install clipboard highlight.js

封装组件

HighCode/index.vue

vue
<template>
    <div
        v-highlight
        class="high-code srollbar_style hljs"
        :class="{
            atom_one_dark: currentTheme === 'dark',
            atom_one_light: currentTheme === 'light',
        }"
        :style="{width:width,height:height,borderRadius:round?'8px':''}"
    >
        <span class="code-lang" :style="{color:currentTheme==='dark'?'#fff':'#1e1e20'}">{{ lang }}</span>
        <div class="code-header">
            <div
                :style="{backgroundColor:currentTheme==='dark'?'#1e1e20':'#d0d4d6'}"
                class="header-item"
                @click="changeTheme"
            >
                <i class="i-ant-design-skin-outlined"></i>
            </div>
            <div
                @click="copyHandle"
                :style="{backgroundColor:currentTheme==='dark'?'#1e1e20':'#d0d4d6'}"
                class="header-item"
            >
                <i class="i-ep-document-copy"></i>
            </div>
        </div>
        <pre>
            <code :class="`language-${lang}`">{{ codeValue }}</code>
      </pre>
    </div>
</template>

<script setup>
import clipboard from '@/utils/clipboard.js'
import { ref } from 'vue'

const props = defineProps({
    codeValue: {
        type: String,
        default: ''
    },
    round: {
        type: Boolean,
        default: true
    },
    lang: {
        type: String,
        default: 'javascript'
    },
    width: {
        type: String,
        default: '620px'
    },
    height: {
        type: String,
        default: '240px'
    }
})

const currentTheme = ref('dark') // light dark
const changeTheme = () => {
    currentTheme.value = currentTheme.value === 'dark' ? 'light' : 'dark'
}
const copyHandle = event => {
    clipboard(props.codeValue, event)
}
</script>

<style lang="scss" scoped>
@import './theme/atom_one_dark';
@import './theme/atom_one_light';

.high-code {
    display: flex;
    flex-direction: column;
    position: relative;
    overflow: hidden;
    z-index: 700;

    &:hover {
        .code-header {
            .header-item {
                opacity: 1;
            }
        }
      
    }

    .code-lang {
        position: absolute;
        right: 15px;
        top: 10px;
        z-index: 999;
    }

    .code-header {
        position: absolute;
        right: 15px;
        top: 35px;
        color: #fff;
        z-index: 999;
        display: flex;
        align-items: center;
        gap: 10px;

        .header-item {
            border-radius: 4px;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 10px;
            cursor: pointer;
            opacity: 0;
            transition: all .3s;

            i {
                font-size: 18px;
            }
        }
    }

    &.srollbar_style::-webkit-scrollbar-track {
        background-color: #eee;
    }

    &.srollbar_style::-webkit-scrollbar-thumb {
        background: rgb(175, 171, 171);
    }

    &.srollbar_style::-webkit-scrollbar {
        width: 8px;
        height: 8px;
    }

    pre {
        width: 100%;
        height: 100%;
        overflow-y: auto;
    }

    pre code {
        font-family: Consolas, Monaco, monospace;
        line-height: 1.3;
        font-size: 14px;
        position: relative;
        overflow-x: visible;
        border-radius: 0;
        box-sizing: border-box;
        display: block;
        border: none;
        margin: 0;
    }

}
</style>
<template>
    <div
        v-highlight
        class="high-code srollbar_style hljs"
        :class="{
            atom_one_dark: currentTheme === 'dark',
            atom_one_light: currentTheme === 'light',
        }"
        :style="{width:width,height:height,borderRadius:round?'8px':''}"
    >
        <span class="code-lang" :style="{color:currentTheme==='dark'?'#fff':'#1e1e20'}">{{ lang }}</span>
        <div class="code-header">
            <div
                :style="{backgroundColor:currentTheme==='dark'?'#1e1e20':'#d0d4d6'}"
                class="header-item"
                @click="changeTheme"
            >
                <i class="i-ant-design-skin-outlined"></i>
            </div>
            <div
                @click="copyHandle"
                :style="{backgroundColor:currentTheme==='dark'?'#1e1e20':'#d0d4d6'}"
                class="header-item"
            >
                <i class="i-ep-document-copy"></i>
            </div>
        </div>
        <pre>
            <code :class="`language-${lang}`">{{ codeValue }}</code>
      </pre>
    </div>
</template>

<script setup>
import clipboard from '@/utils/clipboard.js'
import { ref } from 'vue'

const props = defineProps({
    codeValue: {
        type: String,
        default: ''
    },
    round: {
        type: Boolean,
        default: true
    },
    lang: {
        type: String,
        default: 'javascript'
    },
    width: {
        type: String,
        default: '620px'
    },
    height: {
        type: String,
        default: '240px'
    }
})

const currentTheme = ref('dark') // light dark
const changeTheme = () => {
    currentTheme.value = currentTheme.value === 'dark' ? 'light' : 'dark'
}
const copyHandle = event => {
    clipboard(props.codeValue, event)
}
</script>

<style lang="scss" scoped>
@import './theme/atom_one_dark';
@import './theme/atom_one_light';

.high-code {
    display: flex;
    flex-direction: column;
    position: relative;
    overflow: hidden;
    z-index: 700;

    &:hover {
        .code-header {
            .header-item {
                opacity: 1;
            }
        }
      
    }

    .code-lang {
        position: absolute;
        right: 15px;
        top: 10px;
        z-index: 999;
    }

    .code-header {
        position: absolute;
        right: 15px;
        top: 35px;
        color: #fff;
        z-index: 999;
        display: flex;
        align-items: center;
        gap: 10px;

        .header-item {
            border-radius: 4px;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 10px;
            cursor: pointer;
            opacity: 0;
            transition: all .3s;

            i {
                font-size: 18px;
            }
        }
    }

    &.srollbar_style::-webkit-scrollbar-track {
        background-color: #eee;
    }

    &.srollbar_style::-webkit-scrollbar-thumb {
        background: rgb(175, 171, 171);
    }

    &.srollbar_style::-webkit-scrollbar {
        width: 8px;
        height: 8px;
    }

    pre {
        width: 100%;
        height: 100%;
        overflow-y: auto;
    }

    pre code {
        font-family: Consolas, Monaco, monospace;
        line-height: 1.3;
        font-size: 14px;
        position: relative;
        overflow-x: visible;
        border-radius: 0;
        box-sizing: border-box;
        display: block;
        border: none;
        margin: 0;
    }

}
</style>

两个主题

HighCode/theme/atom_one_dark.scss

SCSS
/*
Atom One Dark by Daniel Gamage
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
*/
.atom_one_dark.hljs,
.atom_one_dark .hljs {
    color: #abb2bf;
    background: #282c34;
}

.atom_one_dark .hljs-comment,
.atom_one_dark .hljs-quote {
    color: #5c6370;
    font-style: italic;
}

.atom_one_dark .hljs-doctag,
.atom_one_dark .hljs-keyword,
.atom_one_dark .hljs-formula {
    color: #c678dd;
}

.atom_one_dark .hljs-section,
.atom_one_dark .hljs-name,
.atom_one_dark .hljs-selector-tag,
.atom_one_dark .hljs-deletion,
.atom_one_dark .hljs-subst {
    color: #e06c75;
}

.atom_one_dark .hljs-literal {
    color: #56b6c2;
}

.atom_one_dark .hljs-string,
.atom_one_dark .hljs-regexp,
.atom_one_dark .hljs-addition,
.atom_one_dark .hljs-attribute,
.atom_one_dark .hljs-meta .hljs-string {
    color: #98c379;
}

.atom_one_dark .hljs-attr,
.atom_one_dark .hljs-variable,
.atom_one_dark .hljs-template-variable,
.atom_one_dark .hljs-type,
.atom_one_dark .hljs-selector-class,
.atom_one_dark .hljs-selector-attr,
.atom_one_dark .hljs-selector-pseudo,
.atom_one_dark .hljs-number {
    color: #d19a66;
}

.atom_one_dark .hljs-symbol,
.atom_one_dark .hljs-bullet,
.atom_one_dark .hljs-link,
.atom_one_dark .hljs-meta,
.atom_one_dark .hljs-selector-id,
.atom_one_dark .hljs-title {
    color: #61aeee;
}

.atom_one_dark .hljs-built_in,
.atom_one_dark .hljs-title .class_,
.atom_one_dark .hljs-class .hljs-title {
    color: #e6c07b;
}

.atom_one_dark .hljs-emphasis {
    font-style: italic;
}

.atom_one_dark .hljs-strong {
    font-weight: bold;
}

.atom_one_dark .hljs-link {
    text-decoration: underline;
}
/*
Atom One Dark by Daniel Gamage
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
*/
.atom_one_dark.hljs,
.atom_one_dark .hljs {
    color: #abb2bf;
    background: #282c34;
}

.atom_one_dark .hljs-comment,
.atom_one_dark .hljs-quote {
    color: #5c6370;
    font-style: italic;
}

.atom_one_dark .hljs-doctag,
.atom_one_dark .hljs-keyword,
.atom_one_dark .hljs-formula {
    color: #c678dd;
}

.atom_one_dark .hljs-section,
.atom_one_dark .hljs-name,
.atom_one_dark .hljs-selector-tag,
.atom_one_dark .hljs-deletion,
.atom_one_dark .hljs-subst {
    color: #e06c75;
}

.atom_one_dark .hljs-literal {
    color: #56b6c2;
}

.atom_one_dark .hljs-string,
.atom_one_dark .hljs-regexp,
.atom_one_dark .hljs-addition,
.atom_one_dark .hljs-attribute,
.atom_one_dark .hljs-meta .hljs-string {
    color: #98c379;
}

.atom_one_dark .hljs-attr,
.atom_one_dark .hljs-variable,
.atom_one_dark .hljs-template-variable,
.atom_one_dark .hljs-type,
.atom_one_dark .hljs-selector-class,
.atom_one_dark .hljs-selector-attr,
.atom_one_dark .hljs-selector-pseudo,
.atom_one_dark .hljs-number {
    color: #d19a66;
}

.atom_one_dark .hljs-symbol,
.atom_one_dark .hljs-bullet,
.atom_one_dark .hljs-link,
.atom_one_dark .hljs-meta,
.atom_one_dark .hljs-selector-id,
.atom_one_dark .hljs-title {
    color: #61aeee;
}

.atom_one_dark .hljs-built_in,
.atom_one_dark .hljs-title .class_,
.atom_one_dark .hljs-class .hljs-title {
    color: #e6c07b;
}

.atom_one_dark .hljs-emphasis {
    font-style: italic;
}

.atom_one_dark .hljs-strong {
    font-weight: bold;
}

.atom_one_dark .hljs-link {
    text-decoration: underline;
}

HighCode/theme/atom_one_light.scss

SCSS
/*
Atom One Light by Daniel Gamage
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
*/
.atom_one_light.hljs,
.atom_one_light .hljs {
    color: #383a42;
    background: #edf2f4;
}

.atom_one_light .hljs-comment,
.atom_one_light .hljs-quote {
    color: #a0a1a7;
    font-style: italic;
}

.atom_one_light .hljs-doctag,
.atom_one_light .hljs-keyword,
.atom_one_light .hljs-formula {
    color: #a626a4;
}

.atom_one_light .hljs-section,
.atom_one_light .hljs-name,
.atom_one_light .hljs-selector-tag,
.atom_one_light .hljs-deletion,
.atom_one_light .hljs-subst {
    color: #e45649;
}

.atom_one_light .hljs-literal {
    color: #0184bb;
}

.atom_one_light .hljs-string,
.atom_one_light .hljs-regexp,
.atom_one_light .hljs-addition,
.atom_one_light .hljs-attribute,
.atom_one_light .hljs-meta .hljs-string {
    color: #50a14f;
}

.atom_one_light .hljs-attr,
.atom_one_light .hljs-variable,
.atom_one_light .hljs-template-variable,
.atom_one_light .hljs-type,
.atom_one_light .hljs-selector-class,
.atom_one_light .hljs-selector-attr,
.atom_one_light .hljs-selector-pseudo,
.atom_one_light .hljs-number {
    color: #986801;
}

.atom_one_light .hljs-symbol,
.atom_one_light .hljs-bullet,
.atom_one_light .hljs-link,
.atom_one_light .hljs-meta,
.atom_one_light .hljs-selector-id,
.atom_one_light .hljs-title {
    color: #4078f2;
}

.atom_one_light .hljs-built_in,
.atom_one_light .hljs-title .class_,
.atom_one_light .hljs-class .hljs-title {
    color: #c18401;
}

.atom_one_light .hljs-emphasis {
    font-style: italic;
}

.atom_one_light .hljs-strong {
    font-weight: bold;
}

.atom_one_light .hljs-link {
    text-decoration: underline;
}
/*
Atom One Light by Daniel Gamage
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
*/
.atom_one_light.hljs,
.atom_one_light .hljs {
    color: #383a42;
    background: #edf2f4;
}

.atom_one_light .hljs-comment,
.atom_one_light .hljs-quote {
    color: #a0a1a7;
    font-style: italic;
}

.atom_one_light .hljs-doctag,
.atom_one_light .hljs-keyword,
.atom_one_light .hljs-formula {
    color: #a626a4;
}

.atom_one_light .hljs-section,
.atom_one_light .hljs-name,
.atom_one_light .hljs-selector-tag,
.atom_one_light .hljs-deletion,
.atom_one_light .hljs-subst {
    color: #e45649;
}

.atom_one_light .hljs-literal {
    color: #0184bb;
}

.atom_one_light .hljs-string,
.atom_one_light .hljs-regexp,
.atom_one_light .hljs-addition,
.atom_one_light .hljs-attribute,
.atom_one_light .hljs-meta .hljs-string {
    color: #50a14f;
}

.atom_one_light .hljs-attr,
.atom_one_light .hljs-variable,
.atom_one_light .hljs-template-variable,
.atom_one_light .hljs-type,
.atom_one_light .hljs-selector-class,
.atom_one_light .hljs-selector-attr,
.atom_one_light .hljs-selector-pseudo,
.atom_one_light .hljs-number {
    color: #986801;
}

.atom_one_light .hljs-symbol,
.atom_one_light .hljs-bullet,
.atom_one_light .hljs-link,
.atom_one_light .hljs-meta,
.atom_one_light .hljs-selector-id,
.atom_one_light .hljs-title {
    color: #4078f2;
}

.atom_one_light .hljs-built_in,
.atom_one_light .hljs-title .class_,
.atom_one_light .hljs-class .hljs-title {
    color: #c18401;
}

.atom_one_light .hljs-emphasis {
    font-style: italic;
}

.atom_one_light .hljs-strong {
    font-weight: bold;
}

.atom_one_light .hljs-link {
    text-decoration: underline;
}

自定义指令v-highlight

JavaScript
import hljs from 'highlight.js'
export const highlight = {
    mounted (el, binding) {
        const blocks = el.querySelectorAll('pre code')
        blocks.forEach(block => {
            hljs.highlightBlock(block)
        })
    },
    updated (el, binding) {
        const blocks = el.querySelectorAll('pre code')
        blocks.forEach(block => {
            hljs.highlightBlock(block)
        })
    }
}

export default highlight
import hljs from 'highlight.js'
export const highlight = {
    mounted (el, binding) {
        const blocks = el.querySelectorAll('pre code')
        blocks.forEach(block => {
            hljs.highlightBlock(block)
        })
    },
    updated (el, binding) {
        const blocks = el.querySelectorAll('pre code')
        blocks.forEach(block => {
            hljs.highlightBlock(block)
        })
    }
}

export default highlight

clipboard封装

JavaScript
import Clipboard from 'clipboard'
import { ElMessage } from 'element-plus'

function clipboardSuccess (msg) {
    ElMessage.success(msg || '复制成功')
}

function clipboardError (msg) {
    ElMessage.error(msg || '复制失败')
}

export default function handleClipboard (text, event, msg) {
    const clipboard = new Clipboard(event.target, {
        text: () => text
    })
    clipboard.on('success', () => {
        clipboardSuccess(msg)
        clipboard.destroy()
    })
    clipboard.on('error', () => {
        clipboardError(msg)
        clipboard.destroy()
    })
    clipboard.onClick(event)
}
import Clipboard from 'clipboard'
import { ElMessage } from 'element-plus'

function clipboardSuccess (msg) {
    ElMessage.success(msg || '复制成功')
}

function clipboardError (msg) {
    ElMessage.error(msg || '复制失败')
}

export default function handleClipboard (text, event, msg) {
    const clipboard = new Clipboard(event.target, {
        text: () => text
    })
    clipboard.on('success', () => {
        clipboardSuccess(msg)
        clipboard.destroy()
    })
    clipboard.on('error', () => {
        clipboardError(msg)
        clipboard.destroy()
    })
    clipboard.onClick(event)
}
AI助手