<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
實現照片上傳,富文字為空時的提示,官網詳見Braft Editor
import React, { PureComponent, Fragment } from 'react'; import { connect } from 'dva'; import BraftEditor from 'braft-editor' import 'braft-editor/dist/index.css' import moment from 'moment'; import Link from 'umi/link'; import { Row, Col, Card, Button, message, Divider, Table, Modal, Form, Select, Input, notification } from 'antd'; import styles from './createNotice.less'; import { router } from 'umi'; const FormItem = Form.Item; const { Option } = Select; /* eslint react/no-multi-comp:0 */ @connect(({ notice, loading }) => ({ notice, loading: loading.models.notice, })) @Form.create() class CreateNotice extends PureComponent { constructor(props) { super(props) this.state = { modalVisible: false, title: '', labelId: '', labelName: '', content: '', editorValue: '', editorState: BraftEditor.createEditorState(null) } } componentDidMount() { this.requLabel() } requLabel() { const { dispatch } = this.props dispatch({ type: 'notice/fetchLabel' }) } //訊息提醒 openNotification = (type, msg) => { notification[type]({ message: msg, }); }; //返回 goBack = () => { Modal.confirm({ title: '提示', content: '返回將不儲存已編輯資訊,確定離開嗎?', okText: '確定', cancelText: '取消', onOk: () => { router.go(-1) }, onCancel: () => { } }) } //富文字的值改變時觸發 handleChange = (editorState) => { const { form } = this.props this.setState({ editorState }) form.setFieldsValue({ content: editorState }) } //label的select切換 onChange = (values) => { this.setState({ labelId: values.key, labelName: values.label }) } //預覽 preView = () => { this.props.form.validateFields((error, values) => { // if (!error) { this.setState({ modalVisible: true, title: values.title, content: values.content.toHTML() }) // } }) } //關閉 handleOk = () => { this.setState({ modalVisible: false }) } //釋出 handleSubmit = (event) => { const { dispatch, form } = this.props; const { labelId, editorState } = this.state event.preventDefault() form.validateFields((error, values) => { if (error) { return } let edit = this.state.editorState.isEmpty() //用isEmpty判斷是否為空 if (edit) { this.openNotification('warn', '請輸入內容') return } if (!error) { const submitData = { title: values.title, infoLabelId: labelId, content: window.btoa(window.encodeURIComponent(values.content.toHTML())) // or values.content.toRAW() } Modal.confirm({ title: '提示', content: '確認釋出嗎?', okText: '確定', cancelText: '取消', onOk: () => { dispatch({ type: 'notice/publish', payload: submitData, callback: res => { if (res.success) { this.openNotification('success', '釋出成功') router.go(-1) } else { this.openNotification('error', '釋出失敗') } } }) }, onCancel: () => { } }) } }) } //上傳媒體 uploadPic = (param) => { //也可以用fetch或者axios,用formData const token = localStorage.getItem('meiyun-operation-token') const serverURL = '/meiyun-resource/oss/endpoint/put-file' const xhr = new XMLHttpRequest const fd = new FormData() const successFn = (response) => { let url = JSON.parse(xhr.responseText).data.link // 檔案上傳到伺服器端成功後獲取地址 // 上傳成功後呼叫param.success並傳入上傳後的檔案地址 param.success({ url, meta: { id: 'xxx', title: 'xxx', alt: 'xxx', loop: true, // 指定音視訊是否迴圈播放 autoPlay: true, // 指定音視訊是否自動播放 controls: true, // 指定音視訊是否顯示控制欄 poster: 'http://xxx/xx.png', // 指定視訊播放器的封面 } }) } const progressFn = (event) => { // 上傳進度發生變化時呼叫param.progress param.progress(event.loaded / event.total * 100) } const errorFn = (response) => { // 上傳發生錯誤時呼叫param.error param.error({ msg: '上傳失敗' }) } xhr.upload.addEventListener("progress", progressFn, false) xhr.addEventListener("load", successFn, false) xhr.addEventListener("error", errorFn, false) xhr.addEventListener("abort", errorFn, false) fd.append('file', param.file) xhr.open('POST', serverURL, true) xhr.setRequestHeader('Blade-Auth', 'Bearer ' + token); xhr.send(fd) } render() { const { form: { getFieldDecorator }, notice: { labelData }, loading, } = this.props; const { modalVisible, title, labelName, content } = this.state const formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 18 } } const controls = [ 'font-size', 'font-family', 'list-ol', 'list-ul', 'hr', 'text-align', 'bold', 'italic', 'underline', 'text-color', 'separator', 'superscript', 'subscript', 'separator', 'media', 'letter-spacing', 'line-height', 'clear',] return ( <div className={styles.container}> <div className={styles.title}> <span onClick={this.goBack}>< 公告管理</span> </div> <div className={styles.formBox}> <Form onSubmit={this.handleSubmit} layout="horizontal" {...formItemLayout}> <FormItem label="公告標題" {...formItemLayout}> {getFieldDecorator('title', { rules: [{ required: true, message: '請輸入公告標題' }, { message: '公告標題不能輸入<或>', pattern: new RegExp('^[^<|^>]+$', 'g') }, { message: '公告標題不能超過30個字元', max: 30 }] })(<Input placeholder="請輸入公告標題" style={{ width: 300 }} />)} </FormItem> <FormItem {...formItemLayout} label="標籤"> {getFieldDecorator('labelId')( <Select showSearch style={{ width: 300 }} placeholder="請選擇標籤" labelInValue={true} optionFilterProp="children" onChange={this.onChange} onFocus={this.onFocus} onBlur={this.onBlur} filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 } > {labelData.length && labelData.map(item => { return ( <Option value={item.id} key={item.id}>{item.labelTitle}</Option> ) })} </Select> )} </FormItem> {/* </Col> </Row> */} <FormItem {...formItemLayout} label="內容"> {getFieldDecorator('content', { rules: [{ required: true, message: '請輸入正文內容' }], })(<BraftEditor ref={instance => this.editorInstance = instance} className={styles.myEditor} controls={controls} onChange={this.handleChange} forceNewLine={true} placeholder="請輸入正文內容" media={{ uploadFn: this.uploadPic }} />)} </FormItem> <FormItem> <Row gutter={{ md: 24, lg: 48, xl: 48 }}> <Col md={18} sm={24}></Col> <Col md={6} sm={24}> <Button style={{ marginRight: 20 }} onClick={this.preView}>預覽</Button> <Button type="primary" htmlType="submit">釋出</Button> </Col> </Row> </FormItem> </Form> </div> {modalVisible && <Modal title="預覽" visible={modalVisible} maskClosable={false} width={1000} footer={[ <Button key="submit" type="primary" loading={loading} onClick={this.handleOk}> 關閉 </Button> ]} onOk={this.handleOk} onCancel={this.handleOk}> <div> <h2 style={{ textAlign: 'center' }}>{title}</h2> <p>{labelName}</p> <div dangerouslySetInnerHTML={{ __html: content }}></div> </div> </Modal>} </div> ) } } export default CreateNotice
最近接到一個需求,需要支援在文字輸入框支援圖片貼上上傳,但是在我們這邊管理頁面,對於使用者提的一些問題顯示又不支援 Matkdown。
所以選擇 braft-editor 來實現,發現提供一些設定項,因為我這邊不需要那些加粗,下劃線等等按鈕,只需要上傳圖片,貼上然後配合 COS 存連結就好了。
首先我這個是 React 專案,其它專案不太清楚,然後使用 yarn。
在 utils 官方倉庫中,有相關 issues,連結在下方:
其中也有人提及了一些解決方案,但是並沒有解決問題,一直報錯:
TS7016: Could not find a declaration file for module ‘braft-utils’. ‘xxx/node_modules/braft-utils/dist/index.js’ implicitly has an ‘any’ type.
Try npm i --save-dev @types/braft-utils if it exists or add a new declaration (.d.ts) file containing declare module 'braft-utils';
看這個報錯資訊,有提示,用 npm 安裝那個依賴,我已經試過了,並沒有效果,不存在那個依賴包。
直接少廢話,以下是解決方式:
yarn add braft-finder yarn add braft-utils yarn add draft-js-multidecorators yarn add draftjs-utils
然後在你當前需要引入的檔案那,同級目錄底下建立一個名為 xxx.d.ts 檔案,放入以下定義:
declare module 'braft-utils'; declare module 'braft-finder';
弄完之後記得重新 yarn dev ,之後就會出現如下頁面,完美解決。
弄完這個問題,還就那個焦頭爛額的,不過總算沒有 bug 了,在這裡記錄一下,以免大家踩坑。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45