Forráskód Böngészése

Merge branch 'develop' of http://git.zanxiangnet.com/wjx/ad-manage

wjx 2 éve
szülő
commit
8b7be8d1b3

+ 1 - 0
package.json

@@ -82,6 +82,7 @@
     "react-dom": "^16.8.6",
     "react-helmet-async": "^1.0.4",
     "react-image-crop": "^8.6.9",
+    "react-lazyimg-component": "^1.0.1",
     "react-resizable": "^3.0.4",
     "react-responsive-carousel": "^3.2.18",
     "react-sortable-hoc": "^2.0.0",

+ 48 - 0
src/pages/launchSystemNew/components/videoFrame/index.less

@@ -0,0 +1,48 @@
+.popover {
+    display: flex;
+    flex-direction: column;
+    gap: 2px;
+}
+.content {
+    width: 400px;
+    height: 80px;
+    overflow-x: scroll;
+    display: flex;
+    gap: 4px;
+    padding: 2px;
+    box-sizing: border-box;
+
+    .imgBox {
+        height: 100%;
+        border-radius: 6px;
+        position: relative;
+
+        &>img {
+            height: 100%;
+            border-radius: 6px;
+            cursor: pointer;
+        }
+
+        &>.look {
+            position: absolute;
+            padding: 2px 6px;
+            background-color: rgba(0, 0, 0, .6);
+            color: #FFF;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            border-radius: 4px;
+            opacity: 0;
+            transition: all 0.2s;
+            cursor: pointer;
+        }
+
+        &:hover .look {
+            opacity: 1;
+        }
+    }
+
+    &>.select {
+        box-shadow: 0 0 0 1.5px #00b96b;
+    }
+}

+ 76 - 0
src/pages/launchSystemNew/components/videoFrame/index.tsx

@@ -0,0 +1,76 @@
+import { videoUrlMessage } from "@/utils/compress"
+import { ScissorOutlined } from "@ant-design/icons"
+import { Button, Popover, Image } from "antd"
+import React, { useState } from "react"
+import style from './index.less'
+import Lazyimg from "react-lazyimg-component"
+
+interface Props {
+    url: string,
+    onChange?: (url: string) => void
+}
+/**
+ * 获取oo视频帧序列
+ * @returns 
+ */
+const VideoFrame: React.FC<Props> = ({ url, onChange }) => {
+
+    /************************************/
+    const [urlList, setUrlList] = useState<string[]>([])
+    const [urlNum, seturlNum] = useState<number>(30)
+    const [open, setOpen] = useState(false);
+    const [selectUrl, setSelectUrl] = useState<string>()
+    const [visible, setVisible] = useState<boolean>(false)
+    const [lookUrl, setLookUrl] = useState<string>()
+    /************************************/
+
+    const handleOk = () => {
+        if (selectUrl) {
+            onChange?.(selectUrl)
+            setOpen(false);
+        }
+    };
+
+    const onVisibleChange = async (newOpen: boolean) => {
+        if (newOpen && url && url.includes('.mp4')) {
+            let data = await videoUrlMessage(url)
+            let duration = data.videoLength
+            let gap = duration / urlNum
+            setUrlList(Array(urlNum).fill('').map((item, index) => url + `?x-oss-process=video/snapshot,t_${(index * gap * 1000).toFixed(0)}`))
+        }
+        setOpen(newOpen);
+    };
+
+    const selectHandle = (url: string) => {
+        setSelectUrl(url)
+    }
+
+    return <Popover
+        content={<div className={style.popover}>
+            <div className={style.content}>
+                {urlList.map((url, index) => <div className={`${style.imgBox} ${selectUrl === url ? style.select : ''}`} key={index}>
+                    <Lazyimg onClick={() => { selectHandle(url) }} className={`lazy`} src={url} />
+                    <span className={style.look} onClick={() => { setLookUrl(url); setVisible(true) }}>查看</span>
+                </div>)}
+            </div>
+            <Image width={200} style={{ display: 'none' }} src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?x-oss-process=image/blur,r_50,s_50/quality,q_1/resize,m_mfit,h_200,w_200"
+                preview={{
+                    visible,
+                    src: lookUrl,
+                    onVisibleChange: (value) => {
+                        setVisible(value);
+                    },
+                }}
+            />
+            <Button type="primary" style={{ backgroundColor: '#00b96b', borderColor: '#00b96b' }} onClick={handleOk} disabled={!selectUrl} size="small">确定</Button>
+        </div>}
+        destroyTooltipOnHide={true}
+        trigger="click"
+        visible={open}
+        onVisibleChange={onVisibleChange}
+    >
+        <Button type="link" icon={<ScissorOutlined />}></Button>
+    </Popover>
+}
+
+export default React.memo(VideoFrame)

+ 43 - 22
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/material.tsx

@@ -7,6 +7,7 @@ import React, { useEffect, useState } from "react"
 import { useModel } from "umi"
 import styles from './index.less'
 import { getVideoImgUrl } from "@/utils/utils"
