- 新增 question-importer 服务,支持从 JSON 文件批量导入题目到题库 - 重构用户端答题流程,整合抽题、答题、结果分析页面状态管理 - 将题目分类从 UI 模板类型切换为业务分类(question_category_name) - 在题库管理页面支持题目批量选择和删除功能 - 优化用户端组别选择,增加“已结束”状态提示和禁用逻辑 - 修复题目新增/更新 API 请求参数格式问题 - 为富文本编辑器配置排除不必要的工具栏按键
129 lines
3.1 KiB
Vue
129 lines
3.1 KiB
Vue
<script setup lang="ts">
|
||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||
import { nextTick, onBeforeUnmount, onMounted, shallowRef } from 'vue'
|
||
import { getAliOssTokenAxios } from '@/service/api/upload'
|
||
import { browserPathJoin } from '@/utils/date'
|
||
import { initOSSClient, uploadFileToOSS } from '@/utils/oss'
|
||
import '@wangeditor/editor/dist/css/style.css'
|
||
|
||
interface Props {
|
||
modelValue: string
|
||
placeholder?: string
|
||
mode?: 'default' | 'simple'
|
||
height?: string
|
||
path?: string
|
||
excludeKeys?: string[]
|
||
}
|
||
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
modelValue: '',
|
||
placeholder: '请输入内容...',
|
||
mode: 'default',
|
||
height: '300px',
|
||
path: 'temp',
|
||
excludeKeys: () => [],
|
||
})
|
||
|
||
const emit = defineEmits(['update:modelValue'])
|
||
|
||
// Editor Logic
|
||
const editorRef = shallowRef()
|
||
const toolbarConfig = {
|
||
excludeKeys: props.excludeKeys || [],
|
||
}
|
||
|
||
type InsertFnType = (url: string, alt: string, href: string) => void
|
||
|
||
async function customUpload(file: File, insertFn: InsertFnType) {
|
||
try {
|
||
const { data: tokenData, error: tokenError } = await getAliOssTokenAxios()
|
||
if (tokenError || !tokenData) {
|
||
window.$message?.error('获取上传凭证失败')
|
||
return
|
||
}
|
||
const client = initOSSClient(tokenData?.data || {})
|
||
const path = `${props.path}/${Date.now()}/${file.name}`
|
||
|
||
await uploadFileToOSS(client, file, path)
|
||
|
||
const url = browserPathJoin(import.meta.env.VITE_BASE_OSS_URL, path)
|
||
insertFn(url, file.name, url)
|
||
}
|
||
catch (error) {
|
||
console.error('上传失败', error)
|
||
window.$message?.error('上传失败')
|
||
}
|
||
}
|
||
|
||
const editorConfig = {
|
||
placeholder: props.placeholder,
|
||
MENU_CONF: {
|
||
uploadImage: {
|
||
customUpload,
|
||
},
|
||
uploadVideo: {
|
||
customUpload,
|
||
},
|
||
},
|
||
}
|
||
|
||
// 监听 placeholder 变化
|
||
// watch(() => props.placeholder, (_newVal) => {
|
||
// if (editorRef.value) {
|
||
// // 似乎 wangeditor 不支持动态修改 placeholder,这里作为占位
|
||
// }
|
||
// })
|
||
onMounted(() => {
|
||
// eslint-disable-next-line no-console
|
||
console.log(toolbarConfig, 'toolbarConfig')
|
||
nextTick(() => {
|
||
// eslint-disable-next-line no-console
|
||
console.log(editorRef.value)
|
||
})
|
||
})
|
||
|
||
function handleCreated(editor: any) {
|
||
editorRef.value = editor
|
||
// eslint-disable-next-line no-console
|
||
console.log('Toolbar keys:', editor.getAllMenuKeys())
|
||
}
|
||
|
||
function handleChange(editor: any) {
|
||
emit('update:modelValue', editor.getHtml())
|
||
}
|
||
|
||
onBeforeUnmount(() => {
|
||
const editor = editorRef.value
|
||
if (editor == null)
|
||
return
|
||
editor.destroy()
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="overflow-hidden border border-gray-200 rounded-lg bg-white">
|
||
<Toolbar
|
||
style="border-bottom: 1px solid #eee"
|
||
:editor="editorRef"
|
||
:default-config="toolbarConfig"
|
||
:mode="mode"
|
||
/>
|
||
<div :style="{ height: props.height }">
|
||
<Editor
|
||
:model-value="modelValue"
|
||
:style="{ height: '100%', overflowY: 'hidden' }"
|
||
:default-config="editorConfig"
|
||
:mode="mode"
|
||
@on-created="handleCreated"
|
||
@on-change="handleChange"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
:deep(.w-e-text-container) {
|
||
background-color: transparent;
|
||
}
|
||
</style>
|