提交 506c37f5 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update pnpm-workspace.yaml to exclude minimum release age for vite@8.2.2

上级 91d8ca2c
......@@ -55,10 +55,10 @@
"@vitejs/plugin-react": "^6.0.5",
"jsdom": "^29.1.1",
"less": "^4.2.0",
"oxfmt": "^0.59.0",
"oxlint": "^1.74.0",
"oxfmt": "^0.64.0",
"oxlint": "^1.79.0",
"typescript": "^7.0.2",
"vite": "^8.2.1",
"vite": "^8.2.2",
"vite-plugin-mkcert": "^2.1.0",
"vitest": "^4.1.10"
}
......
......@@ -70,14 +70,6 @@ dd {
display: inline-block !important;
}
.ant-space-item {
.ant-btn.css-dev-only-do-not-override-1scgnrh {
display: flex;
align-items: center;
justify-content: center;
}
}
.form-devices-inline {
.ant-form-inline {
.ant-form-item {
......
......@@ -2,13 +2,13 @@
.wrap-phone-privew {
.ant-modal {
.ant-modal-close {
.phone-close {
inset-inline-end: 17px;
top: 0px;
top: 0;
right: 0;
}
.ant-modal-content {
.phone-content {
background: url('@/assets/images/phone_6.7.png') no-repeat;
background-size: 100% 100%;
box-shadow: none;
......@@ -16,7 +16,7 @@
border-radius: 48px;
padding: 36px 32px 34px 32px;
}
.priview-modal {
.preview-modal {
padding-top: 40px;
width: 430px;
height: 932px;
......@@ -33,7 +33,7 @@
width: 430px;
border-radius: 30px;
}
.previee-container {
.preview-container {
height: 100%;
overflow: hidden;
position: relative;
......@@ -431,7 +431,7 @@
}
// 画廊展示
.gallery-prview-container {
.gallery-preview-container {
.gallery-img {
max-height: 300px;
height: 300px;
......@@ -680,17 +680,17 @@
}
}
.priview-drawer-container {
.preview-drawer-container {
height: 932px;
top: 42px;
left: 0px;
width: 430px;
border-radius: 15px 0 40px 40px;
overflow: hidden;
.priview-drawer-header {
.preview-drawer-header {
padding: 10px;
}
.priview-drawer-body {
.preview-drawer-body {
padding: 10px;
height: 100%;
overflow-x: hidden;
......
......@@ -45,6 +45,7 @@ export default function DigitalHumanDialog({ editor }) {
footer={null}
width={1600}
className='digital-human-modal'
classNames={{ container: 'digital-human-container', body: 'digital-human-body' }}
centered
destroyOnHidden
>
......
......@@ -74,6 +74,10 @@ export default function DigitalHumanFigureField({ figures, loading, value, onCha
onCancel={() => setOpen(false)}
width={920}
className='digital-human-picker-modal figure-picker-modal'
classNames={{
container: 'digital-human-picker-container',
body: 'digital-human-picker-body figure-picker-body',
}}
>
{loading && figures.length === 0 ? (
<div className='picker-loading'>
......
......@@ -151,6 +151,10 @@ export default function DigitalHumanVoiceSettings({
onCancel={() => setOpen(false)}
width={620}
className='digital-human-picker-modal voice-picker-modal'
classNames={{
container: 'digital-human-picker-container',
body: 'digital-human-picker-body',
}}
>
<div className='picker-tabs' role='tablist' aria-label='声音性别'>
<button
......
......@@ -8,12 +8,12 @@
--dh-canvas: #f8f6f7;
max-width: calc(100vw - 24px);
.ant-modal-content {
.digital-human-container {
overflow: hidden;
border-radius: 10px;
}
.ant-modal-body {
.digital-human-body {
max-height: calc(100vh - 96px);
overflow-y: auto;
overscroll-behavior: contain;
......@@ -988,17 +988,17 @@
}
.digital-human-picker-modal {
.ant-modal-content {
.digital-human-picker-container {
border-radius: 14px;
}
.ant-modal-body {
.digital-human-picker-body {
max-height: 65vh;
overflow-y: auto;
}
}
.figure-picker-modal .ant-modal-body {
.figure-picker-modal .figure-picker-body {
overflow: hidden;
}
......@@ -1178,7 +1178,7 @@
width: calc(100vw - 24px) !important;
max-width: calc(100vw - 24px);
.ant-modal-body {
.digital-human-body {
padding: 12px;
}
}
......
......@@ -19,9 +19,6 @@ const AudioModal = ({ open, onClose, payload = {}, runtime }) => {
const fileAccept = ['.mp3', '.wav', '.ogg']
const [form] = Form.useForm()
const [uploading, setUploading] = useState(false) // 文件上传状态
const [progress, setProgress] = useState(0)
const [file, setFile] = useState({})
const [audioUrl, setAudioUrl] = useState('')
useEffect(() => {
......@@ -44,17 +41,21 @@ const AudioModal = ({ open, onClose, payload = {}, runtime }) => {
message.error('请上传正确格式的音频文件')
return false
}
setProgress(0)
setUploading(false)
setFile(file)
return true
},
customRequest: async () => {
customRequest: async ({ file, onSuccess, onError }) => {
setUploading(true)
const url = await uploadFile(file)
setAudioUrl(url)
form.setFieldsValue({ audioUrl: url })
setUploading(false)
try {
const url = await uploadFile(file)
setAudioUrl(url)
form.setFieldsValue({ audioUrl: url })
onSuccess?.({ url })
} catch (error) {
message.error('音频上传失败,请重试')
onError?.(error instanceof Error ? error : new Error('音频上传失败'))
} finally {
setUploading(false)
}
},
}
......@@ -80,7 +81,6 @@ const AudioModal = ({ open, onClose, payload = {}, runtime }) => {
children: [{ text: '' }],
})
setAudioUrl('')
setProgress(0)
setUploading(false)
form.resetFields()
onClose()
......@@ -106,7 +106,7 @@ const AudioModal = ({ open, onClose, payload = {}, runtime }) => {
<div className='editor-content-form'>
<Form layout='vertical' name='validate_other' form={form} onFinish={onFinish}>
<Form.Item label='上传音频' name='audioUrl'>
<Spin spinning={uploading} description={`${progress}%`}>
<Spin spinning={uploading} tip='上传中'>
<div className='editor-dragger'>
<Dragger {...uploadProps} showUploadList={false}>
{!audioUrl && (
......
......@@ -35,8 +35,6 @@ const ImageModal = (props, ref) => {
const [bgColorValue, setBgColorValue] = useState('#000000')
const [textColor, setTextColor] = useState('#ffffff')
const [uploading, setUploading] = useState(false) // 文件上传状态
const [progress, setProgress] = useState(0)
const [file, setFile] = useState({})
const [imgUrl, setImgUrl] = useState('')
const [initValues, setInitValues] = useState({
title: '',
......@@ -108,15 +106,21 @@ const ImageModal = (props, ref) => {
message.error('请上传正确格式的图片')
return false
}
setFile(file)
return true
},
customRequest: async () => {
customRequest: async ({ file, onSuccess, onError }) => {
setUploading(true)
const url = await uploadFile(file)
setImgUrl(url)
form.setFieldsValue({ bgImgUrl: url })
setUploading(false)
try {
const url = await uploadFile(file)
setImgUrl(url)
form.setFieldsValue({ bgImgUrl: url })
onSuccess?.({ url })
} catch (error) {
message.error('图片上传失败,请重试')
onError?.(error instanceof Error ? error : new Error('图片上传失败'))
} finally {
setUploading(false)
}
},
}
......@@ -144,7 +148,6 @@ const ImageModal = (props, ref) => {
if (parseInt(node.random) === parseInt(nowRandom)) {
SlateTransforms.setNodes(editor, { ...node, ...values }, { at: path })
setImgUrl('')
setProgress(0)
setUploading(false)
form.resetFields()
setTitleVisible(false)
......@@ -179,7 +182,6 @@ const ImageModal = (props, ref) => {
children: [{ text: '' }],
})
setImgUrl('')
setProgress(0)
setUploading(false)
form.resetFields()
setTitleVisible(false)
......@@ -197,7 +199,7 @@ const ImageModal = (props, ref) => {
initialValues={initValues}
>
<Form.Item label='章头背景图片' name='bgImgUrl'>
<Spin spinning={uploading} description={`${progress}%`}>
<Spin spinning={uploading}>
<div className='editor-dragger'>
<Dragger {...uploadProps} showUploadList={false}>
{!imgUrl && (
......
import { useState, useEffect, useImperativeHandle, forwardRef } from 'react'
import { CloudUploadOutlined } from '@ant-design/icons'
import { Form, Input, Spin, Upload, Button } from 'antd'
import { Form, Input, Spin, Upload, Button, message } from 'antd'
import OnlineImageList from '../../shared/online-image/OnlineImageList'
import { uploadFile } from '@/utils/oss'
......@@ -11,7 +11,6 @@ const GalleryFormItem = (props, ref) => {
const { activeKey, form, setPicList, galleryInfo, isOnline = false } = props
const [uploading, setUploading] = useState(false) // 文件上传状态
const [file, setFile] = useState({})
const [imgUrl, setImgUrl] = useState('')
useImperativeHandle(ref, () => {
......@@ -59,32 +58,36 @@ const GalleryFormItem = (props, ref) => {
maxCount: 1,
showUploadList: false,
accept: fileAccept.join(','),
beforeUpload: (file) => {
setFile(file)
},
customRequest: async () => {
customRequest: async ({ file, onSuccess, onError }) => {
setUploading(true)
const url = await uploadFile(file)
const values = {
['content']: {
[activeKey]: {
url: url,
title: '',
desc: '',
try {
const url = await uploadFile(file)
const values = {
['content']: {
[activeKey]: {
url: url,
title: '',
desc: '',
},
},
},
}
setImgUrl(url)
form.setFieldsValue({ ...values })
const allContent = form.getFieldValue('content')
const newGalleryList = []
// eslint-disable-next-line guard-for-in
for (const key in allContent) {
newGalleryList.push({ ...allContent[key], key: key })
}
setImgUrl(url)
form.setFieldsValue({ ...values })
const allContent = form.getFieldValue('content')
const newGalleryList = []
// eslint-disable-next-line guard-for-in
for (const key in allContent) {
newGalleryList.push({ ...allContent[key], key: key })
}
setPicList(newGalleryList)
form.setFieldsValue({ gallery: newGalleryList })
onSuccess?.({ url })
} catch (error) {
message.error('图片上传失败,请重试')
onError?.(error instanceof Error ? error : new Error('图片上传失败'))
} finally {
setUploading(false)
}
setPicList(newGalleryList)
form.setFieldsValue({ gallery: newGalleryList })
setUploading(false)
},
}
......@@ -169,7 +172,15 @@ const GalleryFormItem = (props, ref) => {
<img src={imgUrl} alt='' />
</div>
<div className='editor-uploader-result-tips'>
<Button size='small' color='primary' variant='outlined' onClick={() => setImgUrl(null)}>
<Button
size='small'
color='primary'
variant='outlined'
onClick={() => {
setImgUrl(null)
form.setFieldValue(['content', activeKey, 'url'], '')
}}
>
替换
</Button>
</div>
......
......@@ -14,6 +14,7 @@ export default function HistoryFeatureDialog({ open, onClose, runtime }) {
mask={{ closable: false }}
width='90%'
onCancel={onClose}
classNames={{ container: 'history-content' }}
wrapClassName='history1'
style={{ padding: 0 }}
>
......
......@@ -10,7 +10,7 @@ const HistoryEditor = (props) => {
const { setHistoryVisible, chapterId, bookId, setContent, saveContent } = props
const [loading, setLoading] = useState(false)
const [delMoadal, setDelModal] = useState(false)
const [delModal, setDelModal] = useState(false)
const [recordContent, setRecordContent] = useState('') // 内容
const [recordList, setRecordList] = useState([]) // 列表
const [recordId, setRecordId] = useState(null) // 当前的记录id
......@@ -134,7 +134,8 @@ const HistoryEditor = (props) => {
mask={false}
centered
className='history2'
open={delMoadal}
classNames={{ container: 'history2-content' }}
open={delModal}
onOk={handleSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
......
......@@ -8,7 +8,7 @@
overflow: hidden;
}
.ant-modal,
.ant-modal-content,
.history-content,
.ant-modal-body {
max-height: 90vh;
min-height: 90vh;
......@@ -17,7 +17,7 @@
}
}
.history1 {
.ant-modal-content {
.history-content {
padding: 0;
.history-content-container {
......@@ -297,7 +297,7 @@
}
// 画廊展示
.gallery-prview-container {
.gallery-preview-container {
.gallery-img {
max-height: 300px;
height: 300px;
......@@ -493,7 +493,7 @@
.history2 {
z-index: 1000;
.ant-modal-content {
.history2-content {
padding: 20px;
}
}
......@@ -20,8 +20,6 @@ const ImageModal = (props, ref) => {
const fileAccept = ['.png', '.jpg', '.jpeg', '.svg']
const [form] = Form.useForm()
const [uploading, setUploading] = useState(false) // 文件上传状态
const [progress, setProgress] = useState(0)
const [file, setFile] = useState({})
const [initValues, setInitValues] = useState({ imgUrl: '', imgTitle: '', imgDescript: '' })
const [tempNodeInfo, setTempNodeInfo] = useState(null)
......@@ -32,7 +30,6 @@ const ImageModal = (props, ref) => {
setInitValues({ imgUrl: image.src, imgTitle: image.alt, imgDescript: image.href })
form.setFieldsValue({ imgUrl: image.src, imgTitle: image.alt, imgDescript: image.href })
setTempNodeInfo(imageInfo)
setFile({})
setImageInfo({})
}
}, [imageInfo])
......@@ -48,20 +45,26 @@ const ImageModal = (props, ref) => {
message.error('请上传正确格式的图片')
return false
}
setFile(file)
return true
},
customRequest: async () => {
customRequest: async ({ file, onSuccess, onError }) => {
setUploading(true)
const url = await uploadFile(file)
setImgUrl(url)
form.setFieldsValue({ imgUrl: url })
setUploading(false)
try {
const url = await uploadFile(file)
setImgUrl(url)
form.setFieldsValue({ imgUrl: url })
onSuccess?.({ url })
} catch (error) {
message.error('图片上传失败,请重试')
onError?.(error instanceof Error ? error : new Error('图片上传失败'))
} finally {
setUploading(false)
}
},
}
const clear = () => {
setImgUrl('')
setProgress(0)
setUploading(false)
setTempNodeInfo(null)
setInitValues({ imgUrl: imgUrl, imgTitle: '', imgDescript: '' })
......@@ -158,7 +161,7 @@ const ImageModal = (props, ref) => {
name='imgUrl'
rules={[{ required: true, message: '请上传图片' }]}
>
<Spin spinning={uploading} description={`${progress}%`}>
<Spin spinning={uploading}>
<div className='editor-dragger'>
<Dragger {...uploadProps} showUploadList={false}>
{!imgUrl && (
......@@ -178,7 +181,15 @@ const ImageModal = (props, ref) => {
<img src={imgUrl} alt='' />
</div>
<div className='editor-uploader-result-tips'>
<Button size='small' color='primary' variant='outlined' onClick={() => setImgUrl(null)}>
<Button
size='small'
color='primary'
variant='outlined'
onClick={() => {
setImgUrl(null)
form.setFieldsValue({ imgUrl: '' })
}}
>
替换
</Button>
</div>
......
......@@ -54,6 +54,7 @@ export function PracticeSettingsFeatureDialog({ open, onClose, payload, runtime
header: 'practice-topic-header',
body: 'practice-topic-content',
wrapper: 'practice-topic-modal',
container: 'practice-topic-container',
}}
mask={{ closable: false }}
closeIcon={false}
......
......@@ -5,7 +5,7 @@ import { useEditorStore } from '@/stores/editorStore'
import TopicItem from './topicItem'
import { addChapterTopic, getChapterTopic } from '../../../shared/request'
import { clacTopicText } from '@/utils/common'
import { calcTopicText } from '@/utils/common'
const InsertTopic = (props, ref) => {
const {
......@@ -77,7 +77,7 @@ const InsertTopic = (props, ref) => {
<div className='practice-insert-topic list-topic-box'>
{radioList.length > 0 && (
<div className='list-topic-container'>
<h4>{clacTopicText('radio')}. 单选题</h4>
<h4>{calcTopicText('radio')}. 单选题</h4>
<div>
{radioList.map((item, index) => {
return (
......@@ -108,7 +108,7 @@ const InsertTopic = (props, ref) => {
{checkboxList.length > 0 && (
<div className='list-topic-container'>
<h4>{clacTopicText('checkbox', radioList)}. 多选题</h4>
<h4>{calcTopicText('checkbox', radioList)}. 多选题</h4>
<div>
{checkboxList.map((item, index) => {
return (
......@@ -138,7 +138,7 @@ const InsertTopic = (props, ref) => {
)}
{equalsList.length > 0 && (
<div className='list-topic-container'>
<h4>{clacTopicText('judge', radioList, checkboxList)}. 判断题</h4>
<h4>{calcTopicText('judge', radioList, checkboxList)}. 判断题</h4>
<div>
{equalsList.map((item, index) => {
return (
......@@ -168,7 +168,7 @@ const InsertTopic = (props, ref) => {
)}
{pressList.length > 0 && (
<div className='list-topic-container'>
<h4>{clacTopicText('fill', radioList, checkboxList, equalsList)}. 填空题</h4>
<h4>{calcTopicText('fill', radioList, checkboxList, equalsList)}. 填空题</h4>
<div>
{pressList.map((item, index) => {
return (
......@@ -199,7 +199,7 @@ const InsertTopic = (props, ref) => {
{textareaList.length > 0 && (
<div className='list-topic-container'>
<h4>
{clacTopicText('textarea', radioList, checkboxList, equalsList, pressList)}. 简答题
{calcTopicText('textarea', radioList, checkboxList, equalsList, pressList)}. 简答题
</h4>
<div>
{textareaList.map((item, index) => {
......
......@@ -4,7 +4,7 @@
inset-inline-end: 17px;
top: 16px;
}
.ant-modal-content {
.practice-topic-container {
padding: 15px 18px;
}
.ant-divider {
......
......@@ -13,7 +13,12 @@ export default function PreviewFeatureDialog({ open, onClose, runtime }) {
keyboard={false}
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#fff' }} />}
mask={{ closable: false }}
classNames={{ body: 'phone-body', wrapper: 'phone-wrapper' }}
classNames={{
container: 'phone-content',
body: 'phone-body',
wrapper: 'phone-wrapper',
close: 'phone-close',
}}
wrapClassName='wrap-phone-privew'
width='494px'
onCancel={onClose}
......
......@@ -9,7 +9,7 @@ import { getChapterTopic, expandReadInfo } from '../../shared/request'
import TopicItem from '../practice/components/topicItem'
import '@/components/editor/preview.less'
import { clacTopicText } from '@/utils/common'
import { calcTopicText } from '@/utils/common'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'
......@@ -46,7 +46,7 @@ const correctList = [
const PreviewScreen = (props, ref) => {
const { editor, bookId, chapterId, gData, nowTitle } = props
const [loading, setLoading] = useState(false) // 展示
const [prviewHtml, setPrviewHtml] = useState('') // 预览html
const [previewHtml, setPrviewHtml] = useState('') // 预览html
const [open, setOpen] = useState(false) // 抽屉
const [toolTip, setTooltip] = useState({})
......@@ -114,12 +114,15 @@ const PreviewScreen = (props, ref) => {
}
const handlerClick = () => {
const eventNamespace = '.editorPreview'
const previewContent = $('.preview-content-html')
const secondaryContent = $('.expand-content, .gallery-preview-container')
const iParentW = $('.preview-content-it').innerWidth()
const iParentH = $('.preview-content-it').innerHeight()
const parentOffset = $('.preview-content-it').offset() // 获取父元素相对于文档的位置
const parentRect = $('.preview-content-it')[0].getBoundingClientRect() // 获取父元素相对于文档的位置
$('.preview-content-html').on('click', (ev) => {
previewContent.off(eventNamespace).on(`click${eventNamespace}`, (ev) => {
ev.stopPropagation()
const target = ev.target
......@@ -326,7 +329,7 @@ const PreviewScreen = (props, ref) => {
setGallery([])
})
$('.expand-content, .gallery-prview-container').on('click', (ev) => {
secondaryContent.off(eventNamespace).on(`click${eventNamespace}`, (ev) => {
const target = ev.target
if (target.nodeName.toLowerCase() === 'img') {
......@@ -338,10 +341,15 @@ const PreviewScreen = (props, ref) => {
setSecondType('扩展图片')
}
})
return () => {
previewContent.off(eventNamespace)
secondaryContent.off(eventNamespace)
}
}
useEffect(() => {
handlerClick()
return handlerClick()
}, [])
const getChapterId = async () => {
......@@ -356,7 +364,7 @@ const PreviewScreen = (props, ref) => {
if (data) {
const content = get(data, 'content', '')
setPrviewHtml(content)
$('#previee-container')
$('#preview-container')
.find('video audio')
.each((index, item) => {
$(item).attr('controls', true).attr('controlslist', 'nodownload')
......@@ -383,7 +391,7 @@ const PreviewScreen = (props, ref) => {
// if (data) {
// const content = get(data, 'content', '');
// setPrviewHtml(content);
// $('#previee-container')
// $('#preview-container')
// .find('video audio')
// .each((index, item) => {
// $(item).attr('controls', true);
......@@ -417,8 +425,8 @@ const PreviewScreen = (props, ref) => {
}
return (
<div className='priview-modal'>
<div className='previee-container' id='previee-container'>
<div className='preview-modal'>
<div className='preview-container' id='preview-container'>
<div className='chapter-head-title'>
<h2>
{opend ? (
......@@ -456,14 +464,14 @@ const PreviewScreen = (props, ref) => {
<Spin spinning={loading} classNames={{ root: 'chapter-loading' }}>
<div
className='preview-content-html'
dangerouslySetInnerHTML={{ __html: prviewHtml }}
dangerouslySetInnerHTML={{ __html: previewHtml }}
></div>
</Spin>
</div>
{isType && (
<div className='preview-content-other'>
{isType === 'gallery' && (
<div className='gallery-prview-container'>
<div className='gallery-preview-container'>
{gallery && gallery.length > 0 ? (
<>
<div className='gallery-img'>
......@@ -491,7 +499,7 @@ const PreviewScreen = (props, ref) => {
<div className='practice-insert-topic'>
{radioList.length > 0 && (
<div className='topic_style'>
<h3>{clacTopicText('radio')}. 单选题</h3>
<h3>{calcTopicText('radio')}. 单选题</h3>
<div className='practice-list'>
{radioList.map((item, index) => {
return (
......@@ -540,7 +548,7 @@ const PreviewScreen = (props, ref) => {
)}
{checkBoxList.length > 0 && (
<div className='topic_style'>
<h3>{clacTopicText('checkbox', radioList)}. 多选题</h3>
<h3>{calcTopicText('checkbox', radioList)}. 多选题</h3>
<div className='practice-list'>
{checkBoxList.map((item, index) => {
return (
......@@ -589,7 +597,7 @@ const PreviewScreen = (props, ref) => {
)}
{judgeList.length > 0 && (
<div className='topic_style'>
<h3>{clacTopicText('judge', radioList, checkBoxList)}. 判断题</h3>
<h3>{calcTopicText('judge', radioList, checkBoxList)}. 判断题</h3>
<div className='practice-list'>
{judgeList.map((item, index) => {
return (
......@@ -638,7 +646,7 @@ const PreviewScreen = (props, ref) => {
)}
{fillList.length > 0 && (
<div className='topic_style'>
<h3>{clacTopicText('fill', radioList, checkBoxList, judgeList)}. 填空题</h3>
<h3>{calcTopicText('fill', radioList, checkBoxList, judgeList)}. 填空题</h3>
<div className='practice-list'>
{fillList.map((item, index) => {
return (
......@@ -688,7 +696,7 @@ const PreviewScreen = (props, ref) => {
{textareaList.length > 0 && (
<div className='topic_style'>
<h3>
{clacTopicText('textarea', radioList, checkBoxList, judgeList, fillList)}.
{calcTopicText('textarea', radioList, checkBoxList, judgeList, fillList)}.
简答题
</h3>
<div className='practice-list'>
......@@ -786,10 +794,10 @@ const PreviewScreen = (props, ref) => {
open={open}
getContainer={false}
placement='left'
classNames={{ header: 'priview-drawer-header', body: 'priview-drawer-body' }}
classNames={{ header: 'preview-drawer-header', body: 'preview-drawer-body' }}
width='200'
title='章节目录'
rootClassName='priview-drawer-container'
rootClassName='preview-drawer-container'
>
<Tree
className='draggable-tree'
......
......@@ -20,9 +20,6 @@ const VideoModal = ({ open, onClose, payload = {}, runtime }) => {
const fileAccept = ['.ogg', '.mp4', '.webm']
const [form] = Form.useForm()
const [uploading, setUploading] = useState(false) // 文件上传状态
const [progress, setProgress] = useState(0)
const [file, setFile] = useState({})
const [videoUrl, setVideoUrl] = useState('')
useEffect(() => {
......@@ -45,16 +42,21 @@ const VideoModal = ({ open, onClose, payload = {}, runtime }) => {
message.error('请上传正确格式的视频')
return false
}
setProgress(0)
setUploading(false)
setFile(file)
return true
},
customRequest: async () => {
customRequest: async ({ file, onSuccess, onError }) => {
setUploading(true)
const url = await uploadFile(file)
setVideoUrl(url)
form.setFieldsValue({ videoUrl: url })
setUploading(false)
try {
const url = await uploadFile(file)
setVideoUrl(url)
form.setFieldsValue({ videoUrl: url })
onSuccess?.({ url })
} catch (error) {
message.error('视频上传失败,请重试')
onError?.(error instanceof Error ? error : new Error('视频上传失败'))
} finally {
setUploading(false)
}
},
}
......@@ -81,7 +83,6 @@ const VideoModal = ({ open, onClose, payload = {}, runtime }) => {
})
setVideoUrl('')
setProgress(0)
setUploading(false)
form.resetFields()
onClose()
......@@ -111,7 +112,7 @@ const VideoModal = ({ open, onClose, payload = {}, runtime }) => {
name='videoUrl'
rules={[{ required: true, message: '请上传视频' }]}
>
<Spin spinning={uploading} description={`${progress}%`}>
<Spin spinning={uploading} tip='上传中'>
<div className='editor-dragger'>
<Dragger {...uploadProps} showUploadList={false}>
{!videoUrl && (
......
......@@ -131,11 +131,9 @@ export function usePagedList<Row, Filters, Query>({
}, [initialFilters, initialPage, loadFor, state.pageSize])
const refresh = useCallback(() => {
if (useAppliedFilters) {
setState((current) => ({ ...current, appliedFilters: state.filters }))
}
void loadFor(state.page, state.pageSize, state.filters)
}, [loadFor, state.filters, state.page, state.pageSize, useAppliedFilters])
const filters = useAppliedFilters ? state.appliedFilters : state.filters
void loadFor(state.page, state.pageSize, filters)
}, [loadFor, state.appliedFilters, state.filters, state.page, state.pageSize, useAppliedFilters])
const refreshUnfiltered = useCallback(() => {
void loadFor(state.page, state.pageSize, initialFilters)
......
......@@ -38,7 +38,7 @@
}
.audit-preview-tree,
.previee-container {
.preview-container {
height: 100%;
min-width: 0;
border: 0;
......@@ -94,13 +94,13 @@
}
}
.previee-container {
.preview-container {
display: flex;
flex-direction: column;
border-radius: 0;
}
&.wrap-phone-privew .previee-container .chapter-head-title {
&.wrap-phone-privew .preview-container .chapter-head-title {
flex: 0 0 var(--audit-review-header-height);
height: var(--audit-review-header-height);
line-height: var(--audit-review-header-height);
......@@ -528,7 +528,7 @@
.audit-review-preview {
.audit-preview-tree,
.previee-container {
.preview-container {
height: 360px;
}
}
......
......@@ -15,7 +15,7 @@ import {
convertToAntdTreeData,
findTreeElementByKey,
findFirstNotHasChildren,
clacTopicText,
calcTopicText,
} from '@/utils/common'
import { findReviewCommentRange, useReviewHighlights } from '../../review-comments/highlights'
import { get } from 'lodash-es'
......@@ -86,7 +86,7 @@ const PreviewScreen = (props, ref) => {
} = props
const [gData, setGData] = useState([])
const [loading, setLoading] = useState(false) // 展示
const [prviewHtml, setPrviewHtml] = useState('') // 预览html
const [previewHtml, setPrviewHtml] = useState('') // 预览html
const [open, setOpen] = useState(false) // 抽屉
const [secondType, setSecondType] = useState(false)
const [pageMap, setPageMap] = useState({}) // 叶子节点页码映射
......@@ -118,9 +118,9 @@ const PreviewScreen = (props, ref) => {
const previewContentRef = useRef(null)
const previewText = useMemo(() => {
const element = document.createElement('div')
element.innerHTML = prviewHtml || ''
element.innerHTML = previewHtml || ''
return element.textContent || ''
}, [prviewHtml])
}, [previewHtml])
const getPreviewRoot = useCallback(() => previewContentRef.current || undefined, [])
useReviewHighlights({
......@@ -130,7 +130,7 @@ const PreviewScreen = (props, ref) => {
draftAnchor: draftCommentAnchor,
activeCommentId,
enabled: true,
rootVersion: prviewHtml,
rootVersion: previewHtml,
})
// 递归函数,获取所有节点的key
......@@ -225,7 +225,7 @@ const PreviewScreen = (props, ref) => {
content,
text: contentElement.textContent || '',
})
$('#previee-container')
$('#preview-container')
.find('video audio')
.each((index, item) => {
$(item).attr('controls', true).attr('controlslist', 'nodownload')
......@@ -244,12 +244,15 @@ const PreviewScreen = (props, ref) => {
}
const handlerClick = () => {
const eventNamespace = '.auditPreview'
const previewContent = $('.preview-content-html')
const secondaryContent = $('.expand-content, .gallery-preview-container')
const iParentW = $('.preview-content-it').innerWidth()
const iParentH = $('.preview-content-it').innerHeight()
const parentOffset = $('.preview-content-it').offset() // 获取父元素相对于文档的位置
const parentRect = $('.preview-content-it')[0].getBoundingClientRect() // 获取父元素相对于文档的位置
$('.preview-content-html').on('click', async (ev) => {
previewContent.off(eventNamespace).on(`click${eventNamespace}`, async (ev) => {
ev.stopPropagation()
const target = ev.target
......@@ -455,7 +458,7 @@ const PreviewScreen = (props, ref) => {
setGallery([])
})
$('.expand-content, .gallery-prview-container').on('click', (ev) => {
secondaryContent.off(eventNamespace).on(`click${eventNamespace}`, (ev) => {
const target = ev.target
if (target.nodeName.toLowerCase() === 'img') {
......@@ -467,10 +470,15 @@ const PreviewScreen = (props, ref) => {
setSecondType('扩展图片')
}
})
return () => {
previewContent.off(eventNamespace)
secondaryContent.off(eventNamespace)
}
}
useEffect(() => {
handlerClick()
return handlerClick()
}, [isType])
useEffect(() => {
......@@ -594,7 +602,7 @@ const PreviewScreen = (props, ref) => {
)
return (
<div className={`priview-modal${desktop ? ' desktop-preview' : ''}`}>
<div className={`preview-modal${desktop ? ' desktop-preview' : ''}`}>
{/* <Spin spinning={loading}> */}
{desktop && (
<aside
......@@ -613,7 +621,7 @@ const PreviewScreen = (props, ref) => {
{!treeCollapsed && renderChapterTree()}
</aside>
)}
<div className='previee-container' id='previee-container'>
<div className='preview-container' id='preview-container'>
<div className='chapter-head-title'>
<h2 className='tree'>
{opend ? (
......@@ -658,7 +666,7 @@ const PreviewScreen = (props, ref) => {
<div
ref={previewContentRef}
className='preview-content-html'
dangerouslySetInnerHTML={{ __html: prviewHtml }}
dangerouslySetInnerHTML={{ __html: previewHtml }}
onMouseDown={() => {
onTextSelection?.(null)
}}
......@@ -676,7 +684,7 @@ const PreviewScreen = (props, ref) => {
{isType && (
<div className='preview-content-other'>
{isType === 'gallery' && (
<div className='gallery-prview-container'>
<div className='gallery-preview-container'>
{gallery && gallery.length > 0 ? (
<>
<div className='gallery-img'>
......@@ -704,7 +712,7 @@ const PreviewScreen = (props, ref) => {
<div className='practice-insert-topic'>
{radioList.length > 0 && (
<div className='topic_style'>
<h3>{clacTopicText('radio')}. 单选题</h3>
<h3>{calcTopicText('radio')}. 单选题</h3>
<div className='practice-list'>
{radioList.map((item, index) => {
return (
......@@ -753,7 +761,7 @@ const PreviewScreen = (props, ref) => {
)}
{checkBoxList.length > 0 && (
<div className='topic_style'>
<h3>{clacTopicText('checkbox', radioList)}. 多选题</h3>
<h3>{calcTopicText('checkbox', radioList)}. 多选题</h3>
<div className='practice-list'>
{checkBoxList.map((item, index) => {
return (
......@@ -802,7 +810,7 @@ const PreviewScreen = (props, ref) => {
)}
{judgeList.length > 0 && (
<div className='topic_style'>
<h3>{clacTopicText('judge', radioList, checkBoxList)}. 判断题</h3>
<h3>{calcTopicText('judge', radioList, checkBoxList)}. 判断题</h3>
<div className='practice-list'>
{judgeList.map((item, index) => {
return (
......@@ -851,7 +859,7 @@ const PreviewScreen = (props, ref) => {
)}
{fillList.length > 0 && (
<div className='topic_style'>
<h3>{clacTopicText('fill', radioList, checkBoxList, judgeList)}. 填空题</h3>
<h3>{calcTopicText('fill', radioList, checkBoxList, judgeList)}. 填空题</h3>
<div className='practice-list'>
{fillList.map((item, index) => {
return (
......@@ -901,7 +909,7 @@ const PreviewScreen = (props, ref) => {
{textareaList.length > 0 && (
<div className='topic_style'>
<h3>
{clacTopicText('textarea', radioList, checkBoxList, judgeList, fillList)}.
{calcTopicText('textarea', radioList, checkBoxList, judgeList, fillList)}.
简答题
</h3>
<div className='practice-list'>
......@@ -1007,10 +1015,10 @@ const PreviewScreen = (props, ref) => {
open={open}
getContainer={false}
placement='left'
classNames={{ header: 'priview-drawer-header', body: 'priview-drawer-body' }}
classNames={{ header: 'preview-drawer-header', body: 'preview-drawer-body' }}
size={300}
title='章节目录'
rootClassName='priview-drawer-container'
rootClassName='preview-drawer-container'
>
{renderChapterTree()}
</Drawer>
......
......@@ -586,6 +586,7 @@ const Examine = () => {
onCancel={() => setEditKey(false)}
classNames={{
wrapper: 'chapter-title-modal',
container: 'chapter-title-container',
}}
>
<EditChapterTitle
......
......@@ -123,7 +123,7 @@
inset-inline-end: 17px;
top: 16px;
}
.ant-modal-content {
.chapter-title-container {
padding: 15px 18px;
}
.ant-divider {
......
......@@ -8,7 +8,7 @@ import { getCommentInfo, commentReply, commentDel } from '../api'
const { TextArea } = Input
const DiscussDetail = () => {
const [showReply, setShowReply] = useState(false)
const [delMoadal, setDelModal] = useState(false)
const [delModal, setDelModal] = useState(false)
const [searchParams] = useSearchParams()
const id = Number(searchParams.get('id'))
const name = searchParams.get('book_name') || ''
......@@ -97,7 +97,7 @@ const DiscussDetail = () => {
<Modal
mask={false}
centered
open={delMoadal}
open={delModal}
onOk={handleDel}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
......
......@@ -37,7 +37,7 @@ const AppVersion = () => {
changePageSize,
} = useAppVersionList()
const [isEdit, setIsEdit] = useState(false)
const [delMoadal, setDelModal] = useState(false)
const [delModal, setDelModal] = useState(false)
const [isadd, setisadd] = useState(true)
const [form] = Form.useForm()
const checkPermission = usePermission()
......@@ -264,7 +264,7 @@ const AppVersion = () => {
<Modal
mask={false}
centered
open={delMoadal}
open={delModal}
onOk={handleDel}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
......
......@@ -86,7 +86,7 @@
inset-inline-end: 17px;
top: 16px;
}
.ant-modal-content {
.chapter-title-container {
padding: 15px 18px;
}
.ant-divider {
......
......@@ -180,7 +180,7 @@ export const hexToRgb = (hex) => {
: null
}
export const clacTopicText = (type, radio, checkbox, judge, fill, textarea) => {
export const calcTopicText = (type, radio, checkbox, judge, fill, textarea) => {
let i = 0
const arr = ['一', '二', '三', '四', '五']
if (type === 'radio') {
......
......@@ -15,12 +15,12 @@ export default defineConfig(() => {
port: 5174,
strictPort: true,
proxy: {
'/api/admin': {
target: 'http://127.0.0.1:7419',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api\/admin/, ''),
},
// '/api/admin': {
// target: 'http://127.0.0.1:7419',
// changeOrigin: true,
// secure: false,
// rewrite: (path) => path.replace(/^\/api\/admin/, ''),
// },
'/api': 'https://zijingebook.ezijing.com',
},
},
......
......@@ -33,10 +33,10 @@
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.5",
"jsdom": "^29.1.1",
"oxfmt": "^0.59.0",
"oxlint": "^1.74.0",
"oxfmt": "^0.64.0",
"oxlint": "^1.79.0",
"typescript": "^7.0.2",
"vite": "^8.2.1",
"vite": "^8.2.2",
"vite-plugin-mkcert": "^2.1.0",
"vitest": "^4.1.10"
}
......
.mobile-download-modal .ant-modal-content {
.mobile-download-modal .mobile-download-modal__container {
padding: 40px 52px 48px;
border-radius: 24px;
font-family:
......@@ -6,14 +6,14 @@
'Helvetica Neue', Arial, sans-serif;
}
.mobile-download-modal .ant-modal-close {
.mobile-download-modal .mobile-download-modal__close {
top: 22px;
right: 26px;
color: #b8bcc3;
font-size: 28px;
}
.mobile-download-modal .ant-modal-close:hover {
.mobile-download-modal .mobile-download-modal__close:hover {
color: #90959d;
background: transparent;
}
......@@ -125,7 +125,7 @@
.mobile-download-modal {
max-width: calc(100vw - 32px);
}
.mobile-download-modal .ant-modal-content {
.mobile-download-modal .mobile-download-modal__container {
padding: 28px 24px 36px;
}
.mobile-download-modal__content h2.ant-typography {
......
......@@ -38,6 +38,10 @@ export function MobileDownloadModal() {
<Modal
centered
className='mobile-download-modal'
classNames={{
container: 'mobile-download-modal__container',
close: 'mobile-download-modal__close',
}}
closeIcon={<CloseCircleFilled />}
footer={null}
open={open}
......
差异被折叠。
......@@ -5,3 +5,6 @@ packages:
allowBuilds:
es5-ext: false
esbuild: true
minimumReleaseAgeExclude:
- vite@8.2.2
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论