车间能级提升-智能设备管理系统
baoshiwei
2025-04-24 89a5fedfe041ebacb2d81ecae1023b206cd3f353
Merge remote-tracking branch 'origin/main'
已添加37个文件
已修改34个文件
3241 ■■■■■ 文件已修改
eims-ui-mobile/.eslintrc-auto-import.json 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/pages.config.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/components/fg-tabbar/fg-tabbar.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/env.d.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/interceptors/request.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/interceptors/route.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages.json 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/equ/equ-list.vue 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/equ/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/home/index.vue 215 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-add.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-record.vue 336 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-st.vue 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/login/index.vue 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/maint/maint-order.vue 388 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/maint/maint-st.vue 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/maint/order-detail.vue 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/my/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/spare/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | 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 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/login.d.ts 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/login.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/maint.d.ts 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/maint.ts 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/menu.d.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/menu.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/ico/ico-ok.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/ico/ico-platform.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/images/camera.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/images/menu1.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/images/pic1.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/images/pic2.jpg 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/images/pic3.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/static/images/pic4.jpeg 补丁 | 查看 | 原始文档 | 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/store/access.ts 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/store/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/store/system-config.ts 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/store/user.ts 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/style/index.scss 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/auto-import.d.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/uni-pages.d.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/uni.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/DateUtils.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/RoleUtils.ts 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/http.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/vite.config.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/insp-st/data.tsx 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/insp-st/index.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/index.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsInspectRecordController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsInspectSt.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsInspectStBo.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/InspectRecordBo.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsInspectStVo.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsInspectRecordMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsInspectRecordService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsInspectRecordServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsInspectStServiceImpl.java 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsMaintOrderServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsInspectRecordMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsInspectStMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | 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
@@ -7,8 +7,8 @@
# h5部署网站的base,配置到 manifest.config.ts é‡Œçš„ h5.router.base
VITE_APP_PUBLIC_BASE=/
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
VITE_SERVER_BASEURL = 'http://192.168.12.33:8080'
VITE_UPLOAD_BASEURL = 'http://192.168.12.33:8080/resource/oss/upload'
# æœ‰äº›åŒå­¦å¯èƒ½éœ€è¦åœ¨å¾®ä¿¡å°ç¨‹åºé‡Œé¢æ ¹æ® develop、trial、release åˆ†åˆ«è®¾ç½®ä¸Šä¼ åœ°å€ï¼Œå‚考代码如下。
# ä¸‹é¢çš„变量如果没有设置,会默认使用 VITE_SERVER_BASEURL or VITE_UPLOAD_BASEURL
@@ -23,3 +23,7 @@
# h5是否需要配置代理
VITE_APP_PROXY=false
VITE_APP_PROXY_PREFIX = '/api'
# åŽç«¯å‚数配置
VITE_APP_CLIENT_ID = '428a8310cd442757ae699df5d894f051'
eims-ui-mobile/pages.config.ts
@@ -4,8 +4,8 @@
  globalStyle: {
    navigationStyle: 'default',
    navigationBarTitleText: '兰宝设备管理',
    navigationBarBackgroundColor: '#f8f8f8',
    navigationBarTextStyle: 'black',
    navigationBarBackgroundColor: '#4D80F0',
    navigationBarTextStyle: 'white',
    backgroundColor: '#FFFFFF',
  },
  easycom: {
@@ -20,7 +20,7 @@
  tabBar: {
    custom: true,
    color: '#999999',
    selectedColor: '#007aff',
    selectedColor: '#4D80F0',
    borderStyle: 'black',
    height: '50px',
    fontSize: '10px',
eims-ui-mobile/src/components/fg-tabbar/fg-tabbar.vue
@@ -6,7 +6,7 @@
    safeAreaInsetBottom
    placeholder
    @change="selectTabBar"
    active-color="#007aff"
    active-color="#4D80F0"
    inactive-color="#7d7e80"
  >
    <block v-for="(item, idx) in tabbarList" :key="item.path">
eims-ui-mobile/src/env.d.ts
@@ -23,6 +23,7 @@
  readonly VITE_UPLOAD_BASEURL: string
  /** æ˜¯å¦æ¸…除console */
  readonly VITE_DELETE_CONSOLE: string
  readonly VITE_APP_CLIENT_ID: string
  // æ›´å¤šçŽ¯å¢ƒå˜é‡...
}
eims-ui-mobile/src/interceptors/request.ts
@@ -1,8 +1,9 @@
/* eslint-disable no-param-reassign */
import qs from 'qs'
import { useUserStore } from '@/store'
import { useAccessStore } from '@/store'
import { platform } from '@/utils/platform'
import { getEnvBaseUrl } from '@/utils'
const clientId = import.meta.env.VITE_APP_CLIENT_ID
export type CustomRequestOptions = UniApp.RequestOptions & {
  query?: Record<string, any>
@@ -50,10 +51,11 @@
      ...options.header,
    }
    // 3. æ·»åŠ  token è¯·æ±‚头标识
    const userStore = useUserStore()
    const { token } = userStore.userInfo as unknown as IUserInfo
    const accessStore = useAccessStore()
    const token = accessStore.accessInfo.access_token
    if (token) {
      options.header.Authorization = `Bearer ${token}`
      options.header.clientid = clientId
    }
  },
}
eims-ui-mobile/src/interceptors/route.ts
@@ -4,15 +4,15 @@
 * å¯ä»¥è®¾ç½®è·¯ç”±ç™½åå•,或者黑名单,看业务需要选哪一个
 * æˆ‘这里应为大部分都可以随便进入,所以使用黑名单
 */
import { useUserStore } from '@/store'
import { useUserStore, useAccessStore } from '@/store'
import { needLoginPages as _needLoginPages, getNeedLoginPages } from '@/utils'
// TODO Check
const loginRoute = '/pages/login/index'
const isLogined = () => {
  const userStore = useUserStore()
  return userStore.isLogined
  const accessStore = useAccessStore()
  return accessStore.isLogined
}
const isDev = import.meta.env.DEV
eims-ui-mobile/src/pages.json
@@ -2,8 +2,8 @@
  "globalStyle": {
    "navigationStyle": "default",
    "navigationBarTitleText": "兰宝设备管理",
    "navigationBarBackgroundColor": "#f8f8f8",
    "navigationBarTextStyle": "black",
    "navigationBarBackgroundColor": "#4D80F0",
    "navigationBarTextStyle": "white",
    "backgroundColor": "#FFFFFF"
  },
  "easycom": {
@@ -16,7 +16,7 @@
  "tabBar": {
    "custom": true,
    "color": "#999999",
    "selectedColor": "#007aff",
    "selectedColor": "#4D80F0",
    "borderStyle": "black",
    "height": "50px",
    "fontSize": "10px",
@@ -60,16 +60,52 @@
      "type": "home",
      "layout": "tabbar",
      "style": {
        "navigationStyle": "custom",
        "navigationBarTitleText": "首页"
      }
    },
    {
      "path": "pages/equ/equ-list",
      "type": "page",
      "layout": "default",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "设备列表"
      }
    },
    {
      "path": "pages/equ/index",
      "type": "page",
      "layout": "tabbar",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "关于"
      }
    },
    {
      "path": "pages/inspect/insp-add",
      "type": "page",
      "layout": "default",
      "style": {
        "navigationBarTitleText": "设备点检"
      }
    },
    {
      "path": "pages/inspect/insp-record",
      "type": "page",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "点检记录",
        "navigationStyle": "custom",
        "navigationBarBackgroundColor": "#4D80F0"
      }
    },
    {
      "path": "pages/inspect/insp-st",
      "type": "page",
      "layout": "default",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "点检汇总"
      }
    },
    {
@@ -77,14 +113,43 @@
      "type": "page",
      "layout": "default",
      "style": {
        "navigationStyle": "custom",
        "navigationBarTitleText": "登录"
      }
    },
    {
      "path": "pages/maint/maint-order",
      "type": "page",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "保养工单",
        "navigationStyle": "custom",
        "navigationBarBackgroundColor": "#4D80F0"
      }
    },
    {
      "path": "pages/maint/maint-st",
      "type": "page",
      "layout": "default",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "保养汇总"
      }
    },
    {
      "path": "pages/maint/order-detail",
      "type": "page",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "工单明细",
        "navigationStyle": "custom",
        "navigationBarBackgroundColor": "#4D80F0"
      }
    },
    {
      "path": "pages/my/index",
      "type": "page",
      "layout": "tabbar",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "我的"
      }
@@ -93,6 +158,7 @@
      "path": "pages/spare/index",
      "type": "page",
      "layout": "tabbar",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "spare"
      }
