haoxuan
2023-11-06 2bb9a863e75312fe90869ea3deea137b46b1bb1e
拉代码
1个文件已添加
12个文件已修改
290 ■■■■ 已修改文件
index.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/CommonModal.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/craftModel.ts 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/devices.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/tasks.ts 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/components/CurrentDateTime.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/components/DashboardTitle.vue 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/components/DeviceCheckList.vue 81 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/components/TaskControl.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/components/TaskStep.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
index.html
@@ -4,7 +4,7 @@
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>看板</title>
    <title>智能工作台</title>
  </head>
  <body>
    <div id="app"></div>
public/favicon.ico

src/api/index.ts
@@ -11,6 +11,13 @@
  msg: string
}
export interface ListResponse<T = any> {
  code: number
  data: T
  msg: string
  total: number
}
export interface TaskListParams {
  /** 1未完成2今天未完成3已完成 */
  type: 1 | 2 | 3
@@ -108,6 +115,21 @@
  })
}
export interface SetCurrentDeviceParams {
  currentDeviceID: string
}
/**
 * 获取当前面板绑定的设备列表
 */
export function apiSetCurrentDevice(data: SetCurrentDeviceParams) {
  return request<BaseResponse<Devices>>({
    url: `/v1/device/setCurrentDeviceId`,
    method: 'post',
    data
  })
}
export interface CraftModelListParams {
  procedureId: number
  page: number
@@ -119,7 +141,7 @@
 * @param params
 */
export function apiGetCraftModelList(params: CraftModelListParams) {
  return request<BaseResponse<CraftModel[]>>({
  return request<ListResponse<CraftModel[]>>({
    url: '/v1/processModel/list',
    method: 'get',
    params
src/components.d.ts
@@ -24,6 +24,7 @@
    ElSteps: typeof import('element-plus/es')['ElSteps']
    ElTabPane: typeof import('element-plus/es')['ElTabPane']
    ElTabs: typeof import('element-plus/es')['ElTabs']
    ElText: typeof import('element-plus/es')['ElText']
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
  }
src/components/CommonModal.vue
@@ -28,7 +28,7 @@
  /** 是否展示模态框 */
  modelValue: boolean
  /** 更宽版本 */
  wider: boolean
  wider?: boolean
}
const props = withDefaults(defineProps<BaseModalProps>(), {
  modelValue: false,
src/stores/craftModel.ts
@@ -1,22 +1,25 @@
import { ref, watch } from 'vue'
import { computed, ref, watch } from 'vue'
import { defineStore } from 'pinia'
import type { CraftModel } from '@/api/craftModel'
import { apiGetCraftModelList } from '@/api'
import { useTasksStore } from '@/stores/tasks'
const watcher = ref()
const page = ref(1)
const total = ref(0)
export const useCraftModelStore = defineStore('craftModel', () => {
  const craftModelList = ref<CraftModel[]>()
  const craftModelList = ref<CraftModel[]>([])
  const taskStore = useTasksStore()
  function getCraftModelList() {
    if (taskStore.activeTask?.Procedure.ID) {
      craftModelList.value = []
      apiGetCraftModelList({
        procedureId: taskStore.activeTask?.Procedure.ID,
        page: 1,
        pageSize: 999
        page: page.value,
        pageSize: 6
      }).then((res) => {
        craftModelList.value = res.data ?? []
        total.value = res.total
      })
    }
  }
@@ -25,10 +28,36 @@
    watch(
      () => taskStore.activeTask,
      () => {
        page.value = 1
        getCraftModelList()
      }
    )
  }
  return { craftModelList, getCraftModelList }
  const loading = ref(false)
  function loadMore() {
    if (taskStore.activeTask?.Procedure.ID && !loading.value && hasMore.value) {
      page.value++
      loading.value = true
      apiGetCraftModelList({
        procedureId: taskStore.activeTask?.Procedure.ID,
        page: page.value,
        pageSize: 6
      })
        .then((res) => {
          craftModelList.value = [...craftModelList.value, ...(res.data ?? [])]
          total.value = res.total
        })
        .finally(() => {
          loading.value = false
        })
    }
  }
  const hasMore = computed(() => {
    return total.value > craftModelList.value.length
  })
  return { craftModelList, getCraftModelList, hasMore, loading, loadMore }
})
src/stores/devices.ts
@@ -1,4 +1,4 @@
import { computed, onUnmounted } from 'vue'
import { computed } from 'vue'
import { defineStore } from 'pinia'
import { getDeviceList } from '@/api'
import { useRequest } from 'vue-hooks-plus'
@@ -7,6 +7,9 @@
export const useDevicesStore = defineStore('device', () => {
  const deviceInfo = computed(() => {
    if (deviceInfo?.value) {
      deviceRes.value.data.currentDeviceID = 'wwwwwwww222222'
    }
    return deviceRes?.value?.data as Devices
  })
@@ -27,10 +30,6 @@
    cancelDevicePolling()
    startDevicePolling()
  }
  onUnmounted(() => {
    cancelDevicePolling()
  })
  return { deviceInfo, startPollingDevice }
})
src/stores/tasks.ts
@@ -77,10 +77,14 @@
  /**
   * 刷新所有数据
   */
  function reload(channel: number) {
  function reloadChannel(channel: number) {
    getChannels(currentType.value).then(() => {
      autoSelectTask(channel)
    })
  }
  function reloadAllData() {
    getChannels(currentType.value)
  }
  function moreChannelTasksBtn(channelNumber: number) {
@@ -128,9 +132,10 @@
    getChannels,
    moreBtnStatus,
    activeTask,
    reload,
    reloadChannel,
    setActiveTask,
    moreChannelTasksBtn,
    foldChannelTasksBtn
    foldChannelTasksBtn,
    reloadAllData
  }
})
src/views/dashboard/components/CurrentDateTime.vue
@@ -13,9 +13,8 @@
$color: #30decd;
.current-date-time {
  width: 370px;
  color: $color;
  font-size: 24px;
  font-size: 20px;
  font-weight: 600;
}
</style>
src/views/dashboard/components/DashboardTitle.vue
@@ -1,29 +1,39 @@
<template>
  <div class="dashboard-title">
    <div class="title-text">
      智能工作台 — {{ deviceStore?.deviceInfo?.currentDeviceID ?? '' }}
      智能工作台 —
      <el-popover
        placement="bottom"
        :width="200"
        trigger="click"
        :content="deviceStore?.deviceInfo?.currentDeviceID ?? ''"
      >
        <template #reference>
          <el-text truncated class="device-name">{{ deviceStore?.deviceInfo?.currentDeviceID ?? '' }}</el-text>
        </template>
      </el-popover>
      <el-icon size="32" color="#0db7f5" style="margin-left: 20px; cursor: pointer" @click="openDevicesModal">
        <IconSlider></IconSlider>
      </el-icon>
    </div>
    <div class="title-status">
      <div class="connection-info" @click="openProblemsModal">
        <el-icon size="30" :color="problemsIconStatus ? '#00ff00' : '#ff0000'">
        <el-icon size="26" :color="problemsIconStatus ? '#00ff00' : '#ff0000'">
          <AlertLightIcon></AlertLightIcon>
        </el-icon>
      </div>
      <div class="cloud-connection-status">
        <el-icon v-if="cloudConnectionIconStatus" size="45" color="#00ff00">
        <el-icon v-if="cloudConnectionIconStatus" size="38" color="#00ff00">
          <IconCloudDone></IconCloudDone>
        </el-icon>
        <el-icon v-else size="45" color="#ff0000">
        <el-icon v-else size="38" color="#ff0000">
          <IconCloudOff></IconCloudOff>
        </el-icon>
      </div>
    </div>
  </div>
  <DeviceCheckList v-model="showDevicesModal" :devices="deviceList"></DeviceCheckList>
  <DeviceCheckList v-model="showDevicesModal" @should-reload="emits('shouldReload')"></DeviceCheckList>
  <TroubleTrackerModal v-model="showProblemsModal" :problems="problemList"></TroubleTrackerModal>
</template>
<script setup lang="ts">
@@ -38,6 +48,10 @@
import { useRequest } from 'vue-hooks-plus'
import { apiGetProblemList } from '@/api'
import { PROBLEMS_POLLING_DURATION } from '@/common/constants'
const emits = defineEmits<{
  shouldReload: []
}>()
// 是否显示问题诊断modal
const showProblemsModal = ref(false)
@@ -117,10 +131,15 @@
  align-items: center;
  position: absolute;
  top: 16px;
  right: 40px;
  right: 6px;
}
.connection-info {
  margin-right: 10px;
  cursor: pointer;
}
.device-name {
  max-width: 340px;
  font-size: 40px;
  color: #fff;
}
</style>
src/views/dashboard/components/DeviceCheckList.vue
@@ -4,18 +4,18 @@
      <template #title>设备选择</template>
      <div class="device-box">
        <el-scrollbar always class="scroller">
          <template v-if="devices?.length">
          <template v-if="deviceInfo?.deviceIDList?.length">
            <div
              v-for="(item, index) in devices"
              v-for="(item, index) in deviceInfo?.deviceIDList"
              :key="index"
              :class="item.checked ? 'device-item check-item' : 'device-item'"
              @click="deviceClick(index)"
              :class="selectedDevice === item ? 'device-item check-item' : 'device-item'"
              @click="deviceClick(item)"
            >
              <div class="item-l">
                <span>{{ item.number }}</span>
                {{ item.name }}
                <span>{{ item }}</span>
                <!--  {{ item }}-->
              </div>
              <div v-if="item.checked" class="item-r">
              <div v-if="selectedDevice === item" class="item-r">
                <el-icon class="item-icon" size="22" color="#00ff00"><CircleCheckFilled /></el-icon>
              </div>
            </div>
@@ -35,7 +35,11 @@
import { useVModel } from '@vueuse/core'
import { CircleCheckFilled } from '@element-plus/icons-vue'
import BigButton from '@/views/dashboard/components/BigButton.vue'
import { ref } from 'vue'
import { useDevicesStore } from '@/stores/devices'
import { storeToRefs } from 'pinia'
import { ref, watch } from 'vue'
import { apiSetCurrentDevice } from '@/api'
import { ElMessage } from 'element-plus'
export interface DeviceCheckListProps {
  modelValue: boolean
}
@@ -45,28 +49,57 @@
const emit = defineEmits<{
  'update:modelValue': [show: boolean]
  shouldReload: []
}>()
const modelData = useVModel(props, 'modelValue', emit)
const deviceStore = useDevicesStore()
const { deviceInfo } = storeToRefs(deviceStore)
const devices = ref([
  {
    number: '111',
    name: '设备A',
    checked: true
  },
  {
    number: '222',
    name: '设备b'
// 弹窗打开时设定当前选中的设备
const selectedDevice = ref<string>('')
watch(modelData, () => {
  if (modelData.value) {
    selectedDevice.value = deviceInfo.value?.currentDeviceID ?? ''
  }
])
})
function deviceClick(deviceId: string) {
  selectedDevice.value = deviceId
}
function saveModal() {
  if (!selectedDevice.value) {
    ElMessage({
      message: '请先选中一个设备',
      type: 'error',
      duration: 3 * 1000
    })
    return
  }
  apiSetCurrentDevice({ currentDeviceID: selectedDevice.value })
    .then(() => {
      ElMessage({
        message: '设定成功',
        type: 'success',
        duration: 2 * 1000
      })
      modelData.value = false
      emit('shouldReload')
    })
    .catch((err) => {
      console.error(err)
      ElMessage({
        message: err.msg,
        type: 'error',
        duration: 3 * 1000
      })
    })
    .finally(() => {
      deviceStore.startPollingDevice()
    })
}
function closeModal() {
  modelData.value = false
}
function saveModal() {}
const deviceClick = (index) => {
  devices.value.find((ele) => (ele.checked = false))
  devices.value[index].checked = true
}
</script>
<style scoped lang="scss">
src/views/dashboard/components/TaskControl.vue
@@ -42,11 +42,7 @@
      </template>
    </div>
  </div>
  <TaskControlModal
    v-model="showTaskControlModal"
    :task="task"
    @produce-start="emit('shouldReload', task)"
  ></TaskControlModal>
  <TaskControlModal v-model="showTaskControlModal" :task="task" @produce-start="onProduceStart"></TaskControlModal>
</template>
<script setup lang="ts">
import type { Task } from '@/api/task'
@@ -80,14 +76,14 @@
 * 完成任务
 */
function finishTaskProduce() {
  if (task?.value?.Procedure?.ID) {
  if (task?.value && task.value?.Procedure?.ID) {
    finishTask({ id: task!.value.Procedure.ID }).then(
      (res) => {
        ElMessage({
          message: '操作成功!',
          type: 'success'
        })
        emit('shouldReload', task.value)
        emit('shouldReload', task.value as Task)
      },
      (err) => {
        console.error(err)
@@ -105,6 +101,10 @@
  }
}
function onProduceStart() {
  emit('shouldReload', task!.value as Task)
}
/**
 * 格式化时间戳
 * @param timestamp 后端返的10位时间戳
src/views/dashboard/components/TaskStep.vue
New file
@@ -0,0 +1,71 @@
<template>
  <div class="task-step">
    <div v-for="(item, index) in props.steps" :key="index" class="task-step-item" :style="{ 'flex-basis': flexBasis }">
      <el-icon v-if="index + 1 < active" class="icon" size="22" color="#01f304"><CircleCheck /></el-icon>
      <el-icon v-if="index + 1 === active" class="icon" size="22" color="#c25915"><Clock /></el-icon>
      <el-icon v-if="index + 1 > active" class="icon" size="22" color="#7d7f83"><Clock /></el-icon>
      <span class="text" :class="{ green: index + 1 < active, red: index + 1 === active, gray: index + 1 > active }">
        {{ item }}
      </span>
      <span class="line"></span>
    </div>
  </div>
</template>
<script setup lang="ts">
import { CircleCheck, Clock } from '@element-plus/icons-vue'
import { computed } from 'vue'
export interface TaskStepProps {
  active: number
  steps: string[]
}
const props = defineProps<TaskStepProps>()
const flexBasis = computed(() => {
  if (props.steps.length) {
    return `${Math.floor((1 / props.steps.length) * 100)}%`
  }
  return '0'
})
</script>
<style scoped lang="scss">
.task-step {
  display: flex;
  align-items: center;
}
.task-step-item {
  display: flex;
  align-items: center;
  flex: 1;
  &:last-child {
    flex-basis: auto !important;
    flex-shrink: 0;
    flex-grow: 0;
    & > .line {
      display: none;
    }
  }
  .icon {
    margin-right: 6px;
  }
  .green {
    color: #01f304;
  }
  .red {
    color: #c25915;
  }
  .gray {
    color: #7d7f83;
  }
}
.text {
  flex-shrink: 0;
}
.line {
  display: inline-block;
  height: 1px;
  background-color: #fff;
  width: 100%;
}
</style>