feat: code highlight
This commit is contained in:
parent
f346249685
commit
f94893c201
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -26,6 +26,7 @@
|
|||
"non-moe",
|
||||
"persistedstate",
|
||||
"Pinia",
|
||||
"prismjs",
|
||||
"rdquo",
|
||||
"shinnku",
|
||||
"signin",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"dompurify": "^3.0.5",
|
||||
"pinia": "^2.1.6",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.2.4"
|
||||
|
@ -33,6 +34,7 @@
|
|||
"@iconify/vue": "^4.1.1",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/node": "^20.4.8",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
||||
"cross-env": "^7.0.3",
|
||||
|
@ -41,6 +43,7 @@
|
|||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.8",
|
||||
"vite-plugin-mock": "^3.0.0",
|
||||
"vite-plugin-prismjs": "^0.0.8",
|
||||
"vue-tsc": "^1.8.8"
|
||||
},
|
||||
"keywords": [
|
||||
|
|
|
@ -26,6 +26,9 @@ dependencies:
|
|||
pinia-plugin-persistedstate:
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.0(pinia@2.1.6)
|
||||
prismjs:
|
||||
specifier: ^1.29.0
|
||||
version: 1.29.0
|
||||
vue:
|
||||
specifier: ^3.3.4
|
||||
version: 3.3.4
|
||||
|
@ -46,6 +49,9 @@ devDependencies:
|
|||
'@types/node':
|
||||
specifier: ^20.4.8
|
||||
version: 20.4.8
|
||||
'@types/prismjs':
|
||||
specifier: ^1.26.0
|
||||
version: 1.26.0
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^4.2.3
|
||||
version: 4.2.3(vite@4.4.8)(vue@3.3.4)
|
||||
|
@ -70,6 +76,9 @@ devDependencies:
|
|||
vite-plugin-mock:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0(esbuild@0.18.19)(mockjs@1.1.0)(vite@4.4.8)
|
||||
vite-plugin-prismjs:
|
||||
specifier: ^0.0.8
|
||||
version: 0.0.8(prismjs@1.29.0)
|
||||
vue-tsc:
|
||||
specifier: ^1.8.8
|
||||
version: 1.8.8(typescript@5.1.6)
|
||||
|
@ -712,6 +721,10 @@ packages:
|
|||
resolution: {integrity: sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==}
|
||||
dev: true
|
||||
|
||||
/@types/prismjs@1.26.0:
|
||||
resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==}
|
||||
dev: true
|
||||
|
||||
/@types/trusted-types@2.0.3:
|
||||
resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==}
|
||||
dev: true
|
||||
|
@ -1147,6 +1160,14 @@ packages:
|
|||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/babel-plugin-prismjs@2.1.0(prismjs@1.29.0):
|
||||
resolution: {integrity: sha512-ehzSKYfeAz4U78zi/sfwsjDPlq0LvDKxNefcZTJ/iKBu+plsHsLqZhUeGf1+82LAcA35UZGbU6ksEx2Utphc/g==}
|
||||
peerDependencies:
|
||||
prismjs: ^1.18.0
|
||||
dependencies:
|
||||
prismjs: 1.29.0
|
||||
dev: true
|
||||
|
||||
/balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
dev: true
|
||||
|
@ -1828,7 +1849,6 @@ packages:
|
|||
/prismjs@1.29.0:
|
||||
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
@ -2069,6 +2089,17 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vite-plugin-prismjs@0.0.8(prismjs@1.29.0):
|
||||
resolution: {integrity: sha512-mBPPMS/hwVUArdqCtp/oajZT7iq1qwJDDCciNZ3R5+Q5tQUuUHXtDKuZHYnklPLElNbENf2FyuOtC4FrgxQRAA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
'@babel/core': 7.22.10
|
||||
babel-plugin-prismjs: 2.1.0(prismjs@1.29.0)
|
||||
transitivePeerDependencies:
|
||||
- prismjs
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vite@4.4.8(@types/node@20.4.8)(sass@1.64.2):
|
||||
resolution: {integrity: sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
|
|
|
@ -84,7 +84,7 @@ const handleRefreshPage = () => location.reload()
|
|||
padding: 10px;
|
||||
z-index: 1009;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
top: 470px;
|
||||
background-color: var(--kungalgame-white);
|
||||
border: 1px solid var(--kungalgame-blue-1);
|
||||
box-shadow: var(--shadow);
|
||||
|
|
|
@ -177,7 +177,6 @@ const handleChange = (editor: IDomEditor) => {
|
|||
width: 100%;
|
||||
margin: 0 auto;
|
||||
z-index: 1008;
|
||||
background-color: var(--kungalgame-trans-white-2);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
|
|
|
@ -22,24 +22,11 @@ u {
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 设置为 none,不然黑夜模式代码块会花屏 */
|
||||
* {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
/* 不显示分割线 */
|
||||
.w-e-bar-divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--kungalgame-blue-5);
|
||||
border-bottom: 1.5px solid var(--kungalgame-trans-white-9);
|
||||
&:hover {
|
||||
border-bottom: 1.5px solid var(--kungalgame-blue-5);
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
/* textarea - css vars */
|
||||
--w-e-textarea-bg-color: var(--kungalgame-white-9);
|
||||
|
|
71
src/styles/html/richText.scss
Normal file
71
src/styles/html/richText.scss
Normal file
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* 这是渲染 wangEditor 输出的富文本的样式
|
||||
* 需要注意的是,.kungalgame-topic-content 这个类名被全局注册了
|
||||
*/
|
||||
.kungalgame-topic-content {
|
||||
border-radius: 5px;
|
||||
padding: 0 10px;
|
||||
margin-top: 20px;
|
||||
overflow-x: auto;
|
||||
* {
|
||||
max-width: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--kungalgame-blue-5);
|
||||
border-bottom: 1.5px solid var(--kungalgame-trans-white-9);
|
||||
&:hover {
|
||||
border-bottom: 1.5px solid var(--kungalgame-blue-5);
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.kungalgame-topic-content p,
|
||||
.kungalgame-topic-content li {
|
||||
white-space: pre-wrap; /* 保留空格 */
|
||||
}
|
||||
|
||||
.kungalgame-topic-content blockquote {
|
||||
border-left: 8px solid var(--kungalgame-blue-1);
|
||||
padding: 10px 10px;
|
||||
margin: 10px 0;
|
||||
background-color: var(--kungalgame-trans-blue-0);
|
||||
}
|
||||
|
||||
.kungalgame-topic-content code {
|
||||
font-family: monospace;
|
||||
background-color: var(--kungalgame-trans-blue-0);
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.kungalgame-topic-content pre > code {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.kungalgame-topic-content table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.kungalgame-topic-content td,
|
||||
.kungalgame-topic-content th {
|
||||
border: 1px solid var(--kungalgame-blue-4);
|
||||
min-width: 50px;
|
||||
height: 20px;
|
||||
}
|
||||
.kungalgame-topic-content th {
|
||||
background-color: var(--kungalgame-trans-blue-0);
|
||||
}
|
||||
|
||||
.kungalgame-topic-content ul,
|
||||
.kungalgame-topic-content ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.kungalgame-topic-content input[type='checkbox'] {
|
||||
margin-right: 5px;
|
||||
}
|
|
@ -50,7 +50,7 @@ import KUNGalgameFooter from '@/components/KUNGalgameFooter.vue'
|
|||
margin: 0 auto;
|
||||
/* 容器的阴影 */
|
||||
box-shadow: var(--shadow);
|
||||
background-color: var(--kungalgame-trans-white-5);
|
||||
background-color: var(--kungalgame-trans-white-2);
|
||||
color: var(--kungalgame-font-color-3);
|
||||
border: 1px solid var(--kungalgame-blue-1);
|
||||
}
|
||||
|
@ -62,8 +62,6 @@ import KUNGalgameFooter from '@/components/KUNGalgameFooter.vue'
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: var(--kungalgame-trans-white-5);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
|
|
|
@ -32,6 +32,7 @@ const checkPublish = (topicData: EditCreateTopicRequestData) => {
|
|||
message('Title cannot be empty!', '标题不可为空!', 'warn')
|
||||
return false
|
||||
} else if (topicData.content.trim()) {
|
||||
// TODO:
|
||||
console.log(getPlainText(topicData.content.trim()).length)
|
||||
return true
|
||||
} else {
|
||||
|
|
|
@ -1,47 +1,159 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import prismjs from 'prismjs'
|
||||
import 'prismjs/themes/prism.css'
|
||||
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import '@/styles/editor/editor.scss'
|
||||
|
||||
const width = ref('')
|
||||
const html = ref<HTMLInputElement | null>(null)
|
||||
|
||||
defineProps<{
|
||||
content: string
|
||||
}>()
|
||||
|
||||
onMounted(() => {
|
||||
width.value = computed(() => {
|
||||
return 100 + '%'
|
||||
}).value
|
||||
// 高亮渲染
|
||||
prismjs.highlightAll()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 内容区右侧的话题展示区,这里富文本必须用 v-html,已经确定文本经过三次处理 -->
|
||||
<!-- 这里用的 v-html,样式是页面刷新后才会有的,所以必须动态绑定样式 -->
|
||||
<div class="w-e-text-container">
|
||||
<div v-html="content" data-slate-editor></div>
|
||||
<div class="kungalgame-topic-content">
|
||||
<div v-html="content" ref="html"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/html/richText.scss';
|
||||
|
||||
/* 内容区右侧的话题展示区 */
|
||||
.w-e-text-container {
|
||||
.kungalgame-topic-content {
|
||||
/** 100 + 20 + 20 + 1 = 141px */
|
||||
width: calc(100% - 141px);
|
||||
font-size: 15px;
|
||||
padding: 17px;
|
||||
border-left: 1px solid var(--kungalgame-blue-1);
|
||||
color: var(--kungalgame-font-color-3);
|
||||
}
|
||||
|
||||
.kungalgame-topic-content {
|
||||
border-radius: 5px;
|
||||
padding: 0 10px;
|
||||
margin-top: 20px;
|
||||
overflow-x: auto;
|
||||
:deep(*) {
|
||||
max-width: v-bind(width);
|
||||
max-width: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
:deep(a) {
|
||||
color: var(--kungalgame-blue-5);
|
||||
border-bottom: 1.5px solid var(--kungalgame-trans-white-9);
|
||||
&:hover {
|
||||
border-bottom: 1.5px solid var(--kungalgame-blue-5);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(span) {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
/* 下面全部是 wangEditor 原生样式 */
|
||||
|
||||
:deep(p, li) {
|
||||
white-space: pre-wrap; /* 保留空格 */
|
||||
}
|
||||
|
||||
:deep(blockquote) {
|
||||
border-left: 8px solid var(--kungalgame-blue-1);
|
||||
padding: 10px 10px;
|
||||
margin: 10px 0;
|
||||
background-color: var(--kungalgame-trans-blue-0);
|
||||
}
|
||||
|
||||
:deep(pre > code) {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
:deep(code) {
|
||||
font-family: monospace;
|
||||
background-color: var(--kungalgame-trans-blue-0);
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
text-shadow: none;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
:deep(table) {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
:deep(td, th) {
|
||||
border: 1px solid var(--kungalgame-blue-4);
|
||||
min-width: 50px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
:deep(th) {
|
||||
background-color: var(--kungalgame-trans-blue-0);
|
||||
}
|
||||
|
||||
:deep(ul, ol) {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
:deep(input[type='checkbox']) {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤 prism 相关样式的代码
|
||||
*/
|
||||
|
||||
/* 这一步把 prism 的背景过滤掉 */
|
||||
:deep(pre) {
|
||||
padding: 0;
|
||||
border: 1px solid var(--kungalgame-blue-4);
|
||||
background-color: var(--kungalgame-trans-white-9);
|
||||
}
|
||||
|
||||
:deep(.token) {
|
||||
background-color: var(--kungalgame-trans-white-9);
|
||||
}
|
||||
|
||||
:deep(.toolbar) {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
& > .toolbar-item {
|
||||
/* 这里直接根据 DOM 结构写的,写的不是很明了,要怪就怪写 prism 插件的人吧 hhh */
|
||||
button,
|
||||
& > span {
|
||||
background-color: var(--kungalgame-trans-white-9);
|
||||
border: 1px solid var(--kungalgame-blue-4);
|
||||
color: var(--kungalgame-blue-4);
|
||||
box-shadow: unset;
|
||||
margin-left: 10px;
|
||||
border-radius: 0;
|
||||
padding: 2px 5px;
|
||||
&:hover {
|
||||
background-color: var(--kungalgame-blue-4);
|
||||
color: var(--kungalgame-white);
|
||||
}
|
||||
}
|
||||
button {
|
||||
cursor: pointer;
|
||||
&:focus {
|
||||
color: var(--kungalgame-red-4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.w-e-text-container {
|
||||
.kungalgame-topic-content {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,42 @@ import vue from '@vitejs/plugin-vue'
|
|||
import { visualizer } from 'rollup-plugin-visualizer'
|
||||
// 导入 vite tsx 支持
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
// 导入 prismjs 高亮
|
||||
import { prismjsPlugin } from 'vite-plugin-prismjs'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue(), visualizer() as PluginOption, vueJsx()],
|
||||
plugins: [
|
||||
vue(),
|
||||
visualizer() as PluginOption,
|
||||
vueJsx(),
|
||||
prismjsPlugin({
|
||||
languages: [
|
||||
'javascript',
|
||||
'css',
|
||||
'markup',
|
||||
'go',
|
||||
'java',
|
||||
'python',
|
||||
'nginx',
|
||||
'yaml',
|
||||
'bash',
|
||||
'sql',
|
||||
'git',
|
||||
'markdown',
|
||||
'php',
|
||||
'vim',
|
||||
],
|
||||
plugins: [
|
||||
'line-numbers',
|
||||
'show-language',
|
||||
'toolbar',
|
||||
'copy-to-clipboard',
|
||||
],
|
||||
theme: 'okaidia',
|
||||
css: true,
|
||||
}),
|
||||
],
|
||||
/* src 别名为 @ */
|
||||
resolve: {
|
||||
alias: {
|
||||
|
|
Loading…
Reference in a new issue