node-activity.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <template>
  2. <g
  3. class="flow-node__activity"
  4. :status="activity.status"
  5. >
  6. <rect
  7. class="flow-node__activity-background"
  8. x="-12"
  9. y="-12"
  10. :width="ellapsed?65:24"
  11. height="24"
  12. rx="12"
  13. />
  14. <icon-refresh v-if="activity.status=='running'" v-bind="iconProps" class="flow-node__activity-icon" />
  15. <icon-wait v-else-if="activity.status=='waiting'" v-bind="iconProps" class="flow-node__activity-icon"/>
  16. <icon-ok v-else-if="activity.status=='finish'" v-bind="iconProps"class="flow-node__activity-icon"/>
  17. <icon-fail v-else-if="activity.status=='error'" v-bind="iconProps"class="flow-node__activity-icon"/>
  18. <icon-question v-else v-bind="iconProps" class="flow-node__activity-icon" />
  19. <text :class="{active:ellapsed}" class="flow-node__activity-time" x="13" y="4" fill="black">
  20. {{ ellapsed }}
  21. </text>
  22. </g>
  23. </template>
  24. <script>
  25. import IconWait from '@/assets/icons/wait.svg'
  26. import IconFail from '@/assets/icons/fail.svg'
  27. import IconOk from '@/assets/icons/ok.svg'
  28. import IconQuestion from '@/assets/icons/question.svg'
  29. import IconRefresh from '@/assets/icons/refresh.svg'
  30. import utils from '@/utils/utils'
  31. export default {
  32. name: 'FlowNodeStatus',
  33. components: {IconWait, IconFail, IconOk, IconQuestion, IconRefresh},
  34. props: {
  35. activity: {type: Object, default: () => {}}
  36. },
  37. data () {
  38. return {
  39. finishTime: null
  40. }
  41. },
  42. computed: {
  43. iconProps () {
  44. return {
  45. x: -9,
  46. y: -9,
  47. viewBox: '-4 -4 72 72',
  48. width: 18,
  49. height: 18
  50. }
  51. },
  52. ellapsed () {
  53. if (!this.finishTime) return null
  54. const s = new Date(Date.parse(this.activity.startTime))
  55. if (!dateIsValid(s)) { return null }
  56. let intervalms = this.finishTime - s
  57. if (intervalms < 0) {
  58. intervalms = 0
  59. }
  60. const min = Math.floor(intervalms / 60000)
  61. const sec = (intervalms / 1000) % 60
  62. return utils.padStart(min.toFixed(0), 2, '0') + ':' + utils.padStart(sec.toFixed(0), 2, '0')
  63. }
  64. },
  65. watch: {
  66. activity (val, oldVal) {
  67. this.finishTime = null
  68. this.updateTime()
  69. /* const finish = new Date(Date.parse(val.endTime))
  70. if (dateIsValid(finish)) {
  71. } */
  72. }
  73. },
  74. mounted () {
  75. this._timeOut = setTimeout(this.updateTime, 999)
  76. },
  77. beforeDestroy () {
  78. clearTimeout(this._timeOut)
  79. },
  80. methods: {
  81. updateTime () {
  82. const finish = new Date(Date.parse(this.activity.endTime))
  83. if (dateIsValid(finish)) {
  84. this.finishTime = finish
  85. return
  86. }
  87. this.finishTime = new Date(new Date().getTime() + 1000)
  88. this._timeOut = setTimeout(this.updateTime, 999)
  89. }
  90. }
  91. }
  92. // Golang 0 date
  93. const invalidDate = -62135596800000
  94. function dateIsValid (d) {
  95. if (Object.prototype.toString.call(d) === '[object Date]') {
  96. if (!isNaN(d.getTime())) { // d.valueOf() could also work
  97. if (d.getTime() === invalidDate) {
  98. return false
  99. }
  100. return true
  101. }
  102. }
  103. return false
  104. }
  105. </script>
  106. <style>
  107. .flow-node__activity {
  108. font-size:12px;
  109. opacity:0;
  110. user-select: none;
  111. pointer-event:none;
  112. transition: all var(--transition-speed);
  113. }
  114. .flow-view.flow-node--activity .flow-node__activity {
  115. opacity:0.8;
  116. }
  117. .flow-node__activity-background {
  118. stroke: rgba(0,0,0,0.2);
  119. transition: all var(--transition-speed);
  120. }
  121. .flow-node__activity-icon{
  122. width:20px;
  123. height:20px;
  124. }
  125. .flow-node__activity-time{
  126. opacity:0;
  127. width:0;
  128. transition: all var(--transition-speed);
  129. }
  130. .flow-node__activity-time.active{
  131. opacity:1;
  132. }
  133. .flow-node__activity-icon >* {
  134. transform-origin: 32px 32px;
  135. stroke-width: 6px;
  136. stroke: inherits;
  137. }
  138. .flow-node__activity[status=running] .flow-node__activity-icon >* {
  139. -webkit-animation: spin 1s infinite linear;
  140. -moz-animation: spin 1s infinite linear;
  141. animation: spin 1s infinite linear;
  142. stroke: #aa0;
  143. }
  144. .flow-node__activity[status=error] .flow-node__activity-icon > * {
  145. stroke: #f22;
  146. }
  147. .flow-node__activity[status=waiting] .flow-node__activity-icon >* {
  148. -webkit-animation: shake 1s infinite linear;
  149. -moz-animation: shake 1s infinite linear;
  150. animation: shake 1s infinite linear;
  151. }
  152. .flow-node__activity[status=finish] .flow-node__activity-icon >* {
  153. stroke: #2c2;
  154. }
  155. /*** ANIMATIONS ***/
  156. @-moz-keyframes spin {
  157. from { -moz-transform: rotate(0deg); }
  158. to { -moz-transform: rotate(-360deg); }
  159. }
  160. @-webkit-keyframes spin {
  161. from { -webkit-transform: rotate(0deg); }
  162. to { -webkit-transform: rotate(-360deg); }
  163. }
  164. @keyframes spin {
  165. from {transform:rotate(0deg);}
  166. to {transform:rotate(-360deg);}
  167. }
  168. @keyframes shake {
  169. 10%, 90% {
  170. transform: rotate(-2deg);
  171. }
  172. 20%, 80% {
  173. transform: rotate(4deg);
  174. }
  175. 30%, 50%, 70% {
  176. transform: rotate(-7deg);
  177. }
  178. 40%, 60% {
  179. transform: rotate(7deg);
  180. }
  181. }
  182. </style>