使用 Pinia 和函数计算实现 WebContainer 四种状态进度展示

77 阅读1分钟

目标

实现 webContainer 四种状态的进度展示

commit: github.com/Arabeseque/…

思路:

pinia 全局状态中定义变量 status表示当前的进度。

定义循序变量 PlaygroundStatusOrder 表示装填转移的顺序。

使用函数获取当前的是否已经完成,展示对应的样式。

image-20240218095049090

代码实现

pinia导出

export const usePlaygroundStore = defineStore('playground', (): PlaygroundStateRaw => {
  return {
    ...
    status: undefined,
  }
}) as unknown as () => PlaygroundState

定义固定的状态顺序

export const PlaygroundStatusOrder = [
  'init',
  'mount',
  'install',
  'start',
  'ready',
] as const

实现内容

<script setup lang="ts">
​
const play = usePlaygroundStore()
​
// 判断是否到达阶段,返回 `current` `done` `todo`等内容
function getStep(status: PlaygroundStatus) {
  if (status === 'error' || play.status === 'error')
    return 'error'
  const indexCurrent = PlaygroundStatusOrder.indexOf(play.status!)
  const index = PlaygroundStatusOrder.indexOf(status)
  if (indexCurrent === index)
    return 'current'
  if (indexCurrent > index)
    return 'done'
  return 'todo'
}
​
// 根据阶段修改样式
function getStatusIcon(status: PlaygroundStatus) {
  const step = getStep(status)
  switch (step) {
    case 'error':
      return 'i-ph-x-circle-duotone text-error text-xl'
    case 'current':
      return 'i-svg-spinners-90-ring-with-bg scale-95 text-xl'
    case 'done':
      return 'i-ph-check-circle-duotone text-primary text-xl'
    case 'todo':
      return 'i-ph-dot-duotone text-xl'
  }
}
​
function getTextClass(status: PlaygroundStatus) {
  const step = getStep(status)
  switch (step) {
    case 'error':
      return 'text-red'
    case 'current':
      return ''
    case 'done':
      return 'text-primary'
    case 'todo':
      return 'op50'
  }
}
</script>
​
<template>
  <div
    v-if="play.status !== 'ready'"
    flex="~ col items-center justify-center"
    h-full capitalize
  >
    <div grid="~ cols-[max-content_1fr] gap-2 items-center justify-center">
      <div :class="getStatusIcon('init')" />
      <span :class="getTextClass('init')">Initialize WebContainer</span>
      <div :class="getStatusIcon('mount')" />
      <span :class="getTextClass('mount')">Mount files</span>
      <div :class="getStatusIcon('install')" />
      <span :class="getTextClass('install')">Install Dependencies</span>
      <div :class="getStatusIcon('start')" />
      <span :class="getTextClass('start')">Boot Nuxt Server</span>
    </div>
  </div>
</template>
​