+import VideoFrame from "@/pages/launchSystemNew/components/videoFrame"
 
 interface Props {
     value?: any[]
@@ -66,7 +67,7 @@ const Material: React.FC<Props> = (props) => {
 
     const getVideoCapture = useAjax((params) => get_tools_video_capture(params))
     /**************************/
-    
+
     // 回填
     useEffect(() => {
         if (visible) {
@@ -143,6 +144,21 @@ const Material: React.FC<Props> = (props) => {
         })
     }
 
+    const setFrame = (url: string, num: number, field: string) => {
+        materials = materials?.map((item: any, index: number) => {
+            if (num === index) {
+                if (item) {
+                    item[field] = url
+                    return { ...item }
+                } else {
+                    return { [field]: url }
+                }
+            }
+            return item
+        })
+        form.setFieldsValue({ materials })
+    }   
+
     return <>
         <span onClick={() => { setVisible(true) }}>{value && value?.length > 0 ? '编辑' : '添加'}</span>
         {visible && <Modal
@@ -192,7 +208,7 @@ const Material: React.FC<Props> = (props) => {
             }}
             onOk={handleOk}
         >
-            <Form name="dynamic_form_item" form={form} labelAlign='left' >
+            <Form name="dynamic_form_item" form={form} labelAlign='left'>
                 <Form.List name="materials">
                     {(fields, { add, remove }) => (<>
                         {fields.map((field, num) => (<div key={field.key}>
@@ -236,26 +252,31 @@ const Material: React.FC<Props> = (props) => {
                                             name={[field.name, item.name]}
                                             key={index}
                                         >
-                                            <div className={`${styles.box} ${styles.image}`} style={{ width: 300, height: 160 }} onClick={() => {
-                                                init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]], maxSize: item.restriction.imageRestriction.fileSize * 1024 })
-                                                setMaterialConfig({
-                                                    ...materialConfig,
-                                                    type: item.name,
-                                                    max: 1,
-                                                    index: num,
-                                                    adcreativeTemplateId
-                                                })
-                                                setTimeout(() => {
-                                                    set_selectVideoVisible(true)
-                                                }, 100)
-                                            }}>
-                                                <p>
-                                                    {materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name) ? <img src={materials[num][item.name]} /> : <>
-                                                        <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
-                                                        <span>{`${item.restriction.imageRestriction.fileFormat?.map((str: any) => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
-                                                    </>}
-                                                </p>
-                                            </div>
+                                            <Space align="end">
+                                                <div className={`${styles.box} ${styles.image}`} style={{ width: 300, height: 160 }} onClick={() => {
+                                                    init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]], maxSize: item.restriction.imageRestriction.fileSize * 1024 })
+                                                    setMaterialConfig({
+                                                        ...materialConfig,
+                                                        type: item.name,
+                                                        max: 1,
+                                                        index: num,
+                                                        adcreativeTemplateId
+                                                    })
+                                                    setTimeout(() => {
+                                                        set_selectVideoVisible(true)
+                                                    }, 100)
+                                                }}>
+                                                    <p>
+                                                        {materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name) ? <img src={materials[num][item.name]} /> : <>
+                                                            <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
+                                                            <span>{`${item.restriction.imageRestriction.fileFormat?.map((str: any) => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
+                                                        </>}
+                                                    </p>
+                                                </div>
+                                                {videoUploads && Object.keys(videoUploads)?.length > 0 && <div style={{ width: 32 }}>
+                                                    {materials?.length > 0 && materials[num] && (Object.keys(materials[num])?.includes('video') || Object.keys(materials[num])?.includes('short_video1')) && <VideoFrame onChange={(e) => setFrame(e, num, item.name)} url={materials[num]?.video || materials[num]?.short_video1}/>}
+                                                </div>}
+                                            </Space>
                                         </Form.Item>
                                     }
                                     if (item.name === 'image_list') {

+ 17 - 0
src/utils/compress.ts

@@ -71,6 +71,23 @@ export const videoMessage = (videos: RcFile[]): Promise<{ width: number, height:
     })
 };
 
+export const videoUrlMessage = (videoUrl: string): Promise<{ width: number, height: number, videoLength: number }> => {
+    return new Promise((resolve, reject) => {
+        if (videoUrl) {
+            var videoObj = document.createElement("video");
+            videoObj.onloadedmetadata = function (evt) {
+                URL.revokeObjectURL(videoUrl);
+                // 执行上传的方法,获取外网路径,上传进度等
+                resolve({ width: videoObj.videoWidth, height: videoObj.videoHeight, videoLength: videoObj.duration })
+            };
+            videoObj.src = videoUrl;
+            videoObj.load();
+        } else {
+            reject()
+        }
+    })
+}
+
 /** 
  * blob 转 base64
  * @param blob 图片2进制流