安装库
Shell
pnpm install clipboard highlight.jspnpm 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 highlightimport 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 highlightclipboard封装
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)
}