eims-ui-mobile/src/pages/equ/equ-list.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,150 @@
<route lang="json5" type="page">
{
  layout: 'default',
  needLogin: true,
  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/equ/index.vue
@@ -1,6 +1,7 @@
<route lang="json5">
{
  layout: 'tabbar',
  needLogin: true,
  style: {
    navigationBarTitleText: '关于',
  },
eims-ui-mobile/src/pages/home/index.vue
@@ -3,37 +3,118 @@
{
  layout: 'tabbar',
  style: {
    navigationStyle: 'custom',
    navigationBarTitleText: '首页',
  },
}
</route>
<template>
  <view
    class="bg-white overflow-hidden pt-2 px-4"
    :style="{ marginTop: safeAreaInsets?.top + 'px' }"
    class="bg-base overflow-hidden"
    :style="{
      marginTop: safeAreaInsets?.top + 'px',
    }"
  >
    <view class="mt-12">
      <image src="/static/logo.svg" alt="" class="w-28 h-28 block mx-auto" />
    </view>
    <view class="text-center text-4xl main-title-color mt-4">unibest</view>
    <view class="text-center text-2xl mt-2 mb-8">最好用的 uniapp å¼€å‘模板</view>
    <view class="bg-white h-[80rpx] px-2 flex items-center justify-between">
      <view class="flex items-center" @click="handleUserInfo">
        <wd-icon name="user" size="40rpx" class="icon-color-base"></wd-icon>
        <text class="ml-1 text-color-base">张三</text>
      </view>
    <view class="text-justify max-w-100 m-auto text-4 indent mb-2">{{ description }}</view>
    <view class="text-center mt-8">
      å½“前平台是:
      <text class="text-green-500">{{ PLATFORM.platform }}</text>
      <view class="flex items-center">
        <wd-icon name="notification" size="40rpx" class="icon-color-base mr-3"></wd-icon>
        <wd-icon name="tips" size="40rpx" class="icon-color-base mr-3"></wd-icon>
        <wd-icon name="app" size="40rpx" class="icon-color-base"></wd-icon>
      </view>
    </view>
    <view class="text-center mt-4">
      æ¨¡æ¿åˆ†æ”¯æ˜¯ï¼š
      <text class="text-green-500">tabbar</text>
    <view class="bg-white py-2 px-2">
      <wd-img class="w-full h-[260rpx]" src="/static/images/pic4.jpeg" />
    </view>
    <view class="bg-noti flex flex-row">
      <wd-notice-bar
        text="这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息"
        prefix="clock"
        type="info"
        :scrollable="false"
        custom-class="flex-1  overflow-hidden"
      />
      <wd-button type="text" class="w-[120rpx]">查看</wd-button>
    </view>
    <view class="bg-white">
      <wd-card type="rectangle">
        <template #title>
          <view class="flex items-center menu-title-box">
            <view class="menu-indicator"></view>
            <view class="ml-1 text-xs">数据总览</view>
          </view>
        </template>
        <view class="flex flex-row justify-around">
          <view class="flex flex-col justify-center">
            <text class="text-lg text-center">0</text>
            <text class="text-color-gray">设备总数</text>
          </view>
          <view class="flex flex-col justify-center">
            <text class="text-lg text-center">0</text>
            <text class="text-color-gray">故障设备数</text>
          </view>
          <view class="flex flex-col justify-center">
            <text class="text-lg text-center">0</text>
            <text class="text-color-gray">未修复故障</text>
          </view>
        </view>
      </wd-card>
    </view>
    <view class="bg-white mt-2">
      <wd-card type="rectangle">
        <template #title>
          <view class="flex items-center menu-title-box">
            <view class="menu-indicator"></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 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>
    </view>
    <view class="bg-white mt-2">
      <wd-card type="rectangle">
        <template #title>
          <view class="flex items-center menu-title-box">
            <view class="menu-indicator"></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 inspectMenu"
            :key="item.id"
            @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>
    </view>
  </view>
</template>
<script lang="ts" setup>
import { TestEnum } from '@/typings'
import PLATFORM from '@/utils/platform'
import { getAllMenusApi } from '@/service/menu'
defineOptions({
  name: 'Home',
@@ -41,19 +122,105 @@
// èŽ·å–å±å¹•è¾¹ç•Œåˆ°å®‰å…¨åŒºåŸŸè·ç¦»
const { safeAreaInsets } = uni.getSystemInfoSync()
const author = ref('菲鸽')
const description = ref(
  'unibest æ˜¯ä¸€ä¸ªé›†æˆäº†å¤šç§å·¥å…·å’ŒæŠ€æœ¯çš„ uniapp å¼€å‘模板,由 uniapp + Vue3 + Ts + Vite4 + UnoCss + UniUI + VSCode æž„建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp æ‹¥æœ‰ best ä½“验。',
)
// æµ‹è¯• uni API è‡ªåЍ引入
const getAllMenus = async () => {
  const menuList = await getAllMenusApi()
  console.error(menuList)
}
const goItemPage = (path: string) => {
  const url = `/${path}`
  uni.navigateTo({
    url,
  })
}
onLoad(() => {
  console.log(author)
  console.log(TestEnum.A)
  // getAllMenus()
})
const equMenu = reactive([
  {
    id: 1,
    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/menu2.png',
    path: 'pages/inspect/insp-st',
  },
  {
    id: 2,
    name: '保养汇总',
    icon: '/static/menu/menu1.png',
    path: 'pages/maint/maint-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>
.main-title-color {
  color: $uni-color-primary;
}
.bg-noti {
  background: #f4f9ff;
}
:deep(.wd-card) {
  margin-bottom: 0;
}
:deep(.wd-card__footer) {
  padding: 0 !important;
}
:deep(.wd-card__footer)::after {
  height: 0 !important;
}
.menu-title-box {
  height: 30rpx;
}
.menu-indicator {
  width: 6rpx;
  height: 24rpx;
  border-radius: 10rpx;
  background-color: $uni-color-primary;
}
.slot-img {
  width: 72rpx;
  height: 72rpx;
  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,336 @@
<route lang="json5">
{
  needLogin: true,
  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="34rpx"></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 class="w-full h-[1rpx] bg-base"></view>
      <wd-textarea
        label="特记事项"
        label-width="200rpx"
        type="textarea"
        v-model="inspSt.specialNote"
        auto-height
        :maxlength="200"
        show-word-limit
        placeholder="请输入特记事项"
        clearable
      />
    </view>
  </z-paging>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useUserStore, useAccessStore, useSystemConfigStore } from '@/store'
import { getInspStRecordList, updateInspectSt, 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 {
  id: string
  inspCode: string
  equName: string
  assetNo: string
  planTimeStr?: string
  status: string
  inspUser: number | string
  specialNote: string
}
const dataChange = ref(false)
const userStore = useUserStore()
const collSelects = ref<string[]>([])
// ç‚¹æ£€æ±‡æ€»æ•°æ®(上个页面传值)
const inspSt = reactive<InspSt>({
  id: '',
  inspCode: '',
  equName: '',
  assetNo: '',
  planTimeStr: '',
  status: '',
  inspUser: '',
  specialNote: '',
})
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?.userId
}
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) => {
      updateInspSt(resolve)
    })
    .catch((res) => {
      console.error(res)
    })
}
function updateInspSt(resolve: any) {
  // æ›´æ–°ç‚¹æ£€æ±‡æ€»
  inspSt.status = '1'
  updateInspectSt(inspSt)
    .then((res: any) => {
      paging.value.reload()
      uni.$emit('insp-st-refresh')
      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.id = options.id
  inspSt.inspCode = options.inspCode
  inspSt.equName = options.equName
  inspSt.assetNo = options.assetNo
  inspSt.planTimeStr = options.planTimeStr
  inspSt.specialNote = options?.specialNote
  inspSt.inspUser = userStore?.userInfo?.userId
})
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;
  font-size: 32rpx;
}
</style>
eims-ui-mobile/src/pages/inspect/insp-st.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
<route lang="json5" type="page">
{
  layout: 'default',
  needLogin: true,
  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">
                {{
                  viewMode === 'Day' ? item.planTimeStr : item?.planTimeStr?.substring(0, 7) || ''
                }}
              </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="34rpx"></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'
import { onShow } from '@dcloudio/uni-app'
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 }) {
  reloadData()
}
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, viewMode: viewMode.value })
    .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 itemClick(item) {
  const inspCode = `${item.equId}_${item.planTime}_${viewMode.value}`
  uni.navigateTo({
    url: `/pages/inspect/insp-record?id=${item.id}&inspCode=${inspCode}&specialNote=${item.specialNote ?? ''}&equName=${item.equName ?? ''}&assetNo=${item.assetNo ?? ''}&planTimeStr=${item.planTimeStr ?? ''}`,
  })
}
function reloadData() {
  paging.value.reload()
}
onLoad(() => {
  uni.$on('insp-st-refresh', reloadData)
})
onUnload(() => {
  uni.$off('insp-st-refres', reloadData)
})
</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
@@ -2,19 +2,153 @@
{
  layout: 'default',
  style: {
    navigationStyle: 'custom',
    navigationBarTitleText: '登录',
  },
}
</route>
<script setup lang="ts">
</script>
<template>
  <wd-img class="w-full h-[260rpx]" src="/static/images/pic2.jpg" />
  <wd-form ref="form" :model="model" class="mt-2">
    <wd-cell-group border>
      <wd-input
        label="用户名"
        label-width="200rpx"
        prop="username"
        clearable
        v-model="model.username"
        placeholder="请输入用户名"
        :rules="[{ required: true, message: '请填写用户名' }]"
      />
      <wd-input
        label="密码"
        label-width="200rpx"
        prop="password"
        show-password
        clearable
        v-model="model.password"
        placeholder="请输入密码"
        :rules="[{ required: true, message: '请填写密码' }]"
      />
    </wd-cell-group>
    <view class="footer">
      <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>
<style scoped lang="scss">
<script setup lang="ts">
import { currRoute } from '@/utils'
import { useUserStore, useAccessStore, useSystemConfigStore } from '@/store'
import { useToast } from 'wot-design-uni'
import { login, getUserInfo } from '@/service/login'
import type { UserInfo } from '@/service/login.d'
import { TestEnum } from '@/typings'
const userStore = useUserStore()
const accessStore = useAccessStore()
const configStore = useSystemConfigStore()
const { success: showSuccess } = useToast()
const model = reactive<{
  username: string
  password: string
}>({
  username: '',
  password: '',
})
const rember = ref<boolean>(false)
function handleChange({ value }) {
  console.log(value)
}
const form = ref()
function handleSubmit() {
  form.value
    .validate()
    .then(({ valid, errors }) => {
      if (valid) {
        toLogin()
      }
    })
    .catch((error) => {
      console.log(error, 'error')
    })
}
onLoad(() => {
  const { remberPassword, username, password } = configStore.systemConfigInfo
  if (remberPassword) {
    rember.value = true
    model.username = username
    model.password = password
  }
})
const toLogin = async () => {
  // è®°ä½å¯†ç 
  if (rember.value) {
    configStore.setConfigInfo({ ...model, ...{ remberPassword: true } })
  }
  const res = await login(model)
  accessStore.setAccessInfo(res as any)
  const backUserInfo: any = await getUserInfo()
  /**
   * ç™»å½•超时的情况
   */
  if (!backUserInfo) {
    throw new Error('获取用户信息失败.')
  }
  const { permissions = [], roles = [], user } = backUserInfo
  /**
   * ä»ŽåŽå°user -> vben user转换
   */
  const userInfo: UserInfo = {
    avatar: user.avatar ?? '',
    permissions,
    realName: user.nickName,
    roles,
    userId: user.userId,
    deptId: user.deptId,
    username: user.userName,
  }
  userStore.setUserInfo(userInfo)
  const { query } = currRoute()
  uni.switchTab({ url: query.redirect })
}
</script>
<style scoped lang="scss">
.footer {
  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/pages/maint/maint-order.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,388 @@
<route lang="json5" type="page">
{
  needLogin: true,
  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-cell>
        <template #title>
          <text class="text-color-gray">设备</text>
        </template>
      </wd-cell>
      <view class="h-[2px] w-full bg-base"></view>
      <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">{{ maintSt.equName }}</view>
              <view class="text-color-gray ml-2 text-mini">{{ maintSt.assetNo }}</view>
            </view>
            <view class="flex items-center">
              <text class="text-color-gray text-mini">{{ maintSt.planTime }}</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">工单总数: {{ maintSt.orderCount }}</text>
              |
              <text class="mx-3">已完成: {{ maintSt.wcCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text class="mr-3">待保养: {{ maintSt.dbyCount }}</text>
              |
              <text class="mx-3">保养中: {{ maintSt.byCount }}</text>
              |
              <text class="ml-3">待验证: {{ maintSt.dyzCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text>状态:</text>
              <template v-if="maintSt.orderCount === maintSt.wcCount">
                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></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 class="mb-[2px]">
        <template #title>
          <text class="text-color-gray">保养项</text>
        </template>
      </wd-cell>
      <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 w-[50%]">
                <wd-text :text="item.maintName" :lines="1"></wd-text>
              </view>
              <view class="text-color-gray ml-2 text-mini">{{ item.maintCode }}</view>
            </view>
            <view class="flex items-center w-[20%] justify-end">
              <text class="text-color-gray text-mini">
                {{ item?.planTime }}
              </text>
            </view>
          </view>
        </template>
        <view class="flex h-[140rpx]" items-center>
          <image class="slot-img text-center" src="/static/ico/ico-platform.png" />
          <view class="flex-1 text-color-gray text-xs flex-row">
            <view class="mr-3 mt-2">计划保养日期: {{ item.planTime }}</view>
            <view class="mr-3 mt-2">保养开始时间: {{ item.startTime }}</view>
            <view class="mr-3 mt-2">保养结束时间: {{ item.endTime }}</view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text>状态:</text>
              <template v-if="item.status === '0'">
                <wd-icon class="icon-color-warning" name="books" size="34rpx"></wd-icon>
                <text class="ml-1">待保养</text>
              </template>
              <template v-else-if="item.status === '1'">
                <wd-icon class="icon-color-base" name="books" size="34rpx"></wd-icon>
                <text class="ml-1">保养中</text>
              </template>
              <template v-else-if="item.status === '2'">
                <wd-icon class="icon-color-purple" name="books" size="34rpx"></wd-icon>
                <text class="ml-1">待验证</text>
              </template>
              <template v-else-if="item.status === '3'">
                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></wd-icon>
                <text class="ml-1">已完成</text>
              </template>
              <text class="mx-3">|</text>
              <wd-icon class="icon-color-base" name="camera" size="30rpx"></wd-icon>
              <text class="ml-1">{{ item.maintUserName }}</text>
            </view>
          </view>
          <!--操作工或维修工角色-->
          <template v-if="isOperatorOrRepair()">
            <wd-button
              v-if="item.status === '0'"
              size="small"
              icon="edit-outline"
              @click.stop="handleStartMaint(item)"
            >
              å¼€å§‹ä¿å…»
            </wd-button>
            <wd-button
              v-if="item.status === '1'"
              size="small"
              icon="edit-outline"
              @click.stop="itemClick(item)"
            >
              ä¿å…»ä¸­
            </wd-button>
            <wd-button
              v-if="item.status === '2'"
              size="small"
              icon="edit-outline"
              @click.stop="itemClick(item)"
            >
              å¾…验证
            </wd-button>
            <wd-button
              v-if="item.status === '3'"
              size="small"
              icon="check-outline"
              @click.stop="itemClick(item)"
            >
              å·²å®Œæˆ
            </wd-button>
          </template>
          <!--管理员角色-->
          <template v-else-if="isLeader()">
            <wd-button
              v-if="item.status === '0'"
              size="small"
              icon="warn-bold"
              disabled
            >
              å¾…保养
            </wd-button>
            <wd-button
              v-if="item.status === '1'"
              size="small"
              icon="warn-bold"
              disabled
            >
              ä¿å…»ä¸­
            </wd-button>
            <wd-button
              v-if="item.status === '2'"
              size="small"
              icon="edit-outline"
              @click.stop="itemClick(item)"
            >
              å¾…验证
            </wd-button>
            <wd-button
              v-if="item.status === '3'"
              size="small"
              icon="check-outline"
              @click.stop="itemClick(item)"
            >
              å·²å®Œæˆ
            </wd-button>
          </template>
        </view>
      </wd-card>
      <wd-cell>
        <template #title>
          <text class="text-color-gray">其他信息</text>
        </template>
      </wd-cell>
      <view class="h-[2px] w-full bg-base"></view>
      <wd-textarea
        label="特记事项"
        label-width="200rpx"
        type="textarea"
        v-model="maintSt.specialNote"
        auto-height
        :maxlength="200"
        show-word-limit
        placeholder="请输入特记事项"
        clearable
      />
    </view>
  </z-paging>
</template>
<script setup lang="ts">
import type { MaintStVO } from '@/service/maint.d'
import { getMaintSt, getMaintStOrderList, updateMaintOrder } from '@/service/maint'
import { ref, reactive } from 'vue'
import { useToast, useMessage } from 'wot-design-uni'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
const message = useMessage()
const paging = ref(null)
const dataList = ref([])
interface QueryParams {
  pageNum: number
  pageSize: number
  maintCode: string
  status?: string
}
const maintSt = reactive<MaintStVO>({
  id: '',
  equName: '',
  assetNo: '',
  planTime: '',
  orderCount: null,
  wcCount: null,
  dbyCount: null,
  byCount: null,
  dyzCount: null,
  specialNote: '',
})
const queryList = (pageNum?: number, pageSize?: number) => {
  const params: QueryParams = {
    pageNum,
    pageSize,
    maintCode: maintCode.value,
  }
  getMaintStOrderList(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 itemClick(item) {
  goToOrderDetail(item)
}
function goToOrderDetail(item) {
  uni.navigateTo({
    url: `/pages/maint/order-detail?id=${item.id}`,
  })
}
function handleStartMaint(item) {
  message
    .confirm({
      msg: '确定开始保养?',
      title: '提示',
      beforeConfirm: ({ resolve }) => {
        // ç¡®è®¤å¼€å§‹ä¿®æ”¹çŠ¶æ€ä¸º1-保养中
        const data = Object.assign({}, item)
        data.status = '1'
        updateOrder(data, resolve)
      },
    })
    .then(() => {})
    .catch((error) => {
      console.log(error)
    })
}
/**
 * æ›´æ–°ä¿å…»å·¥å•
 * @param item
 * @param resolve
 */
function updateOrder(data: any, resolve: any) {
  updateMaintOrder(data)
    .then((res: any) => {
      resolve(true)
      if (res?.code === 200) {
        reloadData()
      }
    })
    .catch((res) => {
      console.error(res)
    })
}
function reloadData() {
  paging.value.reload()
}
const goBack = () => {
  uni.navigateBack()
}
function handleClickRight() {}
/**
 * æŸ¥è¯¢ä¿å…»æ˜Žç»†éœ€è¦çš„code
 */
const maintCode = computed(() => `${maintSt.equId}_${maintSt.planTime}`)
onLoad((options) => {
  const itemString = decodeURIComponent(options.maintSt)
  const item = JSON.parse(itemString)
  Object.assign(maintSt, item)
})
</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;
  font-size: 32rpx;
}
</style>
eims-ui-mobile/src/pages/maint/maint-st.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,181 @@
<route lang="json5" type="page">
{
  layout: 'default',
  needLogin: true,
  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="equName" :options="equList" @change="handleEquName" />
        <wd-drop-menu-item v-model="status" :options="statusList" @change="handleStatus" />
      </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 w-[50%]">
                <wd-text :text="item.equName" :lines="1"></wd-text>
              </view>
              <view class="text-color-gray ml-2 text-mini">{{ item.assetNo }}</view>
            </view>
            <view class="flex items-center w-[20%] justify-end">
              <text class="text-color-gray text-mini">
                {{ item?.planTime }}
              </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.orderCount }}</text>
              |
              <text class="mx-3">已完成: {{ item.wcCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text class="mr-3">待保养: {{ item.dbyCount }}</text>
              |
              <text class="mx-3">保养中: {{ item.byCount }}</text>
              |
              <text class="ml-3">待验证: {{ item.dyzCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text>状态:</text>
              <template v-if="item.orderCount === item.wcCount">
                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></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 { getMaintStList } from '@/service/maint'
interface QueryParams {
  pageNum: number
  pageSize: number
  equName?: string
  status?: string
}
const status = ref<string>('-1')
const equName = ref<string>('-1')
const statusList = ref<Record<string, any>[]>([
  { label: '所有状态', value: '-1' },
  { label: '待保养', value: '0' },
  { label: '保养中', value: '1' },
  { label: '待验证', value: '2' },
  { label: '已完成', value: '3' },
])
const equList = ref<Record<string, any>[]>([{ label: '所有设备', value: '-1' }])
function handleStatus({ 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
  const params: QueryParams = {
    pageNum,
    pageSize,
    equName: equName.value,
    status: status.value,
  }
  if (equName.value === '-1') {
    delete params.equName
  }
  if (status.value === '-1') {
    delete params.status
  }
  getMaintStList(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 itemClick(item) {
  const itemString = encodeURIComponent(JSON.stringify(item))
  uni.navigateTo({
    url: `/pages/maint/maint-order?maintSt=${itemString}`,
  })
}
function reloadData() {
  paging.value.reload()
}
onLoad(() => {
  queryList()
  uni.$on('maint-st-refresh', reloadData)
})
onUnload(() => {
  uni.$off('maint-st-refres', reloadData)
})
</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/maint/order-detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,228 @@
<route lang="json5" type="page">
{
  needLogin: true,
  style: {
    navigationBarTitleText: '工单明细',
    navigationStyle: 'custom',
    navigationBarBackgroundColor: '#4D80F0',
  },
}
</route>
<template>
  <view class="bg-base h-[100vh]">
    <wd-navbar
      title="工单明细"
      left-arrow
      @click-left="goBack"
      right-text="提交"
      @click-right="handleClickRight"
      custom-style="background: #4D80F0;"
      safeAreaInsetTop
    ></wd-navbar>
    <wd-form ref="form" :model="order" :rules="rules">
      <wd-cell-group custom-class="group" title="设备信息" border>
        <wd-cell title="设备名称" title-width="200rpx" is-link>
          <text>{{ order?.equName }}</text>
        </wd-cell>
        <wd-cell title="资产编号" title-width="200rpx" is-link>
          <text>{{ order?.assetNo }}</text>
        </wd-cell>
      </wd-cell-group>
      <wd-cell-group custom-class="mt-2" title="保养信息" border>
        <wd-cell title="保养单号" title-width="200rpx" is-link>
          <text>{{ order?.maintCode }}</text>
        </wd-cell>
        <wd-cell title="保养项" title-width="200rpx" is-link>
          <text>{{ order?.maintName }}</text>
        </wd-cell>
        <wd-cell title="计划保养日期" title-width="200rpx" is-link>
          <text>{{ order?.planTime }}</text>
        </wd-cell>
        <wd-datetime-picker
          label="保养开始时间"
          label-width="200rpx"
          placeholder="请选择时间"
          prop="startTime"
          v-model="startTime"
          @open="openStartTime"
          @confirm="handleStartTime"
        />
        <wd-datetime-picker
          label="保养结束时间"
          label-width="200rpx"
          placeholder="请选择时间"
          prop="endTime"
          v-model="endTime"
          @open="openEndTime"
          @confirm="handleEndTime"
        />
        <wd-textarea
          label="工作描述"
          label-width="200rpx"
          type="textarea"
          v-model="order.maintDesc"
          auto-height
          :maxlength="200"
          show-word-limit
          placeholder="请输入工作描述"
          clearable
        />
        <wd-cell title="保养图片" title-width="200rpx" prop="fileList">
          <wd-upload
            :auto-upload="false"
            :file-list="fileList"
            @change="handleFileChange"
          ></wd-upload>
        </wd-cell>
        <wd-textarea
          label="备注"
          label-width="200rpx"
          type="textarea"
          v-model="order.remark"
          auto-height
          :maxlength="200"
          show-word-limit
          placeholder="请输入备注"
          clearable
        />
      </wd-cell-group>
    </wd-form>
  </view>
</template>
<script setup lang="ts">
import { getMaintOrder, updateMaintOrder } from '@/service/maint'
import { formatDate } from '@/utils/DateUtils'
import { reactive } from 'vue'
import { FormRules } from 'wot-design-uni/components/wd-form/types'
import { useToast, useMessage } from 'wot-design-uni'
const message = useMessage()
const fileList = ref<[]>()
const startTime = ref<number>(0)
const endTime = ref<number>(0)
interface MaintOrder {
  id: string
  equName: string
  maintCode: string
  maintName: string
  planTime: string
  startTime: string
  endTime?: string
  maintDesc: string
  remark: string
}
const order = reactive<MaintOrder>({
  id: '',
  equName: '',
  maintCode: '',
  maintName: '',
  planTime: '',
  startTime: '',
  endTime: '',
  maintDesc: '',
  remark: '',
})
const rules: FormRules = {
  startTime: [
    {
      required: true,
      message: '请选择保养开始时间',
    },
  ],
  endTime: [
    {
      required: true,
      message: '请选择保养结束时间',
    },
  ],
  maintDesc: [
    {
      required: true,
      message: '请输入工作描述',
    },
  ],
}
function openStartTime() {
  if (startTime.value === 0) {
    startTime.value = Date.now()
  }
}
function openEndTime() {
  if (endTime.value === 0) {
    endTime.value = Date.now()
  }
}
function handleStartTime(value) {
  order.startTime = formatDate(new Date(value.value))
}
function handleEndTime(value) {
  order.endTime = formatDate(new Date(value.value))
}
function handleFileChange({ fileList }) {}
function initMaintOrder(id: any) {
  getMaintOrder(id)
    .then((res: any) => {
      Object.assign(order, res)
      startTime.value = new Date(order.startTime).getTime()
      endTime.value = new Date(order.endTime).getTime()
    })
    .catch((res) => {})
}
function updateOrder(resolve: any) {
  updateMaintOrder(order)
    .then((res: any) => {
      resolve(true)
    })
    .catch((res) => {
      console.error(res)
    })
}
const goBack = () => {
  uni.navigateBack()
}
function handleClickRight() {
  message
    .confirm({
      msg: '确定提交?',
      title: '提示',
      beforeConfirm: ({ resolve }) => {
        updateOrder(resolve)
      },
    })
    .then(() => {})
    .catch((error) => {
      console.log(error)
    })
}
onLoad((options) => {
  initMaintOrder(options.id)
})
</script>
<style scoped lang="scss">
:deep(.wd-navbar__text) {
  font-size: 26rpx;
  color: white;
}
:deep(.wd-icon-arrow-left:before),
:deep(.wd-navbar__title) {
  color: white;
  font-weight: bold !important;
  font-size: 32rpx;
}
</style>
eims-ui-mobile/src/pages/my/index.vue
@@ -1,6 +1,7 @@
<route lang="json5" type="page">
{
  layout: 'tabbar',
  needLogin: true,
  style: {
    navigationBarTitleText: '我的',
  },
eims-ui-mobile/src/pages/spare/index.vue
@@ -1,6 +1,7 @@
<route lang="json5" type="page">
{
  layout: 'tabbar',
  needLogin: true,
  style: {
    navigationBarTitleText: 'spare',
  },
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,40 @@
import { http } from '@/utils/http'
import type { InspectStVO, InspectRecordVO } from './inspect.d'
/**
 * ç‚¹æ£€æ±‡æ€»åˆ—表
 */
export const getInspStList = (params: any) => {
  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.put<void>('/eims/inspRecord', data)
}
/**
 * æ‰¹é‡æ›´æ–°ç‚¹æ£€è®°å½•
 * @param data
 */
export const updateInspRecordBatch = (data: any) => {
  return http.put<void>('/eims/inspRecord/editBatch', data)
}
/**
 * æ›´æ–°ç‚¹æ£€æ±‡æ€»
 * @param data
 */
export const updateInspectSt = (data: any) => {
  return http.put<void>('/eims/inspectSt', data)
}
eims-ui-mobile/src/service/login.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
export interface LoginParams {
  username: string;
  password: string;
  clientId?: string;
  tenantId?: string;
  grantType?: string;
}
export interface User {
  avatar: string;
  createTime: string;
  deptId: number;
  deptName: string;
  email: string;
  loginDate: string;
  loginIp: string;
  nickName: string;
  phonenumber: string;
  remark: string;
  roles: Role[];
  sex: string;
  status: string;
  tenantId: string;
  userId: number;
  userName: string;
  userType: string;
}
export interface UserInfoResp {
  permissions: string[];
  roles: string[];
  user: User;
}
interface BasicUserInfo {
  /**
   * å¤´åƒ
   */
  avatar: string;
  /**
   * éƒ¨é—¨id
   */
  deptId: number;
  /**
   * ç”¨æˆ·æƒé™
   */
  permissions: string[];
  /**
   * ç”¨æˆ·æ˜µç§°
   */
  realName: string;
  /**
   * ç”¨æˆ·è§’色
   */
  roles: string[];
  /**
   * ç”¨æˆ·id
   */
  userId: number | string;
  /**
   * ç”¨æˆ·å
   */
  username: string;
}
/** ç”¨æˆ·ä¿¡æ¯ */
interface UserInfo extends BasicUserInfo {
  /**
   * æ‹“展使用
   */
  [key: string]: any;
}
eims-ui-mobile/src/service/login.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
import { http } from '@/utils/http'
import type { LoginParams, UserInfoResp } from './login.d'
const clientId = import.meta.env.VITE_APP_CLIENT_ID
const DEFAULT_TENANT_ID = '000000'
const GRANT_TYPE = 'password'
/** get è¯·æ±‚ */
export const login = (params: LoginParams) => {
  const { username, password } = params
  // æž„造新的请求参数,避免直接修改原始对象
  const requestData = {
    username,
    password,
    clientId,
    tenantId: DEFAULT_TENANT_ID,
    grantType: GRANT_TYPE,
  }
  return http.post<any>('/auth/login', requestData)
}
export const getUserInfo = () => {
  return http.get<null | UserInfoResp>('/system/user/getInfo')
}
eims-ui-mobile/src/service/maint.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,152 @@
export interface MaintStVO {
  /**
   *
   */
  id: number | string;
  /**
   *
   */
  title?: string;
  /**
   *
   */
  equId?: number | string;
  equName?: string;
  assetNo?: string;
  status?: string;
  /**
   *
   */
  planTime?: string;
  /**
   * ä¿å…»äºº
   */
  maintUser?: number;
  orderCount?: number,
  wcCount?: number,
  dbyCount?: number,
  byCount?: number,
  dyzCount?: number,
  /**
   * éªŒè¯äºº
   */
  verifyUser?: number;
  verifyTime?: string;
  /**
   * ç‰¹è®°äº‹é¡¹
   */
  specialNote?: string;
  /**
   * å¤‡æ³¨
   */
  remark?: string;
}
export interface MaintOrderVO {
  /**
   *
   */
  id: number | string;
  /**
   *
   */
  equId?: number | string;
  equName?: string;
  assetNo?: string;
  /**
   * è®¡åˆ’表冗余-保养类型(字典)
   */
  maintType?: string;
  /**
   * å·¥ä½œæè¿°
   */
  maintDesc?: string;
  /**
   * è®¡åˆ’表冗余-保养周期
   */
  maintCycle?: number;
  /**
   * è®¡åˆ’表冗余-保养周期单位(字典)
   */
  maintCycleUnit?: string;
  /**
   * è®¡åˆ’表冗余- ä¿å…»è§„则(字典)保养时间计算:1-按周期  2-按上次保养时间
   */
  maintRule?: string;
  /**
   * è®¡åˆ’表冗余-保养人
   */
  maintUser?: number;
  maintUserName?: string;
  /**
   * è®¡åˆ’表冗余-保养部门
   */
  maintDept?: number;
  /**
   * ä¿å…»å•号
   */
  maintCode?: string;
  maintName?: string;
  /**
   *
   */
  status?: string;
  verifyUser?: number;
  /**
   * éªŒè¯äºº
   */
  verifyUserName?: string;
  /**
   * ä¿å…»è®¡åˆ’æ—¶é—´
   */
  planTime?: number | string;
  /**
   * ä¿å…»å¼€å§‹æ—¶é—´
   */
  startTime: any;
  /**
   * ä¿å…»ç»“束时间
   */
  endTime: any;
  /**
   * è®¡åˆ’id
   */
  planId?: number | string;
  /**
   * åˆ›å»ºæ—¶é—´
   */
  createTime?: string;
  /**
   * å¤‡æ³¨
   */
  remark?: string;
}
eims-ui-mobile/src/service/maint.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
import { http } from '@/utils/http'
import type { MaintStVO, MaintOrderVO } from './maint.d'
type ID = number | string
/**
 * æ ¹æ®id查询汇总
 * @param id
 */
export const getMaintSt = (id: ID) => {
  return http.get<MaintStVO>(`/eims/maintSt/${id}`)
}
/**
 * ä¿å…»æ±‡æ€»åˆ—表
 */
export const getMaintStList = (params: any) => {
  return http.get<MaintStVO[]>('/eims/maintSt/list', params)
}
/**
 * æ ¹æ®id查询工单
 * @param id
 */
export const getMaintOrder = (id: ID) => {
  return http.get<MaintOrderVO>(`/eims/maintOrder/${id}`)
}
/**
 * ä¿å…»æ±‡æ€»åˆ—表下保养工单列表
 */
export const getMaintStOrderList = (params: any) => {
  return http.get<MaintOrderVO[]>('/eims/maintOrder/detailList', params)
}
/**
 * æ›´æ–°ä¿å…»å·¥å•
 * @param data
 */
export const updateMaintOrder = (data: any) => {
  return http.put<void>('/eims/maintOrder', data)
}
eims-ui-mobile/src/service/menu.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
/**
 * @description: èœå•meta
 * @param title èœå•名
 * @param icon èœå•图标
 * @param noCache æ˜¯å¦ä¸ç¼“å­˜
 * @param link å¤–链链接
 */
export interface MenuMeta {
  icon: string;
  link?: string;
  noCache: boolean;
  title: string;
}
/**
 * @description: èœå•
 * @param name èœå•名
 * @param path èœå•路径
 * @param hidden æ˜¯å¦éšè—
 * @param component ç»„件名称 Laout
 * @param alwaysShow æ€»æ˜¯æ˜¾ç¤º
 * @param query è·¯ç”±å‚æ•°(json形式)
 * @param meta è·¯ç”±ä¿¡æ¯
 * @param children å­è·¯ç”±ä¿¡æ¯
 */
export interface Menu {
  alwaysShow?: boolean;
  children: Menu[];
  component: string;
  hidden: boolean;
  meta: MenuMeta;
  name: string;
  path: string;
  query?: string;
  redirect?: string;
}
eims-ui-mobile/src/service/menu.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
import { http } from '@/utils/http'
import type { Menu } from './menu.d'
export const getAllMenusApi = () => {
  return http.get<Menu>('/system/menu/getRouters')
}
eims-ui-mobile/src/static/ico/ico-ok.png
eims-ui-mobile/src/static/ico/ico-platform.png
eims-ui-mobile/src/static/images/camera.png
eims-ui-mobile/src/static/images/menu1.png
eims-ui-mobile/src/static/images/pic1.png
eims-ui-mobile/src/static/images/pic2.jpg
eims-ui-mobile/src/static/images/pic3.png
eims-ui-mobile/src/static/images/pic4.jpeg
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/store/access.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
type AccessToken = null | string
const initState = {
  access_codes: [],
  access_menus: [],
  access_routes: [],
  access_token: null,
  is_access_checked: false,
  login_expired: false,
  refresh_token: null,
}
interface AccessInfo {
  /**
   * æƒé™ç 
   */
  access_codes: string[]
  /**
   * å¯è®¿é—®çš„菜单列表
   */
  access_menus: string[]
  /**
   * å¯è®¿é—®çš„路由列表
   */
  access_routes: string[]
  /**
   * ç™»å½• accessToken
   */
  access_token: AccessToken
  /**
   * æ˜¯å¦å·²ç»æ£€æŸ¥è¿‡æƒé™
   */
  is_access_checked: boolean
  /**
   * ç™»å½•是否过期
   */
  login_expired: boolean
  /**
   * ç™»å½• accessToken
   */
  refresh_token: AccessToken
}
export const useAccessStore = defineStore(
  'accessInfo',
  () => {
    const accessInfo = ref<AccessInfo>({ ...initState })
    const setAccessInfo = (val: AccessInfo) => {
      accessInfo.value = val
    }
    const setAccessToken = (val: AccessToken) => {
      accessInfo.value.access_token = val
    }
    const clearAccessInfo = () => {
      accessInfo.value = { ...initState }
    }
    // ä¸€èˆ¬æ²¡æœ‰reset需求,不需要的可以删除
    const reset = () => {
      accessInfo.value = { ...initState }
    }
    const isLogined = computed(() => !!accessInfo.value.access_token)
    return {
      accessInfo,
      setAccessInfo,
      setAccessToken,
      clearAccessInfo,
      isLogined,
      reset,
    }
  },
  {
    persist: true,
  },
)
eims-ui-mobile/src/store/index.ts
@@ -15,3 +15,5 @@
// æ¨¡å—统一导出
export * from './user'
export * from './access'
export * from './system-config'
eims-ui-mobile/src/store/system-config.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
const initState = {
  remberPassword: false,
  username: '',
  password: '',
}
export const useSystemConfigStore = defineStore(
  'systemConfig',
  () => {
    const systemConfigInfo = ref<any>({ ...initState })
    const setConfigInfo = (val: any) => {
      systemConfigInfo.value = val
    }
    const clearConfigInfo = () => {
      systemConfigInfo.value = { ...initState }
    }
    // ä¸€èˆ¬æ²¡æœ‰reset需求,不需要的可以删除
    const reset = () => {
      systemConfigInfo.value = { ...initState }
    }
    return {
      systemConfigInfo,
      setConfigInfo,
      clearConfigInfo,
      reset,
    }
  },
  {
    persist: true,
  },
)
eims-ui-mobile/src/store/user.ts
@@ -1,14 +1,15 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { UserInfoResp, UserInfo } from '@/service/login.d'
const initState = { nickname: '', avatar: '' }
const initState = null
export const useUserStore = defineStore(
  'user',
  'userInfo',
  () => {
    const userInfo = ref<IUserInfo>({ ...initState })
    const userInfo = ref<UserInfo>({ ...initState })
    const setUserInfo = (val: IUserInfo) => {
    const setUserInfo = (val: UserInfo) => {
      userInfo.value = val
    }
@@ -19,13 +20,11 @@
    const reset = () => {
      userInfo.value = { ...initState }
    }
    const isLogined = computed(() => !!userInfo.value.token)
    return {
      userInfo,
      setUserInfo,
      clearUserInfo,
      isLogined,
      reset,
    }
  },
eims-ui-mobile/src/style/index.scss
@@ -16,3 +16,40 @@
  // ä¿®æ”¹æŒ‰é’®èƒŒæ™¯è‰²
  // --wot-button-primary-bg-color: green;
}
// base文字颜色
.text-color-base {
  color: $uni-text-color;
}
.text-color-gray {
  color: $uni-text-color-grey;
}
// å›¾æ ‡é¢œè‰²
.icon-color-base {
  color: $uni-color-primary;
}
.icon-color-gray {
  color: $uni-text-color-grey;
}
.icon-color-success {
  color: $uni-color-success;
}
.icon-color-warning {
  color: $uni-color-warning;
}
.icon-color-purple {
  color: #824eed;
}
.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,8 +5,15 @@
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/maint/maint-order" |
       "/pages/maint/maint-st" |
       "/pages/maint/order-detail" |
       "/pages/my/index" |
       "/pages/spare/index";
}
eims-ui-mobile/src/uni.scss
@@ -16,7 +16,7 @@
/* é¢œè‰²å˜é‡ */
/* è¡Œä¸ºç›¸å…³é¢œè‰² */
$uni-color-primary: #007aff;
$uni-color-primary: #4D80F0;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
eims-ui-mobile/src/utils/DateUtils.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
import dayjs from 'dayjs'
export function formatDate(inputDate: Date) {
  return dayjs(inputDate).format('YYYY-MM-DD HH:mm:ss')
}
eims-ui-mobile/src/utils/RoleUtils.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
import { useUserStore } from '@/store'
// import { ROLE_OPERATOR, ROLE_REPAIR, ROLE_LEADER, ROLE_LINE, ROLE_SUPER_ADMIN } from '@/utils/constants'
/**
 * è¶…级管理员
 */
export const ROLE_SUPER_ADMIN = 'superadmin'
/**
 * é¢†å¯¼
 */
export const ROLE_LEADER = 'leader'
/**
 * æ“ä½œå‘˜
 */
export const ROLE_OPERATOR = 'operator'
/**
 * çº¿é•¿
 */
export const ROLE_LINE = 'line'
/**
 * ç»´ä¿®å‘˜
 */
export const ROLE_REPAIR = 'repair'
/**
 * ç™»å½•角色是操作工或维修工
 */
export const isOperatorOrRepair = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return roles.includes(ROLE_OPERATOR) || roles.includes(ROLE_REPAIR)
}
/**
 * ç™»å½•角色是操作工或维修工
 */
export const isLeader = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return (
    roles.includes(ROLE_LINE) || roles.includes(ROLE_SUPER_ADMIN) || roles.includes(ROLE_LEADER)
  )
}
eims-ui-mobile/src/utils/http.ts
@@ -1,6 +1,10 @@
import { CustomRequestOptions } from '@/interceptors/request'
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({
@@ -11,14 +15,39 @@
      // #endif
      // å“åº”成功
      success(res) {
        uni.hideLoading()
        // çŠ¶æ€ç  2xx,参考 axios çš„设计
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 2.1 æå–核心数据 res.data
          resolve(res.data as IResData<T>)
          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',
              title: (res.data as IResData<T>).msg || '请求错误',
            })
            reject(res)
          }
        } else if (res.statusCode === 401) {
          // 401错误  -> æ¸…理用户信息,跳转到登录页
          // userStore.clearUserInfo()
          // uni.navigateTo({ url: '/pages/login/login' })
          useAccessStore().clearAccessInfo()
          useUserStore().clearUserInfo()
          uni.navigateTo({ url: '/pages/login/index' })
          reject(res)
        } else {
          // å…¶ä»–错误 -> æ ¹æ®åŽç«¯é”™è¯¯ä¿¡æ¯è½»æç¤º
@@ -37,6 +66,7 @@
          title: '网络错误,换个网络试试',
        })
        reject(err)
        uni.hideLoading()
      },
    })
  })
@@ -76,5 +106,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
eims-ui-mobile/vite.config.ts
@@ -43,6 +43,7 @@
    VITE_SHOW_SOURCEMAP,
    VITE_APP_PROXY,
    VITE_APP_PROXY_PREFIX,
    VITE_APP_CLIENT_ID,
  } = env
  console.log('环境变量 env -> ', env)
eims-ui/apps/web-antd/src/views/eims/insp-st/data.tsx
@@ -46,7 +46,24 @@
  {
    title: '保养时间',
    field: 'planTimeStr',
    minWidth: 200
    minWidth: 200,
    slots: {
      default: ({ row }) => {
        // åˆ¤æ–­æ˜¯å¦éœ€è¦æ˜¾ç¤ºæ—¶é—´
        if (!row.type) return '';
        // èŽ·å–åŽŸå§‹æ—¶é—´å­—ç¬¦ä¸²
        const fullDate = row.planTimeStr;
        // æ ¹æ®æ¡ä»¶å†³å®šæ˜¾ç¤ºæ ¼å¼
        // ç¤ºä¾‹æ¡ä»¶ï¼šå¦‚æžœ status æ˜¯æŸä¸ªç‰¹å®šå€¼ï¼ˆä¾‹å¦‚ 'monthly'),则显示 YYYY-MM
        if (row.type === 'Month') {
          return fullDate.slice(0, 7); // æˆªå–前 7 ä¸ªå­—符,得到 YYYY-MM
        }
        // é»˜è®¤æ˜¾ç¤ºå®Œæ•´æ—¥æœŸ YYYY-MM-DD
        return fullDate;
      }
    }
  },
  {
    title: '状态',
eims-ui/apps/web-antd/src/views/eims/insp-st/index.vue
@@ -255,16 +255,17 @@
const monBtn = ref<any>('primary');
function queryData(type: ViewMode) {
  // TODO æ—¥è§†å›¾å’Œæœˆè§†å›¾åŽç«¯å·²ç»Ÿä¸€æ–¹æ³• å‰ç«¯å¯åˆ é™¤æ—¥è§†å›¾å¤šä½™é…ç½®
  if (type === ViewMode.Day) {
    dayBtn.value = 'primary';
    monBtn.value = 'default';
    curViewMode.value = ViewMode.Day;
    tableApi.setGridOptions(gridOptions2);
    // tableApi.setGridOptions(gridOptions2);
  } else if (type === ViewMode.Month) {
    dayBtn.value = 'default';
    monBtn.value = 'primary';
    curViewMode.value = ViewMode.Month;
    tableApi.setGridOptions(gridOptions);
    // tableApi.setGridOptions(gridOptions);
  }
  tableApi.query();
}
@@ -273,7 +274,7 @@
<template>
  <Page :auto-content-height="true">
    <div class="flex h-full gap-[8px] flex-col">
      <BasicTable class="h-2/3" table-title="点检记录列表">
      <BasicTable class="h-2/3" table-title="点检汇总列表">
        <template #toolbar-tools>
          <Space>
            <Button :type="dayBtn" @click.stop="queryData(ViewMode.Day)">日视图</Button>
@@ -300,8 +301,7 @@
            <ghost-button v-access:code="['eims:inspectSt:edit']" @click.stop="handleEdit(row)">
              {{ $t('pages.common.edit') }}
            </ghost-button>
            <Popconfirm :get-popup-container="getVxePopupContainer" placement="left" title="确认删除?"
                        @confirm="handleDelete(row)">
            <Popconfirm :get-popup-container="getVxePopupContainer" placement="left" title="确认删除?" @confirm="handleDelete(row)">
              <ghost-button danger v-access:code="['eims:inspectSt:remove']" @click.stop="">
                {{ $t('pages.common.delete') }}
              </ghost-button>
@@ -309,8 +309,7 @@
          </Space>
        </template>
      </BasicTable>
      <BasisSubTable :columns="recordCol" :list-api="listInspStRecord" :req-value="recordId" class="h-1/3"
                     req-key="inspCode" title="点检记录" />
      <BasisSubTable :columns="recordCol" :list-api="listInspStRecord" :req-value="recordId" class="h-1/3" req-key="inspCode" title="点检记录" />
    </div>
    <InspStDrawer @reload="tableApi.query()" />
  </Page>
eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped>
</style>
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsInspectRecordController.java
@@ -15,6 +15,7 @@
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.eims.domain.bo.EimsMaintOrderBo;
import org.dromara.eims.domain.bo.InspectRecordBo;
import org.dromara.eims.domain.vo.EimsMaintOrderVo;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
@@ -80,6 +81,7 @@
            Map<String, Object> params = bo.getParams();
            params.put("beginPlanTime",startOfMonth);
            params.put("endPlanTime",endOfMonth);
            params.put("type","Month");
        }
        return eimsInspectRecordService.queryPageListCustom(bo, pageQuery);
    }
@@ -130,6 +132,14 @@
        return toAjax(eimsInspectRecordService.updateByBo(bo));
    }
    @SaCheckPermission("eims:inspectRecord:edit")
    @Log(title = "点检记录-批量修改", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping("editBatch")
    public R<Void> editBatch(@Validated(EditGroup.class) @RequestBody InspectRecordBo bo) {
        return toAjax(eimsInspectRecordService.updateBatchByBo(bo));
    }
    /**
     * åˆ é™¤ç‚¹æ£€è®°å½•
     *
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsInspectSt.java
@@ -28,6 +28,10 @@
     */
    @TableId(value = "id")
    private Long id;
    /**
     * æ±‡æ€»ç±»åž‹ Day-日汇总  Month-月汇总
     */
    private String type;
    /**
     * æ ‡é¢˜
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsInspectStBo.java
@@ -28,17 +28,19 @@
     */
    @NotNull(message = "不能为空", groups = { EditGroup.class })
    private Long id;
    /**
     * æ±‡æ€»ç±»åž‹ Day-日汇总  Month-月汇总
     */
    private String type;
    /**
     * æ ‡é¢˜
     */
    @NotBlank(message = "标题不能为空", groups = { AddGroup.class, EditGroup.class })
    private String title;
    /**
     * è®¾å¤‡id
     */
    @NotNull(message = "设备id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long equId;
    private String equName;
    private String assetNo;
@@ -46,7 +48,6 @@
    /**
     * è®¡åˆ’æ—¶é—´
     */
    @NotNull(message = "计划时间不能为空", groups = { AddGroup.class, EditGroup.class })
    private Date planTime;
    /**
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/InspectRecordBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package org.dromara.eims.domain.bo;
import lombok.Data;
import java.util.List;
@Data
public class InspectRecordBo {
    /**
     * ç§»åŠ¨ç«¯æ‰¹é‡æ›´æ–°
     */
    private List<EimsInspectRecordBo> inspRecordList;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsInspectStVo.java
@@ -41,7 +41,10 @@
     */
    @ExcelProperty(value = "标题")
    private String title;
    /**
     * æ±‡æ€»ç±»åž‹ Day-日汇总  Month-月汇总
     */
    private String type;
    /**
     * è®¾å¤‡id
     */
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsInspectRecordMapper.java
@@ -18,6 +18,7 @@
 */
public interface EimsInspectRecordMapper extends BaseMapperPlus<EimsInspectRecord, EimsInspectRecordVo> {
    Page<EimsInspectRecordVo> selectInspRecordList(@Param("page") Page<EimsInspectRecordVo> page, @Param(Constants.WRAPPER) Wrapper<EimsInspectRecord> queryWrapper);
    // TODO æ—¥è§†å›¾å’Œæœˆè§†å›¾ç»Ÿä¸€æŽ¥å£ï¼Œå¾…删除
    Page<EimsInspectStVo> selectInspRecordDayList(@Param("page") Page<EimsInspectStVo> page, @Param(Constants.WRAPPER) Wrapper<EimsInspectSt> queryWrapper);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsInspectRecordService.java
@@ -1,6 +1,7 @@
package org.dromara.eims.service;
import org.dromara.eims.domain.bo.EimsInspectStBo;
import org.dromara.eims.domain.bo.InspectRecordBo;
import org.dromara.eims.domain.vo.EimsInspectRecordVo;
import org.dromara.eims.domain.bo.EimsInspectRecordBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -58,6 +59,7 @@
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    Boolean updateByBo(EimsInspectRecordBo bo);
    Boolean updateBatchByBo(InspectRecordBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ç‚¹æ£€è®°å½•信息
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsInspectRecordServiceImpl.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.SneakyThrows;
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -11,10 +13,12 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.eims.domain.EimsInspectPlan;
import org.dromara.eims.domain.EimsInspectSt;
import org.dromara.eims.domain.EimsMaintPlan;
import org.dromara.eims.domain.bo.EimsInspectStBo;
import org.dromara.eims.domain.bo.InspectRecordBo;
import org.dromara.eims.domain.vo.EimsInspectStVo;
import org.dromara.eims.domain.vo.EimsMaintOrderVo;
import org.dromara.eims.mapper.EimsInspectPlanMapper;
@@ -238,6 +242,19 @@
        return baseMapper.updateById(update) > 0;
    }
    @Override
    public Boolean updateBatchByBo(InspectRecordBo bo) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        List<EimsInspectRecord> inspRecordList = MapstructUtils.convert(bo.getInspRecordList(), EimsInspectRecord.class);
        inspRecordList.forEach(item->{
            // æ›´æ–°ä¸ºå·²ç‚¹æ£€
            item.setStatus(DictConstants.EIMS_INSPECT_STATUS_DETAIL.Y);
            item.setInspTime(new Date());
            item.setInspUser(loginUser.getUserId());
        });
        return baseMapper.updateBatchById(inspRecordList);
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsInspectStServiceImpl.java
@@ -21,6 +21,8 @@
import org.dromara.eims.service.IEimsInspectStService;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
@@ -59,23 +61,29 @@
     */
    @Override
    public TableDataInfo<EimsInspectStVo> queryPageList(EimsInspectStBo bo, PageQuery pageQuery) {
        bo.setType(bo.getViewMode());
        // æœˆè§†å›¾
        if(bo.getViewMode().equals("Month")){
//        if(bo.getViewMode().equals("Month")){
            QueryWrapper<EimsInspectSt> qw = buildWrapper(bo);
            Page<EimsInspectStVo> result = baseMapper.selectInspStList(pageQuery.build(), qw);
            // å¡«å……数据
            fillStData(result);
            fillStData(result,bo.getViewMode());
            return TableDataInfo.build(result);
            // æ—¥è§†å›¾
        }else if(bo.getViewMode().equals("Day")){
            Page<EimsInspectStVo> result = recordMapper.selectInspRecordDayList(pageQuery.build(), buildGroupWrapper(bo));
            return TableDataInfo.build(result);
        }
//        }else if(bo.getViewMode().equals("Day")){
//            Page<EimsInspectStVo> result = recordMapper.selectInspRecordDayList(pageQuery.build(), buildGroupWrapper(bo));
//            return TableDataInfo.build(result);
//        }
       return null;
//       return null;
    }
    private void fillStData(Page<EimsInspectStVo> result) {
    /**
     *
      * @param result
     * @param type Day-日视图 Month-月视图
     */
    private void fillStData(Page<EimsInspectStVo> result,String type) {
        List<EimsInspectStVo> records = result.getRecords();
        for (int i = 0; i < records.size(); i++) {
            EimsInspectStVo stVo = records.get(i);
@@ -84,9 +92,19 @@
            LambdaQueryWrapper<EimsInspectRecord> recordLqw = Wrappers.lambdaQuery();
            recordLqw.eq(EimsInspectRecord::getEquId, stVo.getEquId());
            LocalDate planTime = stVo.getPlanTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            // æœˆè§†å›¾æŸ¥è¯¢èŒƒå›´
            LocalDate startOfMonth = planTime.with(TemporalAdjusters.firstDayOfMonth());
            LocalDate endOfMonth = planTime.with(TemporalAdjusters.lastDayOfMonth());
            recordLqw.between(EimsInspectRecord::getPlanTime, startOfMonth, endOfMonth);
            // æ—¥è§†å›¾æŸ¥è¯¢èŒƒå›´
            if(type.equals("Day")){
                recordLqw.between(EimsInspectRecord::getPlanTime, planTime, planTime);
            }else {
            //  æœˆè§†å›¾æŸ¥è¯¢èŒƒå›´
                recordLqw.between(EimsInspectRecord::getPlanTime, startOfMonth, endOfMonth);
            }
            // æ‰§è¡ŒæŸ¥è¯¢
            List<EimsInspectRecordVo> recordList = recordMapper.selectVoList(recordLqw);
            // TODO æ ¹æ®å­—å…¸eims_inspect_status
@@ -96,7 +114,7 @@
            // TODO æ ¹æ®å­—å…¸eims_inspect_result
            Map<String, Long> rMap = recordList.stream()
                .filter(order ->order.getInspResult()!=null && List.of("1", "2").contains(order.getInspResult()))
                .collect(Collectors.groupingBy(EimsInspectRecordVo::getStatus, Collectors.counting()));
                .collect(Collectors.groupingBy(EimsInspectRecordVo::getInspResult, Collectors.counting()));
            stVo.setRecordCount(recordList.size());
            stVo.setUnCheckCount(cMap.getOrDefault("0", 0L).intValue());
@@ -157,6 +175,7 @@
        qw.like(StringUtils.isNotBlank(bo.getTitle()), "st.title", bo.getTitle());
        qw.like(StringUtils.isNotBlank(bo.getEquName()), "equ.equ_name", bo.getEquName());
        qw.like(StringUtils.isNotBlank(bo.getAssetNo()), "equ.asset_no", bo.getAssetNo());
        qw.eq( "st.type", bo.getType());
        qw.between(params.get("beginPlanTime") != null && params.get("endPlanTime") != null,
            "st.plan_time", params.get("beginPlanTime"), params.get("endPlanTime"));
        qw.eq(bo.getInspUser() != null, "st.maint_user", bo.getInspUser());
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsMaintOrderServiceImpl.java
@@ -21,7 +21,9 @@
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.eims.domain.EimsMaintPlan;
import org.dromara.eims.domain.bo.EimsMaintPlanBo;
import org.dromara.eims.domain.vo.EimsEquVo;
import org.dromara.eims.domain.vo.MaintOrdeGroupVo;
import org.dromara.eims.mapper.EimsEquMapper;
import org.dromara.eims.mapper.EimsMaintPlanMapper;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.vo.SysDeptVo;
@@ -49,6 +51,7 @@
    private final EimsMaintOrderMapper baseMapper;
    private final EimsMaintPlanMapper planMapper;
    private final SysDeptMapper sysDeptMapper;
    private final EimsEquMapper equMapper;
    /**
     * æŸ¥è¯¢ä¿å…»å·¥å•
@@ -58,7 +61,13 @@
     */
    @Override
    public EimsMaintOrderVo queryById(Long id){
        return baseMapper.selectVoById(id);
        EimsMaintOrderVo orderVo = baseMapper.selectVoById(id);
        EimsEquVo equVo = equMapper.selectVoById(orderVo.getEquId());
        if(equVo!=null){
            orderVo.setEquName(equVo.getEquName());
            orderVo.setAssetNo(equVo.getAssetNo());
        }
        return orderVo;
    }
    /**
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsInspectRecordMapper.xml
@@ -21,7 +21,7 @@
            ${ew.getCustomSqlSegment}
    </select>
    <!--TODO æ—¥è§†å›¾å’Œæœˆè§†å›¾ç»Ÿä¸€æŽ¥å£ï¼Œå¾…删除-->
    <select id="selectInspRecordDayList" resultMap="EimsInspectStVoResult">
        SELECT
            MAX(ir.id) id,
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsInspectStMapper.xml
@@ -6,7 +6,7 @@
    <resultMap type="org.dromara.eims.domain.vo.EimsInspectStVo" id="MaintInspStVoResult">
    </resultMap>
    <select id="selectInspStList" resultMap="MaintInspStVoResult">
        SELECT st.*,equ.equ_name equName,equ.asset_no assetNo,    DATE_FORMAT(st.plan_time, '%Y-%m') as planTimeStr FROM eims_inspect_st st
        SELECT st.*,equ.equ_name equName,equ.asset_no assetNo,    DATE_FORMAT(st.plan_time, '%Y-%m-%d') as planTimeStr FROM eims_inspect_st st
                                             LEFT JOIN eims_equ equ on st.equ_id = equ.equ_id
            ${ew.getCustomSqlSegment}
    </select>