Browse Source

fix:题卡编辑bug

lvkun996 3 years ago
parent
commit
2ec498423c

+ 5 - 0
README.md

@@ -55,3 +55,8 @@ npm test
 ## More
 
 You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).
+
+
+
+
+### 题卡宽度 200 * 387

+ 0 - 1
config/config.ts

@@ -9,7 +9,6 @@ import routes from './routes';
 const { REACT_APP_ENV } = process.env;
 
 export default defineConfig({
-  base: './',
   publicPath: './',
   hash: true,
   antd: {},

+ 2 - 2
config/proxy.ts

@@ -11,8 +11,8 @@ export default {
     // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
     '/api': {
       // 要代理的地址
-      target: 'http://open.test.luojigou.vip:8899',
-      // target: 'http://192.168.1.149:38092',
+      // target: 'http://open.test.luojigou.vip:8899',
+      target: 'http://192.168.1.2:38092',
       // target: 'https://preview.pro.ant.design',
       // 配置了这个可以从 http 代理到 https
       // 依赖 origin 的功能可能需要这个,比如 cookie

+ 1 - 1
config/routes.ts

@@ -88,7 +88,7 @@
   },
   {
     path: '/',
-    redirect: '/welcome',
+    redirect: '/QuestionBankManage/question-bank',
   },
   {
     component: './404',

+ 30 - 13
src/components/RightClick/index.tsx

@@ -6,24 +6,34 @@ console.log(styles['My-Menu']);
 
 interface IProps {
   render?: boolean,
+  changeRnder: () => void
   deleteImg: (imgUrl: string) => void
 }
 
 const RightClick: React.FC<IProps> = ({
   render = false,
+  changeRnder,
   deleteImg
 }) => {
 
   const [ imgUrl, setImgUrl ] = useState<string>('')
 
+  const [ finish, setFinish ] = useState<boolean>(false)
+
+  console.log('render:', render);
+  
   const closeMyMenu = () => {
 
     let menu = document.getElementById("My-Menu") as HTMLElement
     
     if (!menu) return
+
     menu.style.display = 'none';
 
     window.oncontextmenu = () => true
+
+    changeRnder()
+
   }
 
   if (render) {
@@ -31,27 +41,32 @@ const RightClick: React.FC<IProps> = ({
     window.oncontextmenu = function (e) {
 
       console.log('oncontextmenu', e);
+      
       setImgUrl(e?.target?.src)
+
       e.preventDefault();
 
       let menu = document.getElementById("My-Menu") as HTMLElement;
 
-      console.log(e.clientX, e.clientY);
-      
-      menu.style.position = 'fixed'
+      if (!!menu) {
+        menu.style.position = 'fixed'
+
+        menu.style.left = e.clientX + 'px';
 
-      menu.style.left = e.clientX + 'px';
+        menu.style.top = e.clientY + 'px';
 
-      menu.style.top = e.clientY + 'px';
+        menu.style.display = 'block'
 
-      menu.style.display = 'block'
+        menu.style.zIndex = `1000`
 
-      menu.style.zIndex = `1000`
+        setFinish(true)
+      } 
 
     }
 
     window.onclick = function (e) {
-
+      console.log('触发');
+      
       closeMyMenu()
 
     }
@@ -64,11 +79,13 @@ const RightClick: React.FC<IProps> = ({
   const MyMenu = classNames( styles['My-Menu'] )
 
   const RenderMenu = () => {
-    return  <Row id='My-Menu' style={{width: '125px', height: '30px', position: 'absolute', left: 0, top: 0}} className={MyMenu} >
-              <Col span={24} style={{height: '30px'}} className={styles.col}  onClick={(e) => deleteImg(imgUrl)}>
-                <span style={{color: '#000', width: `100%`, cursor: "pointer"}} >删除</span>
-              </Col>
-            </Row>
+    return  (
+      <Row id='My-Menu' style={{width: '125px', height: '30px', position: 'fixed', zIndex: -1}} className={MyMenu} >
+        <Col span={24} style={{height: '30px'}} className={styles.col}  onClick={(e) => deleteImg(imgUrl)}>
+          <span style={{color: '#000', width: `100%`, cursor: "pointer"}} >删除</span>
+        </Col>
+      </Row>
+    )
   }
 
   return <>

+ 12 - 4
src/components/UploadImage/index.tsx

@@ -13,14 +13,16 @@ interface IProps {
   imageUrl: string,
   saveImageUrl: (imgUrl: string) => void,
   uploadCount?: number, // -1 代表可反复上传 0 代表不能上传 > 1 代表上传次数
-  title: string
+  title: string,
+  onClick?: (imageUrl: string) => void
 }
 
 const UploadImage: React.FC<IProps> = ({
   saveImageUrl,
   imageUrl,
-  uploadCount = -1, 
-  title
+  uploadCount = -1,
+  title,
+  onClick
 }) => {
   
   const [ loading, setLoading ]  = useState<boolean>(false)
@@ -63,6 +65,7 @@ const UploadImage: React.FC<IProps> = ({
       <div style={{ marginTop: 8 }}>{ title  }</div>
     </div>
   );
+
   return (
     <Row style={{width: '100%'}} > 
     {/* style={{ width: '80px', height: '80px',border: '1px solid #000', display: "flex", justifyContent: 'center', alignItems: 'center' }} */}
@@ -81,7 +84,12 @@ const UploadImage: React.FC<IProps> = ({
               beforeUpload={beforeUpload}
               onChange={handleChange}
             >
-            {imageUrl ? <img src={imageUrl} alt="avatar" style={{ width: '80px' }} /> : uploadLoadingButton} 
+            {imageUrl 
+              ? 
+              <img  src={imageUrl} alt="avatar" style={{ width: '80px' }} /> 
+              : 
+              uploadLoadingButton
+            } 
           </Upload>
       </Col>
     </Row>

+ 2 - 23
src/pages/QuestionBankManage/QuestionCard/index.tsx

@@ -4,38 +4,18 @@ import api from '@/services/api/index'
 import { PlayCircleTwoTone, PauseCircleTwoTone } from '@ant-design/icons'
 import { Row, Tabs, Button, Select, Input, Col, Table, Tooltip, Divider, Switch, message, Popconfirm } from 'antd';
 import AudioManage from '@/common/AudioManage';
-import { useStateSync } from '@/hooks/index' 
+import { useStateSync } from '@/hooks/index'
 import { history } from 'umi';
 import { descText } from '@/common/MessageManage';
 import { getSectionExcludState } from '@/services/api/section'
 
-const { TabPane } = Tabs
-
 const {
   getQuestionBank,
   getQuestionCard,
-  getQuestionBook,
   useQuestionCard,
   delQuestionCard
 } = api.question
 
-interface ISelectTmp {
-  dataList?: Record<string, any>[],
-  onChange?: (value: string) => void
-}
-
-/** 搜索模板 */
-const SelectTmp: React.FC<ISelectTmp> = ({
-  dataList,
-  onChange
-}) => {
-
-  return  <Select allowClear style={{ width: 120 }} onChange={onChange} >
-            {
-              dataList?.map( data => <Select.Option value={data.id} key={data.id} >{data.label}</Select.Option>)
-            }
-          </Select>
-}
 
 /** 表格 */
 const QuestionTable = ({
@@ -201,7 +181,7 @@ const QuestionSearch: React.FC<IQuestionSearch> = ({
             <Row gutter={4} >
               {
                 QBanklist && QBanklist.map( qb =>
-                  <Col  key={qb.id} >
+                  <Col  key={qb.id} style={{marginTop: '10px'}} >
                     <Button
                       type={currentTabsKey === qb.id ? "primary" : "default"} 
                       onClick={() => onChangeTabs(qb.id)}
@@ -248,7 +228,6 @@ const QuestionSearch: React.FC<IQuestionSearch> = ({
           </Col>
         </Row>
       </Col>
-      
     </Row>
   )
 }

+ 302 - 89
src/pages/QuestionBankManage/index.tsx

@@ -1,6 +1,6 @@
 import React, { forwardRef, useEffect, useLayoutEffect, useRef, useState } from 'react'
 import BaseTmp from '@/components/BaseTmp';
-import { Row, Col, Radio, RadioChangeEvent , Select, Input, Button, message } from 'antd'
+import { Row, Col, Radio, RadioChangeEvent, Select, Input, Button, message, Tooltip, Modal, Image } from 'antd'
 import classNames from 'classnames';
 import type { Position } from 'react-rnd';
 import { Rnd } from 'react-rnd'
@@ -15,7 +15,20 @@ import iPhonexBangs from '@/static/common/iPhoneX-top.png'
 
 import { getSectionExcludState } from '@/services/api/section'
 
-const { getQuestionBank, getQuestionBook, addQuestionCard, getQuestionCardByid, putQuestionCard } = api.question
+import { CloseCircleOutline, QuestionCircleOutlined } from '@ant-design/icons'
+
+import './style/preview.less'
+
+const bg = "https://app-resources-luojigou.luojigou.vip/FhaDyMVgzWGpaTqZTHlGBWN1oKzp";
+
+const TipText = `鼠标双击删除图片`
+
+const {
+  getQuestionBank,
+  addQuestionCard,
+  getQuestionCardByid,
+  putQuestionCard
+} = api.question
 
 type TMerge = IQPData & IQData & IIData & {
   color: string,
@@ -57,8 +70,8 @@ const colorList = [
   {color: '', label: ''}
 ]
 
-const QPData = { pw: 100, ph: 100, px: 40, py: 80}
-const QData = { w: 80, h: 80, x: 0, y: 0}
+const QPData = { pw: 90, ph: 90, px: 20, py: 80}
+const QData = { w: 60, h: 60, x: 0, y: 0}
 const QIData = { iw: 40, ih: 40,  ix: 0, iy: 0 }
 
 const permutationKey = ( type: TType ) => {
@@ -82,7 +95,8 @@ interface IQuestionCard {
   deleteImg: ( _imgUrl: string ) => void,
   onDragStop: (type: TType,  index: number, {x, y}: {x: number, y: number}) => void
   saveImageUrl: (imgUrl: string, index: number) => void,
-  addBackgoundPicture: () => void
+  addBackgoundPicture: () => void,
+  onClick: (id: string | number, e: any) => void,
 }
 
 const QuestionCard: React.FC<IQuestionCard> = ({
@@ -93,7 +107,8 @@ const QuestionCard: React.FC<IQuestionCard> = ({
   onInput,
   saveImageUrl,
   onDragStop,
-  addBackgoundPicture
+  addBackgoundPicture,
+  onClick
 }) => {
 
   const [ menuState, setMenuState ] = useState<boolean>(false)
@@ -105,13 +120,6 @@ const QuestionCard: React.FC<IQuestionCard> = ({
   const backgroundList = content.length >= 1 && content?.filter( item => item.bg === 'true' )
 
   const _content = content.length >= 1 && content?.filter( item => item.bg === 'false')
-
-  // const backgroundImg = ''
-
-  console.log("content:", content);
-
-  console.log("backgroundList:", backgroundList );
-  
   
   /**
    * 
@@ -120,21 +128,36 @@ const QuestionCard: React.FC<IQuestionCard> = ({
    */
 
   const pictureRightClick = ( e: React.MouseEvent ) => {
+
     e.preventDefault()
-    if (e.button === 2) {
-      setMenuState(true)
+
+    console.log("鼠标右键事件:", e);
+    if (e.target.id === 'questionId') {
+      if (e.button === 2) {
+        console.log("鼠标右键事件:", true);
+        setMenuState(true)
+      }
+    } else {
+      setMenuState(false)
     }
+    
   }
 
-
+  const changeRnder = () => {
+    setMenuState(false)
+  }
 
   const RenderButton = ({color}: {color: string}) => {
-    return <div className={styles['render-button']} style={{backgroundColor: color}} ></div>
+    return <div className={styles['render-button']} style={{backgroundColor: color}} />
   }
 
   return (
     <div className={styles.question} >
-      <RightClick render={menuState} deleteImg={(_imgUrl) => deleteImg(_imgUrl)} />
+      <RightClick  
+        render={menuState} 
+        deleteImg={(_imgUrl) => deleteImg(_imgUrl)}
+        changeRnder={changeRnder}
+      />
       <div className={styles['question-card-iphonex']} >
         <img src={iPhonexBangs} alt="" />
       </div>
@@ -152,14 +175,16 @@ const QuestionCard: React.FC<IQuestionCard> = ({
           />
         </Col>
       </Row>
-      <Row className={styles['opration-area']}  justify='start' >
-        <Col className='opration-left' span={16} style={{height: `438px`}}>
+      <Row className={styles['opration-area']}  justify="center" >
+        <Col 
+          className={styles['opration-left']}
+        >
           {
             _content && _content?.map( (item, index) => (
                 <Rnd
                   key={index}
-                  minHeight={80}
-                  minWidth={80}
+                  minHeight={20}
+                  minWidth={20}
                   bounds='parent'
                   className='parent-rnd'
                   style={{ border: `1px solid ${item.color}`, zIndex: 2}}
@@ -192,18 +217,18 @@ const QuestionCard: React.FC<IQuestionCard> = ({
                     }}
                   > 
                   {
-                    item.imgUrl ? 
+                    item?.imgUrl ? 
                       <img
-                      src={item.imgUrl}
-                      id={questionId}
-                      style={{width: item.w + 'px',  height: item.h  + 'px', boxSizing: 'border-box'}}
-                      onMouseDown={ (e: React.MouseEvent) => pictureRightClick(e) }
+                        src={item?.imgUrl}
+                        id={questionId}
+                        style={{width: item.w + 'px',  height: item.h  + 'px', boxSizing: 'border-box', display: 'block'}}
+                        onDoubleClick={(e) => onClick(item.id, e)}
                     />
                     :
                     <UploadImg 
                       title='上传试题'
-                      imageUrl={item.imgUrl}
-                      saveImageUrl={ (imgUrl) => saveImageUrl(imgUrl, index)}
+                      imageUrl={item?.imgUrl}
+                      saveImageUrl={ (imgUrl) => saveImageUrl(imgUrl, item.id)}
                       uploadCount={1}
                     />
                   }
@@ -215,7 +240,7 @@ const QuestionCard: React.FC<IQuestionCard> = ({
                     onMouseDown={e => e.stopPropagation()}
                     size={{ width: item.iw,  height: item.ih }}
                     position={{ x: item.ix, y: item.iy }}
-                    onDragStop={(e, d) => { onDragStop( 'button', index, { x: d.x, y: d.y }) }}
+                    onDragStop={(e, d) => { onDragStop( 'button', item.id, { x: d.x, y: d.y }) }}
                   >
                     { item.color ? <RenderButton color={item.color}  /> : null}
                   </Rnd>
@@ -229,13 +254,14 @@ const QuestionCard: React.FC<IQuestionCard> = ({
             backgroundList.map( (bg, index) =>
               <Rnd
                 key={index}
-                minHeight={80}
-                minWidth={80}
+                minHeight={20}
+                minWidth={20}
                 bounds='parent'
                 style={{ 
                   border: `1px solid blue`,
                   zIndex: 1,
-                  background: `url(${bg.imgUrl}) no-repeat  center/100% 100% `,
+                  background: `url(${bg?.imgUrl}) no-repeat  center/100% 100% `,
+                  display: 'block'
                 }}
                 position={{ x: bg.px, y: bg.py }}
                 size={{width: bg.pw, height: bg.ph}}
@@ -249,18 +275,30 @@ const QuestionCard: React.FC<IQuestionCard> = ({
                 }}
               >
                 {
-                  bg.imgUrl ? null : <UploadImg 
-                                      imageUrl={bg.imgUrl as string}
-                                      saveImageUrl={(imgUrl) => saveImageUrl(imgUrl, bg.id)}
-                                      title='上传背景'
-                                      uploadCount={1}
-                                    /> 
+                  bg?.imgUrl 
+                  ? 
+                  <div
+                    style={{
+                      width: bg.pw + 'px',
+                      height: bg.ph + 'px',
+                      position: 'absolute',
+                      top: 0,
+                      left: 0
+                    }}
+                    onDoubleClick={(e) => onClick(bg.id, e)}
+                  /> :
+                  <UploadImg 
+                    imageUrl={bg?.imgUrl as string}
+                    saveImageUrl={(imgUrl) => saveImageUrl(imgUrl, bg.id)}
+                    title='上传背景'
+                    uploadCount={1}
+                  /> 
                 }
               </Rnd>
             )
           }
         </Col>
-        <Col span={8} className={styles['answer-area']} > 答案区域 </Col>
+        <Col className={styles['answer-area']} > 答案区域 </Col>
       </Row>
       {/* 底部操作按钮 */}
 
@@ -268,7 +306,6 @@ const QuestionCard: React.FC<IQuestionCard> = ({
         style={{
           width: "378px", 
           height: '50px', 
-          border: '1px solid #000',
           borderTop: 'none',
           marginTop: "-2px", 
           marginLeft: '-1px',
@@ -280,7 +317,6 @@ const QuestionCard: React.FC<IQuestionCard> = ({
           <Button  type="primary" onClick={addBackgoundPicture} > +背景 </Button>
         </Col>
       </Row>
-
     </div>
   )
 }
@@ -319,7 +355,6 @@ const AnswerCard: React.FC<IAnswerCard> = ({
               :
               <></>
             )
-           
           )
         }
       </Radio.Group>
@@ -340,12 +375,11 @@ const AnswerCard: React.FC<IAnswerCard> = ({
                   <UploadImg 
                     title=''
                     saveImageUrl={(imageUrl) => saveImageAnswerUrl(imageUrl, index) } 
-                    imageUrl={answer.imgUrl}
+                    imageUrl={answer?.imgUrl}
                   />
                 </Col>
                 <Col>对应答案</Col>
                 <Col> 
-
                   <RenderColorRadio  _color={answer.color} index={index} />
                 </Col>
               </Row>
@@ -358,15 +392,15 @@ const AnswerCard: React.FC<IAnswerCard> = ({
 
   return (
     <Row className={styles['answer-card'] }  >
-      <Col 
-        span={24} 
+      <Col
+        span={24}
         className={styles['ability-point']}
       >
         &nbsp;
         {/* 能力选择: */}
       </Col>
-      <Col 
-        span={24} 
+      <Col
+        span={24}
         className={styles['answer-setting']} 
         style={{height: '232px', borderTop: '1px solid #000', paddingTop: '40px'}} 
       >
@@ -400,7 +434,10 @@ const QuestionBankManage: React.FC = () => {
 
   const [ bankList, setBankList  ] = useState<{id: string, label: string}[]>([])
 
-  const [ currentBank, setCurrentBank ] = useState<{id: string, label: string}>({})
+  const [ currentBank, setCurrentBank ] = useState<{id: string, label: string}>({
+    id: '',
+    label: ''
+  })
 
   const [ title, setTitle ] = useState<string>()
 
@@ -430,6 +467,13 @@ const QuestionBankManage: React.FC = () => {
     semesterName: ""
   })
 
+  /**预览弹窗 */
+  const [ previewVisible, setPreviewVisible ] = useState<boolean>(false)
+
+
+  console.log(answerList);
+  
+
   const location = useLocation()
 
   const cardId = location.query.cardId
@@ -474,12 +518,33 @@ const QuestionBankManage: React.FC = () => {
     
   }, [previous])
 
+  /**删除试题获取背景图 */
+  const onClear = ( id: string | number , e: any) => {
+    console.log("删除试题获取背景图:", id, e);
+    
+    const r = content.map(  item =>  {
+      if ( item.id === id && item.bg === 'false') {
+        return {
+          ...item,
+          imgUrl: ''
+        }
+      } else if (item.id === id && item.bg === 'true') {
+        return false
+      } else {
+        return item
+      }
+    }).filter( _ => !!_ )
+    console.log(r);
+    
+    setContent(r)
+  }
+
   /**保存结果答案 */
   const saveImageAnswerUrl = (imgUrl: string, index: number) => {
     const r = answerList.map( (answer, i) => {
       return {
         ...answer,
-        imgUrl: index === i ? imgUrl: answer.imgUrl
+        imgUrl: index === i ? imgUrl: answer?.imgUrl
       }
     })
     setAnswerList(r)
@@ -505,12 +570,12 @@ const QuestionBankManage: React.FC = () => {
   const onInput = (e: InputEvent) => setLabel(e.target.value)
 
   /** 保存图片 */
-  const saveImageUrl = (imgUrl: string, index: number) => {
+  const saveImageUrl = (imgUrl: string, id: number) => {
 
     const r = content?.map( (item, i) => { 
       return {
         ...item,
-        imgUrl: index === i ? imgUrl : item.imgUrl
+        imgUrl: id === item.id ? imgUrl : item.imgUrl
       }
     })
 
@@ -525,17 +590,16 @@ const QuestionBankManage: React.FC = () => {
 
   ) => {
 
-    console.log( x, y);
-    
-
     const [ _w, _h , _x, _y ] = permutationKey(type)
 
     const r = content?.map( (item, i) => {
+      console.log(item.id, index);
+      
       const target = item.id === index
       return {
         ...item,
-        [_x]: target? x : item[_x],
-        [_y]: target? y : item[_y]
+        [_x]: target? x.toFixed(2) : item[_x],
+        [_y]: target? y.toFixed(2) : item[_y]
       }
     })
 
@@ -567,7 +631,7 @@ const QuestionBankManage: React.FC = () => {
 
         const _QPData = {
           ...QPData, 
-          px: even ? 140 : 30, 
+          px: even ? 100 : 10, 
           py:  lt ? 80 : 200,
         }
 
@@ -663,12 +727,13 @@ const QuestionBankManage: React.FC = () => {
     index?: number,
   ) => {
 
-    const rw = Number(w.replace('px', ''))
-    const rh = Number(h.replace('px', ''))
+    const rw = Number(w.replace('px', '')).toFixed(2)
+
+    const rh = Number(h.replace('px', '')).toFixed(2)
 
     const [ _w, _h , _x, _y ] = permutationKey(type)
 
-    console.log(_w, _h , _x, _y, index);
+    console.log('修改题卡大小:',  rw,  rh, index);
     
     const r = content?.map( (item, i) => {
       const target = item.id === index
@@ -676,8 +741,8 @@ const QuestionBankManage: React.FC = () => {
         ...item,
         [_w]: target? rw : item[_w],
         [_h]: target? rh : item[_h],
-        [_x]: target? pos.x : item[_x],
-        [_y]: target? pos.y : item[_y]
+        [_x]: target? pos.x.toFixed(2) : item[_x],
+        [_y]: target? pos.y.toFixed(2) : item[_y]
       }
     })
     if (type === 'parent' || type === "bg") {
@@ -700,23 +765,18 @@ const QuestionBankManage: React.FC = () => {
       setLabel(result.label)
       setTitle(result.title)
       setAbility(result.ableVar)
-
-      console.log('bankList:', bankList);
       
       setCurrentBank({
-        label: result.bankLabel,
+        label: result.bankName,
         id: result.bankId
       })
 
       setSection({
-        label: result.semesterName,
+        semesterName: result.semesterName,
         id: result.semesterId
       })
-
-
-  
-
     }
+
   }
 
   /**返回上一次 */
@@ -734,10 +794,10 @@ const QuestionBankManage: React.FC = () => {
       return 
     }
 
-    // if (!ability?.length) {
-    //   message.error("请填写能力点")
-    //   return 
-    // }
+    if (!ability?.length) {
+      message.error("请填写能力点")
+      return 
+    }
 
 
     if ( !section.id ) {
@@ -769,9 +829,9 @@ const QuestionBankManage: React.FC = () => {
       return
     }
     
-    const r2 = content.filter( item => !item.imgUrl && item.bg === "false" ).length
+    const r2 = content.filter( item => !item?.imgUrl && item.bg === "false" ).length
 
-    const r3 = answerList.filter( item => !item.imgUrl ).length
+    const r3 = answerList.filter( item => !item?.imgUrl ).length
 
     if( r2 ) {
       message.error("存在未上传的问题")
@@ -803,11 +863,11 @@ const QuestionBankManage: React.FC = () => {
       label,
       title,
       semesterId: section.id,
-      ableVar: "逻辑能力",
+      ableVar: ability,
       id: cardId ? cardId : '',
       bankId: currentBank?.id,
       bankLabel: currentBank?.label,
-      semesterName: section?.laebl
+      semesterName: section?.semesterName
     }
 
     _addQuestionCard(state, $par)
@@ -865,6 +925,12 @@ const QuestionBankManage: React.FC = () => {
     }
   }
 
+  /** 预览游戏 */
+
+  const previewView = () => {
+      setPreviewVisible(true)
+  }
+
   return (
     <BaseTmp>
       <Row  style={{marginTop: '20px', width: '100%'}} >
@@ -881,13 +947,14 @@ const QuestionBankManage: React.FC = () => {
       <Row style={{marginTop: '20px'}} >
         <Col span={2}  > 学段:  </Col>
         <Col> 
-          <Select defaultValue={section.id}  style={{ width: 300 }} onChange={onChangeSection} >
+        
+          <Select key={section.id} defaultValue={section.id}  style={{ width: 300 }} onChange={onChangeSection} >
+     
             {
-              sectionList && sectionList.map( data => 
-                <Select.Option 
-                  value={data.id} 
+              sectionList.map( data =>
+                <Select.Option
+                  value={data.id}
                   key={data.id}
-                  
                 >
                   {data.semesterName}
                 </Select.Option>
@@ -900,15 +967,20 @@ const QuestionBankManage: React.FC = () => {
         <Col span={2}  > 卡片名称:  </Col>
         <Col> <Input style={{ width: "300px" }} onInput={onInputTitle} value={title}  /> </Col>
       </Row>
-      {/* <Row style={{marginTop: '20px'}} >
-        <Col> 能力点:  </Col>
+      <Row style={{marginTop: '20px'}} >
+        <Col span={2}  > 能力点:  </Col>
         <Col> <Input onInput={onInputAbility} value={ability}  /> </Col>
-      </Row> */}
+      </Row>
       <Row 
         className={styles['question-bank-manage']}
         style={{marginTop: '20px'}} 
       >
-        <Col>题卡:</Col>
+        <Col>
+          题卡:<br />
+          <Tooltip placement="top"  title={TipText} >
+            <QuestionCircleOutlined />
+          </Tooltip>
+        </Col>
         <Col span={7.5} >
           <QuestionCard
             label={label}
@@ -920,6 +992,7 @@ const QuestionBankManage: React.FC = () => {
             deleteImg={deleteImg}
             saveImageUrl={saveImageUrl}
             addBackgoundPicture={addBackgoundPicture}
+            onClick={(id, e) => onClear(id, e)}
           />
         </Col>
         <Col span={13.5}>
@@ -941,12 +1014,152 @@ const QuestionBankManage: React.FC = () => {
             保存
           </Button>
         </Col>
+        <Col span={2} >
+          <Button  type="primary" onClick={previewView} > 预览 </Button>
+        </Col>
+        
         <Col span={2} >
           <Button type="primary" onClick={() =>  submit(1)} >
             {submitButtonLabel}
           </Button>
         </Col>
       </Row>
+
+      <Modal
+        title="预览"
+        visible={ previewVisible }
+        closable={false}
+        onCancel={() =>  setPreviewVisible(false)}
+        onOk={ () => setPreviewVisible(false)}
+      >
+ <div className="game-card-v2">
+    <div className="header">
+      <div  className="header-content" >
+        <div className="level-count">
+          { [0,1,2,3,4].map( item =>  <div key={item} className={classNames(['level-item',  'level-item-active'])} /> )  }
+         
+        </div>
+        <div className="count-down">
+          <img className="clock" src='https://app-resources-luojigou.luojigou.vip/FtYk1U3hzbNiea1x6OS8_ZnCluq-' />
+          <div className={classNames(['showCountDownAni', 'flack-className'])} >
+            60
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div
+      className="content"
+      style={{
+        background: `url(https://app-resources-luojigou.luojigou.vip/Fqj0sowjX078sk3PgbBlSvT_Ti9R) no-repeat top left`,
+        backgroundSize: '100%',
+      }}
+    >
+      <div
+        className="board"
+        style={{
+          background: `url(${bg}) no-repeat center`,
+          backgroundSize: '100%',
+        }}
+      >
+        <div className="board-header" id="game-label">
+          <div className="trumpt">
+            <image className="dog" src='https://app-resources-luojigou.luojigou.vip/Fg6qeptPdjRsg3eDUyuGiXOTSPJV' />
+            <image className="trumpt-icon"  src="https://app-resources-luojigou.luojigou.vip/FnahepHJN1cSRD03VEFLebxL5Br-"/>
+          </div>
+          <div className="tip van-multi-ellipsis--l2">
+            我是问题
+          </div>
+        </div>
+        <div className="question">
+          <div className="question-card">
+            {
+              content.map( item => (
+              <div
+                className="game-item"
+                style={{
+                  width: item.pw + 'px',
+                  height: item.ph + 'px',
+                  top: item.py + 'px',
+                  left: item.px + 'px',
+                }}
+                key={item.id}
+              >
+                <div
+                  className="round"
+                  style={{
+                    backgroundColor: item.color,
+                    width: item.iw + 'px',
+                    height: item.ih + 'px',
+                    top: item.iy + 'px',
+                    left: item.ix + 'px',
+                  }}
+                >
+                </div>
+                <img
+                  style={{
+                    width: item.w + 'px',
+                    height: item.h + 'px',
+                    top: item.y + 'px',
+                    left: item.x + 'px',
+                    display: 'block'
+                  }}
+                  src={item.imgUrl}
+                />
+                {
+                  item.bg == 'true' &&
+                <div
+                  className={classNames(['game-item', "game-bg"])}
+                  style={{
+                    width: item.pw + 'rpx',
+                    height: item.ph + 'rpx',
+                    top: item.py + 'rpx',
+                    left: item.px + 'rpx',
+                  }}
+                >
+                  <Image
+                    style={{
+                      width: item.w + 'rpx',
+                      height: item.h + 'rpx',
+                      top: item.y + 'rpx',
+                      left: item.x + 'rpx',
+                      display: 'block'
+                    }}
+                    src={item.imgUrl}
+                  />
+                </div>
+                }
+              </div>
+              ))
+            }
+          </div>
+          <div className="question-answer" id="right-box">
+            {
+              answerList.filter( answer => !!answer.imgUrl).map( (item, index) => (
+                <div
+                  key={item.color}
+                  className="answer-item"
+                >
+                  {   console.log(index)}
+                  
+                    {/* <div
+                      className="answer-round"
+                      style={{ backgroundColor: item.color }}
+                    >
+                    </div> */}
+                    <img style={{objectFit: 'cover'}} src={item.imgUrl} />
+                  {/* <div className="line" v-if="index !== gameOption.length - 1" /> */}
+                </div>
+              ))
+            }
+            
+          </div>
+        </div>
+
+      </div>
+    </div>
+    </div>
+      </Modal>
     </BaseTmp>
   )
 

+ 27 - 4
src/pages/QuestionBankManage/style/index.less

@@ -31,17 +31,29 @@
   }
 
   .opration-area {
-    height: 438px;
+    width: 100%;
+    height: 387px;
+    border-bottom: 1px solid #000;
+    box-sizing: border-box;
+    margin: 0 auto;
   }
 
-  .answer-area {
+  .opration-left {
+    width: 200px;
     height: 100%;
+    border-right: 1px solid #000;
     border-left: 1px solid #000;
+    border-bottom: '1px solid #000'
+  }
+
+  .answer-area {
+    width: 84px;
+    height: 100%;
+    border-right: 1px solid #000;
     display: flex;
     flex-direction: column;
     justify-content: center;
     align-items: center;
-    box-sizing: border-box;
   }
 
 }
@@ -50,7 +62,6 @@
   width: 700px;
   height: 667px;
   border: 1px solid #000;
-  // border-top: none;
   border-left: none;
 }
 
@@ -72,4 +83,16 @@
   border-radius: 50%;
 }
 
+.close-circle-outlined {
+  position: absolute;
+  top: 0;
+  right: 0;
+  transform: translate(50%, -50%);
+  cursor: pointer;
+  z-index: 20000;
+  font-size: 40px;
+  pointer-events: none;
+  -webkit-user-drag: none;
+}
+
 

+ 271 - 0
src/pages/QuestionBankManage/style/preview.less

@@ -0,0 +1,271 @@
+.game-card-v2 {
+  width: 375px;
+  height: 667px;
+  background-color: rgb(253, 223, 168);
+  .header {
+    width: 375px;
+    background: #f9bf4f;
+    border-radius: 0px 0px 20px 20px;
+    padding-bottom: 9px;
+    box-sizing: border-box;
+    padding-left: 27px;
+    height: 63px;
+    line-height: 63px;
+    display: flex;
+    align-items: center;
+    .header-content {
+      display: flex;
+      align-items: center;
+    }
+    .level-count {
+      width: 128px;
+      height: 32px;
+      background: #faa400;
+      box-shadow: inset 0px 1px 3px 0px rgba(162, 76, 0, 0.25);
+      border-radius: 100px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 0 8px;
+      box-sizing: border-box;
+      .level-item {
+        width: 16px;
+        height: 16px;
+        background: #ffcc6a;
+        border-radius: 50%;
+      }
+      .level-item-active {
+        background-color: #1191e7;
+      }
+    }
+    .count-down {
+      width: 75px;
+      height: 32px;
+      background: #faa400;
+      box-shadow: inset 0px 1px 3px 0px rgba(162, 76, 0, 0.25);
+      border-radius: 0px 100px 100px 0px;
+      margin-left: 27px;
+      font-size: 15px;
+      font-family: PingFangSC-Medium, PingFang SC;
+      font-weight: 500;
+      color: #ffffff;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      position: relative;
+      .clock {
+        width: 33px;
+        height: 40px;
+        position: absolute;
+        left: -16px;
+        top: -3px;
+      }
+
+      .flack-class {
+        // animation: name duration timing-function delay iteration-count direction fill-mode;
+        animation: flack 1s linear infinite;
+        color: red;
+      }
+
+      @keyframes flack {
+        0% {
+          font-size: 28px;
+        }
+        10% {
+          font-size: 40px;
+        }
+        40% {
+          font-size: 28px;
+        }
+      }
+    }
+  }
+
+  .content {
+    width: 375px;
+    height: 604px;
+    padding-top: 9px;
+  }
+
+  .board {
+    width: 357px;
+    height: 575px;
+    margin: 0 auto;
+    margin-top: 9px;
+    position: relative;
+    top: 0;
+    z-index: 2;
+    .board-header {
+      width: 100%;
+      height: 74px;
+      position: relative;
+      margin-bottom: 7px;
+      .trumpt {
+        width: 42px;
+        height: 42px;
+        position: relative;
+        top: 22px;
+        left: 27px;
+        .dog {
+          width: 42px;
+          height: 42px;
+          position: absolute;
+          z-index: 1;
+        }
+        .trumpt-icon {
+          width: 20px;
+          height: 20px;
+          position: absolute;
+          z-index: 2;
+          right: -6px;
+          bottom: -3px;
+        }
+      }
+      .tip {
+        width: 253px;
+        height: 36px;
+        font-size: 12px;
+        font-family: PingFangSC-Medium, PingFang SC;
+        font-weight: 500;
+        color: #ffffff;
+        position: absolute;
+        top: 25px;
+        left: 78px;
+      }
+    }
+    .question {
+      width: 284px;
+      height: 387px;
+      display: flex;
+      margin-top: 20px;
+      margin-left: 27px;
+      .question-card {
+        width: 204px;
+        height: 387px;
+        background-color: #fff;
+        border-top-left-radius: 20px;
+        border-bottom-left-radius: 20px;
+        margin-top: 0px;
+        margin-left: 8px;
+        overflow: hidden;
+        position: relative;
+        .game-item {
+          position: absolute;
+          z-index: 3;
+          .round {
+            border-radius: 50%;
+            position: absolute;
+            z-index: 3;
+          }
+          image {
+            position: absolute;
+            z-index: 2;
+          }
+        }
+        .game-bg {
+          position: absolute;
+          z-index: 1;
+        }
+      }
+      .question-answer {
+        width: 80px;
+        height: 388px;
+        background: #ffffff;
+        border-radius: 0px 20px 20px 0px;
+        border: 1px solid #006caa;
+        box-sizing: border-box;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        margin-top: -1px;
+        .answer-item {
+          width: 160px;
+          height: 25%;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+          align-items: center;
+          position: relative;
+          .answer-round {
+            width: 18px;
+            height: 18px;
+            border-radius: 50%;
+            position: absolute;
+            top: 2px;
+            right: 2px;
+          }
+          img {
+            width: 60px;
+            height: 60px;
+            object-fit: cover;
+            // height: 62.5%;
+          }
+          .line {
+            width: 33.5px;
+            height: 1px;
+            background-color: #006caa;
+            position: absolute;
+            bottom: 0;
+            left: 50%;
+            transform: translateX(-50%);
+          }
+        }
+        .answer-item:last-child {
+          border: none;
+        }
+      }
+    }
+
+    .okbutton {
+      width: 91px;
+      height: 91px;
+      position: absolute;
+      right: 0;
+      bottom: 0;
+      z-index: 31;
+    }
+
+    .movable-area {
+      width: 357px;
+      height: 492px;
+      bottom: 0;
+      background-color: transparent;
+      position: absolute;
+      left: 0;
+      z-index: 30;
+      .movable-view {
+        width: 46px;
+        height: 46px;
+        .movable-image {
+          width: 46px;
+          height: 46px;
+          transition: width 0.2s;
+        }
+      }
+    }
+  }
+}
+
+.card-fly {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 375px;
+  height: 812px;
+  z-index: 300;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  .warpper {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    width: 248px;
+    height: 150px;
+    .card {
+      width: 100%;
+      height: 100%;
+    }
+  }
+}

+ 15 - 14
src/services/api/question.ts

@@ -1,10 +1,11 @@
 import { CRequest } from "./request"
 import Global from "@/common/global"
-const proxyApi = Global.getIsDev ? '/api' : 'https://open.luojigou.vip/'
+
+const proxyApi = Global.getIsDev ? '/api' : '/question-api'
 
 /** /gamecontest/admin/bank */
 export async function getQuestionBank (options?: any) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/bank`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/bank`, {
     method: "GET",
     ...options
   })
@@ -12,7 +13,7 @@ export async function getQuestionBank (options?: any) {
 
 /** gamecontest/admin/card */
 export async function getQuestionCard (params: any, options?: any) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/card`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/card`, {
     method: "GET",
     params: params,
     ...options
@@ -26,7 +27,7 @@ export async function addQuestionBank (
   },
   options?: any
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/bank/add`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/bank/add`, {
     method: "POST",
     params: params,
     ...options
@@ -35,14 +36,14 @@ export async function addQuestionBank (
 
 /** /gamecontest/admin/bank/add */
 export async function delQuestionBank (id: number | string) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/bank/${id}`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/bank/${id}`, {
     method: "DELETE"
   })
 }
 
 /** gamecontest/admin/bank */
 export async function putQuestionBank (data: any) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/bank`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/bank`, {
     data,
     method: "PUT"
   })
@@ -52,7 +53,7 @@ export async function putQuestionBank (data: any) {
 export async function getQuestionBook (
   params: { bankId: string | number}
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/textbook`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/textbook`, {
     params,
     method: "GET"
   })
@@ -62,7 +63,7 @@ export async function getQuestionBook (
 export async function postQuestionBook (
   data: any
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/textbook/addTextBook`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/textbook/addTextBook`, {
     data,
     method: "POST"
   })
@@ -72,7 +73,7 @@ export async function putQuestionBook (
   id: string,
   params: {label: string}
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/textbook/${id}`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/textbook/${id}`, {
     params,
     method: "PUT"
   })
@@ -91,7 +92,7 @@ export async function useQuestionCard (
     state: number
   }
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/card/${id}`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/card/${id}`, {
     params,
     method: "PUT"
   })
@@ -107,7 +108,7 @@ export async function useQuestionCard (
 export async function delQuestionCard (
   id: string,
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/card/${id}`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/card/${id}`, {
     method: "DELETE"
   })
 }
@@ -179,7 +180,7 @@ export async function addQuestionCard (
   state: 0 | 1,
   data: any
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/card/${state}`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/card/${state}`, {
     data,
     method: "POST"
   })
@@ -192,7 +193,7 @@ export async function putQuestionCard (
   state: 0 | 1,
   data: any
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/card/questionCard/${state}`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/card/questionCard/${state}`, {
     data,
     method: "PUT"
   })
@@ -206,7 +207,7 @@ export async function putQuestionCard (
  * 
  */
 export async function getQuestionCardByid (cardId: string | number) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/card/${cardId}`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/card/${cardId}`, {
     method: "GET"
   })
 }

+ 5 - 2
src/services/api/request.ts

@@ -1,14 +1,17 @@
 import { message } from 'antd';
 import { request } from 'umi'
+import Global from "@/common/global"
 
+const proxyApi = Global.getIsDev ? '/api' : 'https://open.luojigou.vip'
+// const proxyApi = Global.getIsDev ? '/api' : 'http://192.168.1.2/question-api'
 
 export const CRequest = <T>(
   url: string,
   data: any
 ): Promise<T> => {
   return new Promise( async resolve => {
-
-    const $r = (await request(url, data))
+    let _url = proxyApi + url
+    const $r = (await request(_url, data))
     console.log("$r:", $r);
     if ( $r.status === 400 ) {
       message.error($r.message)

+ 4 - 4
src/services/api/section.ts

@@ -11,7 +11,7 @@ const proxyApi = Global.getIsDev ? '/api' : 'https://open.luojigou.vip/'
  * 
  */
 export async function getSectionExcludState () {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/semester/list`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/semester/list`, {
     method: "GET"
   })
 }
@@ -30,7 +30,7 @@ export async function addSection (
   },
   options?: any
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/semester/save`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/semester/save`, {
     method: "POST",
     data: params,
     ...options
@@ -49,7 +49,7 @@ export async function updateSection (
   params: Partial<Pick<API.Section, "semesterName" | "semesterDescription" | "id">>,
   options?: any
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/semester/update`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/semester/update`, {
     method: "PUT",
     data: params,
     ...options
@@ -65,7 +65,7 @@ export async function opSection (
     status: API.SectionState
   },
 ) {
-  return CRequest<API.ResponseFormat>(`${proxyApi}/gamecontest/admin/semester/status`, {
+  return CRequest<API.ResponseFormat>(`/gamecontest/admin/semester/status`, {
     method: "PUT",
     params: params,
   })