| talent-card-admin/src/style.css | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| talent-card-admin/src/views/TalentNewsEditView.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| talent-card-admin/src/views/TalentNewsPublishView.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
talent-card-admin/src/style.css
@@ -3511,6 +3511,10 @@ border-radius: 4px; } .news-editor-file-input { display: none; } .article-management-table { color: #111827; font-size: 12px; talent-card-admin/src/views/TalentNewsEditView.vue
@@ -9,7 +9,7 @@ Link, Picture, } from '@element-plus/icons-vue' import { getAdminNewsDetail, updateAdminNews, uploadAdminImage } from '../api/admin' import { getAdminNewsDetail, updateAdminNews, uploadAdminImage, uploadAdminVideo } from '../api/admin' interface NewsForm { title: string @@ -36,6 +36,8 @@ const route = useRoute() const router = useRouter() const editorRef = ref<HTMLElement>() const imageInputRef = ref<HTMLInputElement>() const videoInputRef = ref<HTMLInputElement>() const createdAt = ref('-') const updatedAt = ref('-') const author = ref('-') @@ -44,6 +46,53 @@ function updateContent(event: Event) { form.content = (event.target as HTMLElement).innerHTML } function syncEditor() { form.content = editorRef.value?.innerHTML || '' } function runEditorCommand(item: string) { const commands: Record<string, [string, string?]> = { H: ['formatBlock', 'h2'], B: ['bold'], I: ['italic'], U: ['underline'], S: ['strikeThrough'], 笔: ['foreColor', '#1f2937'], 刷: ['hiliteColor', '#fff3cd'], 列: ['insertUnorderedList'], 齐: ['justifyCenter'], 引: ['formatBlock', 'blockquote'], 表: ['insertHTML', '<table><tbody><tr><td>内容</td><td>内容</td></tr></tbody></table>'], } if (item === '链') { const url = window.prompt('请输入链接地址') if (url) document.execCommand('createLink', false, url) } else if (item === '图') imageInputRef.value?.click() else if (item === '视') videoInputRef.value?.click() else { const command = commands[item] if (command) document.execCommand(command[0], false, command[1]) } syncEditor() } function undo() { document.execCommand('undo'); syncEditor() } function redo() { document.execCommand('redo'); syncEditor() } async function insertImage(event: Event) { const input = event.target as HTMLInputElement const file = input.files?.[0] if (!file) return const result = await uploadAdminImage(file) editorRef.value?.focus() document.execCommand('insertImage', false, result.url) input.value = '' syncEditor() } async function insertVideo(event: Event) { const input = event.target as HTMLInputElement const file = input.files?.[0] if (!file) return const result = await uploadAdminVideo(file) editorRef.value?.focus() document.execCommand('insertHTML', false, `<video controls src="${result.url}"></video>`) input.value = '' syncEditor() } function hasContent() { @@ -148,19 +197,23 @@ class="news-editor-tool" type="button" :title="item" @mousedown.prevent @click="runEditorCommand(item)" > <el-icon v-if="item === '链'"><Link /></el-icon> <el-icon v-else-if="item === '图'"><Picture /></el-icon> <span v-else>{{ item }}</span> </button> <button class="news-editor-tool" type="button" title="后退"> <button class="news-editor-tool" type="button" title="后退" @mousedown.prevent @click="undo"> <el-icon><ArrowLeftBold /></el-icon> </button> <button class="news-editor-tool" type="button" title="前进"> <button class="news-editor-tool" type="button" title="前进" @mousedown.prevent @click="redo"> <el-icon><ArrowRightBold /></el-icon> </button> </div> <div ref="editorRef" class="news-editor-body" contenteditable="true" @input="updateContent" /> <input ref="imageInputRef" class="news-editor-file-input" type="file" accept="image/*" @change="insertImage" /> <input ref="videoInputRef" class="news-editor-file-input" type="file" accept="video/mp4" @change="insertVideo" /> </div> </el-form-item> talent-card-admin/src/views/TalentNewsPublishView.vue
@@ -1,5 +1,5 @@ <script setup lang="ts"> import { reactive } from 'vue' import { reactive, ref } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import type { UploadFile } from 'element-plus' @@ -9,7 +9,7 @@ Link, Picture, } from '@element-plus/icons-vue' import { createAdminNews, uploadAdminImage } from '../api/admin' import { createAdminNews, uploadAdminImage, uploadAdminVideo } from '../api/admin' interface NewsForm { title: string @@ -34,11 +34,61 @@ }) const router = useRouter() const editorRef = ref<HTMLElement>() const imageInputRef = ref<HTMLInputElement>() const videoInputRef = ref<HTMLInputElement>() const toolbarItems = ['H', 'B', 'I', 'U', 'S', '笔', '刷', '链', '列', '齐', '引', '表', '图', '视'] function updateContent(event: Event) { form.content = (event.target as HTMLElement).innerHTML } function syncEditor() { form.content = editorRef.value?.innerHTML || '' } function runEditorCommand(item: string) { const commands: Record<string, [string, string?]> = { H: ['formatBlock', 'h2'], B: ['bold'], I: ['italic'], U: ['underline'], S: ['strikeThrough'], 笔: ['foreColor', '#1f2937'], 刷: ['hiliteColor', '#fff3cd'], 列: ['insertUnorderedList'], 齐: ['justifyCenter'], 引: ['formatBlock', 'blockquote'], 表: ['insertHTML', '<table><tbody><tr><td>内容</td><td>内容</td></tr></tbody></table>'], } if (item === '链') { const url = window.prompt('请输入链接地址') if (url) document.execCommand('createLink', false, url) } else if (item === '图') imageInputRef.value?.click() else if (item === '视') videoInputRef.value?.click() else { const command = commands[item] if (command) document.execCommand(command[0], false, command[1]) } syncEditor() } function undo() { document.execCommand('undo'); syncEditor() } function redo() { document.execCommand('redo'); syncEditor() } async function insertImage(event: Event) { const input = event.target as HTMLInputElement const file = input.files?.[0] if (!file) return const result = await uploadAdminImage(file) editorRef.value?.focus() document.execCommand('insertImage', false, result.url) input.value = '' syncEditor() } async function insertVideo(event: Event) { const input = event.target as HTMLInputElement const file = input.files?.[0] if (!file) return const result = await uploadAdminVideo(file) editorRef.value?.focus() document.execCommand('insertHTML', false, `<video controls src="${result.url}"></video>`) input.value = '' syncEditor() } function hasContent() { @@ -98,19 +148,23 @@ class="news-editor-tool" type="button" :title="item" @mousedown.prevent @click="runEditorCommand(item)" > <el-icon v-if="item === '链'"><Link /></el-icon> <el-icon v-else-if="item === '图'"><Picture /></el-icon> <span v-else>{{ item }}</span> </button> <button class="news-editor-tool" type="button" title="后退"> <button class="news-editor-tool" type="button" title="后退" @mousedown.prevent @click="undo"> <el-icon><ArrowLeftBold /></el-icon> </button> <button class="news-editor-tool" type="button" title="前进"> <button class="news-editor-tool" type="button" title="前进" @mousedown.prevent @click="redo"> <el-icon><ArrowRightBold /></el-icon> </button> </div> <div class="news-editor-body" contenteditable="true" @input="updateContent" /> <div ref="editorRef" class="news-editor-body" contenteditable="true" @input="updateContent" /> <input ref="imageInputRef" class="news-editor-file-input" type="file" accept="image/*" @change="insertImage" /> <input ref="videoInputRef" class="news-editor-file-input" type="file" accept="video/mp4" @change="insertVideo" /> </div> </el-form-item>