mirror of https://github.com/openspug/spug
				
				
				
			fix issue
							parent
							
								
									4e900006be
								
							
						
					
					
						commit
						80c6e3d498
					
				|  | @ -47,7 +47,7 @@ class DeployRequest(models.Model, ModelMixin): | |||
| 
 | ||||
|     @property | ||||
|     def is_quick_deploy(self): | ||||
|         if self.deploy.extend == '1' and self.extra: | ||||
|         if self.type == '1' and self.deploy.extend == '1' and self.extra: | ||||
|             extra = json.loads(self.extra) | ||||
|             return extra[0] in ('branch', 'tag') | ||||
|         return False | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ from .views import * | |||
| 
 | ||||
| urlpatterns = [ | ||||
|     path('request/', RequestView.as_view()), | ||||
|     path('request/info/', get_request_info), | ||||
|     path('request/ext1/', post_request_ext1), | ||||
|     path('request/ext1/rollback/', post_request_ext1_rollback), | ||||
|     path('request/ext2/', post_request_ext2), | ||||
|  |  | |||
|  | @ -173,25 +173,28 @@ def _deploy_ext1_host(req, helper, h_id, env): | |||
|             f'mkdir -p {extend.dst_repo} && [ -e {extend.dst_dir} ] && [ ! -L {extend.dst_dir} ]') | ||||
|         if code == 0: | ||||
|             helper.send_error(host.id, f'检测到该主机的发布目录 {extend.dst_dir!r} 已存在,为了数据安全请自行备份后删除该目录,Spug 将会创建并接管该目录。') | ||||
|         # clean | ||||
|         clean_command = f'ls -d {extend.deploy_id}_* 2> /dev/null | sort -t _ -rnk2 | tail -n +{extend.versions + 1} | xargs rm -rf' | ||||
|         helper.remote_raw(host.id, ssh, f'cd {extend.dst_repo} && {clean_command}') | ||||
|         # transfer files | ||||
|         tar_gz_file = f'{req.spug_version}.tar.gz' | ||||
|         try: | ||||
|             ssh.put_file(os.path.join(REPOS_DIR, 'build', tar_gz_file), os.path.join(extend.dst_repo, tar_gz_file)) | ||||
|         except Exception as e: | ||||
|             helper.send_error(host.id, f'Exception: {e}') | ||||
|         if req.type == '1': | ||||
|             # clean | ||||
|             clean_command = f'ls -d {extend.deploy_id}_* 2> /dev/null | sort -t _ -rnk2 | tail -n +{extend.versions + 1} | xargs rm -rf' | ||||
|             helper.remote_raw(host.id, ssh, f'cd {extend.dst_repo} && {clean_command}') | ||||
|             # transfer files | ||||
|             tar_gz_file = f'{req.spug_version}.tar.gz' | ||||
|             try: | ||||
|                 ssh.put_file(os.path.join(REPOS_DIR, 'build', tar_gz_file), os.path.join(extend.dst_repo, tar_gz_file)) | ||||
|             except Exception as e: | ||||
|                 helper.send_error(host.id, f'Exception: {e}') | ||||
| 
 | ||||
|         command = f'cd {extend.dst_repo} && rm -rf {req.spug_version} && tar xf {tar_gz_file} && rm -f {req.deploy_id}_*.tar.gz' | ||||
|         helper.remote_raw(host.id, ssh, command) | ||||
|         helper.send_step(h_id, 1, '\033[32m完成√\033[0m\r\n') | ||||
|             command = f'cd {extend.dst_repo} && rm -rf {req.spug_version} && tar xf {tar_gz_file} && rm -f {req.deploy_id}_*.tar.gz' | ||||
|             helper.remote_raw(host.id, ssh, command) | ||||
|             helper.send_step(h_id, 1, '\033[32m完成√\033[0m\r\n') | ||||
|         else: | ||||
|             helper.send_step(h_id, 1, '\033[33m跳过√\033[0m\r\n') | ||||
| 
 | ||||
