车间能级提升-智能设备管理系统
zhuguifei
2025-04-21 0f5bd3db43a7ea07d3373b38bd24126544de5e2f
完成设备点检
已添加14个文件
已修改11个文件
1350 ■■■■■ 文件已修改
eims-ui-mobile/.eslintrc-auto-import.json 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages.json 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/equ/equ-list.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/home/index.vue 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-add.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-record.vue 298 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-st.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/login/index.vue 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/equ.d.ts 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/equ.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/inspect.d.ts 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/inspect.ts 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/ico/ico-ok.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/images/camera.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/menu/menu0.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/menu/menu1.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/menu/menu2.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/menu/menu3.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/menu/menu4.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/menu/menu5.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/style/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/auto-import.d.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/uni-pages.d.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/http.ts 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/.eslintrc-auto-import.json
@@ -3,11 +3,14 @@
    "Component": true,
    "ComponentPublicInstance": true,
    "ComputedRef": true,
    "DirectiveBinding": true,
    "EffectScope": true,
    "ExtractDefaultPropTypes": true,
    "ExtractPropTypes": true,
    "ExtractPublicPropTypes": true,
    "InjectionKey": true,
    "MaybeRef": true,
    "MaybeRefOrGetter": true,
    "PropType": true,
    "Ref": true,
    "VNode": true,
@@ -65,6 +68,7 @@
    "onUnload": true,
    "onUnmounted": true,
    "onUpdated": true,
    "onWatcherCleanup": true,
    "provide": true,
    "reactive": true,
    "readonly": true,
