vue3实现markdown文档转HTML并可更换样式

vue3实现markdown文档转HTML

安装marked

npm install marked
<template><!-- 后台可添加样式编辑器 --><div class="markdown-editor" :class="{ 'fullscreen': isFullscreen, 'preview-mode': isPreviewMode }"><div class="editor-container"><!-- Markdown 输入区域 --><div class="markdown-input" v-show="!isPreviewMode"><el-card class="editor-card"><template #header><div class="header"><div class="toolbar"><!-- 导出文件 --><el-button-group><el-button size="small" @click="exportFile" title="导出文件"><el-icon><Download /></el-icon></el-button></el-button-group><!-- 格式化工具 --><el-button-group><el-button size="small" @click="insertMarkdown('heading')" title="标题"><strong>H</strong></el-button><el-button size="small" @click="insertMarkdown('bold')" title="加粗"><strong>B</strong></el-button><el-button size="small" @click="insertMarkdown('italic')" title="斜体"><em>I</em></el-button><el-button size="small" @click="insertMarkdown('quote')" title="引用"><el-icon><ChatLineSquare /></el-icon></el-button><el-button size="small" @click="insertMarkdown('code')" title="代码"><el-icon><Notebook /></el-icon></el-button><el-button size="small" @click="insertMarkdown('link')" title="链接"><el-icon><Link /></el-icon></el-button><el-button size="small" @click="insertMarkdown('image')" title="图片"><el-icon><Picture /></el-icon></el-button><el-button size="small" @click="insertMarkdown('list')" title="列表"><el-icon><List /></el-icon></el-button></el-button-group><!-- 预览控制 --><el-button-group><el-button size="small" @click="togglePreview" title="预览模式" :type="isPreviewMode ? 'primary' : ''"class="preview-button"><el-icon><View /></el-icon>预览</el-button></el-button-group></div></div></template><el-input v-model="markdownContent" type="textarea" :rows="20" placeholder="请输入 Markdown 内容..."@input="handleContentChange" /></el-card></div><!-- HTML 预览区域 --><div class="preview-container"><el-card class="preview-card"><template #header><div class="header"><div><span v-show="!isPreviewMode">HTML 预览</span><el-button v-show="isPreviewMode" size="small" @click="togglePreview" title="退出预览"class="preview-exit-button"><el-icon><Close /></el-icon>退出预览</el-button></div><div style="display: flex; gap: 10px;align-items: center;"><!-- 预览页面导出为图片 --><!-- <div class="export-img"><el-button size="small" @click="exportImg" title="下载png图片"><el-icon><Download /></el-icon></el-button></div> --></div></div></template><div class="preview-content"><div class="card" ref="card"><div class="card-header"></div><div class="card-content"><div class="card-content-inner" v-html="htmlContent"></div></div><div class="card-footer"></div></div></div></el-card></div><div><!-- 样式选择区 --><el-card class="style-card"><template #header><div class="header"><span>style 选择</span><el-button @click="handleAddStyle" size="small"  title="添加样式"class="preview-exit-button"><el-icon><Close /></el-icon>添加样式</el-button></div></template><!-- 样式列表 --><div class="style-content"><divv-for="style in styleList":key="style.value"class="style-item":class="{ 'active': currentStyle === style.value }"@click="setStyle(style.value)">{{ style.name }}</div></div></el-card></div></div><!-- 添加样式弹窗 --><AddStyleDialog ref="addStyleDialogRef" @confirm="handleStyleConfirm" @preview="handleStylePreview"></AddStyleDialog></div>
</template><script setup lang="ts">
import AddStyleDialog from './components/AddStyleDialog.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ref, onMounted, reactive,onUnmounted } from 'vue'
import { marked } from 'marked'
import {Download, ChatLineSquare, Notebook, Link,Picture, List, View, FullScreen
} from '@element-plus/icons-vue'const addStyleDialogRef = ref()
const editorContainerRef = ref<HTMLElement>()
const addedStyles = ref<{name: string; element: HTMLStyleElement}[]>([])const handleAddStyle = () => {addStyleDialogRef.value?.open()
}// 确认添加样式
const handleStyleConfirm = () => {}// 预览样式
const handleStylePreview = (styleData: { name: string; code: string }) => {currentStyle.value = ''// 移除所有旧的样式标签const oldStyles = document.querySelectorAll('style[data-md-style]')oldStyles.forEach(style => style.remove())// 创建新的样式标签const styleTag = document.createElement('style')styleTag.type = 'text/css'styleTag.setAttribute('data-md-style', 'true') // 添加标识,方便后续删除// 根据选择的样式设置内容styleTag.innerHTML = styleData.code// 插入到页面头部document.head.appendChild(styleTag)
}// 样式列表
const styleList = [{name: '样式1',value: 'style1'},{name: '样式2',value: 'style2'}
]// 编辑器状态
const markdownContent = ref('')
const currentStyle = ref('style1') // 默认选择样式1
const htmlContent = ref('')
const textareaRef = ref<HTMLTextAreaElement | null>(null)
const card: any = ref('')
const isPreviewMode = ref(false)
const isFullscreen = ref(false)// 编辑历史
const history = reactive({past: [] as string[],future: [] as string[]
})// 文件操作函数
const exportFile = () => {const blob = new Blob([markdownContent.value], { type: 'text/markdown' })const url = URL.createObjectURL(blob)const a = document.createElement('a')a.href = urla.download = 'markdown.md'a.click()URL.revokeObjectURL(url)
}// 编辑历史操作
const saveHistory = () => {history.past.push(markdownContent.value)history.future = []if (history.past.length > 50) {history.past.shift()}
}// 预览控制
const togglePreview = () => {isPreviewMode.value = !isPreviewMode.value
}const toggleFullscreen = () => {const element = document.documentElementif (!isFullscreen.value) {if (element.requestFullscreen) {element.requestFullscreen()}} else {if (document.exitFullscreen) {document.exitFullscreen()}}isFullscreen.value = !isFullscreen.value
}let cssData1 = `
.card {max-width: 420px;background: #ffffff;border-radius: 16px;overflow: hidden;box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;line-height: 1.65;color: #333;margin: 24px auto;transition: all 0.3s ease;
}.card:hover {transform: translateY(-4px);box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}.card-header {background: #ff2442;height: 6px;border-radius: 16px 16px 0 0;
}.card-content {padding: 32px;
}.card-content-inner {padding: 0;
}.card-content-inner > *:first-child {margin-top: 0;
}.card-content-inner > *:last-child {margin-bottom: 0;
}.card-content-inner h1 {font-size: 24px;font-weight: 700;margin: 0 0 24px;color: #1a1a1a;letter-spacing: -0.2px;line-height: 1.4;position: relative;padding-bottom: 16px;
}.card-content-inner h1:after {content: "";position: absolute;bottom: 0;left: 0;width: 36px;height: 3px;background: #ff2442;border-radius: 2px;
}.card-content-inner h2 {font-size: 20px;font-weight: 600;margin: 32px 0 20px;color: #2c2c2c;padding-bottom: 8px;border-bottom: 1px solid #f5f5f5;
}.card-content-inner p {font-size: 16px;margin: 0 0 24px;color: #444;text-align: justify;hyphens: auto;
}.card-content-inner ol,
.card-content-inner ul {padding-left: 24px;margin: 0 0 24px;
}.card-content-inner ol li,
.card-content-inner ul li {margin-bottom: 12px;padding-left: 8px;
}.card-content-inner ol li {position: relative;counter-increment: list-counter;
}.card-content-inner ol li::before {content: counter(list-counter);position: absolute;left: -26px;top: 2px;width: 20px;height: 20px;background: #ffebee;color: #ff2442;border-radius: 50%;display: flex;align-items: center;justify-content: center;font-size: 12px;font-weight: 600;
}.card-content-inner ul li::before {content: "•";color: #ff2442;font-weight: bold;display: inline-block;width: 1em;margin-left: -1em;
}.card-content-inner strong {color: #ff2442;font-weight: 600;
}.card-content-inner a {color: #ff2442;text-decoration: none;border-bottom: 1px solid rgba(255, 36, 66, 0.3);transition: all 0.2s ease;
}.card-content-inner a:hover {color: #e01e3c;border-bottom-color: #e01e3c;
}.card-content-inner code {background: #fff0f2;padding: 2px 6px;border-radius: 4px;font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;font-size: 14px;color: #ff2442;
}.card-content-inner pre {background: #fffafb;padding: 18px;border-radius: 8px;overflow-x: auto;margin: 0 0 24px;font-size: 14px;line-height: 1.5;border-left: 3px solid #ff2442;
}.card-content-inner pre code {background: none;padding: 0;color: #444;font-size: 14px;
}.card-content-inner blockquote {border-left: 3px solid #ffcdd2;padding: 4px 20px 4px 20px;margin: 0 0 24px;color: #666;background: #fffafa;border-radius: 0 8px 8px 0;font-style: italic;
}.card-content-inner hr {border: 0;height: 1px;background: linear-gradient(to right, rgba(255, 36, 66, 0.1), transparent);margin: 32px 0;
}.card-footer {padding: 16px 32px;background: #fffafa;border-top: 1px solid #f9f0f0;color: #999;font-size: 13px;display: flex;justify-content: space-between;
}/* 小红书特色元素 */
.card-content-inner h1 + p {font-size: 17px;color: #666;margin-top: -8px;margin-bottom: 28px;
}.card-content-inner img {max-width: 100%;border-radius: 12px;margin: 24px 0;display: block;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}/* 留白增强 */
.card-content-inner p + h2 {margin-top: 36px;
}.card-content-inner ul + h2,
.card-content-inner ol + h2 {margin-top: 40px;
}
`
let cssData2 = `
.card {max-width: 680px;background: #ffffff;border-radius: 12px;overflow: hidden;box-shadow: 0 8px 30px rgba(0, 0, 0, 0.06);font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, sans-serif;line-height: 1.7;color: #333;margin: 40px auto;transition: transform 0.3s ease;
}.card:hover {transform: translateY(-3px);
}.card-header {background: linear-gradient(135deg, #6e8efb, #a777e3);height: 6px;border-radius: 12px 12px 0 0;
}.card-content {padding: 40px;
}.card-content-inner {padding: 0;
}.card-content-inner > *:first-child {margin-top: 0;
}.card-content-inner > *:last-child {margin-bottom: 0;
}.card-content-inner h1 {font-size: 28px;font-weight: 700;margin: 0 0 30px;color: #1a1a1a;letter-spacing: -0.01em;line-height: 1.3;
}.card-content-inner h2 {font-size: 22px;font-weight: 600;margin: 40px 0 20px;color: #2c2c2c;padding-bottom: 8px;border-bottom: 1px solid #f0f0f0;
}.card-content-inner h3 {font-size: 18px;font-weight: 600;margin: 35px 0 15px;color: #3a3a3a;
}.card-content-inner p {font-size: 17px;margin: 0 0 28px;color: #444;text-align: justify;hyphens: auto;
}.card-content-inner ol,
.card-content-inner ul {padding-left: 24px;margin: 0 0 28px;
}.card-content-inner ol li,
.card-content-inner ul li {margin-bottom: 12px;padding-left: 12px;
}.card-content-inner ol li {position: relative;counter-increment: list-counter;
}.card-content-inner ol li::before {content: counter(list-counter);position: absolute;left: -24px;top: 0;width: 24px;height: 24px;background: #f5f7ff;color: #6e8efb;border-radius: 50%;display: flex;align-items: center;justify-content: center;font-size: 13px;font-weight: 500;
}.card-content-inner ul li::before {content: "•";color: #a777e3;font-weight: bold;display: inline-block;width: 1em;margin-left: -1em;
}.card-content-inner strong {color: #2c2c2c;font-weight: 600;
}.card-content-inner em {font-style: italic;color: #555;
}.card-content-inner a {color: #6e8efb;text-decoration: none;border-bottom: 1px solid rgba(110, 142, 251, 0.3);transition: all 0.2s ease;
}.card-content-inner a:hover {color: #a777e3;border-bottom-color: #a777e3;
}.card-content-inner code {background: #f8f9ff;padding: 3px 6px;border-radius: 4px;font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;font-size: 15px;color: #6e8efb;
}.card-content-inner pre {background: #f8f9ff;padding: 20px;border-radius: 8px;overflow-x: auto;margin: 0 0 30px;font-size: 15px;line-height: 1.5;border-left: 3px solid #a777e3;
}.card-content-inner pre code {background: none;padding: 0;color: #444;font-size: 15px;
}.card-content-inner blockquote {border-left: 3px solid #e0e0e0;padding: 4px 20px 4px 24px;margin: 0 0 30px;color: #555;font-style: italic;background: #fafbff;border-radius: 0 8px 8px 0;
}.card-content-inner hr {border: 0;height: 1px;background: #f0f0f0;margin: 40px 0;
}.card-footer {padding: 20px 40px;background: #fafbff;border-top: 1px solid #f0f0f0;color: #777;font-size: 14px;display: flex;justify-content: space-between;
}/* 留白增强 */
.card-content-inner p + h2,
.card-content-inner ul + h2,
.card-content-inner ol + h2 {margin-top: 50px;
}.card-content-inner p + h3 {margin-top: 40px;
}.card-content-inner img {max-width: 100%;border-radius: 8px;margin: 30px 0;display: block;
}
`
const setStyle = (e:any) => {currentStyle.value = e// 移除所有旧的样式标签const oldStyles = document.querySelectorAll('style[data-md-style]')oldStyles.forEach(style => style.remove())// 创建新的样式标签const styleTag = document.createElement('style')styleTag.type = 'text/css'styleTag.setAttribute('data-md-style', 'true') // 添加标识,方便后续删除// 根据选择的样式设置内容styleTag.innerHTML = currentStyle.value === 'style1' ? cssData1 : cssData2// 插入到页面头部document.head.appendChild(styleTag)
}// 配置marked选项
marked.setOptions({breaks: true, // 将回车转换为 <br>gfm: true, // 启用 GitHub 风格的 Markdown// sanitize: false, // 允许HTML标签
})// 使用marked进行Markdown转HTML
const convertMarkdownToHtml = (markdown: string): any => {return marked(markdown)
}// 处理内容变化
const handleContentChange = () => {saveHistory()updatePreview()
}// 更新预览
const updatePreview = () => {htmlContent.value = convertMarkdownToHtml(markdownContent.value)
}// 在光标位置插入Markdown语法
const insertMarkdown = (type: string) => {const textarea = document.querySelector('.el-textarea__inner') as HTMLTextAreaElementif (!textarea) returnconst start = textarea.selectionStartconst end = textarea.selectionEndconst selected = markdownContent.value.substring(start, end)let insertion = ''switch (type) {case 'bold':insertion = `**${selected || '粗体文本'}**`breakcase 'italic':insertion = `*${selected || '斜体文本'}*`breakcase 'heading':insertion = `\n## ${selected || '标题'}\n`breakcase 'link':insertion = `[${selected || '链接文本'}](https://example.com)`breakcase 'list':insertion = `\n- ${selected || '列表项'}\n- 列表项\n- 列表项\n`breakcase 'quote':insertion = `\n> ${selected || '引用文本'}\n`breakcase 'code':insertion = selected ?`\`\`\`\n${selected}\n\`\`\`` :`\`\`\`\n代码块\n\`\`\``breakcase 'image':insertion = `![${selected || '图片描述'}](https://example.com/image.jpg)`breakdefault:return}const newContent =markdownContent.value.substring(0, start) +insertion +markdownContent.value.substring(end)markdownContent.value = newContent// 保存历史记录saveHistory()// 更新预览updatePreview()// 聚焦回文本区域setTimeout(() => {textarea.focus()const newCursorPos = start + insertion.lengthtextarea.setSelectionRange(newCursorPos, newCursorPos)}, 0)
}// 复制HTML到剪贴板
const copyHtml = () => {navigator.clipboard.writeText(htmlContent.value).then(() => {ElMessage({message: 'HTML已复制到剪贴板',type: 'success',duration: 2000})}).catch(err => {ElMessage({message: '复制失败: ' + err,type: 'error',duration: 2000})})
}// 组件挂载时初始化
onMounted(() => {// 设置示例Markdown内容markdownContent.value = `# MD2Card> MD2Card 是一个 markdown 转知识卡片工具,可以让你用 Markdown 制作优雅的图文海报。 🌟![](https://picsum.photos/600/300)## 它的主要功能:1. 将 Markdown 转化为**知识卡片**
2. 多种主题风格任你选择
3. 长文自动拆分,或者根据 markdown --- 横线拆分
4. 可以复制图片到剪贴板,或者下载为PNG、SVG图片
5. 所见即所得
6. 免费
`updatePreview()// 初始化应用默认样式setStyle(currentStyle.value)
})onUnmounted(()=>{// 移除所有的样式标签const oldStyles = document.querySelectorAll('style[data-md-style]')oldStyles.forEach(style => style.remove())
})
</script><style scoped lang="scss">
.markdown-editor {height: calc(100vh - 40px);box-sizing: border-box;overflow: hidden;
}.markdown-editor.fullscreen {position: fixed;top: 0;left: 0;right: 0;bottom: 0;z-index: 9999;padding: 0;
}.editor-container {height: 100%;display: flex;.markdown-input,.preview-container {flex: 1;height: 100%;overflow-y: auto;}.style-card{width: 260px;}
}.editor-card,
.preview-card,
.style-card {height: 100%;position: relative;transition: all 0.3s ease;display: flex;flex-direction: column;.preview-content{height: auto;}:deep(.el-card__body) {flex: 1;overflow: auto;padding: 0;display: flex;flex-direction: column;}/* Editor card specific styles */&.editor-card {:deep(.el-textarea) {flex: 1;display: flex;flex-direction: column;.el-textarea__inner {flex: 1;resize: none;border: none;box-shadow: none;padding: 20px;}}}/* Preview card specific styles */&.preview-card {height: 100%;:deep(.el-card__body) {// height: 100%;// overflow: auto;padding: 0;display: flex;flex-direction: column;}.card {flex: 1;min-height: 0; /* 修复flex容器滚动问题 */padding: 20px;}.card-content {height: auto;}}/* Style card specific styles */&.style-card {.style-content {flex: 1;overflow: auto;padding: 10px;.style-item {padding: 8px 12px;margin-bottom: 8px;border: 1px solid #e0deed;color: #0f0a29;background: #fff;border-radius: 4px;cursor: pointer;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;transition: all 0.2s ease;&:hover {border-color: #a598e5;}&.active {border-color: #4c33cc;color: #4c33cc;background: #f6f5fc;font-weight: 500;}}}}
}.header {display: flex;justify-content: space-between;align-items: center;
}.toolbar {display: flex;gap: 10px;
}/* 工具栏按钮组之间的分隔 */
.toolbar .el-button-group+.el-button-group {margin-left: 8px;padding-left: 8px;border-left: 1px solid #dcdfe6;
}/* 激活状态的按钮样式 */
:deep(.el-button--primary) {background-color: var(--el-color-primary-light-3);border-color: var(--el-color-primary-light-3);color: #fff;
}/* 预览模式下的布局调整 */
.preview-button {z-index: 1000;
}.preview-mode {:deep(.el-row) {.el-col:first-child {display: none;}.el-col:last-child {width: 100%;position: relative;.preview-exit-button {position: absolute;top: 76px;left: 26px;z-index: 2000;background-color: #fff;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);color: var(--el-text-color-primary);&:hover {background-color: var(--el-color-primary-light-9);}}}}.header .preview-button {display: none;}
}/* 预览按钮样式 */
.preview-button {margin-right: 10px;
}/* 预览模式下的退出按钮样式 */
.preview-exit-button {/* position: absolute;top: 10px;left: 10px;z-index: 2000; */background-color: #fff;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);color: var(--el-text-color-primary);&:hover {background-color: var(--el-color-primary-light-9);}
}/* 全屏模式下的样式调整 */
.fullscreen {:deep(.el-main) {padding: 0;}.editor-card,.preview-card {border-radius: 0;}
}/* 响应式布局 */
@media screen and (max-width: 768px) {.toolbar {flex-wrap: wrap;}.toolbar .el-button-group {margin-bottom: 8px;}.header {flex-direction: column;align-items: flex-start;gap: 10px;}
}
</style>

添加弹窗

<template><el-dialog v-model="dialogVisible" title="添加CSS样式" width="700px" top="5vh"><div class="css-editor-container"><!-- 样式名称输入框 --><el-inputv-model="styleName"placeholder="样式名称(如:my-style)"style="margin-bottom: 15px"/><!-- 带有基本语法高亮的 CSS 编辑器 --><div class="code-editor-wrapper"><textareav-model="cssCode"@input="updateHighlight"class="code-input"spellcheck="false"placeholder="请输入CSS代码..."></textarea></div></div><!-- 底部按钮 --><template #footer><span class="dialog-footer"><el-button @click="handlePreview">预览</el-button><el-button type="primary" @click="handleConfirm">确认</el-button></span></template></el-dialog>
</template><script setup>
import { ref, computed } from 'vue'const dialogVisible = ref(false)
const styleName = ref('')
const cssCode = ref('')const updateHighlight = () => {// 自动更新高亮
}// 打开对话框
const open = () => {dialogVisible.value = truecssCode.value = ''styleName.value = ''
}// 确认按钮处理函数
const emit = defineEmits(['confirm','preview'])
const handleConfirm = () => {// 打印输入的CSS样式和样式名称console.log('样式名称:', styleName.value)console.log('CSS样式:', cssCode.value)// // 触发confirm事件,将数据传递给父组件// emit('confirm', {//   name: styleName.value.trim(),//   code: cssCode.value.trim()// })// // 关闭对话框// dialogVisible.value = false
}const handlePreview = () => {// 预览CSS样式console.log('预览CSS样式:', cssCode.value)// // 触发confirm事件,将数据传递给父组件emit('preview', {name: styleName.value.trim(),code: cssCode.value.trim()})// 关闭对话框dialogVisible.value = false
}defineExpose({ open })
</script><style scoped>
.css-editor-container {display: flex;flex-direction: column;
}.code-editor-wrapper {position: relative;height: 300px;border: 1px solid #dcdfe6;border-radius: 4px;overflow: hidden;margin-bottom: 15px;
}.code-highlight {position: absolute;top: 0;left: 0;width: 100%;height: 100%;margin: 0;padding: 10px;font-family: 'Courier New', monospace;font-size: 14px;line-height: 1.5;white-space: pre-wrap;word-wrap: break-word;overflow: auto;background: #f5f7fa;pointer-events: none;z-index: 1;
}.code-input {position: absolute;top: 0;left: 0;width: 100%;height: 100%;margin: 0;padding: 10px;font-family: 'Courier New', monospace;font-size: 14px;line-height: 1.5;color: #333;background: transparent;border: none;outline: none;resize: none;z-index: 2;
}/* 语法高亮样式 */
.comment {color: #999;font-style: italic;
}.punctuation {color: #333;
}.selector {color: #905;
}.property {color: #07a;
}.value {color: #690;
}.dialog-footer {display: flex;justify-content: flex-end;gap: 10px;
}
</style>

注:
这是一个静态的功能参考,在这里的样式使用的给定的样式切换,后续对接接口后可换成一个下拉列表,选择后通过接口返回的css样式数据来更换一样的效果。这里预想的是回台直接返回样式的一个字符串格式内容,其他的想法可自行修改。
这里的css样式只是使用了一个文本输入框实现了功能效果,不带样式代码高亮显示的功能

在这里插入图片描述
在这里插入图片描述

样式可使用deepseek来生成

复制这段文字,修改生成条件就可生成不同的样式的css代码,直接粘贴到css代码输入框中,点击预览就可看到新css样式的效果。

/* 
可以让 deepseek 等大模型实现css,实现卡片样式自定义,以下是发送的提示词模板:我需要在 md2card.com 实现自定义小红书卡片样式
以下是 md2card.com 中卡片的HTML结构:
```html
<div class="card"><div class="card-header"></div><div class="card-content"><div class="card-content-inner"><h1 data-text="标题">标题</h1><h2 data-text="标题二">标题二</h2><p>内容</p><ol><li data-index="0">列表</li></ol></div></div><div class="card-footer"></div>
</div>`card-content-inner` 为 markdown 编译后的内容区域,还包括标题、列表、引用、代码、加粗等常见 markdown 编译的内容请为我设计一张卡片,其风格为“简约现代”,可以进一步融入“留白”的设计理念,应用“小红书知识分享”的场景
【设计风格要求】:
【输入要求】:只需要返回 css 代码*/
.card {
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Temu 实时获取商品动态:一个踩坑后修好的抓数脚本笔记

Temu 作为一个增长迅猛的购物平台&#xff0c;其商品价格、库存等信息&#xff0c;对许多做运营分析的小伙伴来说非常有参考价值。 我在写这个小工具的时候&#xff0c;踩了很多坑&#xff0c;特别记录下来&#xff0c;希望对你有用。 初版代码&#xff1a;想当然的“直接来一下…

【软考高级系统架构论文】论数据分片技术及其应用

论文真题 数据分片就是按照一定的规则,将数据集划分成相互独立、 正交的数据子集,然后将数据子集分布到不同的节点上。通过设计合理的数据分片规则,可将系统中的数据分布在不同的物理数据库中,达到提升应用系统数据处理速度的目的。 请围绕“论数据分片技术及其应用”论题…

VR飞夺泸定桥沉浸式历史再现​

当你戴上 VR 设备开启这场震撼人心的 VR 飞夺泸定桥体验&#xff0c;瞬间就会被拉回到 1935 年那个战火纷飞的 VR 飞夺泸定桥的岁月&#xff0c;置身于泸定桥的西岸 。映入眼帘的是一座由 13 根铁索组成的泸定桥&#xff0c;它横跨在波涛汹涌的大渡河上&#xff0c;桥下江水咆哮…

libwebsockets编译

#安装 libwebsocket git clone https://github.com/warmcat/libwebsockets && \ mkdir libwebsockets/build && cd libwebsockets/build && \ cmake -DMAKE_INSTALL_PREFIX:PATH/usr -DCMAKE_C_FLAGS"-fpic" .. && \ make &&…

使用docker部署epg节目单,同时管理自己的直播源

配置 Docker 环境 拉取镜像并运行&#xff1a; docker run -d \--name php-epg \-v /etc/epg:/htdocs/data \-p 5678:80 \--restart unless-stopped \taksss/php-epg:latest 默认数据目录为 /etc/epg &#xff0c;根据需要自行修改 默认端口为 5678 &#xff0c;根据需要自行修…

H5新增属性

✅ 一、表单相关新增属性&#xff08;Form Attributes&#xff09; 这些属性增强了表单功能&#xff0c;提升用户体验和前端验证能力。 1. placeholder 描述&#xff1a;在输入框为空时显示提示文本。示例&#xff1a; <input type"text" placeholder"请输…

【C++】简单学——引用

引用的概念 为一个变量指定一个别名 引用的规则 用之前要初始化使用了之后就不能修改指向了&#xff08;对一个引用赋值实际上是对原本被引用的那个值进行赋值&#xff0c;而不是改变指向&#xff09;一个对象可以同时有多个引用 问&#xff1a;引用可以完全代替指针吗&…

C#编程与1200PLC S7通信

读取q0.0的状态,i0.0的状态实时在窗口更新 PLC里写一个程序 用常闭按钮接i0.0信号 &#xff0c;延时接通Q0.0 按按钮&#xff0c;上位机测试效果, 2396fcfa823aa951d 程序前提是引用了S7通信文件 using Sharp7; using System; using System.Collections.Generic; using S…

el-table复选框分页多选

场景&#xff1a; 你想要对el-table表格数据进行批量处理&#xff0c;会使用复选框&#xff0c;但如果表格的数据是分页请求回来的&#xff0c;则在切换页码的时候&#xff0c;之前选中的数据会被清空掉&#xff0c;本文就是为了解决这个问题。 解决思路&#xff1a; 主要分…

大IPD之——学习华为的市场队伍建设(二十)

企业要生存&#xff0c;就必须要拥有自己的核心竞争力。这样在行业内与其他企业竞争时&#xff0c;才能立于不败之地&#xff0c;而伴随着企业的市场化&#xff0c;市场机制对企业价值创造的影响力越来越大。30多年来&#xff0c;华为高度重视市场队伍与市场能力建设&#xff0…

Datawhlale_快乐学习大模型_task02_NLP 基础概念

书籍地址 简要总结一下个人理解 文章目录 1.1 NLP1.2 发展历程1.3 NLP任务1.3.1 中文分词1.3.2 子词切分1.3.3 词性标注1.3.4 文本分类1.3.5 实体识别1.3.6 关系抽取1.3.7 文本摘要1.3.8 机器翻译1.3.9 自动问答 1.4 文本表示的发展1.4.1 词向量1.4.2 语言模型1.4.3 Word2Vec…

AUTOSAR图解==>AUTOSAR_AP_SWS_Persistency

AUTOSAR 持久化功能集群解析 1. 引言 AUTOSAR (AUTomotive Open System ARchitecture) 适配平台中的持久化功能集群(Persistency Functional Cluster)是一个核心组件&#xff0c;为应用程序提供数据持久化服务。本文档详细分析了AUTOSAR持久化功能集群的架构、主要组件和工作…

Ollama常用命令详解:本地大语言模型管理指南

前言 Ollama是一个强大的本地大语言模型管理工具&#xff0c;让我们可以轻松地在本地部署和运行各种开源大模型。本文将详细介绍Ollama的核心命令使用方法&#xff0c;帮助您快速上手本地AI模型的管理和使用。 1. 查看已安装模型 - ollama list 基本用法 ollama list功能说…

[免费]SpringBoot+Vue共享单车信息系统【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue共享单车信息系统【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue共享单车信息系统 Java毕业设计_哔哩哔哩_bilibili 项目介绍 快速发展的社会中&#xff…

内网提权-DC-3靶场实验(Ubantu16.04)

靶场地址 https://download.vulnhub.com/dc/DC-3-2.zip 打开DC-3 使用kali扫描获取靶场ip 目录扫描获取后台地址 弱口令admin/snoopy进入后台 此处可写入一句话木马 创建文件写入一句话木马 哥斯拉上线 使用lsb_release -a命令查看内核版本 方法一 使用ubuntu漏洞库发现该…

Nginx:互斥锁 accept_mutex配置

如何配置 Nginx 的互斥锁 accept_mutex 1. 理解 accept_mutex 的作用 accept_mutex 是 Nginx 用于控制多工作进程&#xff08;worker processes&#xff09;接收新连接时避免「惊群问题&#xff08;Thundering Herd&#xff09;」的机制。 启用时&#xff08;accept_mutex o…

aws(学习笔记第四十六课) codepipeline-build-deploy

文章目录 aws(学习笔记第四十六课) codepipeline-build-deploy学习内容:1. 代码链接及整体架构1.1 代码链接1.2 整体架构1.2.1 初始化阶段的`codecommit repo`以及`codebuild project`设定1.2.2 创建`vpc`,`public alb`,`alb listener`以及`fargate service`等1.2.3 创建`so…

Vue 项目中的组件职责划分评审与组件设计规范制定

在现代前端系统中&#xff0c;Vue&#xff08;无论是 2.x 还是 3.x&#xff09;提供了良好的组件化机制&#xff0c;为构建复杂交互系统打下了基础。然而&#xff0c;随着项目规模增长&#xff0c;组件职责不清、代码重叠、维护困难等问题频发&#xff0c;严重影响开发效率与可…

react 的过渡动画

一、React的过渡动画 1、react-transition-group 在开发中&#xff0c;我们想要给一个组件的显示和消失&#xff0c;添加某种过渡动画&#xff0c;可以很好的增加用户体验&#xff0c; React社区为我们提供了react-transition-group用来完成过渡动画&#xff0c; React曾为…

深度学习:PyTorch人工神经网络优化方法分享(1)

本文目录&#xff1a; 一、从梯度角度入手&#xff08;一&#xff09;梯度下降算法回顾&#xff08;二&#xff09;常用优化算法1.SGD&#xff08;Stochastic Gradient Descent&#xff09;- 随机梯度下降2.BGD (Batch Gradient Descent) - 批量梯度下降3.MBGD (Mini-Batch Gra…