|         # pre host | ||||
|         repo_dir = os.path.join(extend.dst_repo, req.spug_version) | ||||
|         if extend.hook_pre_host: | ||||
|             helper.send_step(h_id, 2, f'{human_time()} 发布前任务...       \r\n') | ||||
|             command = f'cd {repo_dir} ; {extend.hook_pre_host}' | ||||
|             command = f'cd {repo_dir} && {extend.hook_pre_host}' | ||||
|             helper.remote(host.id, ssh, command) | ||||
| 
 | ||||
|         # do deploy | ||||
|  | @ -202,7 +205,7 @@ def _deploy_ext1_host(req, helper, h_id, env): | |||
|         # post host | ||||
|         if extend.hook_post_host: | ||||
|             helper.send_step(h_id, 4, f'{human_time()} 发布后任务...       \r\n') | ||||
|             command = f'cd {extend.dst_dir} ; {extend.hook_post_host}' | ||||
|             command = f'cd {extend.dst_dir} && {extend.hook_post_host}' | ||||
|             helper.remote(host.id, ssh, command) | ||||
| 
 | ||||
|         helper.send_step(h_id, 100, f'\r\n{human_time()} ** \033[32m发布成功\033[0m **') | ||||
|  | @ -237,7 +240,7 @@ def _deploy_ext2_host(helper, h_id, actions, env, spug_version): | |||
|                     command += f'&& rm -rf {action["dst"]} && mv /tmp/{spug_version}/{sd_dst} {action["dst"]} ' | ||||
|                     command += f'&& rm -rf /tmp/{spug_version}* && echo "transfer completed"' | ||||
|             else: | ||||
|                 command = f'cd /tmp ; {action["data"]}' | ||||
|                 command = f'cd /tmp && {action["data"]}' | ||||
|             helper.remote(host.id, ssh, command) | ||||
| 
 | ||||
|     helper.send_step(h_id, 100, f'\r\n{human_time()} ** \033[32m发布成功\033[0m **') | ||||
|  |  | |||
|  | @ -334,6 +334,18 @@ def post_request_ext2(request): | |||
|     return json_response(error=error) | ||||
| 
 | ||||
| 
 | ||||
| def get_request_info(request): | ||||
|     form, error = JsonParser( | ||||
|         Argument('id', type=int, help='参数错误') | ||||
|     ).parse(request.GET) | ||||
|     if error is None: | ||||
|         req = DeployRequest.objects.get(pk=form.id) | ||||
|         response = req.to_dict(selects=('status', 'reason')) | ||||
|         response['status_alias'] = req.get_status_display() | ||||
|         return json_response(response) | ||||
|     return json_response(error=error) | ||||
| 
 | ||||
| 
 | ||||
| def do_upload(request): | ||||
|     repos_dir = settings.REPOS_DIR | ||||
|     file = request.FILES['file'] | ||||
|  |  | |||
|  | @ -15,6 +15,8 @@ import store from './store'; | |||
| function Ext1Console(props) { | ||||
|   const outputs = useLocalStore(() => ({})); | ||||
|   const terms = useLocalStore(() => ({})); | ||||
|   const [mini, setMini] = useState(false); | ||||
|   const [visible, setVisible] = useState(true); | ||||
|   const [fetching, setFetching] = useState(true); | ||||
| 
 | ||||
|   useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, []) | ||||
|  | @ -39,7 +41,7 @@ function Ext1Console(props) { | |||
|         Object.assign(outputs, res.outputs) | ||||
|         setTimeout(() => setFetching(false), 100) | ||||
|         socket = _makeSocket() | ||||
|         store.fetchRecords() | ||||
|         store.fetchInfo(props.request.id) | ||||
|       }) | ||||
|     return () => socket && socket.close() | ||||
|   } | ||||
|  | @ -85,8 +87,8 @@ function Ext1Console(props) { | |||
|   } | ||||
| 
 | ||||
