中泽后台管理前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

edit.tsx 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. import React, { useEffect, useState, useRef } from 'react';
  2. import {
  3. ProForm,
  4. ProFormText,
  5. ProFormUploadButton,
  6. ProFormSelect,
  7. ProFormDatePicker,
  8. ProFormRadio
  9. } from '@ant-design/pro-components';
  10. import { Form, Modal, Drawer, message, Upload, Space, Button, Switch,Input,Tooltip } from 'antd';
  11. import {InfoCircleOutlined} from '@ant-design/icons';
  12. import { useIntl, FormattedMessage } from '@umijs/max';
  13. import { DictValueEnumObj } from '@/components/DictTag';
  14. import Editor from '@/components/ueditor';
  15. import { upload } from '@/services/news/news';
  16. import { getDataEnumList } from '@/services/system/Enum';
  17. import Quill from "quill";
  18. import "quill/dist/quill.snow.css";
  19. import moment from 'moment';
  20. import { trim } from 'lodash';
  21. let editor: any;
  22. export type NewsProps = {
  23. onCancel: (flag?: boolean, formVals?: any) => void;
  24. onSubmit: (values: any) => Promise<void>;
  25. open: boolean;
  26. currentRow: any;
  27. date: any;
  28. title: string;
  29. content: string;
  30. surface: string;
  31. surfaceUrl: string;
  32. };
  33. let toolbarOptions = [
  34. ['bold', 'italic', 'underline', 'strike'], // 切换按钮
  35. ['blockquote', 'code-block'],
  36. ['link', 'image'],
  37. [{ 'header': 1 }, { 'header': 2 }], // 用户自定义按钮值
  38. [{ 'list': 'ordered' }, { 'list': 'bullet' }],
  39. [{ 'script': 'sub' }, { 'script': 'super' }], // 上标/下标
  40. [{ 'indent': '-1' }, { 'indent': '+1' }], // 减少缩进/缩进
  41. [{ 'direction': 'rtl' }], // 文本下划线
  42. [{ 'size': ['small', false, 'large', 'huge'] }], // 用户自定义下拉
  43. [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
  44. [{ 'color': [] }, { 'background': [] }], // 主题默认下拉,使用主题提供的值
  45. [{ 'font': [] }],
  46. [{ 'align': [] }],
  47. // [{ 'wordBox': ['20px', '22px', '24px', '26px', '28px', '28px', '30px'] }],
  48. [{ wordBox: ['20px', '22px', '24px', '26px', '28px', '30px', '32px', '34px', '36px', '38px', '40px', '42px', '44px'] }],
  49. ['clean'], // 清除格式
  50. ];
  51. function image(){
  52. var _this3 = this;
  53. var fileInput = this.container.querySelector('input.ql-image[type=file]');
  54. fileInput = null
  55. if (fileInput == null) {
  56. fileInput = document.createElement('input');
  57. fileInput.setAttribute('type', 'file');
  58. fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');
  59. fileInput.classList.add('ql-image');
  60. fileInput.addEventListener('change', async function () {
  61. if (fileInput.files != null && fileInput.files[0] != null) {
  62. let index = editor.getSelection() && editor.getSelection().index ? editor.getSelection().index : 0;
  63. let file = fileInput.files[0]
  64. const formData = new FormData();
  65. formData.append('file', file, file.name);
  66. const res = await upload(formData)
  67. editor.insertEmbed(index, 'image', res.data.url);
  68. var reader = new FileReader();
  69. reader.onload = function (e) {
  70. fileInput.value = "";
  71. };
  72. }
  73. });
  74. this.container.appendChild(fileInput);
  75. }
  76. fileInput.click();
  77. }
  78. const NewsForm: React.FC<NewsProps> = (props) => {
  79. const [form] = Form.useForm();
  80. const intl = useIntl();
  81. let editorRef = useRef<HTMLDivElement>(null)
  82. useEffect(() => {
  83. form.resetFields();
  84. if(!props.open) return;
  85. // const { title, content, surface, surfaceUrl, date } = props;
  86. if(!props.currentRow) return;
  87. const { digest, top,key,content,surface,surfaceUrl,date,title,source} = props.currentRow;
  88. if (props.currentRow) {
  89. form.setFieldsValue({
  90. digest,
  91. top,
  92. key
  93. })
  94. // if (top) {
  95. // setCheckTop(1)
  96. // } else {
  97. // setCheckTop(0)
  98. // }
  99. }
  100. setSurface(surface)
  101. form.setFieldsValue({
  102. title: title,
  103. date: date,
  104. })
  105. if (surfaceUrl && surface) {
  106. setFileList([
  107. {
  108. uid: surface,
  109. thumbUrl: surfaceUrl
  110. }
  111. ])
  112. }
  113. if (editorRef && editorRef.current && editorRef.current.children[0]) {
  114. editorRef.current.children[0].innerHTML = content
  115. }
  116. }, [props.open]);
  117. useEffect(() => {
  118. var Parchment = Quill.import('parchment');
  119. let config = {
  120. scope: Parchment.Scope.INLINE,
  121. // 会有下拉框列表
  122. whitelist: ['20px', '22px', '24px', '26px', '28px', '30px', '32px', '34px', '36px', '38px', '40px', '42px', '44px']
  123. //可设置成没有下拉框的,但会导致无法清除样式
  124. // whitelist: ['1px solid #000000']
  125. };
  126. let wordBox = new Parchment.Attributor.Style('wordBox', 'line-height', config);
  127. Quill.register(wordBox, true);
  128. editor = new Quill('#editor', {
  129. modules: {
  130. toolbar: toolbarOptions,
  131. clipboard: {
  132. dangerouslyPasteHTML: true
  133. }
  134. },
  135. theme: 'snow'
  136. });
  137. // 工具栏附件按钮
  138. let a = document.querySelectorAll(".ql-wordBox")[0]
  139. let b = document.querySelectorAll(".ql-wordBox")[1]
  140. //添加默认显示内容
  141. let stit = document.createElement('span')
  142. stit.innerHTML = intl.formatMessage({ id: 'public.lineSpacing' });
  143. stit.setAttribute('style', 'margin-right:20px;line-height: 24px;')
  144. a.children[0].insertBefore(stit, a.children[0].children[0])
  145. // 遍历下拉框列表添加值
  146. let i = a.children[1].children.length
  147. while (i) {
  148. i--;
  149. let item = a.children[1].children[i]
  150. item.innerHTML = item.dataset.value ? item.dataset.value : '无'
  151. }
  152. // console.log('editor', editor)
  153. var toolbar = editor.getModule('toolbar');
  154. toolbar.image2 = image
  155. // console.log("toolbar.handlers.image", toolbar.handlers.image)
  156. toolbar.addHandler('image', (e) => {
  157. toolbar.image2()
  158. //
  159. });
  160. // toolbar.addHandler('wordBox', (e) => {
  161. // console.log('e-----++++', e)
  162. // //
  163. // });
  164. }, []);
  165. const [fileList, setFileList] = useState<any>([]);
  166. const [surface, setSurface] = useState<any>('');
  167. const [checkTop, setCheckTop] = useState<any>('')
  168. const handleOk = async () => {
  169. let data = form.getFieldsValue()
  170. let val = await form.validateFields()
  171. let htmlStr = editorRef.current?.children[0].innerHTML
  172. htmlStr = htmlStr?.replaceAll('"', "'")
  173. let content = editor.getContents()
  174. // console.log('htmlStr----', htmlStr)
  175. editor.setContents(content)
  176. data.date = moment(data.date).format('YYYY.MM.DD');
  177. if(checkTop){
  178. data.top = 1;
  179. }else{
  180. data.top = 0;
  181. }
  182. // data.top = checkTop;
  183. let formData = {
  184. ...data,
  185. surface,
  186. content: htmlStr
  187. }
  188. form.setFieldsValue({
  189. title: '',
  190. digest: ""
  191. })
  192. setFileList([]);
  193. setCheckTop(false)
  194. editor.clipboard.dangerouslyPasteHTML(0, '')
  195. editor.setContents([])
  196. props.onSubmit(formData);
  197. };
  198. const handleCancel = () => {
  199. form.setFieldsValue({
  200. title: '',
  201. digest: "",
  202. top: ""
  203. })
  204. setFileList([]);
  205. setCheckTop(false)
  206. editor.clipboard.dangerouslyPasteHTML(0, '')
  207. editor.setContents([])
  208. props.onCancel();
  209. };
  210. const handleChange = async (res) => {
  211. const { fileList } = res;
  212. setFileList(fileList);
  213. };
  214. const handleBeforeUpload = async (file: RcFile) => {
  215. const formData = new FormData();
  216. formData.append('file', file, file.name);
  217. const res = await upload(formData);
  218. if (res?.data?.uuid) {
  219. setSurface(res.data.uuid)
  220. }
  221. return false;
  222. };
  223. return (
  224. <Drawer
  225. width={'80%'}
  226. title={props.currentRow ? '新闻编辑' : '新建新闻'}
  227. forceRender
  228. open={props.open}
  229. destroyOnClose
  230. onClose={handleCancel}
  231. extra={
  232. <Space>
  233. <Button onClick={handleCancel}>取消</Button>
  234. <Button type="primary" onClick={handleOk}>确认</Button>
  235. </Space>
  236. }
  237. >
  238. <ProForm
  239. form={form}
  240. grid={true}
  241. submitter={false}
  242. layout="horizontal"
  243. >
  244. <ProFormText
  245. name="title"
  246. label='标题'
  247. labelCol={{
  248. style: { width: 95 }
  249. }}
  250. placeholder='请输入标题'
  251. rules={[
  252. {
  253. required: true,
  254. message: '请输入标题!',
  255. },
  256. ]}
  257. />
  258. <ProForm.Item
  259. label={'封面图'}
  260. extra={'图片尺寸1218*915'}
  261. labelCol={{
  262. style: { width: 95 }
  263. }}
  264. rules={[
  265. {
  266. required: true
  267. }
  268. ]
  269. }
  270. >
  271. <Upload
  272. listType="picture-card" // 设置为图片卡片模式
  273. fileList={fileList}
  274. maxCount={1}
  275. onChange={handleChange}
  276. beforeUpload={handleBeforeUpload}
  277. >
  278. {fileList?.length < 1 && '+' + intl.formatMessage({ id: 'public.uploadImg' })}
  279. </Upload>
  280. </ProForm.Item>
  281. <ProFormDatePicker
  282. name="date"
  283. label='日期'
  284. fieldProps={{
  285. format: (value) => value.format('YYYY.MM.DD'),
  286. }}
  287. labelCol={{
  288. style: { width: 95 }
  289. }}
  290. rules={[
  291. {
  292. required: true
  293. }
  294. ]
  295. }
  296. />
  297. <ProFormSelect
  298. name="column"
  299. key="column"
  300. label='新闻分类'
  301. request={() =>
  302. getDataEnumList({enumUuid: '2024810214616101'}).then((res) => {
  303. let tList = []
  304. res.rows.forEach(item => {
  305. tList.push({
  306. label: item.dataName,
  307. value: item.uuid,
  308. })
  309. })
  310. return tList;
  311. })
  312. }
  313. labelCol={{
  314. style: { width: 95 }
  315. }}
  316. placeholder={'请选择新闻分类'}
  317. />
  318. <ProFormText
  319. name="digest"
  320. label='摘要'
  321. labelCol={{
  322. style: { width: 95 }
  323. }}
  324. placeholder={'请输入摘要'}
  325. />
  326. <ProFormText
  327. name="key"
  328. label='网页关键字'
  329. labelCol={{
  330. style: { width: 95 }
  331. }}
  332. placeholder={'请输入页面关键字'}
  333. />
  334. <ProFormText
  335. name="source"
  336. label='来源'
  337. labelCol={{
  338. style: { width: 95 }
  339. }}
  340. placeholder={'中泽集团'}
  341. initialValue={'中泽集团'}
  342. />
  343. <ProForm.Item
  344. label={'新闻内容'}
  345. labelCol={{
  346. style: { width: 95 }
  347. }}
  348. >
  349. <div id='editor' style={{ width: '70vw', height: '500px' }} ref={editorRef}></div>
  350. </ProForm.Item>
  351. </ProForm>
  352. </Drawer>
  353. );
  354. };
  355. export default NewsForm;