shinnosuke
2026-08-06 eae15fb8297ed56cffaea9d22e914e4c59232b42
fix(admin): 启用资讯富文本工具栏 (bug #24038)
3个文件已修改
127 ■■■■■ 已修改文件
talent-card-admin/src/style.css 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-admin/src/views/TalentNewsEditView.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-admin/src/views/TalentNewsPublishView.vue 64 ●●●●● 补丁 | 查看 | 原始文档 | 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>