|   function switchMiniMode() { | ||||
|     const value = store.tabModes[props.request.id]; | ||||
|     store.tabModes[props.request.id] = !value | ||||
|     setMini(true) | ||||
|     setVisible(false) | ||||
|   } | ||||
| 
 | ||||
|   function handleSetTerm(term, key) { | ||||
|  | @ -97,86 +99,90 @@ function Ext1Console(props) { | |||
|   } | ||||
| 
 | ||||
|   let {local, ...hosts} = outputs; | ||||
|   return store.tabModes[props.request.id] ? ( | ||||
|     <Card | ||||
|       className={styles.item} | ||||
|       bodyStyle={{padding: '8px 12px'}} | ||||
|       onClick={switchMiniMode}> | ||||
|       <div className={styles.header}> | ||||
|         <div className={styles.title}>{props.request.name}</div> | ||||
|         <CloseOutlined onClick={() => store.showConsole(props.request, true)}/> | ||||
|       </div> | ||||
|       {local && ( | ||||
|         <Progress | ||||
|           percent={(local.step + 1) * 18} | ||||
|           status={local.step === 100 ? 'success' : outputs.local.status === 'error' ? 'exception' : 'active'}/> | ||||
|       )} | ||||
|       {Object.values(hosts).map(item => ( | ||||
|         <Progress | ||||
|           key={item.id} | ||||
|           percent={(item.step + 1) * 18} | ||||
|           status={item.step === 100 ? 'success' : item.status === 'error' ? 'exception' : 'active'}/> | ||||
|       ))} | ||||
|     </Card> | ||||
|   ) : ( | ||||
|     <Modal | ||||
|       visible | ||||
|       width={1000} | ||||
|       footer={null} | ||||
|       maskClosable={false} | ||||
|       className={styles.console} | ||||
|       onCancel={() => store.showConsole(props.request, true)} | ||||
|       title={[ | ||||
|         <span key="1">{props.request.name}</span>, | ||||
|         <div key="2" className={styles.miniIcon} onClick={switchMiniMode}> | ||||
|           <ShrinkOutlined/> | ||||
|         </div> | ||||
|       ]}> | ||||
|       <Skeleton loading={fetching} active> | ||||
|         {local && ( | ||||
|           <Collapse defaultActiveKey={['0']} className={styles.collapse} style={{marginBottom: 24}}> | ||||
|             <Collapse.Panel header={( | ||||
|               <div className={styles.header}> | ||||
|                 <b className={styles.title}/> | ||||
|                 <Steps size="small" className={styles.step} current={local.step} status={local.status}> | ||||
|                   <StepItem title="构建准备" item={local} step={0}/> | ||||
|                   <StepItem title="检出前任务" item={local} step={1}/> | ||||
|                   <StepItem title="执行检出" item={local} step={2}/> | ||||
|                   <StepItem title="检出后任务" item={local} step={3}/> | ||||
|                   <StepItem title="执行打包" item={local} step={4}/> | ||||
|                 </Steps> | ||||
|               </div> | ||||
|             )}> | ||||
|               <OutView setTerm={term => handleSetTerm(term, 'local')}/> | ||||
|             </Collapse.Panel> | ||||
|           </Collapse> | ||||
|         )} | ||||
| 
 | ||||
|         <Collapse | ||||
|           defaultActiveKey="0" | ||||
|           className={styles.collapse} | ||||
|           expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}> | ||||
|           {Object.entries(hosts).map(([key, item], index) => ( | ||||
|             <Collapse.Panel | ||||
|               key={index} | ||||
|               header={ | ||||
|                 <div className={styles.header}> | ||||
|                   <b className={styles.title}>{item.title}</b> | ||||
|                   <Steps size="small" className={styles.step} current={item.step} status={item.status}> | ||||
|                     <StepItem title="等待调度" item={item} step={0}/> | ||||
|                     <StepItem title="数据准备" item={item} step={1}/> | ||||
|                     <StepItem title="发布前任务" item={item} step={2}/> | ||||
|                     <StepItem title="执行发布" item={item} step={3}/> | ||||
|                     <StepItem title="发布后任务" item={item} step={4}/> | ||||
|                   </Steps> | ||||
|                 </div>}> | ||||
|               <OutView setTerm={term => handleSetTerm(term, key)}/> | ||||
|             </Collapse.Panel> | ||||
|   return ( | ||||
|     <div> | ||||
|       {mini && ( | ||||
|         <Card | ||||
|           className={styles.item} | ||||
|           bodyStyle={{padding: '8px 12px'}} | ||||
|           onClick={() => setVisible(true)}> | ||||
|           <div className={styles.header}> | ||||
|             <div className={styles.title}>{props.request.name}</div> | ||||
|             <CloseOutlined onClick={() => store.showConsole(props.request, true)}/> | ||||
|           </div> | ||||
|           {local && ( | ||||
|             <Progress | ||||
|               percent={(local.step + 1) * 18} | ||||
|               status={local.step === 100 ? 'success' : outputs.local.status === 'error' ? 'exception' : 'active'}/> | ||||
|           )} | ||||
|           {Object.values(hosts).map(item => ( | ||||
|             <Progress | ||||
|               key={item.id} | ||||
|               percent={(item.step + 1) * 18} | ||||
|               status={item.step === 100 ? 'success' : item.status === 'error' ? 'exception' : 'active'}/> | ||||
|           ))} | ||||
|         </Collapse> | ||||
|       </Skeleton> | ||||
|     </Modal> | ||||
|         </Card> | ||||
|       )} | ||||
|       <Modal | ||||
|         visible={visible} | ||||
|         width={1000} | ||||
|         footer={null} | ||||
|         maskClosable={false} | ||||
|         className={styles.console} | ||||
|         onCancel={() => store.showConsole(props.request, true)} | ||||
|         title={[ | ||||
|           <span key="1">{props.request.name}</span>, | ||||
|           <div key="2" className={styles.miniIcon} onClick={switchMiniMode}> | ||||
|             <ShrinkOutlined/> | ||||
|           </div> | ||||
|         ]}> | ||||
|         <Skeleton loading={fetching} active> | ||||
|           {local && ( | ||||
|             <Collapse defaultActiveKey={['0']} className={styles.collapse} style={{marginBottom: 24}}> | ||||
|               <Collapse.Panel header={( | ||||
|                 <div className={styles.header}> | ||||
|                   <b className={styles.title}/> | ||||
|                   <Steps size="small" className={styles.step} current={local.step} status={local.status}> | ||||
|                     <StepItem title="构建准备" item={local} step={0}/> | ||||
|                     <StepItem title="检出前任务" item={local} step={1}/> | ||||
|                     <StepItem title="执行检出" item={local} step={2}/> | ||||
|                     <StepItem title="检出后任务" item={local} step={3}/> | ||||
|                     <StepItem title="执行打包" item={local} step={4}/> | ||||
|                   </Steps> | ||||
|                 </div> | ||||
|               )}> | ||||
|                 <OutView setTerm={term => handleSetTerm(term, 'local')}/> | ||||
|               </Collapse.Panel> | ||||
|             </Collapse> | ||||
|           )} | ||||
| 
 | ||||
|           <Collapse | ||||
|             defaultActiveKey="0" | ||||
|             className={styles.collapse} | ||||
|             expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}> | ||||
|             {Object.entries(hosts).map(([key, item], index) => ( | ||||
|               <Collapse.Panel | ||||
|                 key={index} | ||||
|                 header={ | ||||
|                   <div className={styles.header}> | ||||
|                     <b className={styles.title}>{item.title}</b> | ||||
|                     <Steps size="small" className={styles.step} current={item.step} status={item.status}> | ||||
|                       <StepItem title="等待调度" item={item} step={0}/> | ||||
|                       <StepItem title="数据准备" item={item} step={1}/> | ||||
|                       <StepItem title="发布前任务" item={item} step={2}/> | ||||
|                       <StepItem title="执行发布" item={item} step={3}/> | ||||
|                       <StepItem title="发布后任务" item={item} step={4}/> | ||||
|                     </Steps> | ||||
|                   </div>}> | ||||
|                 <OutView setTerm={term => handleSetTerm(term, key)}/> | ||||
|               </Collapse.Panel> | ||||
|             ))} | ||||
|           </Collapse> | ||||
|         </Skeleton> | ||||
|       </Modal> | ||||
|     </div> | ||||
|   ) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export default observer(Ext1Console) | ||||
|  | @ -17,6 +17,8 @@ function Ext2Console(props) { | |||
|   const outputs = useLocalStore(() => ({local: {id: 'local'}})); | ||||
|   const [sActions, setSActions] = useState([]); | ||||
|   const [hActions, setHActions] = useState([]); | ||||
|   const [mini, setMini] = useState(false); | ||||
|   const [visible, setVisible] = useState(true); | ||||
|   const [fetching, setFetching] = useState(true); | ||||
| 
 | ||||
|   useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, []) | ||||
|  | @ -45,6 +47,7 @@ function Ext2Console(props) { | |||
|         Object.assign(outputs, res.outputs) | ||||
|         setTimeout(() => setFetching(false), 100) | ||||
|         socket = _makeSocket() | ||||
|         store.fetchInfo(props.request.id) | ||||
|       }) | ||||
|     return () => socket && socket.close() | ||||
|   } | ||||
|  | @ -92,8 +95,8 @@ function Ext2Console(props) { | |||
|   } | ||||
| 
 | ||||
|   function switchMiniMode() { | ||||
|     const value = store.tabModes[props.request.id]; | ||||
|     store.tabModes[props.request.id] = !value | ||||
|     setMini(true) | ||||
|     setVisible(false) | ||||
|   } | ||||
| 
 | ||||
|   function handleSetTerm(term, key) { | ||||
|  | @ -104,81 +107,84 @@ function Ext2Console(props) { | |||
|   } | ||||
| 
 | ||||
|   const hostOutputs = Object.values(outputs).filter(x => x.id !== 'local'); | ||||
|   return store.tabModes[props.request.id] ? ( | ||||
|     <Card | ||||
|       className={styles.item} | ||||
|       bodyStyle={{padding: '8px 12px'}} | ||||
|       onClick={switchMiniMode}> | ||||
|       <div className={styles.header}> | ||||
|         <div className={styles.title}>{props.request.name}</div> | ||||
|         <CloseOutlined onClick={() => store.showConsole(props.request, true)}/> | ||||
|       </div> | ||||
|       <Progress percent={(outputs.local.step + 1) * (90 / (1 + sActions.length)).toFixed(0)} | ||||
|                 status={outputs.local.step === 100 ? 'success' : outputs.local.status === 'error' ? 'exception' : 'active'}/> | ||||
|       {Object.values(outputs).filter(x => x.id !== 'local').map(item => ( | ||||
|         <Progress | ||||
|           key={item.id} | ||||
|           percent={item.step * (90 / (hActions.length).toFixed(0))} | ||||
|           status={item.step === 100 ? 'success' : item.status === 'error' ? 'exception' : 'active'}/> | ||||
|       ))} | ||||
|     </Card> | ||||
|   ) : ( | ||||
|     <Modal | ||||
|       visible | ||||
|       width={1000} | ||||
|       footer={null} | ||||
|       maskClosable={false} | ||||
|       className={styles.console} | ||||
|       onCancel={() => store.showConsole(props.request, true)} | ||||
|       title={[ | ||||
|         <span key="1">{props.request.name}</span>, | ||||
|         <div key="2" className={styles.miniIcon} onClick={switchMiniMode}> | ||||
|           <ShrinkOutlined/> | ||||
|         </div> | ||||
|       ]}> | ||||
|       <Skeleton loading={fetching} active> | ||||
|         <Collapse defaultActiveKey={['0']} className={styles.collapse}> | ||||
|           <Collapse.Panel header={( | ||||
|             <div className={styles.header}> | ||||
|               <b className={styles.title}/> | ||||
|               <Steps size="small" className={styles.step} current={outputs.local.step} status={outputs.local.status}> | ||||
|                 <StepItem title="建立连接" item={outputs.local} step={0}/> | ||||
|                 {sActions.map((item, index) => ( | ||||
|                   <StepItem key={index} title={item.title} item={outputs.local} step={index + 1}/> | ||||
|                 ))} | ||||
|               </Steps> | ||||
|             </div> | ||||
|           )}> | ||||
|             <OutView setTerm={term => handleSetTerm(term, 'local')}/> | ||||
|           </Collapse.Panel> | ||||
|         </Collapse> | ||||
|         {hostOutputs.length > 0 && ( | ||||
|           <Collapse | ||||
|             accordion | ||||
|             defaultActiveKey="0" | ||||
|             className={styles.collapse} | ||||
|             style={{marginTop: 24}} | ||||
|             expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}> | ||||
|             {hostOutputs.map((item, index) => ( | ||||
|               <Collapse.Panel | ||||
|                 key={index} | ||||
|                 header={ | ||||
|                   <div className={styles.header}> | ||||
|                     <b className={styles.title}>{item.title}</b> | ||||
|                     <Steps size="small" className={styles.step} current={item.step} status={item.status}> | ||||
|                       <StepItem title="等待调度" item={item} step={0}/> | ||||
|                       {hActions.map((action, index) => ( | ||||
|                         <StepItem key={index} title={action.title} item={item} step={index + 1}/> | ||||
|                       ))} | ||||
|                     </Steps> | ||||
|                   </div>}> | ||||
|                 <OutView setTerm={term => handleSetTerm(term, item.id)}/> | ||||
|               </Collapse.Panel> | ||||
|             ))} | ||||
|   return ( | ||||
|     <div> | ||||
|       {mini && ( | ||||
|         <Card | ||||
|           className={styles.item} | ||||
|           bodyStyle={{padding: '8px 12px'}} | ||||
|           onClick={() => setVisible(true)}> | ||||
|           <div className={styles.header}> | ||||
|             <div className={styles.title}>{props.request.name}</div> | ||||
|             <CloseOutlined onClick={() => store.showConsole(props.request, true)}/> | ||||
|           </div> | ||||
|           <Progress percent={(outputs.local.step + 1) * (90 / (1 + sActions.length)).toFixed(0)} | ||||
|                     status={outputs.local.step === 100 ? 'success' : outputs.local.status === 'error' ? 'exception' : 'active'}/> | ||||
|           {Object.values(outputs).filter(x => x.id !== 'local').map(item => ( | ||||
|             <Progress | ||||
|               key={item.id} | ||||
|               percent={item.step * (90 / (hActions.length).toFixed(0))} | ||||
|               status={item.step === 100 ? 'success' : item.status === 'error' ? 'exception' : 'active'}/> | ||||
|           ))} | ||||
|         </Card> | ||||
|       )} | ||||
|       <Modal | ||||
|         visible={visible} | ||||
|         width={1000} | ||||
|         footer={null} | ||||
|         maskClosable={false} | ||||
|         className={styles.console} | ||||
|         onCancel={() => store.showConsole(props.request, true)} | ||||
|         title={[ | ||||
|           <span key="1">{props.request.name}</span>, | ||||
|           <div key="2" className={styles.miniIcon} onClick={switchMiniMode}> | ||||
|             <ShrinkOutlined/> | ||||
|           </div> | ||||
|         ]}> | ||||
|         <Skeleton loading={fetching} active> | ||||
|           <Collapse defaultActiveKey={['0']} className={styles.collapse}> | ||||
|             <Collapse.Panel header={( | ||||
|               <div className={styles.header}> | ||||
|                 <b className={styles.title}/> | ||||
|                 <Steps size="small" className={styles.step} current={outputs.local.step} status={outputs.local.status}> | ||||
|                   <StepItem title="建立连接" item={outputs.local} step={0}/> | ||||
|                   {sActions.map((item, index) => ( | ||||
|                     <StepItem key={index} title={item.title} item={outputs.local} step={index + 1}/> | ||||
|                   ))} | ||||
|                 </Steps> | ||||
|               </div> | ||||
|             )}> | ||||
|               <OutView setTerm={term => handleSetTerm(term, 'local')}/> | ||||
|             </Collapse.Panel> | ||||
|           </Collapse> | ||||
|         )} | ||||
|       </Skeleton> | ||||
|     </Modal> | ||||
|           {hostOutputs.length > 0 && ( | ||||
|             <Collapse | ||||
|               accordion | ||||
|               defaultActiveKey="0" | ||||
|               className={styles.collapse} | ||||
|               style={{marginTop: 24}} | ||||
|               expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}> | ||||
|               {hostOutputs.map((item, index) => ( | ||||
|                 <Collapse.Panel | ||||
|                   key={index} | ||||
|                   header={ | ||||
|                     <div className={styles.header}> | ||||
|                       <b className={styles.title}>{item.title}</b> | ||||
|                       <Steps size="small" className={styles.step} current={item.step} status={item.status}> | ||||
|                         <StepItem title="等待调度" item={item} step={0}/> | ||||
|                         {hActions.map((action, index) => ( | ||||
|                           <StepItem key={index} title={action.title} item={item} step={index + 1}/> | ||||
|                         ))} | ||||
|                       </Steps> | ||||
|                     </div>}> | ||||
|                   <OutView setTerm={term => handleSetTerm(term, item.id)}/> | ||||
|                 </Collapse.Panel> | ||||
|               ))} | ||||
|             </Collapse> | ||||
|           )} | ||||
|         </Skeleton> | ||||
|       </Modal> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,7 +9,6 @@ import { BranchesOutlined, BuildOutlined, TagOutlined, PlusOutlined, TagsOutline | |||
| import { Radio, Modal, Popover, Tag, Popconfirm, Tooltip, message } from 'antd'; | ||||
| import { http, hasPermission } from 'libs'; | ||||
| import { Action, AuthButton, TableCard } from 'components'; | ||||
| import styles from './index.module.less'; | ||||
| import store from './store'; | ||||
| 
 | ||||
| function ComTable() { | ||||
|  | @ -146,18 +145,7 @@ function ComTable() { | |||
|   } | ||||
| 
 | ||||
|   function handleDeploy(e, info) { | ||||
|     const right = document.body.clientWidth - 25 - e.target.getBoundingClientRect().x; | ||||
|     const bottom = document.body.clientHeight - 40 - e.target.getBoundingClientRect().y; | ||||
|     store.box.setAttribute('style', `display: block; bottom: ${bottom}px; right: ${right}px;`); | ||||
|     setTimeout(() => { | ||||
|       store.box.setAttribute('class', `${styles.floatBox} ${styles.floatBoxAnimate}`) | ||||
|     }, 10); | ||||
|     setTimeout(() => { | ||||
|       store.showConsole(info); | ||||
|       store.box.setAttribute('style', 'display: none'); | ||||
|       store.box.setAttribute('class', styles.floatBox) | ||||
|     }, 300) | ||||
| 
 | ||||
|     store.showConsole(info); | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|  |  | |||
|  | @ -90,16 +90,14 @@ function Index() { | |||
|       {store.rollbackVisible && <Rollback/>} | ||||
|       {store.tabs.length > 0 && ( | ||||
|         <Space className={styles.miniConsole}> | ||||
|           {store.tabs.map(item => ( | ||||
|           {store.tabs.map(item => item.id ? | ||||
|             item.app_extend === '1' ? ( | ||||
|               <Ext1Console key={item.id} request={item}/> | ||||
|             ) : ( | ||||
|               <Ext2Console key={item.id} request={item}/> | ||||
|             ) | ||||
|           ))} | ||||
|             ) : null)} | ||||
|         </Space> | ||||
|       )} | ||||
|       <div ref={el => store.box = el} id='floatBox' className={styles.floatBox}/> | ||||
|     </AuthDiv> | ||||
|   ) | ||||
| } | ||||
|  |  | |||
|  | @ -100,21 +100,4 @@ | |||
| 
 | ||||
| .upload :global(.ant-upload-list-item) { | ||||
|     margin: 0; | ||||
| } | ||||
| 
 | ||||
| .floatBox { | ||||
|   display: none; | ||||
|   position: fixed; | ||||
|   width: 20px; | ||||
|   height: 10px; | ||||
|   transition: right 0.3s, width 0.3s, height0 .3s, bottom 0.3s ease-in-out; | ||||
|   background: rgba(0, 0, 0, 0.1); | ||||
|   border-radius: 5px; | ||||
| } | ||||
| 
 | ||||
| .floatBoxAnimate { | ||||
|   width: 180px; | ||||
|   height: 64px; | ||||
|   bottom: 12px !important; | ||||
|   right: 24px !important; | ||||
| } | ||||
| } | ||||
|  | @ -12,9 +12,7 @@ class Store { | |||
|   @observable records = []; | ||||
|   @observable record = {}; | ||||
|   @observable counter = {}; | ||||
|   @observable box = null; | ||||
|   @observable tabs = []; | ||||
|   @observable tabModes = {}; | ||||
|   @observable isFetching = false; | ||||
|   @observable addVisible = false; | ||||
|   @observable ext1Visible = false; | ||||
|  | @ -55,6 +53,19 @@ class Store { | |||
|       .finally(() => this.isFetching = false) | ||||
|   }; | ||||
| 
 | ||||
|   fetchInfo = (id) => { | ||||
|     http.get('/api/deploy/request/info/', {params: {id}}) | ||||
|       .then(res => { | ||||
|         for (let item of this.records) { | ||||
|           if (item.id === id) { | ||||
|             Object.assign(item, res) | ||||
|             break | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|       .then(this._updateCounter) | ||||
|   } | ||||
| 
 | ||||
|   _updateCounter = () => { | ||||
|     const counter = {'all': 0, '-3': 0, '0': 0, '1': 0, '3': 0, '99': 0}; | ||||
|     for (let item of this.records) { | ||||
|  | @ -122,18 +133,15 @@ class Store { | |||
|     const index = lds.findIndex(this.tabs, x => x.id === info.id); | ||||
|     if (isClose) { | ||||
|       if (index !== -1) { | ||||
|         this.tabs.splice(index, 1) | ||||
|         delete this.tabModes[info.id] | ||||
|         this.tabs[index] = {} | ||||
|       } | ||||
|       this.fetchRecords() | ||||
|       this.fetchInfo(info.id) | ||||
|     } else if (index === -1) { | ||||
|       this.tabModes[info.id] = true | ||||
|       this.tabs.push(info) | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   readConsole = (info) => { | ||||
|     this.tabModes[info.id] = false | ||||
|     const index = lds.findIndex(this.tabs, x => x.id === info.id); | ||||
|     if (index === -1) { | ||||
|       info = Object.assign({}, info, {mode: 'read'}) | ||||
|  | @ -142,9 +150,7 @@ class Store { | |||
|   }; | ||||
| 
 | ||||
|   leaveConsole = () => { | ||||
|     for (let item of this.tabs) { | ||||
|       item.mode = 'read' | ||||
|     } | ||||
|     this.tabs = [] | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 vapao
						vapao