$id$.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926
  1. /* eslint-disable camelcase */
  2. import React from 'react'
  3. import {Button, Form, Input, Select, Slider, Switch, Row, Col, InputNumber, Icon, Popover, Modal, message} from 'antd'
  4. import {Rnd} from 'react-rnd'
  5. import {ImagePreview, JsonPage} from 'wptpc-design'
  6. import isEmpty from 'lodash/isEmpty'
  7. // import { connect } from 'dva'
  8. import RcColorPicker from 'rc-color-picker'
  9. import 'rc-color-picker/assets/index.css'
  10. import {create, fetchDetail, update, fontList} from './service'
  11. import styles from './index.less'
  12. const IconFont = Icon.createFromIconfontCN({
  13. scriptUrl: '//at.alicdn.com/t/font_2018233_fl4w8ponsnu.js',
  14. });
  15. const {Option} = Select
  16. const {confirm} = Modal
  17. const unique = (arr) => {
  18. return Array.from(new Set(arr))
  19. }
  20. export default class Edit extends React.PureComponent {
  21. state = {
  22. name: '',
  23. description: '',
  24. bg: {
  25. type: 'bg',
  26. w: 750,
  27. h: 1000,
  28. c: '#eaeaea'
  29. },
  30. element: [],
  31. activeObjectIndex: 0,
  32. canvasArea: false,// 鼠标是否在画布区域
  33. editName: '',
  34. imgs: [], // 预览图片的url
  35. previwVisible: false, // 预览框是否展示
  36. fontList: [],//字体列表
  37. }
  38. keyPress = event => {
  39. const {activeObjectIndex, canvasArea} = this.state
  40. const activeObject = this.state.element[activeObjectIndex] || {}
  41. const e = event || window.event;
  42. const {keyCode} = e;
  43. const ctrlKey = e.ctrlKey || e.metaKey;
  44. // console.log("🚀 ~ file: $id$.js ~ line 45 ~ Edit ~ ctrlKey", ctrlKey)
  45. // console.log("🚀 ~ file: $id$.js ~ line 44 ~ Edit ~ keyCode", keyCode)
  46. if (ctrlKey && keyCode === 90) {
  47. e.preventDefault();
  48. this.reback = true;
  49. // 撤销
  50. let list = [];
  51. if (localStorage.getItem('posterElement') && JSON.parse(localStorage.getItem('posterElement'))?.length > 0) {
  52. list = JSON.parse(localStorage.getItem('posterElement'));
  53. if (this.state.id && list.length === 1) {
  54. this.reback = false;
  55. return
  56. }
  57. list = list.slice(0, -1);
  58. let obj = list.slice(-1)[0];
  59. if (!obj) {
  60. this.setState({
  61. name: '',
  62. description: '',
  63. bg: {
  64. type: 'bg',
  65. w: 750,
  66. h: 1000,
  67. c: '#eaeaea'
  68. },
  69. element: [],
  70. })
  71. } else {
  72. this.setState(obj)
  73. }
  74. localStorage.setItem('posterElement', JSON.stringify(list));
  75. }
  76. }
  77. if (!canvasArea) {
  78. return
  79. }
  80. // e.preventDefault(); 不能提取到最外面,有位只有按下方向键才需要阻止默认事件
  81. if (keyCode === 37) {
  82. e.preventDefault();
  83. //左
  84. this.updateElement(activeObjectIndex, {dx: activeObject.dx - 1})
  85. }
  86. if (keyCode === 38) {
  87. e.preventDefault();
  88. //上
  89. this.updateElement(activeObjectIndex, {dy: activeObject.dy - 1})
  90. }
  91. if (keyCode === 39) {
  92. e.preventDefault();
  93. // 右
  94. this.updateElement(activeObjectIndex, {dx: activeObject.dx + 1})
  95. }
  96. if (keyCode === 40) {
  97. e.preventDefault();
  98. //下
  99. this.updateElement(activeObjectIndex, {dy: activeObject.dy + 1})
  100. }
  101. };
  102. // 添加监听键盘事件 防止长按多次发送用keyup
  103. addKeyUpEvent = () => {
  104. window.addEventListener('keydown', this.keyPress);
  105. };
  106. // 移除监听键盘事件
  107. removeKeyUpEvent = () => {
  108. window.removeEventListener('keydowm', this.keyPress);
  109. };
  110. componentDidMount() {
  111. this.addKeyUpEvent();
  112. localStorage.setItem("posterElement", '')
  113. this.setState({posterHeight: document.body.clientHeight - document.getElementById('poster').offsetTop - 35})
  114. if (this.props.match.params.id) {
  115. fetchDetail({id: this.props.match.params.id}).then(res => {
  116. if (!isEmpty(res.data)) {
  117. const {data: {description, name, id, preview, config: [bg, ...element]}} = res
  118. this.setState({name: name, description: description, element: element, id: id, bg: bg, imgs: [preview]})
  119. }
  120. })
  121. fontList().then(res => {
  122. if (res.code === 0) {
  123. this.setState({fontList: res.data})
  124. }
  125. })
  126. }
  127. }
  128. componentDidUpdate(prevProps, prevState) {
  129. const {name, description, bg, element} = this.state;
  130. const {name: nameprev, description: descriptionprev, bg: bgprev, element: elementprev} = prevState;
  131. if (name === nameprev && description === descriptionprev && JSON.stringify(bg) === JSON.stringify(bgprev) && JSON.stringify(element) === JSON.stringify(elementprev)) {
  132. return
  133. }
  134. const temListStr = localStorage.getItem('posterElement');
  135. let obj = {}
  136. let list = [];
  137. if (temListStr) {
  138. list = JSON.parse(temListStr);
  139. }
  140. // 撤销的时候就不再去储存值了
  141. if (this.reback) {
  142. this.reback = false;
  143. return
  144. }
  145. list.push({
  146. element: element,
  147. name: name,
  148. description: description,
  149. bg: bg,
  150. })
  151. // 最多储存 30 步的操作
  152. if (list.length > 30) {
  153. list = list.slice(-30)
  154. }
  155. localStorage.setItem('posterElement', JSON.stringify(list))
  156. }
  157. componentWillUnmount() {
  158. this.removeKeyUpEvent();
  159. localStorage.setItem('posterElement', '')
  160. }
  161. updateBgState = (value) => {
  162. this.setState({bg: {...this.state.bg, ...value}})
  163. }
  164. updateElement = (index, value) => {
  165. // console.log(index, value);
  166. const _ele = this.state.element.slice()
  167. const _tar = _ele[index] // dom
  168. // console.log(_tar,value);
  169. _ele[index] = {
  170. ..._tar,
  171. ...value,
  172. }
  173. this.setState({element: _ele})
  174. }
  175. delElement = (index, name) => {
  176. confirm({
  177. title: '确定删除' + name + '?',
  178. okText: 'Yes',
  179. okType: 'danger',
  180. cancelText: 'No',
  181. onOk: () => {
  182. const _ele = this.state.element.slice()
  183. _ele.splice(index, 1)
  184. this.setState({element: _ele})
  185. }
  186. })
  187. }
  188. // 修改二维码logo
  189. changeQrLogo = (logo) => {
  190. let logos = new Map();
  191. logos.set('wpt_high', 'https://cdn.weipaitang.com/static/20200413eb9effec-3871-ffec3871-cd41-baed9e98535e-W200H200');
  192. logos.set('youjiang', 'https://cdn.weipaitang.com/static/202004141c0b320a-9cdc-320a9cdc-d090-e4b5964fee9d-W200H200');
  193. logos.set('cl', 'https://cdn.weipaitang.com/static/20210607d591559f-70e6-559f70e6-7427-f899f240d7a0-W284H285');
  194. let logoUrl = logos.get(logo);
  195. // 默认logo
  196. if (typeof logoUrl === "undefined"){
  197. logoUrl = "https://cdn.weipaitang.com/static/20200413d4263aa5-6423-3aa56423-4e8d-a96e1d9d93f3-W200H200"
  198. }
  199. return `url(` + logoUrl + `)`
  200. }
  201. addShape = (type) => {
  202. const _ele = this.state.element.slice()
  203. const {dx, dy} = _ele[this.state.activeObjectIndex] || {dx: 0, dy: 45}
  204. switch (type) {
  205. case 'text':
  206. _ele.push({
  207. content: '我是预览文本',
  208. type: 'text',
  209. t: '#text_' + (this.state.element.filter(item => item.type === 'text').length + 1) + '#',
  210. font: 'pingfang_sc_semibold',
  211. size: 36,
  212. dx: dx + 36,
  213. dy: dy + 36,
  214. width: 490,
  215. height: 45,
  216. c: '%23333333'
  217. })
  218. break
  219. case 'pic':
  220. _ele.push({
  221. type: 'pic',
  222. path: '#pic_' + (this.state.element.filter(item => item.type === 'pic').length + 1) + '#',
  223. content: 'https://cdn.weipaitang.com/img/20210417B2gW3tnOlVzgfja0TSqvdlE1YhnFV4QOtwswRdvwLiQKqnyDGLxPXHP3I3vXTiL0-W1080H1080/w/640',
  224. w: '100',
  225. h: '100',
  226. dx: dx + 15,
  227. dy: dy + 15,
  228. round: 'false'
  229. // align: 'both-align'
  230. })
  231. break
  232. case 'mat':
  233. _ele.push({
  234. type: 'mat',
  235. path: '#mat_' + (this.state.element.filter(item => item.type === 'mat').length + 1) + '#',
  236. content: 'https://cdn.weipaitang.com/img/202012147ystzory-b7hg-43vc-kg1g-goacjxmsbimr-W2415H2415/w/640',
  237. w: '100',
  238. h: '100',
  239. dx: dx + 15,
  240. dy: dy + 15,
  241. round: 'false'
  242. // align: 'both-align'
  243. })
  244. break
  245. case 'qr':
  246. _ele.push({
  247. content: '我是预览二维码',
  248. type: 'qr',
  249. t: '#qr_' + (this.state.element.filter(item => item.type === 'pic').length + 1) + '#',
  250. dx: dx + 15,
  251. dy: dy + 15,
  252. size: 140,
  253. logo: 'true'
  254. })
  255. break
  256. default:
  257. break
  258. }
  259. this.setState({element: _ele, activeObjectIndex: _ele.length - 1}, () => {
  260. // this.setState({ activeObjectIndex: this.state.element.length - 1 })
  261. })
  262. }
  263. save = () => {
  264. const {id, name, description, bg, element} = this.state
  265. const handleFun = id ? update : create
  266. element.map(item => {
  267. delete item.x
  268. delete item.y
  269. !item.fixed && (delete item.fixed)
  270. !item.relative && (delete item.relative)
  271. !item.group && (delete item.group)
  272. })
  273. handleFun({
  274. id,
  275. name,
  276. description,
  277. config: ([bg]).concat(element)
  278. }).then(res => {
  279. if (res.code === 0) {
  280. if (!id) {
  281. message.success('添加成功~')
  282. window.location.href = '/poster/edit/' + res.data.id
  283. } else {
  284. message.success('设置成功~')
  285. this.setState({
  286. imgs: [res.data.preview],
  287. previwVisible: true
  288. })
  289. }
  290. }
  291. })
  292. }
  293. // 动态的加载字体
  294. // loadFonts= async ()=> {
  295. // const font = new FontFace(
  296. // "font84",
  297. // "url(https://cdn.weipaitang.com/static/public/202012016ac3fed2-0fe8-fed20fe8-56d0-cdacb08d7de0.ttf)"
  298. // );
  299. // await font.load();
  300. // document.fonts.add(font);
  301. // document.body.classList.add("fonts-loaded");
  302. // }
  303. render() {
  304. const {
  305. bg: {w: bgWidth, h: bgHeight, c: bgColor},
  306. activeObjectIndex,
  307. element,
  308. name,
  309. description,
  310. imgs,
  311. previwVisible
  312. } = this.state
  313. const activeObject = element[activeObjectIndex] || {}
  314. const group = element.map(item => item.group)
  315. const _element = element.map((item, index) => ({...item, index: index}))
  316. // console.log('activeObject',activeObject);
  317. return (
  318. <div id="poster" style={{height: this.state.posterHeight, display: 'flex', flexDirection: 'column'}}>
  319. <div className={styles.header}>
  320. <span>模版:<Input style={{width: '200px'}} value={name} onChange={e => this.setState({name: e.target.value})}/></span>
  321. <span>&emsp;描述:<Input style={{width: '200px'}} value={description}
  322. onChange={e => this.setState({description: e.target.value})}/></span>
  323. <span>
  324. <Button onClick={() => this.setState({previwVisible: true})}>预览</Button>
  325. <Button type="primary" onClick={this.save}>保存</Button>
  326. </span>
  327. </div>
  328. <div className={styles.content}>
  329. <div className={styles.left}>
  330. <div className={styles.box}>
  331. <h2>背景</h2>
  332. <Form layout="vertical">
  333. <Form.Item label="宽度">
  334. <Input value={bgWidth} onChange={(e) => {
  335. this.updateBgState({w: Number.parseFloat(e.target.value || 0)})
  336. }}/>
  337. </Form.Item>
  338. <Form.Item label="高度">
  339. <Input value={bgHeight} onChange={(e) => {
  340. this.updateBgState({h: Number.parseFloat(e.target.value || 0)})
  341. }}/>
  342. </Form.Item>
  343. <Form.Item label="背景色">
  344. <div style={{width: '60px'}}>
  345. <RcColorPicker
  346. color={bgColor}
  347. onChange={({color}) => this.updateBgState({c: color})}
  348. >
  349. <Input/>
  350. </RcColorPicker>
  351. </div>
  352. </Form.Item>
  353. </Form>
  354. </div>
  355. <div className={styles.box}>
  356. <h2>元素</h2>
  357. <div style={{fontSize: 18, cursor: "pointer"}}>
  358. <Row>
  359. <Col span={12} onClick={() => this.addShape('text')}><IconFont type='iconwenben'/><span
  360. style={{display: 'inline-block', margin: 5}}>文本</span></Col>
  361. <Col span={12} onClick={() => this.addShape('pic')}><Icon type="picture"/><span
  362. style={{display: 'inline-block', margin: 5}}>图片</span></Col>
  363. <Col span={12} onClick={() => this.addShape('qr')}><Icon type="qrcode"/><span
  364. style={{display: 'inline-block', margin: 5}}>二维码</span></Col>
  365. <Col span={12} onClick={() => this.addShape('mat')}><IconFont type='iconsucai'/><span
  366. style={{display: 'inline-block', margin: 5}}>素材</span></Col>
  367. </Row>
  368. </div>
  369. {/* <ul className={styles.elem}>
  370. <li onClick={() => this.addShape('text')}><img src="https://cdn.weipaitang.com/static/20200416b0b57798-76f7-779876f7-9088-1d2665151e70-W200H200" />文字</li>
  371. <li className={styles.pic} onClick={() => this.addShape('pic')}><img src="https://cdn.weipaitang.com/static/20200416a625b5f0-c5a1-b5f0c5a1-7472-58fbb8d4fb83-W200H200"/>图片</li>
  372. <li className={styles.qr} onClick={() => this.addShape('qr')}><img src="https://cdn.weipaitang.com/static/20200416360ff7ff-c855-f7ffc855-0070-1542fd7ea01e-W200H200"/>二维码</li>
  373. </ul> */}
  374. </div>
  375. <div className={styles.box}>
  376. <h2>图层列表</h2>
  377. <ul className={styles.layer}>
  378. {
  379. unique(group).map((_item) => {
  380. console.log(_element, '_element');
  381. return <React.Fragment>
  382. <div>分组:{_item || '未分组'}</div>
  383. {
  384. _element.filter(item => item.group === _item).map((item, index) => (
  385. <li
  386. className={`${activeObjectIndex === item.index ? styles.selected : ''} ${styles[item.type]}`}
  387. onClick={() => this.setState({activeObjectIndex: item.index})}>
  388. <div className={styles.tem}
  389. style={{display: 'inline-block', verticalAlign: -2, backgroundColor: '#1890ff05'}}>
  390. {item.type === 'pic' && <Icon type="picture"/>}
  391. {item.type === 'mat' && <IconFont type="iconsucai"/>}
  392. {item.type === 'text' && <IconFont type="iconwenben"/>}
  393. {item.type === 'qr' && <Icon type="qrcode"/>}
  394. {item.isTemplate ? <IconFont type="iconmu"/> : <IconFont type="iconkongbai"/>}
  395. </div>
  396. {((item.type === 'pic' || item.type === 'mat') ? item.path : item.t || '').toString().replace(/#/g, '')}
  397. <Popover placement="right" content={<div><p onClick={() => this.setState({
  398. visible: true,
  399. editIndex: item.index,
  400. editType: item.type,
  401. editName: (item.type === 'pic' || item.type === 'mat') ? item.path : item.t || ''
  402. })}>重命名</p><p
  403. onClick={() => this.delElement(item.index, item.type === 'pic' ? item.path : item.t)}>删除</p>
  404. </div>}>
  405. <Icon type="setting" theme="filled"/>
  406. </Popover>
  407. </li>
  408. ))
  409. }
  410. </React.Fragment>
  411. })
  412. }
  413. </ul>
  414. </div>
  415. </div>
  416. <div className={styles.middle} onMouseEnter={() => {
  417. this.setState({canvasArea: true})
  418. }} onMouseLeave={() => {
  419. this.setState({canvasArea: false})
  420. }}>
  421. <div id="canvas" style={{
  422. position: 'relative',
  423. width: Number(bgWidth),
  424. height: Number(bgHeight),
  425. lineHeight: 1.3,
  426. backgroundColor: bgColor,
  427. marginBottom: '190px',
  428. margin: '0 auto'
  429. }}
  430. >
  431. {
  432. element.map((item, index) => {
  433. const clampLine = Number((Number(item.height) / Number(item.size)).toFixed(0)) - 1 || 1
  434. if (item.type === 'text') {
  435. return (
  436. <Rnd
  437. disableDragging={item.fixed}
  438. size={{width: Number.parseFloat(item.weight), height: Number.parseFloat(item.height)}}
  439. position={{x: item.dx, y: item.dy - item.size}}
  440. onDragStop={(e, d) => {
  441. this.updateElement(index, {dx: d.x, dy: d.y + item.size})
  442. }}
  443. onResizeStop={(e, direction, ref, delta, position) => { // 单击、双击文字框 出现白屏
  444. if (ref.style.width && ref.style.height) this.updateElement(index, {
  445. width: Number.parseFloat(ref.style.width || 0),
  446. height: Number.parseFloat(ref.style.height || 0), ...position
  447. })
  448. }}
  449. bounds="parent"
  450. className={
  451. activeObjectIndex === index ? styles.outline : ""
  452. }
  453. // enableResizing={{
  454. // top:false, right:true, bottom:false, left:false, topRight:false, bottomRight:false, bottomLeft:false, topLeft:false
  455. // }}
  456. >
  457. <div
  458. className={styles.textRnd}
  459. onClick={() =>
  460. this.setState({activeObjectIndex: index})
  461. }
  462. style={{
  463. WebkitLineClamp: clampLine,
  464. // width: item.float_x === 'center' ? (Number.parseFloat(bgWidth) - Number.parseFloat(item.dx)) + 'px' : (Number.parseFloat(item.width) || 'auto'),
  465. width: Number.parseFloat(item.width) || 'auto',
  466. height: Number.parseFloat(item.height) || 'auto',
  467. fontFamily: item.font,
  468. fontSize: item.size,
  469. color: item.c,
  470. opacity: item.opacity,
  471. position: 'relative',
  472. textAlign: item.float_x,
  473. writingMode: item.vertical && "vertical-lr",
  474. display: item.vertical && "inline-block" || item.float_x === 'center' && "inline-block",
  475. // left: (Number.parseFloat(bgWidth) - Number.parseFloat(item.dx) - Number.parseFloat(item.width)) / 2 + 'px'
  476. }}
  477. >
  478. {item.content}
  479. </div>
  480. </Rnd>
  481. );
  482. }
  483. if (item.type === 'pic' || item.type === 'mat') {
  484. return (
  485. <Rnd
  486. size={{width: Number.parseFloat(item.w), height: Number.parseFloat(item.h)}}
  487. position={{x: item.dx, y: item.dy}}
  488. onDragStop={(e, d) => {
  489. this.updateElement(index, {dx: d.x, dy: d.y})
  490. }}
  491. onResizeStop={(e, direction, ref, delta, position) => {
  492. this.updateElement(index, {
  493. w: Number.parseFloat(ref.style.width),
  494. h: Number.parseFloat(ref.style.height), ...position
  495. })
  496. }}
  497. bounds="parent"
  498. className={activeObjectIndex === index ? styles.outline : ''}
  499. >
  500. <div
  501. onClick={() => this.setState({activeObjectIndex: index})}
  502. style={{
  503. backgroundRepeat: 'no-repeat',
  504. backgroundSize: 'cover',
  505. backgroundImage: `url(${item.content})`,
  506. backgroundPosition: (item.align === 'horizontal-align') ? 'top center' : (item.align === 'vertical-align' ? 'center left' : 'center center'),
  507. width: Number.parseFloat(item.w || 0) + 'px',
  508. height: Number.parseFloat(item.h || 0) + 'px',
  509. borderRadius: ((item.round || '').toString() === 'true') ? '100%' : (`${item.corner || 0}px ${item.corner || 0}px` || 0),
  510. filter: `blur(${(item.gauss || '').split(',')[0] / 2}px)`
  511. }}
  512. >
  513. </div>
  514. </Rnd>
  515. )
  516. }
  517. if (item.type === 'qr') {
  518. console.log(item)
  519. return (
  520. <Rnd
  521. size={{width: Number.parseFloat(item.size), height: Number.parseFloat(item.size)}}
  522. position={{x: item.dx, y: item.dy}}
  523. onDragStop={(e, d) => {
  524. this.updateElement(index, {dx: d.x, dy: d.y})
  525. }}
  526. onResizeStop={(e, direction, ref, delta, position) => {
  527. this.updateElement(index, {size: Number.parseFloat(ref.style.width), ...position})
  528. }}
  529. bounds="parent"
  530. className={activeObjectIndex === index ? styles.outline : ''}
  531. >
  532. {/*<div*/}
  533. {/* onClick={() => this.setState({activeObjectIndex: index})}*/}
  534. {/* style={{*/}
  535. {/* backgroundRepeat: 'no-repeat',*/}
  536. {/* backgroundSize: 'cover',*/}
  537. {/* backgroundImage: `url(${item.logo === 'wpt_high' || item.logo === 'youjiang' || item.logo === 'cl'*/}
  538. {/* ? (item.logo === 'wpt_high'*/}
  539. {/* ? 'https://cdn.weipaitang.com/static/20200413eb9effec-3871-ffec3871-cd41-baed9e98535e-W200H200'*/}
  540. {/* : 'https://cdn.weipaitang.com/static/202004141c0b320a-9cdc-320a9cdc-d090-e4b5964fee9d-W200H200')*/}
  541. {/* : 'https://cdn.weipaitang.com/static/20200413d4263aa5-6423-3aa56423-4e8d-a96e1d9d93f3-W200H200'})`,*/}
  542. {/* width: Number.parseFloat(item.size || '0') + 'px',*/}
  543. {/* height: Number.parseFloat(item.size || '0') + 'px'*/}
  544. {/* }}*/}
  545. {/*>*/}
  546. {/*</div>*/}
  547. <div
  548. onClick={() => this.setState({activeObjectIndex: index})}
  549. style={{
  550. backgroundRepeat: 'no-repeat',
  551. backgroundSize: 'cover',
  552. backgroundImage: this.changeQrLogo(item.logo),
  553. width: Number.parseFloat(item.size || '0') + 'px',
  554. height: Number.parseFloat(item.size || '0') + 'px'
  555. }}
  556. >
  557. </div>
  558. </Rnd>
  559. )
  560. }
  561. })
  562. }
  563. </div>
  564. </div>
  565. <div className={styles.right}>
  566. {
  567. activeObject.type === 'text' && (
  568. <div className={styles.box}>
  569. <h2>
  570. <span>文字</span>
  571. <span style={{float: 'right', fontSize: 14}}>
  572. <span style={{marginRight: 5}}>设置为模板</span>
  573. <Switch checked={activeObject.isTemplate || false}
  574. onChange={(checked) => this.updateElement(activeObjectIndex, {isTemplate: checked})}/>
  575. </span>
  576. </h2>
  577. <Form layout="vertical">
  578. <Form.Item label="内容">
  579. <Input.TextArea rows={3} value={activeObject.content}
  580. onChange={(e) => this.updateElement(activeObjectIndex, {content: e.target.value})}/>
  581. </Form.Item>
  582. <Form.Item label="字体">
  583. <Select value={activeObject.font}
  584. onChange={(value) => this.updateElement(activeObjectIndex, {font: value})}>
  585. {(this.state.fontList || []).map(item => {
  586. return <Option value={item.value}>{item.option}</Option>
  587. })}
  588. </Select>
  589. </Form.Item>
  590. <div style={{display: 'flex'}}>
  591. <Form.Item label="字体大小" style={{flex: 1}}>
  592. <InputNumber style={{width: 80}} min={12} max={100} value={activeObject.size}
  593. onChange={(value) => this.updateElement(activeObjectIndex, {size: value})}/>
  594. </Form.Item>
  595. <Form.Item label="字体颜色" style={{flex: 1}}>
  596. <div style={{width: '60px'}}
  597. >
  598. <RcColorPicker
  599. color={activeObject.c}
  600. onChange={({color}) => this.updateElement(activeObjectIndex, {c: color})}
  601. >
  602. <Input/>
  603. </RcColorPicker>
  604. </div>
  605. </Form.Item>
  606. </div>
  607. <div style={{display: 'flex'}}>
  608. <Form.Item label="X偏移" style={{flex: 1}}>
  609. <InputNumber style={{width: 80}} value={activeObject.dx}
  610. onChange={(value) => this.updateElement(activeObjectIndex, {dx: value})}/>
  611. </Form.Item>
  612. <Form.Item label="Y偏移" style={{flex: 1}}>
  613. <InputNumber style={{width: 80}} value={activeObject.dy}
  614. onChange={(value) => this.updateElement(activeObjectIndex, {dy: value})}/>
  615. </Form.Item>
  616. </div>
  617. <div style={{display: 'flex'}}>
  618. <Form.Item label="文字宽度" style={{flex: 1}}>
  619. <InputNumber value={activeObject.width} style={{width: 80}}
  620. onChange={(value) => this.updateElement(activeObjectIndex, {width: value})}/>
  621. </Form.Item>
  622. <Form.Item label="文字高度" style={{flex: 1}}>
  623. <InputNumber value={activeObject.height} style={{width: 80}}
  624. onChange={(value) => this.updateElement(activeObjectIndex, {height: value})}/>
  625. </Form.Item>
  626. </div>
  627. <div style={{display: 'flex'}}>
  628. <Form.Item label="X居中" style={{flex: 1}}>
  629. <Select value={activeObject.float_x || ""} style={{width: 80}}
  630. onChange={(value) => this.updateElement(activeObjectIndex, {float_x: value})}>
  631. <Option value="">不居中</Option>
  632. <Option value="center">居中</Option>
  633. {/* <Option value="right">right</Option> */}
  634. <Option value="left">居左</Option>
  635. </Select>
  636. </Form.Item>
  637. <Form.Item label="文字垂直" style={{flex: 1}}>
  638. <Switch
  639. checked={activeObject.vertical}
  640. onChange={(checked) => this.updateElement(activeObjectIndex, {vertical: checked})}
  641. checkedChildren="开"
  642. unCheckedChildren="关"
  643. />
  644. </Form.Item>
  645. </div>
  646. <Form.Item label="不透明">
  647. <Slider
  648. min={0}
  649. max={1}
  650. step="0.1"
  651. onChange={(value) => this.updateElement(activeObjectIndex, {opacity: value})}
  652. value={activeObject.opacity}
  653. />
  654. </Form.Item>
  655. <div style={{display: 'flex'}}>
  656. <Form.Item label="固定位置" style={{flex: 1}}>
  657. <Switch
  658. checked={activeObject.fixed}
  659. onChange={(checked) => this.updateElement(activeObjectIndex, {fixed: checked})}
  660. checkedChildren="开"
  661. unCheckedChildren="关"
  662. />
  663. </Form.Item>
  664. <Form.Item label="浮动位置" style={{flex: 1}}>
  665. <Switch
  666. checked={activeObject.relative}
  667. onChange={(checked) => this.updateElement(activeObjectIndex, {relative: checked})}
  668. checkedChildren="开"
  669. unCheckedChildren="关"
  670. />
  671. </Form.Item>
  672. </div>
  673. <Form.Item label="分组" style={{flex: 1}}>
  674. <InputNumber
  675. style={{width: 100}}
  676. value={activeObject.group}
  677. onChange={(value) => this.updateElement(activeObjectIndex, {group: value || undefined})}
  678. />
  679. </Form.Item>
  680. </Form>
  681. </div>
  682. )
  683. }
  684. {
  685. activeObject.type === 'qr' && (
  686. <div className={styles.box}>
  687. <h2>
  688. <span>二维码</span>
  689. <span style={{float: 'right', fontSize: 14}}>
  690. <span style={{marginRight: 5}}>设置为模版</span>
  691. <Switch checked={activeObject.isTemplate || false}
  692. onChange={(checked) => this.updateElement(activeObjectIndex, {isTemplate: checked})}/>
  693. </span>
  694. </h2>
  695. <Form layout="vertical">
  696. <Form.Item label="内容">
  697. <Input value={activeObject.content}
  698. onChange={(e) => this.updateElement(activeObjectIndex, {content: e.target.value})}/>
  699. </Form.Item>
  700. <div style={{display: 'flex'}}>
  701. <Form.Item label="X偏移" style={{flex: 1}}>
  702. <InputNumber style={{width: 80}} value={activeObject.dx}
  703. onChange={(value) => this.updateElement(activeObjectIndex, {dx: value})}/>
  704. </Form.Item>
  705. <Form.Item label="Y偏移" style={{flex: 1}}>
  706. <InputNumber style={{width: 80}} value={activeObject.dy}
  707. onChange={(value) => this.updateElement(activeObjectIndex, {dy: value})}/>
  708. </Form.Item>
  709. </div>
  710. <Form.Item label="LOGO">
  711. <Switch
  712. checked={activeObject.logo === 'wpt_high' || activeObject.logo === 'youjiang' || activeObject.logo === 'cl'}
  713. onChange={(checked) => this.updateElement(activeObjectIndex, {logo: checked ? 'wpt_high' : false})}
  714. checkedChildren="开"
  715. unCheckedChildren="关"
  716. />
  717. {
  718. (activeObject.logo === 'wpt_high' || activeObject.logo === 'youjiang' || activeObject.logo === 'cl') && (
  719. <Select value={activeObject.logo} style={{width: 120, marginLeft: '5px'}}
  720. onChange={value => this.updateElement(activeObjectIndex, {logo: value})}>
  721. <Option value="wpt_high">微拍堂</Option>
  722. <Option value="youjiang">有匠</Option>
  723. <Option value="cl">彩礼</Option>
  724. </Select>
  725. )
  726. }
  727. </Form.Item>
  728. <Form.Item label="大小">
  729. <InputNumber style={{width: 100}} value={activeObject.size}
  730. onChange={(value) => this.updateElement(activeObjectIndex, {size: value})}/>
  731. </Form.Item>
  732. <div style={{display: 'flex'}}>
  733. <Form.Item label="固定位置" style={{flex: 1}}>
  734. <Switch
  735. checked={activeObject.fixed}
  736. onChange={(checked) => this.updateElement(activeObjectIndex, {fixed: checked})}
  737. checkedChildren="开"
  738. unCheckedChildren="关"
  739. />
  740. </Form.Item>
  741. <Form.Item label="浮动位置" style={{flex: 1}}>
  742. <Switch
  743. checked={activeObject.relative}
  744. onChange={(checked) => this.updateElement(activeObjectIndex, {relative: checked})}
  745. checkedChildren="开"
  746. unCheckedChildren="关"
  747. />
  748. </Form.Item>
  749. </div>
  750. <Form.Item label="分组" style={{flex: 1}}>
  751. <InputNumber
  752. style={{width: 100}}
  753. value={activeObject.group}
  754. onChange={(value) => this.updateElement(activeObjectIndex, {group: value || undefined})}
  755. />
  756. </Form.Item>
  757. </Form>
  758. </div>
  759. )
  760. }
  761. {
  762. (activeObject.type === 'pic' || activeObject.type === 'mat') && (
  763. <div className={styles.box}>
  764. <h2>
  765. <span>{activeObject.type === 'pic' ? "图片" : "素材"}</span>
  766. <span style={{float: 'right', fontSize: 14}}>
  767. <span style={{marginRight: 5}}>设置为模板</span>
  768. <Switch checked={activeObject.isTemplate || false}
  769. onChange={(checked) => this.updateElement(activeObjectIndex, {isTemplate: checked})}/>
  770. </span>
  771. </h2>
  772. <Form layout="vertical">
  773. <Form.Item label="路径">
  774. <Input value={activeObject.content}
  775. onChange={(e) => this.updateElement(activeObjectIndex, {content: e.target.value})}/>
  776. </Form.Item>
  777. <div style={{display: 'flex'}}>
  778. <Form.Item label="宽" style={{flex: 1}}>
  779. <InputNumber style={{width: 80}} value={activeObject.w}
  780. onChange={(value) => this.updateElement(activeObjectIndex, {w: value})}/>
  781. </Form.Item>
  782. <Form.Item label="高" style={{flex: 1}}>
  783. <InputNumber style={{width: 80}} value={activeObject.h}
  784. onChange={(value) => this.updateElement(activeObjectIndex, {h: value})}/>
  785. </Form.Item>
  786. </div>
  787. <div style={{display: 'flex'}}>
  788. <Form.Item label="X偏移" style={{flex: 1}}>
  789. <InputNumber style={{width: 80}} value={activeObject.dx}
  790. onChange={(value) => this.updateElement(activeObjectIndex, {dx: value})}/>
  791. </Form.Item>
  792. <Form.Item label="Y偏移" style={{flex: 1}}>
  793. <InputNumber style={{width: 80}} value={activeObject.dy}
  794. onChange={(value) => this.updateElement(activeObjectIndex, {dy: value})}/>
  795. </Form.Item>
  796. </div>
  797. <div style={{display: 'flex'}}>
  798. <Form.Item label="圆形" style={{flex: 1}}>
  799. <Switch checked={(activeObject.round || '').toString() === 'true'} onChange={(checked) => {
  800. if (checked) {
  801. this.updateElement(activeObjectIndex, {round: 'true'})
  802. } else {
  803. this.updateElement(activeObjectIndex, {round: 'false'})
  804. }
  805. }} checkedChildren="开" unCheckedChildren="关"/>
  806. </Form.Item>
  807. <Form.Item label="圆角" style={{flex: 1}}>
  808. <Input
  809. placeholder="圆角半径"
  810. style={{width: 100}}
  811. value={activeObject.corner}
  812. onChange={(e) => this.updateElement(activeObjectIndex, {corner: e.target.value})}
  813. />
  814. </Form.Item>
  815. </div>
  816. <Form.Item label="高斯模糊">
  817. <Input
  818. value={activeObject.gauss}
  819. onChange={(e) => this.updateElement(activeObjectIndex, {gauss: e.target.value})}
  820. />
  821. </Form.Item>
  822. <Form.Item label="图片裁剪选项">
  823. {activeObject.align}
  824. <Select value={activeObject.align}
  825. onChange={(value) => this.updateElement(activeObjectIndex, {align: value})}>
  826. <Option value="">为空不选</Option>
  827. <Option value="vertical-align">垂直居中</Option>
  828. <Option value="horizontal-align">水平居中</Option>
  829. <Option value="both-align">垂直水平居中</Option>
  830. </Select>
  831. </Form.Item>
  832. <div style={{display: 'flex'}}>
  833. <Form.Item label="固定位置" style={{flex: 1}}>
  834. <Switch
  835. checked={activeObject.fixed}
  836. onChange={(checked) => this.updateElement(activeObjectIndex, {fixed: checked})}
  837. checkedChildren="开"
  838. unCheckedChildren="关"
  839. />
  840. </Form.Item>
  841. <Form.Item label="浮动位置" style={{flex: 1}}>
  842. <Switch
  843. checked={activeObject.relative}
  844. onChange={(checked) => this.updateElement(activeObjectIndex, {relative: checked})}
  845. checkedChildren="开"
  846. unCheckedChildren="关"
  847. />
  848. </Form.Item>
  849. {activeObject.type === 'pic' && <Form.Item label="使用原图" style={{flex: 1}}>
  850. <Switch
  851. checked={activeObject.original}
  852. onChange={(checked) => this.updateElement(activeObjectIndex, {original: checked})}
  853. checkedChildren="开"
  854. unCheckedChildren="关"
  855. />
  856. </Form.Item>}
  857. </div>
  858. <Form.Item label="分组" style={{flex: 1}}>
  859. <InputNumber
  860. style={{width: 100}}
  861. value={activeObject.group}
  862. onChange={(value) => this.updateElement(activeObjectIndex, {group: value || undefined})}
  863. />
  864. </Form.Item>
  865. </Form>
  866. </div>
  867. )
  868. }
  869. </div>
  870. </div>
  871. <Modal
  872. title="重命名"
  873. visible={this.state.visible}
  874. onOk={() => {
  875. this.updateElement(this.state.editIndex, {[(this.state.editType === 'pic' || this.state.editType === 'mat') ? 'path' : 't']: '#' + this.state.editName + '#'})
  876. this.setState({visible: false})
  877. }}
  878. onCancel={() => this.setState({visible: false})}
  879. >
  880. <Input value={this.state.editName.replace(/#/g, '')}
  881. onChange={e => this.setState({editName: e.target.value})}/>
  882. </Modal>
  883. <ImagePreview
  884. images={imgs}
  885. visible={previwVisible}
  886. onClose={() => {
  887. this.setState({previwVisible: false})
  888. }}
  889. />
  890. </div>
  891. )
  892. }
  893. }