chore: 前端首次基线提交

运行必须源码:src/、public/、index.html、package.json 及构建配置。
已忽略运行非必须:node_modules/、dist/、.env*。
This commit is contained in:
WorkBuddy
2026-08-17 12:25:42 +08:00
commit e8f0195b61
52 changed files with 16866 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
<template>
<div class="rich-editor" :style="{ zIndex: 3000 }">
<Toolbar
class="rich-editor__toolbar"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
class="rich-editor__body"
:style="{ height: height, overflowY: 'hidden' }"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
@onChange="handleChange"
/>
</div>
</template>
<script setup>
import '@wangeditor/editor/dist/css/style.css'
import { onBeforeUnmount, ref, shallowRef, watch } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { useAuthStore } from '@/stores/auth'
const props = defineProps({
modelValue: { type: String, default: '' },
height: { type: String, default: '320px' },
placeholder: { type: String, default: '请输入内容…' },
mode: { type: String, default: 'default' }, // 'default' | 'simple'
})
const emit = defineEmits(['update:modelValue'])
const authStore = useAuthStore()
// 编辑器实例:必须用 shallowRef
const editorRef = shallowRef()
// 内部 HTML 值
const valueHtml = ref(props.modelValue || '')
// 外部值变化时同步到编辑器(避免打开弹窗时内容不回填)
watch(
() => props.modelValue,
(val) => {
if (val !== valueHtml.value) {
valueHtml.value = val || ''
}
}
)
const toolbarConfig = {
excludeKeys: [
'group-video', // 轮播正文不需要视频
'insertTable',
'codeBlock',
'fullScreen',
],
}
const editorConfig = {
placeholder: props.placeholder,
MENU_CONF: {
// 图片上传复用后台统一图片上传接口
uploadImage: {
server: '/api/admin/upload/image',
fieldName: 'file',
maxFileSize: 5 * 1024 * 1024, // 5MB
allowedFileTypes: ['image/*'],
headers: {
get Authorization() {
return authStore.token ? `Bearer ${authStore.token}` : ''
},
},
// 适配后端返回结构 { code:0, data:{ imageUrl } }
customInsert(res, insertFn) {
const url = res?.data?.imageUrl || res?.data?.image_url || res?.data?.url || ''
if (url) {
insertFn(url, '', url)
}
},
},
},
}
function handleCreated(editor) {
editorRef.value = editor
}
function handleChange(editor) {
emit('update:modelValue', editor.getHtml())
}
// 组件销毁时务必销毁编辑器实例,防止内存泄漏
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
</script>
<style scoped>
.rich-editor {
border: 1px solid #dcdfe6;
border-radius: 6px;
overflow: hidden;
width: 100%;
}
.rich-editor__toolbar {
border-bottom: 1px solid #e4e7ed;
}
.rich-editor__body {
background: #fff;
}
</style>