Browse Source
* Reconstructing the Log Component * Delete pnpm-lock.yaml * Delete pnpm-lock.yaml * add pnpm-lock * Modify comments3.0.0/version-upgrade
labbomb
3 years ago
committed by
GitHub
13 changed files with 594 additions and 927 deletions
@ -0,0 +1,138 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import { |
||||
defineComponent, |
||||
PropType, |
||||
h, |
||||
ref, |
||||
reactive, |
||||
toRefs, |
||||
onMounted, |
||||
onUnmounted |
||||
} from 'vue' |
||||
import { useI18n } from 'vue-i18n' |
||||
import { NIcon, NLog } from 'naive-ui' |
||||
import Modal from '../modal' |
||||
import { DownloadOutlined, FullscreenExitOutlined, FullscreenOutlined, SyncOutlined } from '@vicons/antd' |
||||
import screenfull from 'screenfull' |
||||
|
||||
const props = { |
||||
showModalRef: { |
||||
type: Boolean as PropType<boolean>, |
||||
default: false |
||||
}, |
||||
logRef: { |
||||
type: String as PropType<string>, |
||||
default: '' |
||||
}, |
||||
logLoadingRef: { |
||||
type: Boolean as PropType<boolean>, |
||||
default: false |
||||
}, |
||||
row: { |
||||
type: Object as PropType<any>, |
||||
default: {} |
||||
}, |
||||
showDownloadLog: { |
||||
type: Boolean as PropType<boolean>, |
||||
default: false |
||||
}, |
||||
} |
||||
|
||||
export default defineComponent({ |
||||
name: 'log-modal', |
||||
props, |
||||
emits: ['confirmModal', 'refreshLogs', 'downloadLogs'], |
||||
setup(props, ctx) { |
||||
const { t } = useI18n() |
||||
|
||||
const variables = reactive({ |
||||
isFullscreen: false |
||||
}) |
||||
|
||||
const change = () => { |
||||
variables.isFullscreen = screenfull.isFullscreen |
||||
} |
||||
|
||||
const renderIcon = (icon: any) => { |
||||
return () => h(NIcon, null, { default: () => h(icon) }) |
||||
} |
||||
|
||||
const confirmModal = () => { |
||||
variables.isFullscreen = false |
||||
ctx.emit('confirmModal', props.showModalRef) |
||||
} |
||||
|
||||
const refreshLogs = () => { |
||||
ctx.emit('refreshLogs', props.row) |
||||
} |
||||
|
||||
const handleFullScreen = () => { |
||||
screenfull.toggle(document.querySelectorAll('.logModalRef')[0]) |
||||
} |
||||
|
||||
const downloadLogs = () => { |
||||
ctx.emit('downloadLogs', props.row) |
||||
} |
||||
|
||||
onMounted(() => { |
||||
screenfull.on('change', change) |
||||
}) |
||||
|
||||
onUnmounted(() => { |
||||
screenfull.on('change', change) |
||||
}) |
||||
|
||||
return { t, renderIcon, confirmModal, refreshLogs, downloadLogs, handleFullScreen, ...toRefs(variables) } |
||||
}, |
||||
render() { |
||||
const { t, renderIcon, refreshLogs, downloadLogs, isFullscreen, handleFullScreen, showDownloadLog } = this |
||||
return ( |
||||
<Modal |
||||
class='logModalRef' |
||||
title={t('project.task.view_log')} |
||||
show={this.showModalRef} |
||||
cancelShow={false} |
||||
onConfirm={this.confirmModal} |
||||
style={{ width: '60%' }} |
||||
headerLinks={ref([ |
||||
{ |
||||
text: t('project.workflow.download_log'), |
||||
show: showDownloadLog, |
||||
action: downloadLogs, |
||||
icon: renderIcon(DownloadOutlined) |
||||
}, |
||||
{ |
||||
text: t('project.task.refresh'), |
||||
show: true, |
||||
action: refreshLogs, |
||||
icon: renderIcon(SyncOutlined) |
||||
}, |
||||
{ |
||||
text: isFullscreen ? t('project.task.cancel_full_screen') : t('project.task.enter_full_screen'), |
||||
show: true, |
||||
action: handleFullScreen, |
||||
icon: isFullscreen ? renderIcon(FullscreenExitOutlined) : renderIcon(FullscreenOutlined) |
||||
} |
||||
])} |
||||
> |
||||
<NLog rows={30} log={this.logRef} loading={this.logLoadingRef} /> |
||||
</Modal> |
||||
) |
||||
} |
||||
}) |
@ -1,103 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import { defineComponent, h, PropType, ref, toRefs, watch } from 'vue' |
||||
import { useI18n } from 'vue-i18n' |
||||
import { NIcon, NLog } from 'naive-ui' |
||||
import { SyncOutlined } from '@vicons/antd' |
||||
import { useModal } from './use-modal' |
||||
import Modal from '@/components/modal' |
||||
|
||||
const props = { |
||||
showModalRef: { |
||||
type: Boolean as PropType<boolean>, |
||||
default: false |
||||
}, |
||||
row: { |
||||
type: Object as PropType<any>, |
||||
default: {} |
||||
} |
||||
} |
||||
|
||||
const LogModal = defineComponent({ |
||||
name: 'LogModal', |
||||
props, |
||||
emits: ['confirmModal'], |
||||
setup(props, ctx) { |
||||
const { t } = useI18n() |
||||
const { variables, getLogs } = useModal() |
||||
const renderIcon = (icon: any) => { |
||||
return () => h(NIcon, null, { default: () => h(icon) }) |
||||
} |
||||
|
||||
const confirmModal = () => { |
||||
ctx.emit('confirmModal', props.showModalRef) |
||||
} |
||||
|
||||
const refreshLogs = () => { |
||||
variables.logRef = '' |
||||
variables.loadingRef = true |
||||
variables.skipLineNum = 0 |
||||
variables.limit = 1000 |
||||
|
||||
getLogs() |
||||
} |
||||
|
||||
watch( |
||||
() => props.showModalRef, |
||||
() => { |
||||
if (props.showModalRef) { |
||||
variables.id = props.row.id |
||||
props.showModalRef && variables.id && getLogs() |
||||
} else { |
||||
variables.id = '' |
||||
variables.logRef = '' |
||||
variables.loadingRef = true |
||||
variables.skipLineNum = 0 |
||||
variables.limit = 1000 |
||||
} |
||||
} |
||||
) |
||||
|
||||
return { t, ...toRefs(variables), confirmModal, renderIcon, refreshLogs } |
||||
}, |
||||
render() { |
||||
const { t, renderIcon, refreshLogs } = this |
||||
|
||||
return ( |
||||
<Modal |
||||
title={t('project.task.view_log')} |
||||
show={this.showModalRef} |
||||
cancelShow={false} |
||||
onConfirm={this.confirmModal} |
||||
style={{ width: '60%' }} |
||||
headerLinks={ref([ |
||||
{ |
||||
text: t('project.task.refresh'), |
||||
show: true, |
||||
action: refreshLogs, |
||||
icon: renderIcon(SyncOutlined) |
||||
} |
||||
])} |
||||
> |
||||
<NLog rows={30} log={this.logRef} loading={this.loadingRef} /> |
||||
</Modal> |
||||
) |
||||
} |
||||
}) |
||||
|
||||
export default LogModal |
@ -1,58 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import { reactive, ref } from 'vue' |
||||
import { useAsyncState } from '@vueuse/core' |
||||
import { queryLog } from '@/service/modules/log' |
||||
|
||||
export function useModal() { |
||||
const variables = reactive({ |
||||
id: ref(''), |
||||
loadingRef: ref(true), |
||||
logRef: ref(''), |
||||
skipLineNum: ref(0), |
||||
limit: ref(1000) |
||||
}) |
||||
|
||||
const getLogs = () => { |
||||
const { state } = useAsyncState( |
||||
queryLog({ |
||||
taskInstanceId: Number(variables.id), |
||||
limit: variables.limit, |
||||
skipLineNum: variables.skipLineNum |
||||
}).then((res: string) => { |
||||
variables.logRef += res |
||||
|
||||
if (res) { |
||||
variables.limit += 1000 |
||||
variables.skipLineNum += 1000 |
||||
getLogs() |
||||
} else { |
||||
variables.loadingRef = false |
||||
} |
||||
}), |
||||
{} |
||||
) |
||||
|
||||
return state |
||||
} |
||||
|
||||
return { |
||||
variables, |
||||
getLogs |
||||
} |
||||
} |
@ -1,368 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import _ from 'lodash' |
||||
import { |
||||
defineComponent, |
||||
PropType, |
||||
Transition, |
||||
toRefs, |
||||
ref, |
||||
onMounted, |
||||
computed, |
||||
reactive, |
||||
renderSlot |
||||
} from 'vue' |
||||
import { useI18n } from 'vue-i18n' |
||||
import { NButton, NIcon, NTooltip } from 'naive-ui' |
||||
import { queryLog } from '@/service/modules/log' |
||||
import { |
||||
DownloadOutlined, |
||||
SyncOutlined, |
||||
FullscreenOutlined, |
||||
FullscreenExitOutlined |
||||
} from '@vicons/antd' |
||||
import { downloadFile } from '@/service/service' |
||||
import styles from './log.module.scss' |
||||
|
||||
const props = { |
||||
taskInstanceId: { |
||||
type: Number as PropType<number>, |
||||
default: -1 |
||||
}, |
||||
taskInstanceType: { |
||||
type: String as PropType<string>, |
||||
default: '' |
||||
} |
||||
} |
||||
|
||||
export default defineComponent({ |
||||
name: 'workflow-instance-log', |
||||
props, |
||||
emits: ['hideLog'], |
||||
setup(props, ctx) { |
||||
const { t } = useI18n() |
||||
|
||||
const loadingRef = ref(false) |
||||
const loadingIndex = ref(0) |
||||
const isDataRef = ref(true) |
||||
const logBox = ref() |
||||
const logContent = ref() |
||||
const logContentBox = ref() |
||||
const textareaLog = ref() |
||||
const isScreen = ref(false) |
||||
const textareaHeight = computed(() => |
||||
logContentBox.value ? logContentBox.value.clientHeight : 0 |
||||
) |
||||
const contentRef = ref() |
||||
|
||||
const boxRef = reactive({ |
||||
width: '', |
||||
height: '', |
||||
marginLeft: '', |
||||
marginRight: '', |
||||
marginTop: '' |
||||
}) |
||||
|
||||
const refreshLog = () => { |
||||
loadingRef.value = true |
||||
queryLog({ |
||||
taskInstanceId: props.taskInstanceId, |
||||
skipLineNum: loadingIndex.value * 1000, |
||||
limit: loadingIndex.value === 0 ? 1000 : (loadingIndex.value + 1) * 1000 |
||||
}).then((res: any) => { |
||||
setTimeout(() => { |
||||
loadingRef.value = false |
||||
if (res) { |
||||
window.$message.success(t('project.workflow.update_log_success')) |
||||
} else { |
||||
window.$message.warning(t('project.workflow.no_more_log')) |
||||
} |
||||
}, 1500) |
||||
textareaLog.value.innerHTML = res || t('project.workflow.no_log') |
||||
}) |
||||
} |
||||
|
||||
const showLog = () => { |
||||
queryLog({ |
||||
taskInstanceId: props.taskInstanceId, |
||||
skipLineNum: loadingIndex.value * 1000, |
||||
limit: loadingIndex.value === 0 ? 1000 : (loadingIndex.value + 1) * 1000 |
||||
}).then((res: any) => { |
||||
if (!res) { |
||||
isDataRef.value = false |
||||
setTimeout(() => { |
||||
window.$message.warning(t('project.workflow.no_more_log')) |
||||
}, 1000) |
||||
textareaLog.value.innerHTML = |
||||
contentRef.value || t('project.workflow.no_log') |
||||
} else { |
||||
isDataRef.value = true |
||||
contentRef.value = res |
||||
textareaLog.value.innerHTML = |
||||
contentRef.value || t('project.workflow.no_log') |
||||
setTimeout(() => { |
||||
textareaLog.value.scrollTop = 2 |
||||
}, 800) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const initLog = () => { |
||||
window.$message.info(t('project.workflow.loading_log')) |
||||
showLog() |
||||
} |
||||
|
||||
const downloadLog = () => { |
||||
downloadFile('log/download-log', { |
||||
taskInstanceId: props.taskInstanceId |
||||
}) |
||||
} |
||||
|
||||
const screenOpen = () => { |
||||
isScreen.value = true |
||||
const winW = window.innerWidth - 40 |
||||
const winH = window.innerHeight - 40 |
||||
|
||||
boxRef.width = `${winW}px` |
||||
boxRef.height = `${winH}px` |
||||
boxRef.marginLeft = `-${winW / 2}px` |
||||
boxRef.marginRight = `-${winH / 2}px` |
||||
boxRef.marginTop = `-${winH / 2}px` |
||||
|
||||
logContent.value.animate({ scrollTop: 0 }, 0) |
||||
} |
||||
|
||||
const screenClose = () => { |
||||
isScreen.value = false |
||||
boxRef.width = '' |
||||
boxRef.height = '' |
||||
boxRef.marginLeft = '' |
||||
boxRef.marginRight = '' |
||||
boxRef.marginTop = '' |
||||
|
||||
logContent.value.animate({ scrollTop: 0 }, 0) |
||||
} |
||||
|
||||
const toggleScreen = () => { |
||||
if (isScreen.value) { |
||||
screenClose() |
||||
} else { |
||||
screenOpen() |
||||
} |
||||
} |
||||
|
||||
const close = () => { |
||||
ctx.emit('hideLog') |
||||
} |
||||
|
||||
/** |
||||
* up |
||||
*/ |
||||
const onUp = _.throttle( |
||||
function () { |
||||
loadingIndex.value = loadingIndex.value - 1 |
||||
showLog() |
||||
}, |
||||
1000, |
||||
{ |
||||
trailing: false |
||||
} |
||||
) |
||||
|
||||
/** |
||||
* down |
||||
*/ |
||||
const onDown = _.throttle( |
||||
function () { |
||||
loadingIndex.value = loadingIndex.value + 1 |
||||
showLog() |
||||
}, |
||||
1000, |
||||
{ |
||||
trailing: false |
||||
} |
||||
) |
||||
|
||||
const onTextareaScroll = () => { |
||||
textareaLog.value.onscroll = () => { |
||||
// Listen for scrollbar events
|
||||
if ( |
||||
textareaLog.value.scrollTop + textareaLog.value.clientHeight === |
||||
textareaLog.value.clientHeight |
||||
) { |
||||
if (loadingIndex.value > 0) { |
||||
window.$message.info(t('project.workflow.loading_log')) |
||||
onUp() |
||||
} |
||||
} |
||||
// Listen for scrollbar events
|
||||
if ( |
||||
textareaLog.value.scrollHeight === |
||||
textareaLog.value.clientHeight + textareaLog.value.scrollTop |
||||
) { |
||||
// No data is not requested
|
||||
if (isDataRef.value) { |
||||
window.$message.info(t('project.workflow.loading_log')) |
||||
onDown() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
onMounted(() => { |
||||
initLog() |
||||
onTextareaScroll() |
||||
}) |
||||
|
||||
return { |
||||
t, |
||||
logBox, |
||||
logContentBox, |
||||
loadingRef, |
||||
textareaLog, |
||||
logContent, |
||||
textareaHeight, |
||||
isScreen, |
||||
boxRef, |
||||
showLog, |
||||
downloadLog, |
||||
refreshLog, |
||||
toggleScreen, |
||||
close, |
||||
...toRefs(props) |
||||
} |
||||
}, |
||||
render() { |
||||
return ( |
||||
<div> |
||||
<span> |
||||
{this.taskInstanceId && this.taskInstanceType !== 'SUB_PROCESS' && ( |
||||
<span> |
||||
{renderSlot(this.$slots, 'history')} |
||||
<slot name='history'></slot> |
||||
<span onClick={this.showLog}> |
||||
{renderSlot(this.$slots, 'log')} |
||||
</span> |
||||
</span> |
||||
)} |
||||
<Transition name='fade'> |
||||
{ |
||||
<div class={styles['log-pop']}> |
||||
<div class={styles['log-box']} style={{ ...this.boxRef }}> |
||||
<div class={styles['title']}> |
||||
<div class={styles['left-item']}> |
||||
{this.t('project.workflow.view_log')} |
||||
</div> |
||||
<div class={styles['right-item']}> |
||||
<NTooltip> |
||||
{{ |
||||
trigger: () => ( |
||||
<NButton |
||||
strong |
||||
secondary |
||||
circle |
||||
type='info' |
||||
class={styles.button} |
||||
onClick={this.downloadLog} |
||||
> |
||||
<NIcon> |
||||
<DownloadOutlined /> |
||||
</NIcon> |
||||
</NButton> |
||||
), |
||||
default: () => this.t('project.workflow.download_log') |
||||
}} |
||||
</NTooltip> |
||||
<NTooltip> |
||||
{{ |
||||
trigger: () => ( |
||||
<NButton |
||||
strong |
||||
secondary |
||||
circle |
||||
type='info' |
||||
class={styles.button} |
||||
onClick={() => |
||||
!this.loadingRef && this.refreshLog() |
||||
} |
||||
> |
||||
<NIcon> |
||||
<SyncOutlined /> |
||||
</NIcon> |
||||
</NButton> |
||||
), |
||||
default: () => this.t('project.workflow.refresh_log') |
||||
}} |
||||
</NTooltip> |
||||
<NTooltip> |
||||
{{ |
||||
trigger: () => ( |
||||
<NButton |
||||
strong |
||||
secondary |
||||
circle |
||||
type='info' |
||||
class={styles.button} |
||||
onClick={this.toggleScreen} |
||||
> |
||||
<NIcon> |
||||
{this.isScreen ? ( |
||||
<FullscreenExitOutlined /> |
||||
) : ( |
||||
<FullscreenOutlined /> |
||||
)} |
||||
</NIcon> |
||||
</NButton> |
||||
), |
||||
default: () => |
||||
this.isScreen |
||||
? this.t('project.workflow.cancel_full_screen') |
||||
: this.t('project.workflow.enter_full_screen') |
||||
}} |
||||
</NTooltip> |
||||
</div> |
||||
</div> |
||||
<div class={styles['content']} ref='logContent'> |
||||
<div class={styles['content-log-box']} ref='logContentBox'> |
||||
<textarea |
||||
style={`width: 100%; height: ${this.textareaHeight}px`} |
||||
spellcheck='false' |
||||
ref='textareaLog' |
||||
readonly |
||||
></textarea> |
||||
</div> |
||||
</div> |
||||
<div class={styles['operation']}> |
||||
<NButton |
||||
type='primary' |
||||
size='small' |
||||
round |
||||
onClick={this.close} |
||||
> |
||||
{this.t('project.workflow.close')} |
||||
</NButton> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
} |
||||
</Transition> |
||||
</span> |
||||
</div> |
||||
) |
||||
} |
||||
}) |
@ -1,117 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
.log-pop { |
||||
position: fixed; |
||||
left: 0; |
||||
top: 0; |
||||
width: 100%; |
||||
height: 100%; |
||||
background: rgba(0, 0, 0, 0.4); |
||||
z-index: 10; |
||||
.log-box { |
||||
width: 660px; |
||||
height: 520px; |
||||
background: #fff; |
||||
border-radius: 3px; |
||||
position: absolute; |
||||
left: 50%; |
||||
top: 50%; |
||||
margin-left: -340px; |
||||
margin-top: -250px; |
||||
.title { |
||||
height: 50px; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
border-bottom: 1px solid #dcdedc; |
||||
.left-item { |
||||
font-size: 16px; |
||||
color: #333; |
||||
display: inline-block; |
||||
padding-left: 20px; |
||||
} |
||||
.right-item { |
||||
padding-right: 10px; |
||||
.button { |
||||
margin-right: 10px; |
||||
} |
||||
} |
||||
} |
||||
.content { |
||||
height: calc(100% - 100px); |
||||
background: #002a35; |
||||
padding: 6px 2px; |
||||
.content-log-box { |
||||
width: 100%; |
||||
height: 100%; |
||||
word-break: break-all; |
||||
textarea { |
||||
background: none; |
||||
color: #9cabaf; |
||||
border: 0; |
||||
font-family: 'Microsoft Yahei,Arial,Hiragino Sans GB,tahoma,SimSun,sans-serif'; |
||||
font-weight: bold; |
||||
resize: none; |
||||
line-height: 1.6; |
||||
padding: 0px; |
||||
} |
||||
} |
||||
} |
||||
.operation { |
||||
text-align: right; |
||||
height: 50px; |
||||
line-height: 44px; |
||||
border-top: 1px solid #dcdedc; |
||||
padding-right: 20px; |
||||
background: #fff; |
||||
position: relative; |
||||
} |
||||
} |
||||
} |
||||
@-webkit-keyframes rotateloading { |
||||
from { |
||||
-webkit-transform: rotate(0deg); |
||||
} |
||||
to { |
||||
-webkit-transform: rotate(360deg); |
||||
} |
||||
} |
||||
@-moz-keyframes rotateloading { |
||||
from { |
||||
-moz-transform: rotate(0deg); |
||||
} |
||||
to { |
||||
-moz-transform: rotate(359deg); |
||||
} |
||||
} |
||||
@-o-keyframes rotateloading { |
||||
from { |
||||
-o-transform: rotate(0deg); |
||||
} |
||||
to { |
||||
-o-transform: rotate(359deg); |
||||
} |
||||
} |
||||
@keyframes rotateloading { |
||||
from { |
||||
transform: rotate(0deg); |
||||
} |
||||
to { |
||||
transform: rotate(359deg); |
||||
} |
||||
} |
Loading…
Reference in new issue