123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- import TextAideInput from "@/pages/launchSystemV3/components/TextAideInput"
- import { extractAndFilterBracketsContent, txtLength } from "@/utils/utils"
- import { Button, Card, Form, InputNumber, Modal, Popconfirm, Radio, Space, message } from "antd"
- import React, { useEffect, useState } from "react"
- import { TextTypeList } from "../../const"
- import { DeleteOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"
- import AddTextS from "./addTextS"
- import style from '../index.less'
- import styles from '../Material/index.less'
- import VideoNews from "@/pages/launchSystemNew/components/newsModal/videoNews"
- import SelectCopyWriting from "@/pages/launchSystemV3/tencenTasset/copyWriting/selectCopyWriting"
- interface Props {
- textData: any,
- dynamicMaterialDTos: any,
- mediaType: 0 | 1 | 2 | 3,
- deliveryMode?: string,
- value?: any,
- visible?: boolean
- onClose?: () => void
- onChange?: (value: any) => void
- putInType?: 'NOVEL' | 'GAME'
- }
- /**
- * 创意文案
- * @param param0
- * @returns
- */
- const NewText: React.FC<Props> = ({ visible, onClose, onChange, value, textData, dynamicMaterialDTos, mediaType, deliveryMode, putInType }) => {
- /*************************************/
- const [form] = Form.useForm();
- const type = Form.useWatch('type', form)
- const textDto = Form.useWatch('textDto', form)
- const [textList, setTextList] = useState<PULLIN.TextDtoProps[]>([])
- const [desVisible, setDesVisible] = useState<boolean>(false)
- const [addSTitle, setAddStitle] = useState<string>()
- const [newText, setNewText] = useState<{ description?: string[], title?: string[] }>({})
- const [maxNumber, setMaxNumber] = useState<number>(1)
- const [groupNumber, setGroupNumber] = useState<number>(1)
- /*************************************/
- const handleOk = (values: any) => {
- console.log(values)
- const { type, textDto } = values
- onChange?.({
- type,
- dynamicCreativesTextDetailDTOList: textDto
- })
- }
- useEffect(() => {
- if (value && Object.keys(value).length) {
- const { type, dynamicCreativesTextDetailDTOList } = value
- form.setFieldsValue({ type, textDto: dynamicCreativesTextDetailDTOList })
- }
- }, [value])
- useEffect(() => {
- if (textData && Object.keys(textData)) {
- let newText: { description?: string[], title?: string[] } = {}
- let data = Object.values(textData).map((item: any) => {
- let content = item.children.content
- if (item.name === 'description') {
- newText.description = ['']
- } else if (item.name === 'title') {
- newText.title = ['']
- }
- return { label: content.description, restriction: content.restriction, value: item.name, required: item.required, arrayProperty: item?.arrayProperty }
- })
- setMaxNumber(data?.[0]?.arrayProperty?.maxNumber)
- setNewText(newText)
- if (!(value && Object.keys(value).length)) { form.setFieldsValue({ textDto: [newText] }) }
- setTextList(data)
- }
- }, [textData, value])
- // 一一对应显示素材
- const showMaterial = (index: number) => {
- const dynamicGroup = dynamicMaterialDTos?.dynamicGroup
- if (dynamicGroup && Object.keys(dynamicGroup).length) {
- let dynamic = dynamicGroup[index]
- const keys = Object.keys(dynamic)
- if (deliveryMode === "DELIVERY_MODE_CUSTOMIZE") {
- return <div className={style.detail_body_m}>
- {(keys.includes('video_id') || keys.includes('short_video1')) ? <>
- <div className={style.video}>
- <VideoNews src={dynamic?.video_id?.url || dynamic?.short_video1?.url} keyFrameImageUrl={dynamic?.video_id?.keyFrameImageUrl || dynamic?.short_video1?.keyFrameImageUrl} style={{ width: 40, height: 30 }} />
- {dynamic?.cover_id && <div className={style.cover_image} style={{ marginLeft: 4, width: 40, height: 30, minWidth: 42 }}>
- <img src={dynamic?.cover_id?.url} style={{ maxWidth: '96%', maxHeight: '96%' }} />
- </div>}
- </div>
- </> : keys.includes('image_id') ? <>
- <div className={style.cover_image} style={{ width: 40, height: 30, minWidth: 42 }}>
- <img src={dynamic?.image_id?.url} />
- </div>
- </> : (keys.includes('image_list') || keys.includes('element_story')) ? <>
- {dynamic?.[keys.includes('image_list') ? 'image_list' : 'element_story']?.map((item: { url: string | undefined; }, index: undefined) => <div className={style.cover_image} key={index} style={{ width: 30, height: 24, minWidth: 32 }}>
- <img src={item?.url} />
- </div>)}
- </> : null}
- </div>
- } else {
- return <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap' }}>
- {dynamic?.list?.map((item: any, index: number) => {
- if (Array.isArray(item)) {
- let length = item.length
- return <div className={styles.boxList_body_item} key={index} style={{ width: 30, height: 30 }}>
- <div className={styles.content} style={{ width: 30, height: 30 }}>
- {item.map((l, i) => <img src={l?.url} key={i} style={{ width: length === 6 ? 9.999 : 14.999 }} />)}
- </div>
- </div>
- } else if (item?.url?.includes('mp4') || item?.keyFrameImageUrl) {
- return <div className={styles.boxList_body_item} key={index} style={{ width: 30, height: 30 }}>
- <div className={styles.content} style={{ width: 30, height: 30 }}>
- <VideoNews src={item?.url} style={{ width: 30, height: 30 }} keyFrameImageUrl={item?.keyFrameImageUrl} maskBodyStyle={{ backgroundColor: "rgba(242, 246, 254, 0.1)" }} />
- </div>
- </div>
- } else {
- return <div className={styles.boxList_body_item} key={index} style={{ width: 30, height: 30 }}>
- <div className={styles.content} style={{ width: 30, height: 30 }}><img src={item?.url} /></div>
- </div>
- }
- })}
- </div>
- }
- }
- return null
- }
- const setText = (valList: string[]) => {
- let fieldData = textList.find(item => item.label === addSTitle)
- let count = dynamicMaterialDTos.dynamicGroup.length
- if (type === 1 || type === 0) {
- let len = 0
- const newTextDto = textDto.map((item: { [x: string]: any }) => {
- if (fieldData?.value && (item?.[fieldData?.value]?.length === 0 || !item?.[fieldData?.value]?.every((t: string) => t)) && valList.length >= len + 1) {
- return {
- ...item,
- [fieldData.value]: (item?.[fieldData?.value]?.length === 0 ? [''] : item?.[fieldData?.value]).map((t: string) => {
- if (t) {
- return t
- } else {
- return valList[len++]
- }
- })
- }
- }
- return item
- })
- form.setFieldsValue({
- textDto: newTextDto
- })
- } else if (type === 2) {
- let len = 0
- const newTextDto = textDto.map((item: { [x: string]: any }) => {
- if (fieldData?.value && (item?.[fieldData?.value]?.length === 0 || !item?.[fieldData?.value]?.every((t: string) => t)) && valList.length >= len + 1) {
- return {
- ...item,
- [fieldData.value]: (item?.[fieldData?.value]?.length === 0 ? [''] : item?.[fieldData?.value]).map((t: string) => {
- if (t) {
- return t
- } else {
- return valList[len++]
- }
- })
- }
- }
- return item
- })
- let diffTextDto: any[] = []
- if (newTextDto.length < count && len < valList.length) {
- let diffCount = count - newTextDto.length
- let diffTextCount = valList.length - len
- let diff = 0
- if (diffCount >= diffTextCount) {
- diff = diffTextCount
- } else {
- diff = diffCount
- }
- diffTextDto = Array(diff).fill('').map((item: { [x: string]: any }) => {
- if (fieldData?.value) {
- return { ...item, [fieldData.value]: [valList[len++]] }
- }
- return item
- })
- }
- form.setFieldsValue({
- textDto: [...newTextDto, ...diffTextDto]
- })
- } else if ([3, 4].includes(type)) {
- let len = 0
- const newTextDto = textDto.map((item: { [x: string]: any }) => {
- if (fieldData?.value && (item?.[fieldData?.value]?.length === 0 || !item?.[fieldData?.value]?.every((t: string) => t)) && valList.length >= len + 1) {
- return {
- ...item, [fieldData.value]: (item?.[fieldData?.value]?.length === 0 ? [''] : item?.[fieldData?.value]).map((t: string) => {
- if (t) {
- return t
- } else {
- return valList[len++]
- }
- })
- }
- }
- return item
- })
- let diffTextDto: any[] = []
- if (len < valList.length) {
- let diff = valList.length - len
- diffTextDto = Array(diff).fill('').map((item: { [x: string]: any }) => {
- if (fieldData?.value) {
- return { ...item, [fieldData.value]: [valList[len++]] }
- }
- return item
- })
- }
- form.setFieldsValue({
- textDto: [...newTextDto, ...diffTextDto]
- })
- }
- }
- return <Modal
- title={<Space align="center">
- <strong style={{ fontSize: 20 }}>创意文案</strong>
- {type !== 0 && <>
- {textList.some(item => item.value === "description") && <a style={{ fontSize: 12 }} onClick={() => { setDesVisible(true); setAddStitle(textList.find(item => item.value === "description")?.label) }}>批量添加{textList.find(item => item.value === "description")?.label}</a>}
- {textList.some(item => item.value === "title") && <a style={{ fontSize: 12 }} onClick={() => { setDesVisible(true); setAddStitle(textList.find(item => item.value === "description")?.label) }}>批量添加{textList.find(item => item.value === "description")?.label}</a>}
- </>}
- {textList.some(item => item.value === "description") && <>
- <SelectCopyWriting
- onClick={() => {
- setAddStitle(textList.find(item => item.value === "description")?.label)
- }}
- onChange={(valList) => {
- setText(valList)
- }}
- />
- {/* <Space><span style={{ fontSize: 12 }}>每组数量:</span><InputNumber max={maxNumber} size="small" value={groupNumber} onChange={(e) => setGroupNumber(e || 1)} /></Space> */}
- </>}
- </Space>}
- open={visible}
- onCancel={onClose}
- footer={null}
- width={800}
- className={`modalResetCss`}
- bodyStyle={{ padding: '0 0 40px', position: 'relative', borderRadius: '0 0 8px 8px' }}
- maskClosable={false}
- >
- <Form
- form={form}
- name="newText"
- labelAlign='left'
- layout="vertical"
- colon={false}
- style={{ backgroundColor: '#f1f4fc', maxHeight: 650, overflow: 'hidden', overflowY: 'auto', padding: '0 10px 10px', borderRadius: '0 0 8px 8px' }}
- scrollToFirstError={{
- behavior: 'smooth',
- block: 'center'
- }}
- onFinishFailed={({ errorFields }) => {
- message.error(errorFields?.[0]?.errors?.[0])
- }}
- initialValues={{
- type: 0,
- textDto: [{ description: [''], title: [''] }]
- }}
- onFinish={handleOk}
- >
- <Card className="cardResetCss" style={{ marginTop: 10 }}>
- <Form.Item name="type" label={<strong>文案分配规则</strong>} style={{ marginBottom: 0 }} rules={[{ required: true, message: '请选择文案分配规则!' }]}>
- <Radio.Group onChange={(e) => {
- let value = e.target.value
- let count = dynamicMaterialDTos.dynamicGroup.length
- let oldtextDto: PULLIN.TextDtoProps[] = JSON.parse(JSON.stringify(textDto))
- let length = oldtextDto.length
- if (value === 0 || (mediaType === 2 && value === 2)) {
- oldtextDto = [textDto[0] || {}]
- } else if (value === 1) {
- if (count > length) {
- oldtextDto = oldtextDto.concat(Array(count - length).fill(newText || { description: [''] }))
- } else {
- oldtextDto = oldtextDto.slice(0, count)
- }
- } else if (value === 2) {
- if (count < length) {
- oldtextDto = oldtextDto.slice(0, count)
- }
- }
- form.setFieldsValue({ textDto: oldtextDto })
- }}>
- {TextTypeList.filter(item => (mediaType !== 1 && mediaType !== 3) ? item.value !== 4 : true).map(item => <Radio value={item.value} key={item.value}>{item.label}</Radio>)}
- </Radio.Group>
- </Form.Item>
- </Card>
- <Form.List name="textDto">
- {(fields, { add, remove }) => (<>
- {fields.map(({ key, name, ...restField }, num) => (
- <Card
- title={type === 0 ? null : <div style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
- {type === 1 ? <strong style={{ fontSize: 14 }}>创意组{num + 1}</strong> : type === 0 ? null : <strong style={{ fontSize: 14 }}>文案组{num + 1}</strong>}
- {type === 1 && showMaterial(num)}
- </div>}
- className="cardResetCss"
- style={{ marginTop: 10, width: '100%' }}
- key={key}
- extra={([3, 2, 4].includes(type) && textDto?.length > 1) && <Popconfirm
- title="你确定删除当前文案组?"
- onConfirm={() => remove(name)}
- >
- <DeleteOutlined style={{ color: 'red' }} />
- </Popconfirm>}
- >
- <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, width: '100%', flexDirection: 'column' }}>
- {textList.map(item => {
- return <Form.List
- {...restField}
- name={[name, item.value]}
- key={key}
- >
- {(fields, { add, remove }) => <>
- {fields.map((field, tIndex) => (
- <Form.Item
- {...field}
- label={<Space>
- <strong>{item.label}</strong>
- {textDto?.[num]?.[item.value]?.length > 1 && <a style={{ color: 'red', fontSize: 12 }} onClick={() => remove(field.name)}><MinusCircleOutlined /></a>}
- </Space>}
- style={{ marginBottom: 0, width: '100%' }}
- rules={[{
- required: item.required, validator: (rule, value) => {
- if (!value) {
- return Promise.reject('请输入正确的' + item.label)
- } else if (!value.match(RegExp(item.restriction.textRestriction.textPattern))) {
- return Promise.reject('请输入正确的' + item.label)
- } else if (txtLength(value) > item.restriction.textRestriction.maxLength) {
- return Promise.reject('请输入正确的' + item.label)
- }
- const result = extractAndFilterBracketsContent(value);
- if (result.extracted.length > 4) {
- return Promise.reject('表情数量不得超出: 4 个')
- }
- return Promise.resolve()
- }
- }]}
- >
- <TextAideInput placeholder={'请输入' + item.label} style={{ width: 580 }} maxTextLength={item.restriction.textRestriction.maxLength} putInType={putInType} />
- </Form.Item>
- ))}
- {deliveryMode === 'DELIVERY_MODE_COMPONENT' && item.arrayProperty?.maxNumber > 1 && textDto?.[num]?.[item.value]?.length < item.arrayProperty?.maxNumber && <Form.Item style={{ marginTop: 6, marginBottom: 0 }}>
- <Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />} >
- 新增{item.label}
- </Button>
- </Form.Item>}
- </>}
- </Form.List>
- })}
- </div>
- </Card>
- ))}
- {[3, 2, 4].includes(type) && !(mediaType === 2 && type === 2) && !(type === 2 && textDto.length >= dynamicMaterialDTos.dynamicGroup.length) && <Form.Item style={{ marginTop: 10, marginBottom: 0 }}>
- <Button type="primary" onClick={() => add(newText)} block icon={<PlusOutlined />} disabled={type === 3 && textDto.length >= 30}>
- 新增
- </Button>
- </Form.Item>}
- </>)}
- </Form.List>
- <Form.Item className="submit_pull">
- <Space>
- <Button onClick={onClose}>取消</Button>
- <Button type="primary" htmlType="submit" className="modalResetCss">
- 确定
- </Button>
- </Space>
- </Form.Item>
- </Form>
- {/* 批量添加 */}
- {desVisible && <AddTextS
- visible={desVisible}
- title={addSTitle}
- onClose={() => {
- setDesVisible(false)
- setAddStitle(undefined)
- }}
- onChange={(value) => {
- if (value) {
- let valList = value
- .split(/\r?\n/) // 按换行符分割字符串
- .map(line => line.trim()) // 去除每行的首尾空白
- .filter(line => line !== ''); // 过滤掉空行
- setText(valList)
- }
- setDesVisible(false)
- setAddStitle(undefined)
- }}
- />}
- </Modal>
- }
- export default React.memo(NewText)
|