@@ -82,20 +86,15 @@
    "useAttrs": true,
    "useCssModule": true,
    "useCssVars": true,
    "useId": true,
    "useModel": true,
    "useRequest": true,
    "useSlots": true,
    "useTemplateRef": true,
    "useUpload": true,
    "useUpload2": true,
    "watch": true,
    "watchEffect": true,
    "watchPostEffect": true,
    "watchSyncEffect": true,
    "DirectiveBinding": true,
    "MaybeRef": true,
    "MaybeRefOrGetter": true,
    "onWatcherCleanup": true,
    "useId": true,
    "useModel": true,
    "useTemplateRef": true
    "watchSyncEffect": true
  }
}
eims-ui-mobile/env/.env
@@ -8,7 +8,7 @@
VITE_APP_PUBLIC_BASE=/
VITE_SERVER_BASEURL = 'http://192.168.12.33:8080'
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
VITE_UPLOAD_BASEURL = 'http://192.168.12.33:8080/resource/oss/upload'
# æœ‰äº›åŒå­¦å¯èƒ½éœ€è¦åœ¨å¾®ä¿¡å°ç¨‹åºé‡Œé¢æ ¹æ® develop、trial、release åˆ†åˆ«è®¾ç½®ä¸Šä¼ åœ°å€ï¼Œå‚考代码如下。
# ä¸‹é¢çš„变量如果没有设置,会默认使用 VITE_SERVER_BASEURL or VITE_UPLOAD_BASEURL
eims-ui-mobile/src/pages.json
@@ -64,6 +64,14 @@
      }
    },
    {
      "path": "pages/equ/equ-list",
      "type": "page",
      "layout": "default",
      "style": {
        "navigationBarTitleText": "设备列表"
      }
    },
    {
      "path": "pages/equ/index",
      "type": "page",
      "layout": "tabbar",
@@ -73,6 +81,31 @@
      }
    },
    {
      "path": "pages/inspect/insp-add",
      "type": "page",
      "layout": "default",
      "style": {
        "navigationBarTitleText": "设备点检"
      }
    },
    {
      "path": "pages/inspect/insp-record",
      "type": "page",
      "style": {
        "navigationBarTitleText": "点检记录",
        "navigationStyle": "custom",
        "navigationBarBackgroundColor": "#4D80F0"
      }
    },
    {
      "path": "pages/inspect/insp-st",
      "type": "page",
      "layout": "default",
      "style": {
        "navigationBarTitleText": "点检汇总"
      }
    },
    {
      "path": "pages/login/index",
      "type": "page",
      "layout": "default",
eims-ui-mobile/src/pages/equ/equ-list.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,149 @@
<route lang="json5" type="page">
{
  layout: 'default',
  style: {
    navigationBarTitleText: '设备列表',
  },
}
</route>
<template>
  <z-paging ref="paging" v-model="dataList" @query="queryList" show-refresher-update-time>
    <template #top>
      <wd-drop-menu>
        <wd-drop-menu-item v-model="equTypeId" :options="typeList" @change="handleEquType" />
        <wd-drop-menu-item v-model="status" :options="statusList" @change="handleEquStatu" />
      </wd-drop-menu>
    </template>
    <view class="bg-base">
      <wd-card type="rectangle" v-for="(item, index) in dataList" :key="item.id">
        <template #title>
          <view class="flex justify-between items-baseline">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-xs">{{ item.assetNo }}</text>
            </view>
            <view>
              <text class="icon-color-base">详情</text>
              <wd-icon name="arrow-right" custom-class="icon-color-base"></wd-icon>
            </view>
          </view>
        </template>
        <view class="flex h-[80rpx]" @click.stop="itemClick(item)">
          <image class="slot-img text-center" src="/static/images/camera.png" />
          <view>
            <view class="text-color-base">
              {{ item.equName }}
              <text class="text-color-gray ml-2 text-mini">{{ item.modelNo }}</text>
            </view>
            <view class="text-color-gray text-xs mt-1">
              {{ item.location }} | {{ item.madeIn }}
            </view>
          </view>
        </view>
      </wd-card>
    </view>
  </z-paging>
</template>
<script setup lang="ts">
import { onMounted, getCurrentInstance, ref } from 'vue'
import { getEquList } from '@/service/equ'
// è®¾å¤‡ç±»åž‹
const equTypeId = ref<number>(0)
// è®¾å¤‡çŠ¶æ€
const status = ref<number>(0)
const isSelectEqu = ref(false)
const typeList = ref<Record<string, any>[]>([{ label: '所有设备', value: 0 }])
const statusList = ref<Record<string, any>[]>([{ label: '所有状态', value: 0 }])
function handleEquType({ value }) {
  console.log(value)
}
function handleEquStatu({ value }) {
  console.log(value)
}
const paging = ref(null)
const dataList = ref([])
const queryList = (pageNum?: number, pageSize?: number) => {
  // è¿™é‡Œçš„pageNo和pageSize会自动计算好,直接传给服务器即可
  // è¿™é‡Œçš„请求只是演示,请替换成自己的项目的网络请求,并在网络请求回调中通过paging.value.complete(请求回来的数组)将请求结果传给z-paging
  getEquList({ pageNum, pageSize })
    .then((res: any) => {
      // è¯·å‹¿åœ¨ç½‘络请求回调中给dataList赋值!!只需要调用complete就可以了
      console.log(res)
      paging.value.complete(res.rows)
    })
    .catch((res) => {
      // å¦‚果请求失败写paging.value.complete(false),会自动展示错误页面
      // æ³¨æ„ï¼Œæ¯æ¬¡éƒ½éœ€è¦åœ¨catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
      // åœ¨åº•层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
      paging.value.complete(false)
    })
}
/**
 * è®¾å¤‡æ¡ç›®ç‚¹å‡»äº‹ä»¶
 * @param item
 */
function itemClick(item: any) {
  if (isSelectEqu.value) {
    emitSelectEqu(item)
    uni.navigateBack()
  }
}
/**
 * é€‰æ‹©è®¾å¤‡å›žè°ƒ
 * @param equ
 */
function emitSelectEqu(equ: any) {
  eventChannel.value.emit('selectEqu', {
    data: equ,
  })
}
const eventChannel = ref<any>()
onMounted(() => {
  const instance: any = getCurrentInstance().proxy
  const event = instance.getOpenerEventChannel()
  eventChannel.value = event
  event.on('handleSelectEqu', function (data) {
    isSelectEqu.value = true
    console.log('handleSelectEqu', data)
  })
})
</script>
<style scoped lang="scss">
.menu-title-box {
  height: 30rpx;
  line-height: 30rpx;
}
.slot-img {
  width: 72rpx;
  height: 72rpx;
  margin-right: 24rpx;
}
.text-mini {
  font-size: 22rpx;
}
.menu-indicator {
  width: 6rpx;
  height: 22rpx;
  border-radius: 10rpx;
  background-color: $uni-color-primary;
}
:deep(.wd-card__footer) {
  padding: 10rpx !important;
}
</style>
eims-ui-mobile/src/pages/home/index.vue
@@ -71,33 +71,18 @@
        <template #title>
          <view class="flex items-center menu-title-box">
            <view class="menu-indicator"></view>
            <view class="ml-1 text-xs">数据总览</view>
            <view class="ml-1 text-xs">设备管理</view>
          </view>
        </template>
        <wd-grid :column="4">
          <wd-grid-item use-slot class="flex justify-center items-center">
            <image class="slot-img text-center" src="/static/menu/menu2.png" />
            <text>12</text>
          </wd-grid-item>
          <wd-grid-item use-slot class="flex justify-center items-center">
            <image class="slot-img text-center" src="/static/menu/menu2.png" />
            <text>12</text>
          </wd-grid-item>
          <wd-grid-item use-slot class="flex justify-center items-center">
            <image class="slot-img text-center" src="/static/menu/menu2.png" />
            <text>12</text>
          </wd-grid-item>
          <wd-grid-item use-slot is-dot class="flex justify-center items-center">
            <image class="slot-img text-center" src="/static/menu/menu2.png" />
            <text>12</text>
          </wd-grid-item>
          <wd-grid-item use-slot class="flex justify-center items-center">
            <image class="slot-img text-center" src="/static/menu/menu2.png" />
            <text>12</text>
          </wd-grid-item>
          <wd-grid-item use-slot class="flex justify-center items-center">
            <image class="slot-img text-center" src="/static/menu/menu2.png" />
            <text>12</text>
          <wd-grid-item
            use-slot
            class="flex justify-center items-center"
            v-for="(item, index) in equMenu"
            @click.stop="goItemPage(item.path)"
          >
            <image class="slot-img text-center" :src="item.icon" />
            <text>{{ item.name }}</text>
          </wd-grid-item>
        </wd-grid>
      </wd-card>
@@ -108,17 +93,18 @@
        <template #title>
          <view class="flex items-center menu-title-box">
            <view class="menu-indicator"></view>
            <view class="ml-1 text-xs">数据总览</view>
            <view class="ml-1 text-xs">设备点检</view>
          </view>
        </template>
        <wd-grid :column="4">
          <wd-grid-item
            use-slot
            class="flex justify-center items-center"
            v-for="(item, index) in menuList"
            v-for="(item, index) in inspectMenu"
            :key="item.id"
            @click.stop="goItemPage(item.path)"
          >
            <image class="slot-img text-center" :src="item.url" />
            <image class="slot-img text-center" :src="item.icon" />
            <text>{{ item.name }}</text>
          </wd-grid-item>
        </wd-grid>
@@ -142,28 +128,62 @@
  console.error(menuList)
}
const goItemPage = (path: string) => {
  const url = `/${path}`
  uni.navigateTo({
    url,
  })
}
onLoad(() => {
  // getAllMenus()
})
const menuList = reactive([
const equMenu = reactive([
  {
    id: 1,
    name: '测试',
    url: '/static/menu/menu1.png',
    name: '设备管理',
    icon: '/static/menu/menu1.png',
    path: 'pages/equ/equ-list',
  }
])
const inspectMenu = reactive([
  /*  {
    id: 0,
    name: '设备点检',
    icon: '/static/menu/menu0.png',
    path: 'pages/inspect/insp-add',
  }, */
  {
    id: 1,
    name: '点检概览',
    icon: '/static/menu/menu1.png',
    path: 'pages/inspect/insp-st',
  },
  {
    id: 2,
    name: '测试',
    url: '/static/menu/menu1.png',
    name: '点检汇总',
    icon: '/static/menu/menu2.png',
    path: 'pages/inspect/insp-st',
  },
  {
    id: 3,
    name: '点检记录',
    icon: '/static/menu/menu3.png',
    path: 'pages/inspect/insp-record',
  },
  {
    id: 4,
    name: '点检计划',
    icon: '/static/menu/menu4.png',
    path: 'pages/inspect/insp-add',
  },
])
function handleUserInfo() {
  getAllMenus()
}
</script>
<style lang="scss" scoped>
@@ -201,6 +221,6 @@
.slot-img {
  width: 72rpx;
  height: 72rpx;
  border-radius: 8rpx;
  margin-left: 4rpx;
}
</style>
eims-ui-mobile/src/pages/inspect/insp-add.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,222 @@
<route lang="json5" type="page">
{
  layout: 'default',
  style: {
    navigationBarTitleText: '设备点检',
  },
}
</route>
<template>
  <view class="bg-base">
    <wd-form ref="form" :model="model" :rules="rules">
      <wd-cell-group custom-class="group" title="设备信息" border>
        <wd-input
          @click.stop="handleSelectEqu"
          label="设备名称"
          label-width="200rpx"
          prop="equId"
          readonly
          required
          suffix-icon="arrow-right"
          clearable
          v-model="model.equId"
          placeholder="请选择设备"
          @clicksuffixicon="handleSelectEqu"
        />
        <!--        <wd-picker
          label="设备名称"
          placeholder="请选设备"
          label-width="200rpx"
          prop="equId"
          v-model="model.equId"
          :columns="equList"
        />-->
      </wd-cell-group>
      <wd-cell-group custom-class="mt-2" title="点检信息" border>
        <wd-picker
          label="点检项"
          placeholder="请选点检项"
          label-width="200rpx"
          prop="equId"
          v-model="model.inspName"
          :columns="equList"
        />
        <wd-input
          label="点检编号"
          placeholder="请生成点检编号"
          label-width="200rpx"
          v-model="model.inspCode"
          prop="inspCode"
        >
          <template #suffix>
            <wd-button size="small" @click.stop="handleInspCode">生成</wd-button>
          </template>
        </wd-input>
        <wd-textarea
          label="点检描述"
          label-width="200rpx"
          type="textarea"
          v-model="model.inspDesc"
          auto-height
          :maxlength="200"
          show-word-limit
          placeholder="请输入点检描述"
          clearable
          prop="inspDesc"
        />
        <wd-cell title="点检图片" title-width="200rpx" prop="fileList">
          <wd-upload
            :auto-upload="false"
            :file-list="model.fileList"
            :action="VITE_UPLOAD_BASEURL"
            @change="handleFileChange"
          ></wd-upload>
        </wd-cell>
        <wd-calendar
          label="计划日期"
          label-width="200rpx"
          placeholder="请选择日期"
          prop="planTime"
          v-model="model.planTime"
        />
        <wd-datetime-picker
          label="点检时间"
          label-width="200rpx"
          placeholder="请选择时间"
          prop="inspTime"
          v-model="model.inspTime"
        />
      </wd-cell-group>
      <view class="footer">
        <wd-button type="primary" size="large" @click="handleSubmit" block>提交</wd-button>
      </view>
    </wd-form>
  </view>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { FormRules } from 'wot-design-uni/components/wd-form/types'
import { getEnvBaseUploadUrl } from '@/utils'
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
const form = ref()
const model = reactive<{
  equId: string
  inspName: string
  inspCode: string
  inspDesc: string
  planTime: null | number
  inspTime: number | string
  fileList: []
}>({
  equId: '',
  inspName: '',
  inspCode: '',
  inspDesc: '',
  planTime: null,
  inspTime: '',
  fileList: [],
})
const rules: FormRules = {
  equId: [
    {
      required: true,
      message: '请选择设备',
    },
  ],
  inspName: [
    {
      required: true,
      message: '请选择点检项',
    },
  ],
  inspCode: [
    {
      required: true,
      message: '请输入点检编码',
    },
  ],
  inspDesc: [
    {
      required: true,
      message: '请输入点检描述',
    },
  ],
  fileList: [
    {
      required: false,
      message: '',
    },
  ],
}
const equList = ref<any[]>([
  {
    value: '1',
    label: '1#干燥机',
  },
  {
    value: '2',
    label: '1#干燥机',
  },
])
/**
 * é€‰æ‹©è®¾å¤‡
 */
function handleSelectEqu() {
  uni.navigateTo({
    url: '/pages/equ/equ-list',
    events: {
      // ä¸ºæŒ‡å®šäº‹ä»¶æ·»åŠ ä¸€ä¸ªç›‘å¬å™¨ï¼ŒèŽ·å–è¢«æ‰“å¼€é¡µé¢ä¼ é€åˆ°å½“å‰é¡µé¢çš„æ•°æ®
      selectEqu: function (data) {
        console.log(data)
      },
    },
    success: function (res) {
      // é€šè¿‡eventChannel向被打开页面传送数据
      res.eventChannel.emit('handleSelectEqu', { data: '点检页面选择设备' })
    },
  })
}
/**
 * ç”Ÿæˆç‚¹æ£€ç¼–号
 */
function handleInspCode() {
  console.error('生成点检编码')
}
/**
 * ä¸Šä¼ ç‚¹æ£€å›¾ç‰‡
 * @param fileList
 */
function handleFileChange({ fileList }) {
  model.fileList = fileList
}
function handleSubmit() {
  form.value
    .validate()
    .then(({ valid, errors }) => {
      console.log(valid)
      console.log(errors)
    })
    .catch((error) => {
      console.log(error, 'error')
    })
}
</script>
<style scoped lang="scss">
.footer {
  background: white;
  padding: 60rpx 42rpx;
}
</style>
eims-ui-mobile/src/pages/inspect/insp-record.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,298 @@
<route lang="json5">
{
  style: {
    navigationBarTitleText: '点检记录',
    navigationStyle: 'custom',
    navigationBarBackgroundColor: '#4D80F0',
  },
}
</route>
<template>
  <z-paging ref="paging" v-model="dataList" @query="queryList" show-refresher-update-time>
    <template #top>
      <wd-navbar
        title="点检记录"
        left-arrow
        @click-left="goBack"
        right-text="提交"
        @click-right="handleClickRight"
        custom-style="background: #4D80F0;"
        safeAreaInsetTop
      ></wd-navbar>
      <wd-card type="rectangle">
        <template #title>
          <view class="flex justify-between">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <view class="ml-1 text-sm align-center">{{ inspSt.equName }}</view>
              <view class="text-color-gray ml-2 text-mini">{{ inspSt.assetNo }}</view>
            </view>
            <view class="flex items-center">
              <text class="text-color-gray text-mini">{{ inspSt.planTimeStr }}</text>
            </view>
          </view>
        </template>
        <view class="flex h-[100rpx]" items-center>
          <image class="slot-img text-center" src="/static/images/camera.png" />
          <view class="flex-1">
            <view class="text-color-gray text-xs mt-1 flex">
              <text class="mr-3">点检总数: {{ dataCount }}</text>
              |
              <text class="mx-3">已点检: {{ checkCount }}</text>
              |
              <text class="ml-3">未点检: {{ dataCount - checkCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text class="mr-3">正常: {{ normalNum }}</text>
              |
              <text class="mx-3">异常: {{ abNormalNum }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text>状态:</text>
              <template v-if="dataCount > 0 && dataCount === checkCount">
                <wd-icon class="icon-color-success" name="check-outline" size="40rpx"></wd-icon>
                <text class="ml-1">已完成</text>
              </template>
              <template v-else>
                <wd-icon class="icon-color-base" name="detection" size="40rpx"></wd-icon>
                <text class="ml-1">进行中</text>
              </template>
            </view>
          </view>
        </view>
      </wd-card>
    </template>
    <view class="bg-base">
      <view class="w-full h-[24rpx]"></view>
      <wd-cell>
        <template #title>
          <text class="text-color-gray">点检项</text>
        </template>
        <wd-button size="small" type="text" @click.stop="toggleCollapse">
          {{ isAllExpanded ? '全部折叠' : '全部展开' }}
        </wd-button>
      </wd-cell>
      <wd-collapse v-model="collSelects" title="点检项" ref="collapseRef">
        <wd-collapse-item :name="item.id" v-for="(item, index) in dataList">
          <template #title="{ expanded, disabled, isFirst }">
            <view class="flex justify-between">
              <view class="flex justify-center items-center">
                <text class="text-sm">{{ item.inspName }}</text>
              </view>
              <view class="flex items-center">
                <wd-radio-group
                  v-model="item.inspResult"
                  inline
                  shape="dot"
                  @change="inspResultClick(item)"
                >
                  <wd-radio value="1">正常</wd-radio>
                  <wd-radio value="2">异常</wd-radio>
                </wd-radio-group>
                <wd-icon
                  v-if="expanded"
                  name="arrow-up"
                  size="30rpx"
                  class="icon-color-gray"
                ></wd-icon>
                <wd-icon v-else name="arrow-down" size="30rpx" class="icon-color-gray"></wd-icon>
              </view>
            </view>
          </template>
          <view class="text-color-gray text-xs flex justify-between">
            <text class="mr-3">点检人: {{ item.inspUserName }}</text>
            <text class="mx-3">点检时间: {{ item.inspTime }}</text>
          </view>
        </wd-collapse-item>
      </wd-collapse>
    </view>
  </z-paging>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useUserStore, useAccessStore, useSystemConfigStore } from '@/store'
import { getInspStRecordList, updateInspRecord, updateInspRecordBatch } from '@/service/inspect'
import { useToast, useMessage } from 'wot-design-uni'
import type { CollapseInstance } from 'wot-design-uni/components/wd-collapse/types'
const message = useMessage()
const toast = useToast()
const collapseRef = ref<CollapseInstance>()
const isAllExpanded = ref(false)
// å®šä¹‰æŽ¥å£
interface QueryParams {
  pageNum: number
  pageSize: number
  inspCode: string
  status?: string
}
interface InspSt {
  inspCode: string
  equName: string
  assetNo: string
  planTimeStr?: string
}
const dataChange = ref(false)
const userStore = useUserStore()
const collSelects = ref<string[]>([])
// ç‚¹æ£€æ±‡æ€»æ•°æ®(上个页面传值)
const inspSt = reactive<InspSt>({
  inspCode: '',
  equName: '',
  assetNo: '',
  planTimeStr: '',
})
const paging = ref(null)
const dataList = ref([])
const queryList = (pageNum?: number, pageSize?: number) => {
  // è¿™é‡Œçš„pageNo和pageSize会自动计算好,直接传给服务器即可
  // è¿™é‡Œçš„请求只是演示,请替换成自己的项目的网络请求,并在网络请求回调中通过paging.value.complete(请求回来的数组)将请求结果传给z-paging
  const params: QueryParams = {
    pageNum,
    pageSize,
    inspCode: inspSt.inspCode,
  }
  getInspStRecordList(params)
    .then((res: any) => {
      // è¯·å‹¿åœ¨ç½‘络请求回调中给dataList赋值!!只需要调用complete就可以了
      paging.value.complete(res.rows)
    })
    .catch((res) => {
      // å¦‚果请求失败写paging.value.complete(false),会自动展示错误页面
      // æ³¨æ„ï¼Œæ¯æ¬¡éƒ½éœ€è¦åœ¨catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
      // åœ¨åº•层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
      paging.value.complete(false)
    })
}
function inspResultClick(item: any) {
  // userStore?.userInfo?.realName
}
const goBack = () => {
  uni.navigateBack()
}
function handleClickRight() {
  handleConfirm()
}
const toggleCollapse = () => {
  isAllExpanded.value = !isAllExpanded.value
  collapseRef.value.toggleAll(isAllExpanded.value)
}
function handleConfirm() {
  if (!dataChange.value) {
    message.alert('请操作后提交!')
    return false
  }
  message
    .confirm({
      msg: '确定提交?',
      title: '提示',
      beforeConfirm: ({ resolve }) => {
        updateData(resolve)
      },
    })
    .then(() => {})
    .catch((error) => {
      console.log(error)
    })
}
function updateData(resolve: any) {
  const params = {
    inspRecordList: dataList.value,
  }
  updateInspRecordBatch(params)
    .then((res: any) => {
      paging.value.reload()
      resolve(true)
    })
    .catch((res) => {
      console.error(res)
    })
}
/**
 * ç‚¹æ£€è®°å½•条目点击事件
 * @param item
 */
function itemClick(item: any) {}
watch(
  () => [...dataList.value], // ä½¿ç”¨æ‰©å±•运算符创建新数组以触发监听
  (newVal, oldVal) => {
    if (oldVal.length > 0) {
      dataChange.value = true
    }
  },
  { deep: true },
)
onLoad((options) => {
  inspSt.inspCode = options.inspCode
  inspSt.equName = options.equName
  inspSt.assetNo = options.assetNo
  inspSt.planTimeStr = options.planTimeStr
})
const dataCount = computed(() => dataList.value.length)
const checkCount = computed(() => dataList.value.filter((item) => item.status === '1').length)
const normalNum = computed(() => dataList.value.filter((item) => item.inspResult === '1').length)
const abNormalNum = computed(() => dataList.value.filter((item) => item.inspResult === '2').length)
</script>
<style scoped lang="scss">
.menu-title-box {
  height: 30rpx;
  line-height: 30rpx;
}
.slot-img {
  width: 72rpx;
  height: 72rpx;
  margin-right: 24rpx;
}
.text-mini {
  font-size: 22rpx;
}
.menu-indicator {
  width: 6rpx;
  height: 22rpx;
  border-radius: 10rpx;
  background-color: $uni-color-primary;
}
:deep(.wd-card__footer) {
  padding: 10rpx !important;
}
.col-title-box {
}
.bg-primary {
  background: $uni-color-primary;
}
:deep(.wd-navbar__text) {
  font-size: 26rpx;
  color: white;
}
:deep(.wd-icon-arrow-left:before),
:deep(.wd-navbar__title) {
  color: white;
  font-weight: 0;
}
</style>
eims-ui-mobile/src/pages/inspect/insp-st.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,149 @@
<route lang="json5" type="page">
{
  layout: 'default',
  style: {
    navigationBarTitleText: '点检汇总',
  },
}
</route>
<template>
  <z-paging ref="paging" v-model="dataList" @query="queryList" show-refresher-update-time>
    <template #top>
      <wd-drop-menu>
        <wd-drop-menu-item v-model="viewMode" :options="viewModeList" @change="handleViewMode" />
        <wd-drop-menu-item v-model="equName" :options="equList" @change="handleEquName" />
      </wd-drop-menu>
      <wd-divider></wd-divider>
    </template>
    <view class="bg-base">
      <view class="w-full h-[24rpx]"></view>
      <wd-card type="rectangle" v-for="(item, index) in dataList" :key="item.id">
        <template #title>
          <view class="flex justify-between">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <view class="ml-1 text-sm align-center">{{ item.equName }}</view>
              <view class="text-color-gray ml-2 text-mini">{{ item.assetNo }}</view>
            </view>
            <view class="flex items-center">
              <text class="text-color-gray text-mini">{{ item.planTimeStr }}</text>
            </view>
          </view>
        </template>
        <view class="flex h-[100rpx]" items-center>
          <image class="slot-img text-center" src="/static/images/camera.png" />
          <view class="flex-1">
            <view class="text-color-gray text-xs mt-1 flex">
              <text class="mr-3">点检总数: {{ item.recordCount }}</text>
              |
              <text class="mx-3">已点检: {{ item.checkCount }}</text>
              |
              <text class="ml-3">未点检: {{ item.unCheckCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text class="mr-3">正常: {{ item.normalNum }}</text>
              |
              <text class="mx-3">异常: {{ item.abNormalNum }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text>状态:</text>
              <template v-if="item.recordCount === item.checkCount">
                <wd-icon class="icon-color-success" name="check-outline" size="40rpx"></wd-icon>
                <text class="ml-1">已完成</text>
              </template>
              <template v-else>
                <wd-icon class="icon-color-base" name="detection" size="40rpx"></wd-icon>
                <text class="ml-1">进行中</text>
              </template>
            </view>
          </view>
          <wd-button size="small" icon="edit-outline" @click.stop="itemClick(item)">明细</wd-button>
        </view>
      </wd-card>
    </view>
  </z-paging>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { getInspStList } from '@/service/inspect'
const viewMode = ref<string>('Day')
const equName = ref<string>('所有设备')
const viewModeList = ref<Record<string, any>[]>([
  { label: '日视图', value: 'Day' },
  { label: '月视图', value: 'Month' },
])
const equList = ref<Record<string, any>[]>([{ label: '所有设备', value: '所有设备' }])
function handleViewMode({ value }) {
  console.log(value)
}
function handleEquName({ value }) {
  console.log(value)
}
const paging = ref(null)
const dataList = ref([])
const queryList = (pageNum?: number, pageSize?: number) => {
  // è¿™é‡Œçš„pageNo和pageSize会自动计算好,直接传给服务器即可
  // è¿™é‡Œçš„请求只是演示,请替换成自己的项目的网络请求,并在网络请求回调中通过paging.value.complete(请求回来的数组)将请求结果传给z-paging
  getInspStList({ pageNum, pageSize })
    .then((res: any) => {
      // è¯·å‹¿åœ¨ç½‘络请求回调中给dataList赋值!!只需要调用complete就可以了
      console.log(res)
      paging.value.complete(res.rows)
    })
    .catch((res) => {
      // å¦‚果请求失败写paging.value.complete(false),会自动展示错误页面
      // æ³¨æ„ï¼Œæ¯æ¬¡éƒ½éœ€è¦åœ¨catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
      // åœ¨åº•层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
      paging.value.complete(false)
    })
}
function itemClick(item) {
  const inspCode = `${item.equId}_${item.planTime}_${viewMode.value}`
  uni.navigateTo({
    url: `/pages/inspect/insp-record?inspCode=${inspCode}&equName=${item.equName}&assetNo=${item.assetNo}&planTimeStr=${item.planTimeStr}`,
  })
}
onLoad(() => {
  queryList()
})
</script>
<style scoped lang="scss">
.menu-title-box {
}
.slot-img {
  width: 72rpx;
  height: 72rpx;
  margin-right: 24rpx;
}
.statu-img {
  width: 60rpx;
  height: 40rpx;
}
.text-mini {
  font-size: 22rpx;
}
.menu-indicator {
  width: 6rpx;
  height: 26rpx;
  border-radius: 10rpx;
  background-color: $uni-color-primary;
}
:deep(.wd-card__footer) {
  padding: 10rpx !important;
}
:deep(.wd-card__title-content) {
  padding: 24rpx 0 !important;
}
</style>
eims-ui-mobile/src/pages/login/index.vue
@@ -32,14 +32,21 @@
      />
    </wd-cell-group>
    <view class="footer">
      <view class="w-full text-end">
        <wd-checkbox v-model="rember" shape="square" @change="handleChange">
          è®°ä½å¯†ç 
      <view>
        <wd-checkbox v-model="rember" @change="handleChange">
          <text class="rember-text">记住密码</text>
        </wd-checkbox>
      </view>
      <wd-button class="mt-6" type="primary" size="large" @click="handleSubmit" block>
        æäº¤
      </wd-button>
      <view class="copyright-info">
        <text>
          ä¸Šæµ·å…°å®ä¼ æ„Ÿç§‘技股份有限公司
        </text>
      </view>
    </view>
  </wd-form>
</template>
@@ -128,6 +135,20 @@
<style scoped lang="scss">
.footer {
  padding: 12px;
  padding: 24rpx;
}
.rember-text {
  font-size: 24rpx;
  color: $uni-text-color-grey;
}
.copyright-info {
  position: absolute;
  bottom: 20rpx;
  width: 100%;
  display: flex;
  justify-content: center;
  color: $uni-text-color-disable;
  font-size: 24rpx;
}
</style>
eims-ui-mobile/src/service/equ.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,124 @@
export interface EquVO {
  /**
   *
   */
  equId: number | string;
  /**
   * èµ„产编号
   */
  assetNo: string;
  /**
   * è®¾å¤‡ç¼–码
   */
  equCode: string;
  /**
   * è®¾å¤‡åç§°
   */
  equName: string;
  /**
   * è®¾å¤‡ç±»åž‹
   */
  equTypeName: string;
  /**
   * åž‹å·
   */
  modelNo: string;
  /**
   * åˆ¶é€ å•†
   */
  madeIn: string;
  /**
   * é¢åº¦åŠŸçŽ‡
   */
  ratedPower: number;
  /**
   * é“­ç‰Œä¿¡æ¯
   */
  plateInfo: string;
  /**
   * é‡‡è´­æ—¥æœŸ
   */
  purchaseDate: string;
  /**
   * çŠ¶æ€
   */
  status: string;
  /**
   * æ‰€åœ¨åœºæ‰€
   */
  location: string;
  /**
   * ä½¿ç”¨éƒ¨é—¨ï¼ˆå…³è”id)
   */
  deptUsed: number;
  /**
   * ä½¿ç”¨éƒ¨é—¨åç§°
   */
  deptName: number;
  /**
   * è´£ä»»äºº(关联id)
   */
  respPerson: number;
  /**
   * è´£ä»»äºº
   */
  respPersonName: string;
  /**
   * è”系电话
   */
  contactPhone: string;
  /**
   * æ­£å¼ä½¿ç”¨æ—¥æœŸ
   */
  deployDate: string;
  /**
   * å¼€å§‹è¯•用日期
   */
  trialDate: string;
  /**
   * è®¡åˆ’验收日期
   */
  planAcceptDate: string;
  /**
   * å®žé™…验收日期
   */
  actualAcceptDate: string;
  /**
   * å¯¼å…¥çŠ¶æ€ï¼ˆå­—å…¸ï¼‰
   */
  importStatus: number;
  /**
   * ç›˜ç‚¹æ ‡å¿—
   */
  inventoryFlag: number;
  /**
   * ä¸Šæ¬¡ç›˜ç‚¹æ—¥æœŸ
   */
  inventoryDate: string;
  /**
   * ä½¿ç”¨å¹´é™
   */
  serviceLife: number;
}
eims-ui-mobile/src/service/equ.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
import { http } from '@/utils/http'
import type { EquVO } from './equ.d'
/**
 * è®¾å¤‡åˆ—表
 */
export const getEquList = (params: any) => {
  return http.get<EquVO[]>('/eims/equ/list', params)
}
eims-ui-mobile/src/service/inspect.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,150 @@
/**
 * ç‚¹æ£€æ±‡æ€»
 */
export interface InspectStVO {
  /**
   *
   */
  id: number | string;
  /**
   *
   */
  title: string;
  /**
   *
   */
  equId: number | string;
  status: string;
  /**
   *
   */
  planTime: string;
  /**
   * ä¿å…»äºº
   */
  maintUser: number;
  /**
   * éªŒè¯äºº
   */
  verifyUser: number;
  verifyTime: string;
  /**
   * ç‰¹è®°äº‹é¡¹
   */
  specialNote: string;
  /**
   * å¤‡æ³¨
   */
  remark: string;
}
/**
 * ç‚¹æ£€è®°å½•
 */
export interface InspectRecordVO {
  /**
   * id
   */
  id: string | number;
  /**
   * è®¾å¤‡di
   */
  equId: string | number;
  /**
   * è®¾å¤‡åç§°
   */
  equName: string;
  /**
   * èµ„产编号
   */
  assteNo: string;
  /**
   * ç‚¹æ£€åç§°
   */
  inspName: string;
  /**
   * ç‚¹æ£€æè¿°
   */
  inspDesc: string;
  /**
   * çŠ¶æ€
   */
  status: string;
  /**
   * ç‚¹æ£€ç¼–码
   */
  inspCode: string;
  /**
   * å€¼è®°å½•方式(字典)
   */
  recordMode: string;
  /**
   * å‚考值
   */
  referenceValue: string;
  /**
   * ä¸Šé™
   */
  upperLimit: string;
  /**
   * ä¸‹é™
   */
  lowLimit: string;
  /**
   * æ£€æŸ¥å€¼
   */
  checkValue: string;
  /**
   * ç‚¹æ£€ç»“果(字典)
   */
  inspResult: string;
  /**
   * ç‚¹æ£€æ—¶é—´
   */
  inspTime: string;
  /**
   * è®¡åˆ’点检日期
   */
  planTime: string;
  /**
   * éªŒè¯äºº
   */
  verifyUser: number;
  /**
   * è®¡åˆ’id
   */
  planId: string | number;
  /**
   * å¤‡æ³¨
   */
  remark: string;
}
eims-ui-mobile/src/service/inspect.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
import { http } from '@/utils/http'
import type { InspectStVO, InspectRecordVO } from './inspect.d'
/**
 * ç‚¹æ£€æ±‡æ€»åˆ—表
 */
export const getInspStList = (params: any) => {
  params.viewMode = 'Day'
  return http.get<InspectStVO[]>('/eims/inspectSt/list', params)
}
/**
 * ç‚¹æ£€æ±‡æ€»åˆ—表下点检记录列表
 */
export const getInspStRecordList = (params: any) => {
  return http.get<InspectRecordVO[]>('/eims/inspRecord/stList', params)
}
/**
 * æ›´æ–°ç‚¹æ£€è®°å½•
 * @param data
 */
export const updateInspRecord = (data: any) => {
  return http.post<void>('/eims/inspRecord', data)
}
export const updateInspRecordBatch = (data: any) => {
  return http.put<void>('/eims/inspRecord/editBatch', data)
}
eims-ui-mobile/src/static/ico/ico-ok.png
eims-ui-mobile/src/static/images/camera.png
eims-ui-mobile/src/static/menu/menu0.png
eims-ui-mobile/src/static/menu/menu1.png

eims-ui-mobile/src/static/menu/menu2.png

eims-ui-mobile/src/static/menu/menu3.png
eims-ui-mobile/src/static/menu/menu4.png
eims-ui-mobile/src/static/menu/menu5.png
eims-ui-mobile/src/style/index.scss
@@ -29,10 +29,20 @@
.icon-color-base {
  color: $uni-color-primary;
}
.icon-color-gray {
  color: $uni-text-color-grey;
}
.icon-color-success {
  color: $uni-color-success;
}
.bg-base {
  background: $uni-bg-color-grey;
}
.bg-primary {
  background: $uni-color-primary;
}
eims-ui-mobile/src/types/auto-import.d.ts
@@ -79,12 +79,10 @@
  const useCssVars: typeof import('vue')['useCssVars']
  const useId: typeof import('vue')['useId']
  const useModel: typeof import('vue')['useModel']
  const useNavbarWeixin: (typeof import('../hooks/useNavbarWeixin'))['default']
  const useRequest: typeof import('../hooks/useRequest')['default']
  const useSlots: typeof import('vue')['useSlots']
  const useTemplateRef: typeof import('vue')['useTemplateRef']
  const useUpload: typeof import('../hooks/useUpload')['default']
  const useUpload2: typeof import('../hooks/useUpload2')['default']
  const watch: typeof import('vue')['watch']
  const watchEffect: typeof import('vue')['watchEffect']
  const watchPostEffect: typeof import('vue')['watchPostEffect']
eims-ui-mobile/src/types/uni-pages.d.ts
@@ -5,7 +5,11 @@
interface NavigateToOptions {
  url: "/pages/home/index" |
       "/pages/equ/equ-list" |
       "/pages/equ/index" |
       "/pages/inspect/insp-add" |
       "/pages/inspect/insp-record" |
       "/pages/inspect/insp-st" |
       "/pages/login/index" |
       "/pages/my/index" |
       "/pages/spare/index";
eims-ui-mobile/src/utils/http.ts
@@ -2,6 +2,9 @@
import { useUserStore, useAccessStore } from '@/store'
export const http = <T>(options: CustomRequestOptions) => {
  uni.showLoading({
    title: '加载中',
  })
  // 1. è¿”回 Promise å¯¹è±¡
  return new Promise<IResData<T>>((resolve, reject) => {
    uni.request({
@@ -12,11 +15,26 @@
      // #endif
      // å“åº”成功
      success(res) {
        uni.hideLoading()
        // çŠ¶æ€ç  2xx,参考 axios çš„设计
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 2.1 æå–核心数据 res.data
          if ((res.data as IResData<T>).code === 200) {
            resolve(((res.data as IResData<T>).data || res.data) as IResData<T>)
          } else if ((res.data as IResData<T>).code === 401) {
            uni.showToast({
              icon: 'none',
              title: '登录超时,请重新登录!',
            })
            // 401错误  -> æ¸…理用户信息,跳转到登录页
            useAccessStore().clearAccessInfo()
            useUserStore().clearUserInfo()
            const loginRoute = '/pages/login/index'
            // é‡æ–°ç™»å½•后返回页面
            const url = '/pages/home/index'
            const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
            uni.navigateTo({ url: redirectRoute })
            reject(res)
          } else {
            uni.showToast({
              icon: 'none',
@@ -46,6 +64,7 @@
          title: '网络错误,换个网络试试',
        })
        reject(err)
        uni.hideLoading()
      },
    })
  })
@@ -85,5 +104,19 @@
  })
}
export const httpPut = <T>(
  url: string,
  data?: Record<string, any>,
  query?: Record<string, any>,
) => {
  return http<T>({
    url,
    query,
    data,
    method: 'PUT',
  })
}
http.get = httpGet
http.post = httpPost
http.put = httpPut