车间能级提升-智能设备管理系统
baoshiwei
2025-06-12 bab490d2da009c1a23b352b3b964e0c2dd06a0b3
移动端功能优化
已添加3个文件
已修改23个文件
2031 ■■■■ 文件已修改
eims-ui-mobile/src/components/repair/req-card.vue 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/components/repair/res-card.vue 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-record.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-st.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/maint/maint-order.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/maint/order-detail.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/repair-add.vue 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/repair-fb.vue 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/req-detail.vue 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/req-list.vue 270 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/res-detail.vue 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/res-list.vue 509 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/scan/index.vue 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/types/uni-pages.d.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/utils/RoleUtils.ts 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsRepairRes.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsInspectPlanBo.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsRepairReqBo.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsRepairResBo.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsRepairResVo.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsInspectStServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsRepairFbServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsRepairReqServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsRepairResServiceImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/components/repair/req-card.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,189 @@
<template>
  <wd-card type="rectangle" :key="item.id">
    <template #title>
      <view class="flex justify-between items-center">
        <view class="flex items-center menu-title-box">
          <view class="menu-indicator"></view>
          <text class="ml-1 text-sm">{{ item.code }}</text>
        </view>
        <view>
          <wd-button size="small" v-if="item.status === '0'" type="info">待接单</wd-button>
          <wd-button size="small" v-else-if="item.status === '1'" type="warning">已接单</wd-button>
          <wd-button size="small" v-else-if="item.status === '2'" type="primary">维修中</wd-button>
          <wd-button size="small" v-else-if="item.status === '3'" type="success">已完成</wd-button>
        </view>
      </view>
    </template>
    <wd-swipe-action>
      <view class="flex items-center" @click.stop="itemClick(item)">
        <image
          v-if="item.reqType === '1'"
          class="slot-img text-center"
          src="/static/ico/ico-huiyi.png"
        />
        <image
          v-else-if="item.reqType === '2'"
          class="slot-img text-center"
          src="/static/ico/ico-setting.png"
        />
        <image
          v-else-if="item.reqType === '3'"
          class="slot-img text-center"
          src="/static/ico/ico-faxian.png"
        />
        <view class="flex-1 mt-1">
          <view class="text-color-base">
            <template v-if="item.reqType === '1'">
              <text>设备类型</text>
              <text class="mx-2">|</text>
              <text>{{ item.equName }}</text>
            </template>
            <template v-if="item.reqType === '2'">
              <text>工具类型</text>
              <text class="mx-2">|</text>
              <text>{{ item.fixtureName }}</text>
            </template>
            <template v-if="item.reqType === '3'">
              <text>其他类型</text>
            </template>
            <view class="text-color-gray mt-1 text-mini">
              <text>发生时间: {{ item.occTime }}</text>
            </view>
            <view class="text-color-gray mt-1 text-mini">
              <text>报修人: {{ item.reqUserName }}</text>
            </view>
            <view class="text-color-gray mt-1 text-mini">
              <text>描述: {{ item.reqDesc }}</text>
            </view>
            <view class="text-color-gray mt-1 text-mini">
              <text>紧急程度: </text>
              <wd-tag type="danger" v-if="item.urgencyLevel === '1'">紧急</wd-tag>
              <wd-tag type="warning" v-else-if="item.urgencyLevel === '2'">一般</wd-tag>
              <wd-tag type="success" v-else-if="item.urgencyLevel === '3'">普通</wd-tag>
            </view>
          </view>
          <view class="text-color-gray text-sm mt-1">
            {{ item.location }} {{ item.madeIn }}
          </view>
        </view>
        <view v-if="isRepair() || isEquAdmin()">
          <wd-button size="small" icon="edit-outline" @click.stop="handleSelectReq(item)">
            æŽ¥å•
          </wd-button>
        </view>
      </view>
      <template #right>
        <view class="h-full px-3 flex items-center">
          <wd-button size="small" type="error" @click.stop="handleDelete(item)">删除</wd-button>
        </view>
      </template>
    </wd-swipe-action>
  </wd-card>
</template>
<script setup lang="ts">
import { useToast, useMessage } from 'wot-design-uni'
import type { RepairReqVO } from '@/service/repair.d'
import {addRepairRes} from "@/service/repair";
import { isEquAdmin, isRepair } from "@/utils/RoleUtils";
import { useUserStore } from "@/store";
const userStore = useUserStore()
const message = useMessage()
const toast = useToast()
defineProps({
  item: {
    type: Object as () => RepairReqVO,
    required: true
  }
})
const emit = defineEmits(['click'])
function itemClick(item) {
  emit('click', item)
  // è·³è½¬åˆ°æŠ¥ä¿®å•详情页面
  uni.navigateTo({
    url: '/pages/repair/req-detail?id=' + item.id
  })
}
function handleSelectReq(item: any) {
  // å¼¹å‡ºç¡®è®¤æ˜¯å¦æŽ¥å•弹窗
  message.confirm({
    msg: '确定接单?',
    title: '提示',
    beforeConfirm: ({ resolve }) => {
      resolve(true)
      addNewRepairRes(item)
    },
  })
}
function addNewRepairRes(data: any) {
  console.error(data)
  // é€‰æ‹©æŠ¥ä¿®å•后,修改报修单状态和新增维修工单
  const resCode = `WXD${data.code.slice(3)}`
  const deptId = userStore?.userInfo?.deptId
  const userId = userStore?.userInfo?.userId
  const resData = {
    reqId: data.id,
    reqCode: data.code,
    reqUser: data.reqUser,
    reqDept: data.reqDept,
    resCode,
    status: '1',
    resUser: userId,
    resDept: deptId,
  }
  addRepairRes(resData)
    .then((res: any) => {
      if (res.code === 200) {
        toast.success(res?.msg || '操作成功')
        uni.$emit('list-refresh')
      } else {
        toast.error(res?.msg || '生成维修工单失败,请重试')
      }
    })
    .catch((res) => {
      toast.error(res?.msg || '生成维修工单失败,请重试')
    })
}
</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__title-content) {
  padding: 16rpx !important;
}
:deep(.wd-card__content) {
  padding: 16rpx !important;
}
:deep(.wd-card__footer) {
  padding: 10rpx !important;
}
</style>
eims-ui-mobile/src/components/repair/res-card.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,236 @@
<template>
  <wd-card type="rectangle" :key="item.id">
    <template #title>
      <view class="flex justify-between items-center">
        <view class="flex items-center menu-title-box">
          <view class="menu-indicator"></view>
          <text class="ml-1 text-sm">{{ item.resCode }}</text>
          <text class="text-color-gray ml-2 text-mini">接单:{{ item.createTime }}</text>
        </view>
        <view>
          <wd-tag size="small" v-if="item.status === '1'" type="warning">已接单</wd-tag>
          <wd-tag size="small" v-else-if="item.status === '2'" type="primary">维修中</wd-tag>
          <wd-tag size="small" v-else-if="item.status === '3'" type="success">已完成</wd-tag>
        </view>
      </view>
    </template>
    <view class="flex h-[240rpx] items-center">
      <image
        v-if="item.reqType === '1'"
        class="slot-img text-center"
        src="/static/ico/ico-huiyi.png"
      />
      <image
        v-else-if="item.reqType === '2'"
        class="slot-img text-center"
        src="/static/ico/ico-setting.png"
      />
      <image
        v-else-if="item.reqType === '3'"
        class="slot-img text-center"
        src="/static/ico/ico-faxian.png"
      />
      <view class="flex-1 mt-1">
        <view class="text-color-base">
          <template v-if="item.reqType === '1'">
            <text>设备类型</text>
            <text class="mx-2">|</text>
            <text>{{ item.equName }}</text>
          </template>
          <template v-if="item.reqType === '2'">
            <text>工具类型</text>
            <text class="mx-2">|</text>
            <text>{{ item.fixtureName }}</text>
          </template>
          <template v-if="item.reqType === '3'">
            <text>其他类型</text>
          </template>
          <view class="text-color-gray mt-1 text-mini">
            <text>报修人: {{ item.reqUserName }}</text>
          </view>
          <view class="text-color-gray mt-1 text-mini">
            <text>报修时间: {{ item.reqTime }}</text>
          </view>
          <view class="text-color-gray mt-1 text-mini">
            <text>维修开始: {{ item.startTime }}</text>
          </view>
          <view class="text-color-gray mt-1 text-mini">
            <text>维修结束: {{ item.endTime }}</text>
          </view>
          <view class="text-color-gray mt-1 text-mini">
            <text>维修人: {{ item.resUserName }}</text>
          </view>
        </view>
      </view>
      <view>
        <template v-if="item.status === '3'">
          <view class="h-full flex flex-col">
            <wd-button size="small" icon="warn-bold" @click.stop="itemClick(item)">
              è¯¦æƒ…
            </wd-button>
            <wd-button
              class="mt-4"
              size="small"
              icon="edit-outline"
              @click.stop="goToFeedBack(item)"
              type="warning"
            >
              {{ item.fbId == null ? '写评价' : '查看评价' }}
            </wd-button>
          </view>
        </template>
        <!--操作工或维修工角色-->
        <template v-if="isRepair() || isEquAdmin()">
          <wd-button
            v-if="item.status === '1'"
            size="small"
            icon="edit-outline"
            @click.stop="handleStartRepair(item)"
          >
            å¼€å§‹ç»´ä¿®
          </wd-button>
          <wd-button
            v-else-if="item.status === '2'"
            size="small"
            icon="edit-outline"
            @click.stop="itemClick(item)"
            type="success"
          >
            å®Œæˆç»´ä¿®
          </wd-button>
        </template>
      </view>
    </view>
  </wd-card>
</template>
<script setup lang="ts">
import type { RepairResVO } from '@/service/repair.d'
import { isEquAdmin, isLeader, isLineOrRepair, isRepair } from "@/utils/RoleUtils";
import { formatDate } from "@/utils/DateUtils";
import { updateRepairRes } from "@/service/repair";
import { useToast, useMessage } from 'wot-design-uni'
import { useUserStore } from "@/store";
const userStore = useUserStore()
const message = useMessage()
const toast = useToast()
defineProps({
  item: {
    type: Object as () => RepairResVO,
    required: true
  }
})
const emit = defineEmits(['click'])
function handleClick(item) {
  emit('click', item)
}
/**
 * å¼€å§‹ç»´ä¿®
 * @param item
 */
function handleStartRepair(item: any) {
  // ç¡®è®¤å¼€å§‹ä¿®æ”¹çŠ¶æ€ä¸º2-维修中
  const data = Object.assign({}, item)
  // ç¡®è®¤å¼€å§‹ä¿®æ”¹çŠ¶æ€ä¸º2-维修中
  data.status = '2'
  // è®¾ç½®å¼€å§‹ç»´ä¿®æ—¶é—´
  data.startTime = formatDate(new Date())
  message
    .confirm({
      msg: '确定开始维修?',
      title: '提示',
      beforeConfirm: ({ resolve }) => {
        updateRepair(data, resolve)
      },
    })
    .then(() => {})
    .catch((error) => {
      console.log(error)
    })
}
/**
 * æ›´æ–°ç»´ä¿®å·¥å•
 * @param data
 * @param resolve
 */
function updateRepair(data: any, resolve: any) {
  updateRepairRes(data)
    .then((res: any) => {
      resolve(true)
      if (res?.code === 200) {
        uni.$emit('list-refresh')
        // ç»´ä¿®ä¸­çŠ¶æ€æ‰éœ€è¦è·³è½¬
        if (data?.status === '3') {
          goToDetail(data)
        }
      }
    })
    .catch((res) => {
      console.error(res)
    })
}
/**
 * æ¡ç›®ç‚¹å‡»äº‹ä»¶
 * @param item
 */
function itemClick(item: any) {
  goToDetail(item)
}
function goToDetail(item) {
  uni.navigateTo({
    url: `/pages/repair/res-detail?id=${item.id}`,
  })
}
function goToFeedBack(item) {
  uni.navigateTo({
    url: `/pages/repair/repair-fb?id=${item.id}`,
  })
}
</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__title-content) {
  padding: 16rpx !important;
}
:deep(.wd-card__content) {
  padding: 16rpx !important;
}
:deep(.wd-card__footer) {
  padding: 10rpx !important;
}
</style>
eims-ui-mobile/src/pages.json
@@ -200,6 +200,16 @@
      }
    },
    {
      "path": "pages/repair/req-detail",
      "type": "page",
      "layout": "default",
      "needLogin": true,
      "style": {
        "navigationBarTitleText": "报修详情",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/repair/req-list",
      "type": "page",
      "layout": "default",
eims-ui-mobile/src/pages/inspect/insp-record.vue
@@ -124,7 +124,7 @@
            </view>
          </template>
          <view v-if="item.showDesc" class="mt-2">
          <view v-if="item.inspResult === '2'" class="mt-2">
            <wd-input
              v-model="item.inspDesc"
              placeholder="请输入异常描述"
@@ -177,8 +177,8 @@
      />
      <!-- æ–°å¢žæäº¤æŒ‰é’® -->
      <view class="flex justify-around mt-4">
        <wd-button type="primary" block size="large" v-if="inspSt.status === '0'" @click="handleClickRight">提交</wd-button>
        <wd-button type="success" block size="large" v-if="isLeader() && inspSt.status === '1'" @click="handleComplete">确认完成</wd-button>
        <wd-button type="primary" block size="large" v-if="inspSt.status === '0' || inspSt.status === '1'" @click="handleClickRight">提交</wd-button>
<!--        <wd-button type="success" block size="large" v-if="isLeader() && inspSt.status === '1'" @click="handleComplete">确认完成</wd-button>-->
      </view>
    </view>
  </z-paging>
@@ -186,7 +186,7 @@
<script setup lang="ts">
import { useUserStore, useAccessStore, useSystemConfigStore } from '@/store'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
import { isLeader, isLineOrRepair } from '@/utils/RoleUtils'
import {
  getInspStRecordList,
  getInspSt,
@@ -244,8 +244,8 @@
  status: '',
  inspUser: '',
  specialNote: '',
  runTimes: 0,
  faultTimes: 0,
  runTimes: undefined,
  faultTimes: undefined,
})
const paging = ref(null)
@@ -280,13 +280,6 @@
  // ä¿®æ”¹æ—¶é—´æ ¼å¼ä¸º YYYY-MM-DD HH:mm:ss
  const now = new Date()
  item.inspTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`
  if (item.inspResult === '2') {
    item.showDesc = true
  } else {
    item.showDesc = false
    item.inspDesc = ''
  }
}
const goBack = () => {
@@ -294,8 +287,11 @@
}
function handleClickRight() {
  if (isOperatorOrRepair()) {
  if (inspSt.status === '0') {
    handleConfirm()
  } else if (inspSt.status === '1') {
    handleComplete()
  }
}
@@ -368,6 +364,7 @@
  } else {
    inspSt.status = '0'
  }
  updateInspectSt(inspSt)
    .then((res: any) => {
      toast.success('操作成功')
@@ -385,15 +382,18 @@
 * ç¡®è®¤å®ŒæˆæŒ‰é’®ç‚¹å‡»äº‹ä»¶
 */
function handleComplete() {
  if (!isLeader()) {
    toast.info('无权限操作');
    return;
  console.log('handleComplete', inspSt)
  if (!inspSt.runTimes || !inspSt.faultTimes) {
    message.alert('请填写运行次数和故障次数!')
    return false
  }
  const now = new Date();
  const data: any = Object.assign(
    {},
    {
      id: inspSt.id,
      runTimes: inspSt.runTimes,
      faultTimes: inspSt.faultTimes,
      status: '2',
      verifyUser: userStore?.userInfo?.userId,
      verifyTime: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`
eims-ui-mobile/src/pages/inspect/insp-st.vue
@@ -78,7 +78,8 @@
<script setup lang="ts">
import { getInspStList } from '@/service/inspect'
import dayjs from 'dayjs'
import { useUserStore, useAccessStore, useSystemConfigStore } from '@/store'
import { isLeader, isLineOrRepair, isOperator } from "@/utils/RoleUtils";
// æ ‡ç­¾é¡µç›¸å…³
const activeTab = ref(0) // é»˜è®¤é€‰ä¸­ç¬¬ä¸€ä¸ªæ ‡ç­¾é¡µï¼ˆå¾…点检)
@@ -98,7 +99,7 @@
])
const equList = ref<Record<string, any>[]>([{ label: '所有设备', value: '所有设备' }])
const userStore = useUserStore()
// æ ‡ç­¾é¡µåˆ‡æ¢å¤„理函数
function handleTabChange({ index }) {
  // æ ¹æ®æ ‡ç­¾é¡µç´¢å¼•设置对应的状态值
@@ -142,6 +143,14 @@
  } else {
    delete params.planTime
  }
  console.log('queryList::', isLineOrRepair(), isOperator(), userStore.userInfo)
  if (isLineOrRepair() || isOperator()) {
    params.updateBy = userStore.userInfo.userId
  } else if (isLeader()) {
    delete params.updateBy
  }
  console.log('params::', params)
  getInspStList(params)
    .then((res: any) => {
      // è¯·å‹¿åœ¨ç½‘络请求回调中给dataList赋值!!只需要调用complete就可以了
eims-ui-mobile/src/pages/maint/maint-order.vue
@@ -175,7 +175,7 @@
            >
              <wd-input
                v-model="part.name"
                label="名称"
                label="名称:"
                label-width="100rpx"
                placeholder="备件名称"
@@ -183,7 +183,8 @@
              />
              <wd-input
                v-model="part.quantity"
                label="数量" label-width="100rpx"
                label="数量:"
                label-width="100rpx"
                placeholder="数量" type="number" :maxlength="5" />
            </view>
          </view>
@@ -260,7 +261,7 @@
import { getMaintSt, getMaintStOrderList, updateMaintOrder, updateMaintSt, updateMaintOrderBatch } from '@/service/maint'
import { ref, reactive } from 'vue'
import { useToast, useMessage } from 'wot-design-uni'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
import { isLeader, isLineOrRepair } from '@/utils/RoleUtils'
import { formatDate } from '@/utils/DateUtils'
import { useUserStore } from "@/store";
import { getSpareList } from '@/service/spare'
@@ -561,9 +562,8 @@
}
function handleClickRight() {
  if(isOperatorOrRepair()){
    handleUpdateMaintSt()
  }
}
eims-ui-mobile/src/pages/maint/order-detail.vue
@@ -17,7 +17,7 @@
      safeAreaInsetTop
    >
      <template #right>
        <text v-if="isOperatorOrRepair()" class="text-white">提交</text>
        <text v-if="isLineOrRepair()" class="text-white">提交</text>
        <text v-else-if="isLeader()" class="text-white">验证</text>
      </template>
    </wd-navbar>
@@ -93,7 +93,7 @@
      <wd-cell
        title="保养完成(操作工)"
        title-width="200px"
        v-if="order.status === '1' && isOperatorOrRepair()"
        v-if="order.status === '1' && isLineOrRepair()"
      >
        <view style="text-align: right">
          <wd-switch v-model="isFinish" />
@@ -109,7 +109,7 @@
import { reactive, onMounted } from 'vue'
import { FormRules } from 'wot-design-uni/components/wd-form/types'
import { useToast, useMessage } from 'wot-design-uni'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
import { isLeader, isLineOrRepair } from '@/utils/RoleUtils'
const toast = useToast()
const message = useMessage()
@@ -238,7 +238,7 @@
        toast.warning('当前工单完成状态,不可操作')
        break
    }
  } else if (isOperatorOrRepair()) {
  } else if (isLineOrRepair()) {
    switch (order.status) {
      case '0':
        break
eims-ui-mobile/src/pages/repair/repair-add.vue
@@ -115,9 +115,9 @@
        />
        <wd-cell title="报修图片" title-width="200rpx" prop="fileList">
          <wd-upload
            :file-list="model.fileList"
            v-model:file-list="model.fileList"
            :action="VITE_UPLOAD_BASEURL"
            @change="handleFileChange"
            @success="handleUploadSuccess"
          ></wd-upload>
        </wd-cell>
@@ -130,9 +130,26 @@
          @open="openOccTime"
          @confirm="handleOccTimeConfirm"
        />
        <!-- <wd-picker
          :columns="urgencyList"
          label-key="dictLabel"
          value-key="dictValue"
          label="紧急程度"
          v-model="model.urgencyLevel"
          @confirm="handleConfirmUrgencyLevel"
        /> -->
        <wd-cell title="紧急程度" title-width="200rpx" prop="urgencyLevel">
          <wd-radio-group v-model="model.urgencyLevel" inline  shape="dot"> @change="handleConfirmUrgencyLevel">
          <wd-radio v-for="item in urgencyList" :value="item.dictValue">{{item.dictLabel}}</wd-radio>
        </wd-radio-group>
        </wd-cell>
      </wd-cell-group>
      <wd-cell-group custom-class="mt-2" title="其他信息" border>
      <wd-button style="margin: 20px" block @click="handleSubmit">提交</wd-button>
      <!-- <wd-cell-group custom-class="mt-2" title="其他信息" border>
        <wd-picker
          :columns="faultList"
          label-key="dictLabel"
@@ -149,7 +166,7 @@
          v-model="model.urgencyLevel"
          @confirm="handleConfirmUrgencyLevel"
        />
      </wd-cell-group>
      </wd-cell-group> -->
    </wd-form>
  </view>
</template>
@@ -314,7 +331,10 @@
  model.reqTime = formatDate(new Date())
  model.reqDept = userStore?.userInfo?.deptId
  model.reqUser = userStore?.userInfo?.userId
  model.faultPicture = model.fileList.join(',')
  console.log('model', model)
  const map = model.fileList?.map((file) => file.url)
  console.log('map', map)
  model.faultPicture = map.join(',')
  addRepairReq(model)
    .then((res: any) => {
      if (res?.code === 200) {
@@ -326,17 +346,21 @@
      }
    })
    .catch((res) => {
      console.error('添加报修失败:', res)
      toast.error(res?.data?.msg || '请求失败')
    })
}
/**
 * ä¸Šä¼ ç‚¹æ£€å›¾ç‰‡
 * @param fileList
 */
function handleFileChange({ fileList }) {
  console.log('fileList:', fileList)
  model.fileList = fileList
function handleUploadSuccess({ file, fileList }) {
  console.log('handleUploadSuccess', file)
  // åˆ¤æ–­ file.response是不是 å¯¹è±¡ï¼Œä¸æ˜¯å¯¹è±¡å°†json字符串转换为对象
  if (typeof file.response === 'string') {
    file.response = JSON.parse(file.response)
    console.log('file.response', file.response)
    file.ossId = file.response.data.ossId
    file.url = file.response.data.url
  }
}
function checkData() {
@@ -371,11 +395,14 @@
 */
function openOccTime() {
  occTime.value = Date.now()
  console.log('openOccTime', occTime)
  model.occTime = formatDate(new Date(occTime.value))
}
/**
 * ç¡®è®¤é€‰æ‹©å‘生时间
 */
function handleOccTimeConfirm({ value }) {
  console.log('handleOccTimeConfirm', value)
  model.occTime = formatDate(new Date(value))
}
eims-ui-mobile/src/pages/repair/repair-fb.vue
@@ -57,30 +57,13 @@
        </view>
      </template>
      <wd-cell title="维修满意度">
        <wd-rate v-model="repairFb.repairSatisfaction" change="handleChange"></wd-rate>
        <wd-radio-group v-model="repairFb.repairSatisfaction" inline shape="dot">
          <wd-radio :value="0">不满意</wd-radio>
          <wd-radio :value="1">满意</wd-radio>
        </wd-radio-group>
      </wd-cell>
      <wd-cell title="维修及时性">
        <wd-rate v-model="repairFb.repairTimeliness" change="handleChange"></wd-rate>
      </wd-cell>
      <wd-cell title="维修及态度">
        <wd-rate v-model="repairFb.serviceAttitude" change="handleChange"></wd-rate>
      </wd-cell>
      <wd-cell title="维修现场6S">
        <wd-rate v-model="repairFb.repairSs" change="handleChange"></wd-rate>
      </wd-cell>
      <wd-textarea
        label="结果反馈"
        label-width="200rpx"
        type="textarea"
        v-model="repairFb.fbResult"
        auto-height
        :maxlength="200"
        show-word-limit
        placeholder="请输入结果反馈"
        clearable
      />
      <wd-textarea
        v-if="repairFb.repairSatisfaction === 0"
        label="意见或建议"
        label-width="200rpx"
        type="textarea"
@@ -90,8 +73,12 @@
        show-word-limit
        placeholder="请输入意见或建议"
        clearable
        required
      />
    </wd-cell-group>
    <wd-button style="margin: 20px" block @click="handleRepairFb">提交</wd-button>
  </view>
</template>
<script setup lang="ts">
@@ -103,9 +90,11 @@
  updateRepairFb,
} from '@/service/repair'
import { reactive } from 'vue'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
import { isLeader, isLineOrRepair } from '@/utils/RoleUtils'
import { RepairResVO, RepairFbVO, RepairRecordVO } from '@/service/repair.d'
import { useUserStore } from "@/store";
const userStore = useUserStore()
const isUpdate = ref(false)
const repairRes = reactive<RepairResVO>({
  id: '',
@@ -129,7 +118,7 @@
  resCode: '',
  fbResult: '',
  suggestions: '',
  repairSatisfaction: 0,
  repairSatisfaction: 1,
  repairTimeliness: 0,
  serviceAttitude: 0,
  repairSs: 0,
@@ -192,9 +181,16 @@
}
function handleRepairFb() {
  if(isOperatorOrRepair()){
  if(repairRes.reqUser !== userStore.userInfo.userId){
    uni.showToast({
      title: '无权限,请登录管理员账号操作',
      title: '只有请求人才有评价权限',
      icon: 'none',
    })
    return false
  }
  if (repairFb.repairSatisfaction === 0 && !repairFb.suggestions) {
    uni.showToast({
      title: '请填写意见或建议',
      icon: 'none',
    })
    return false
@@ -209,6 +205,8 @@
            title: '修改成功',
            icon: 'none',
          })
          uni.navigateBack()
          uni.$emit('res-list-refresh')
        }
      })
      .catch((res) => {})
@@ -220,6 +218,8 @@
            title: '评价成功',
            icon: 'none',
          })
          uni.navigateBack()
          uni.$emit('res-list-refresh')
        }
      })
      .catch((res) => {})
eims-ui-mobile/src/pages/repair/req-detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,252 @@
<route lang="json5" type="page">
{
  layout: 'default',
  needLogin: true,
  style: {
    navigationBarTitleText: '报修详情',
    navigationStyle: 'custom'
  },
}
</route>
<template>
  <view class="bg-base container" safeAreaInsetTopBottom>
    <wd-navbar
      title="报修详情"
      left-arrow
      @click-left="goBack"
      custom-style="background: #4D80F0;"
      safeAreaInsetTop
    ></wd-navbar>
    <view class="content">
      <!-- çŠ¶æ€å±•ç¤º -->
      <view class="status-card">
        <view class="status-header">
          <text class="status-code">{{ repairReq.code }}</text>
          <view class="status-tag">
            <wd-button size="small" v-if="repairReq.status === '0'" type="info">待接单</wd-button>
            <wd-button size="small" v-else-if="repairReq.status === '1'" type="warning">已接单</wd-button>
            <wd-button size="small" v-else-if="repairReq.status === '2'" type="primary">维修中</wd-button>
            <wd-button size="small" v-else-if="repairReq.status === '3'" type="success">已完成</wd-button>
          </view>
        </view>
      </view>
      <!-- è®¾å¤‡ä¿¡æ¯ -->
      <wd-cell-group
        v-if="repairReq.reqType === '1'"
        custom-class="mt-2"
        title="设备信息"
        use-slot
        border
      >
        <wd-cell title="设备名称" title-width="200rpx">
          <text>{{ repairReq.equName }}</text>
        </wd-cell>
        <wd-cell title="资产编号" title-width="200rpx">
          <text>{{ repairReq.assetNo }}</text>
        </wd-cell>
      </wd-cell-group>
      <!-- å·¥å…·ä¿¡æ¯ -->
      <wd-cell-group
        v-if="repairReq.reqType === '2'"
        custom-class="mt-2"
        title="工具信息"
        use-slot
        border
      >
        <wd-cell title="工具名称" title-width="200rpx">
          <text>{{ repairReq.fixtureName }}</text>
        </wd-cell>
      </wd-cell-group>
      <!-- æŠ¥ä¿®ä¿¡æ¯ -->
      <wd-cell-group custom-class="mt-2" title="报修信息" use-slot border>
        <wd-cell title="报修类型" title-width="200rpx">
          <text v-if="repairReq.reqType === '1'">设备类型</text>
          <text v-else-if="repairReq.reqType === '2'">工具类型</text>
          <text v-else-if="repairReq.reqType === '3'">其他类型</text>
        </wd-cell>
        <wd-cell title="发生时间" title-width="200rpx">
          <text>{{ repairReq.occTime }}</text>
        </wd-cell>
        <wd-cell title="报修时间" title-width="200rpx">
          <text>{{ repairReq.reqTime }}</text>
        </wd-cell>
        <wd-cell title="报修人" title-width="200rpx">
          <text>{{ repairReq.reqUserName }}</text>
        </wd-cell>
        <wd-cell title="紧急程度" title-width="200rpx">
          <wd-tag type="danger" v-if="repairReq.urgencyLevel === '1'">紧急</wd-tag>
          <wd-tag type="warning" v-else-if="repairReq.urgencyLevel === '2'">一般</wd-tag>
          <wd-tag type="success" v-else-if="repairReq.urgencyLevel === '3'">普通</wd-tag>
        </wd-cell>
      </wd-cell-group>
      <!-- æ•…障描述 -->
      <wd-cell-group custom-class="mt-2" title="故障描述" use-slot border>
        <view class="description-box">
          <text>{{ repairReq.reqDesc }}</text>
        </view>
      </wd-cell-group>
      <!-- æ•…障图片 -->
      <wd-cell-group v-if="repairReq.faultPicture" custom-class="mt-2" title="故障图片" use-slot border>
        <view class="image-box">
          <image
            class="fault-image"
            :src="repairReq.faultPicture"
            mode="aspectFit"
            @click="previewImage"
          />
        </view>
      </wd-cell-group>
    </view>
  </view>
</template>
<script setup lang="ts">
import { reactive, onMounted } from 'vue'
import { useToast } from 'wot-design-uni'
import { getRepairReqList } from '@/service/repair'
import type { RepairReqVO } from '@/service/repair.d'
const toast = useToast()
// æŠ¥ä¿®è¯·æ±‚数据
const repairReq = reactive<RepairReqVO>({
  id: '',
  code: '',
  status: '',
  occTime: '',
  reqTime: '',
  reqDept: 0,
  reqUser: 0,
  reqDesc: '',
  urgencyLevel: '',
  faultPicture: '',
  reqType: '',
  equId: '',
  repairId: '',
  repairDept: 0,
  repairUser: 0,
  faultType: '',
  remark: '',
  reqUserName: '',
  equName: '',
  assetNo: '',
  fixtureName: ''
})
// èŽ·å–æŠ¥ä¿®è¯·æ±‚è¯¦æƒ…
function getRepairReqDetail(id: string | number) {
  getRepairReqList({ id })
    .then((res: any) => {
      if (res && res.rows && res.rows.length > 0) {
        Object.assign(repairReq, res.rows[0])
      } else {
        toast.error('获取报修详情失败')
      }
    })
    .catch((err) => {
      console.error('获取报修详情失败', err)
      toast.error('获取报修详情失败')
    })
}
// é¢„览图片
function previewImage() {
  if (repairReq.faultPicture) {
    uni.previewImage({
      urls: [repairReq.faultPicture],
      current: repairReq.faultPicture
    })
  }
}
// è¿”回上一页
function goBack() {
  uni.navigateBack()
}
// é¡µé¢åŠ è½½æ—¶èŽ·å–æŠ¥ä¿®è¯¦æƒ…
onLoad((options) => {
  if (options && options.id) {
    getRepairReqDetail(options.id)
  } else {
    toast.error('参数错误')
    setTimeout(() => {
      goBack()
    }, 1500)
  }
})
</script>
<style scoped lang="scss">
.container {
  min-height: 100vh;
  background-color: #f5f5f5;
}
.content {
  padding: 20rpx;
}
.status-card {
  background-color: #ffffff;
  border-radius: 12rpx;
  padding: 24rpx;
  margin-bottom: 20rpx;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.status-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.status-code {
  font-size: 32rpx;
  font-weight: bold;
  color: #333333;
}
.description-box {
  padding: 24rpx;
  background-color: #f9f9f9;
  border-radius: 8rpx;
  margin: 16rpx;
  color: #666666;
  line-height: 1.6;
}
.image-box {
  padding: 24rpx;
  display: flex;
  justify-content: center;
}
.fault-image {
  width: 80%;
  height: 400rpx;
  border-radius: 8rpx;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
:deep(.wd-cell-group__title) {
  font-size: 28rpx;
  font-weight: bold;
  color: #4D80F0;
}
:deep(.wd-cell__title) {
  color: #666666;
}
:deep(.wd-cell__value) {
  color: #333333;
  font-weight: 500;
}
</style>
eims-ui-mobile/src/pages/repair/req-list.vue
@@ -23,125 +23,134 @@
        custom-style="background: #4D80F0;"
        safeAreaInsetTop
      ></wd-navbar>
      <wd-drop-menu v-if="!isSelectReq">
        <wd-drop-menu-item
          v-model="reqTypeId"
          label-key="dictLabel"
          value-key="dictValue"
          :options="reqTypeList"
          @change="handleReqType"
        />
        <wd-drop-menu-item
          v-model="filterDate"
          :options="filterDateList"
          @change="handleFilterDate"
        />
        <wd-drop-menu-item
          v-model="status"
          label-key="dictLabel"
          value-key="dictValue"
          :options="statusList"
          @change="handleReqStatu"
        />
      </wd-drop-menu>
<!--      <wd-drop-menu v-if="!isSelectReq">-->
<!--        <wd-drop-menu-item-->
<!--          v-model="reqTypeId"-->
<!--          label-key="dictLabel"-->
<!--          value-key="dictValue"-->
<!--          :options="reqTypeList"-->
<!--          @change="handleReqType"-->
<!--        />-->
<!--        <wd-drop-menu-item-->
<!--          v-model="filterDate"-->
<!--          :options="filterDateList"-->
<!--          @change="handleFilterDate"-->
<!--        />-->
<!--        <wd-drop-menu-item-->
<!--          v-model="status"-->
<!--          label-key="dictLabel"-->
<!--          value-key="dictValue"-->
<!--          :options="statusList"-->
<!--          @change="handleReqStatu"-->
<!--        />-->
<!--      </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-center">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-sm">{{ item.code }}</text>
            </view>
      <ReqCard
        v-for="item in dataList"
        :key="item.id"
        :item="item"
        @itemClick="itemClick"
      ></ReqCard>
<!--      <wd-card type="rectangle" v-for="(item, index) in dataList" :key="item.id">-->
<!--        <template #title>-->
<!--          <view class="flex justify-between items-center">-->
<!--            <view class="flex items-center menu-title-box">-->
<!--              <view class="menu-indicator"></view>-->
<!--              <text class="ml-1 text-sm">{{ item.code }}</text>-->
<!--            </view>-->
            <view>
              <wd-button size="small" v-if="item.status === '0'" type="info">待接单</wd-button>
              <wd-button size="small" v-else-if="item.status === '1'" type="warning">
                å·²æŽ¥å•
              </wd-button>
              <wd-button size="small" v-else-if="item.status === '2'" type="primary">
                ç»´ä¿®ä¸­
              </wd-button>
              <wd-button size="small" v-else-if="item.status === '3'" type="success">
                å·²å®Œæˆ
              </wd-button>
            </view>
          </view>
        </template>
        <wd-swipe-action>
          <view class="flex h-[160rpx] items-center" @click.stop="itemClick(item)">
            <image
              v-if="item.reqType === '1'"
              class="slot-img text-center"
              src="/static/ico/ico-huiyi.png"
            />
            <image
              v-else-if="item.reqType === '2'"
              class="slot-img text-center"
              src="/static/ico/ico-setting.png"
            />
            <image
              v-else-if="item.reqType === '3'"
              class="slot-img text-center"
              src="/static/ico/ico-faxian.png"
            />
            <view class="flex-1 mt-1">
              <view class="text-color-base">
                <template v-if="item.reqType === '1'">
                  <text>设备类型</text>
                  <text class="mx-2">|</text>
                  <text>{{ item.equName }}</text>
                </template>
<!--            <view>-->
<!--              <wd-button size="small" v-if="item.status === '0'" type="info">待接单</wd-button>-->
<!--              <wd-button size="small" v-else-if="item.status === '1'" type="warning">-->
<!--                å·²æŽ¥å•-->
<!--              </wd-button>-->
<!--              <wd-button size="small" v-else-if="item.status === '2'" type="primary">-->
<!--                ç»´ä¿®ä¸­-->
<!--              </wd-button>-->
<!--              <wd-button size="small" v-else-if="item.status === '3'" type="success">-->
<!--                å·²å®Œæˆ-->
<!--              </wd-button>-->
<!--            </view>-->
<!--          </view>-->
<!--        </template>-->
<!--        <wd-swipe-action>-->
<!--          <view class="flex h-[160rpx] items-center" @click.stop="itemClick(item)">-->
<!--            <image-->
<!--              v-if="item.reqType === '1'"-->
<!--              class="slot-img text-center"-->
<!--              src="/static/ico/ico-huiyi.png"-->
<!--            />-->
<!--            <image-->
<!--              v-else-if="item.reqType === '2'"-->
<!--              class="slot-img text-center"-->
<!--              src="/static/ico/ico-setting.png"-->
<!--            />-->
<!--            <image-->
<!--              v-else-if="item.reqType === '3'"-->
<!--              class="slot-img text-center"-->
<!--              src="/static/ico/ico-faxian.png"-->
<!--            />-->
<!--            <view class="flex-1 mt-1">-->
<!--              <view class="text-color-base">-->
<!--                <template v-if="item.reqType === '1'">-->
<!--                  <text>设备类型</text>-->
<!--                  <text class="mx-2">|</text>-->
<!--                  <text>{{ item.equName }}</text>-->
<!--                </template>-->
                <template v-if="item.reqType === '2'">
                  <text>工具类型</text>
                  <text class="mx-2">|</text>
                  <text>{{ item.fixtureName }}</text>
                </template>
<!--                <template v-if="item.reqType === '2'">-->
<!--                  <text>工具类型</text>-->
<!--                  <text class="mx-2">|</text>-->
<!--                  <text>{{ item.fixtureName }}</text>-->
<!--                </template>-->
                <template v-if="item.reqType === '3'">
                  <text>其他类型</text>
                </template>
                <view class="text-color-gray mt-1 text-mini">
                  <text>发生时间: {{ item.occTime }}</text>
                </view>
<!--                <template v-if="item.reqType === '3'">-->
<!--                  <text>其他类型</text>-->
<!--                </template>-->
<!--                <view class="text-color-gray mt-1 text-mini">-->
<!--                  <text>发生时间: {{ item.occTime }}</text>-->
<!--                </view>-->
                <view class="text-color-gray mt-1 text-mini">
                  <text>报修时间: {{ item.reqTime }}</text>
                </view>
                <view class="text-color-gray mt-1 text-mini">
                  <text>报修人: {{ item.reqUserName }}</text>
                </view>
              </view>
              <view class="text-color-gray text-sm mt-1">
                {{ item.location }} {{ item.madeIn }}
              </view>
            </view>
            <view v-if="isSelectReq">
              <wd-button size="small" icon="edit-outline" @click.stop="handleSelectReq(item)">
                æŽ¥å•
              </wd-button>
            </view>
          </view>
          <template #right>
            <view class="h-full px-3 flex items-center">
              <wd-button size="small" type="error" @click.stop="handleDelete(item)">删除</wd-button>
            </view>
          </template>
        </wd-swipe-action>
      </wd-card>
<!--                <view class="text-color-gray mt-1 text-mini">-->
<!--                  <text>报修时间: {{ item.reqTime }}</text>-->
<!--                </view>-->
<!--                <view class="text-color-gray mt-1 text-mini">-->
<!--                  <text>报修人: {{ item.reqUserName }}</text>-->
<!--                </view>-->
<!--              </view>-->
<!--              <view class="text-color-gray text-sm mt-1">-->
<!--                {{ item.location }} {{ item.madeIn }}-->
<!--              </view>-->
<!--            </view>-->
<!--            <view v-if="isSelectReq">-->
<!--              <wd-button size="small" icon="edit-outline" @click.stop="handleSelectReq(item)">-->
<!--                æŽ¥å•-->
<!--              </wd-button>-->
<!--            </view>-->
<!--          </view>-->
<!--          <template #right>-->
<!--            <view class="h-full px-3 flex items-center">-->
<!--              <wd-button size="small" type="error" @click.stop="handleDelete(item)">删除</wd-button>-->
<!--            </view>-->
<!--          </template>-->
<!--        </wd-swipe-action>-->
<!--      </wd-card>-->
    </view>
  </z-paging>
</template>
<script setup lang="ts">
import { onMounted, getCurrentInstance, ref } from 'vue'
import { useToast, useMessage } from 'wot-design-uni'
import { getRepairReqList, delRepairReq } from '@/service/repair'
import { DICT_REPAIR_REQ_STATUS, DICT_REPAIR_REQ_TYPE, getDictInfo } from '@/service/dict'
import dayjs from "dayjs";
import { isEquAdmin, isRepair } from "@/utils/RoleUtils";
import { useUserStore } from "@/store";
import ReqCard from "@/components/repair/req-card.vue";
const userStore = useUserStore()
const message = useMessage()
const toast = useToast()
// æŠ¥ä¿®å•类型
@@ -180,31 +189,38 @@
  const queryParams: any = {
    pageNum,
    pageSize,
    reqType: reqTypeId.value,
    status: status.value,
    params: {},
  }
  if (reqTypeId.value === -1) {
    delete queryParams.reqType
  }
  if (status.value === -1) {
    delete queryParams.status
  }
  // if (reqTypeId.value === -1) {
  //   delete queryParams.reqType
  // }
  // if (status.value === -1) {
  //   delete queryParams.status
  // }
  //
  // if (filterDate.value === '1') {
  //   // èŽ·å–å½“å‰æ—¥æœŸ
  //   const now = dayjs()
  //   queryParams.params = {
  //     beginReqTime: now.startOf('day').format('YYYY-MM-DD 00:00:00'),
  //     endReqTime: now.endOf('day').format('YYYY-MM-DD 23:59:59'),
  //   }
  // } else if (filterDate.value === '2') {
  //   const now = dayjs()
  //   queryParams.params = {
  //     beginReqTime: now.startOf('month').format('YYYY-MM-DD 00:00:00'),
  //     endReqTime: now.endOf('month').format('YYYY-MM-DD 23:59:59'),
  //   }
  // } else {
  //   delete queryParams.params
  // }
  if (filterDate.value === '1') {
    // èŽ·å–å½“å‰æ—¥æœŸ
    const now = dayjs()
    queryParams.params = {
      beginReqTime: now.startOf('day').format('YYYY-MM-DD 00:00:00'),
      endReqTime: now.endOf('day').format('YYYY-MM-DD 23:59:59'),
    }
  } else if (filterDate.value === '2') {
    const now = dayjs()
    queryParams.params = {
      beginReqTime: now.startOf('month').format('YYYY-MM-DD 00:00:00'),
      endReqTime: now.endOf('month').format('YYYY-MM-DD 23:59:59'),
    }
  } else {
    delete queryParams.params
  // æŸ¥è¯¢æœªå®Œæˆçš„æŠ¥ä¿®å•
  queryParams.params.status = '0,1,2'
  queryParams.reqUser = userStore?.userInfo?.userId
  if (isRepair() || isEquAdmin()) {
    queryParams.params.status = '0'
    queryParams.reqUser = undefined
  }
  getRepairReqList(queryParams)
@@ -330,10 +346,10 @@
onLoad(() => {
  initData()
  uni.$on('req-list-refresh', reloadData)
  uni.$on('list-refresh', reloadData)
})
onUnload(() => {
  uni.$off('req-list-refresh', reloadData)
  uni.$off('list-refresh', reloadData)
})
</script>
eims-ui-mobile/src/pages/repair/res-detail.vue
@@ -30,7 +30,15 @@
        <wd-cell title="故障类型" title-width="200rpx" is-link>
          <text>{{ reqType?.dictLabel }}</text>
        </wd-cell>
        <wd-cell title="报修描述" :label="repairRes?.reqDesc" is-link />
        <wd-textarea
          label="报修描述"
          label-width="200rpx"
          type="textarea"
          v-model="repairRes.reqDesc"
          auto-height
          readonly
        />
      </wd-cell-group>
      <wd-cell-group
@@ -64,6 +72,11 @@
      </wd-cell-group>
      <wd-cell-group custom-class="mt-2" title="维修信息" use-slot border>
        <wd-cell title="故障类别" title-width="200rpx" prop="faultType">
          <wd-radio-group v-model="repairRes.faultType" inline  shape="dot"> >
            <wd-radio v-for="item in faultList" :value="item.dictValue">{{item.dictLabel}}</wd-radio>
          </wd-radio-group>
        </wd-cell>
        <wd-textarea
          label="原因分析"
          label-width="200rpx"
@@ -112,28 +125,90 @@
          clearable
        />
      </wd-cell-group>
      <wd-cell-group custom-class="mt-2" title="备件信息" use-slot border>
      <!-- å¤‡ä»¶ä¿¡æ¯å½•入区域 -->
      <view v-if="repairRes.spareParts && repairRes.spareParts.length > 0" class="mt-2">
        <view
          v-for="(part, partIndex) in repairRes.spareParts"
          :key="partIndex"
          class="flex justify-between mt-1"
        >
          <wd-input
            v-model="part.name"
            label="名称:"
            label-width="100rpx"
            placeholder="备件名称"
          />
          <wd-input
            v-model="part.quantity"
            label="数量:"
            label-width="100rpx"
            placeholder="数量" type="number" :maxlength="5" />
        </view>
      </view>
      <!-- ç»´ä¿®è¯´æ˜ŽåŒºåŸŸ -->
      <view class="mt-2 flex justify-end">
        <wd-button type="success" style="margin: 20px" @click.stop="addSparePart()">
          æ·»åР备件
        </wd-button>
      </view>
      <view class="h-[2px] w-full bg-base"></view>
      </wd-cell-group>
    </wd-form>
<!--    <wd-button style="margin: 20px" block @click="handleClickRight">提交</wd-button>-->
    <view class="h-[20px] w-full bg-base"></view>
  </view>
  <!-- å¤‡ä»¶é€‰æ‹©å¼¹å‡ºå±‚ -->
  <wd-popup v-model="showSparePopup" position="bottom" height="33.33vh">
    <view class="flex justify-between p-2 bg-white">
      <wd-button type="text" @click="closeSparePopup">取消</wd-button>
      <wd-button type="text" @click="addOtherSparePart">其他</wd-button>
    </view>
    <wd-input
      v-model="searchKeyword"
      placeholder="请输入备件名称或型号"
      clearable
      @input="filterSpareParts"
    />
    <view class="p-2">
      <view
        v-for="(part, index) in sparePartsList"
        :key="index"
        class="flex justify-between items-center p-2 border-b"
        @click="selectFilteredSparePart(part)"
      >
        <text>{{ part.name }} ({{ part.code }})</text>
        <text>剩余: {{ part.actualStock }}</text>
      </view>
    </view>
  </wd-popup>
</template>
<script setup lang="ts">
import { getRepairRes, updateRepairRes } from '@/service/repair'
import { RepairResVO } from '@/service/repair.d'
import { reactive, onMounted, ref } from 'vue'
import { FormRules } from 'wot-design-uni/components/wd-form/types'
import { useToast, useMessage } from 'wot-design-uni'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
import { DICT_REPAIR_REQ_TYPE, getDictInfo } from '@/service/dict'
import { isEquAdmin, isLeader, isLineOrRepair, isRepair } from "@/utils/RoleUtils";
import { DICT_REPAIR_FAULT_TYPE, DICT_REPAIR_REQ_TYPE, getDictInfo } from "@/service/dict";
import { formatDate } from '@/utils/DateUtils'
const toast = useToast()
const message = useMessage()
const showSparePopup = ref(false)
import { getSpareList } from '@/service/spare'
import { ref } from "vue";
const fileList = ref<[]>()
const selectedPartIndex = ref(-1)
const sparePartsList = ref([])
const searchKeyword = ref('')
// æŠ¥ä¿®å•类型
const reqTypeList = ref<any>([])
// æ•…障类别
const faultList = ref<any>([])
const repairRes = reactive<RepairResVO>({
  id: '',
  resCode: '',
@@ -158,6 +233,59 @@
  ],
}
function filterSpareParts() {
  if (!searchKeyword.value) {
    loadSpareParts()
  } else {
    loadSpareParts(searchKeyword.value)
  }
}
// å¤‡ä»¶é€‰æ‹©ç›¸å…³é€»è¾‘
function addSparePart() {
  // if (!item.spareParts) {
  //   item.spareParts = []
  // }
  repairRes.spareParts.value = []
  selectSparePart()
}
function selectSparePart(item: any) {
  showSparePopup.value = true
  loadSpareParts()
}
function loadSpareParts(value?: string) {
  getSpareList({ name: value, pageNum: 1, pageSize: 10 }).then((res: any) => {
    sparePartsList.value = res.rows
  })
}
function selectFilteredSparePart(part: any) {
  repairRes.spareParts.value.push({
    id: part.id,
    name: part.name,
    quantity: '',
  })
  closeSparePopup()
}
function addOtherSparePart() {
  repairRes.spareParts.value.push({
    id:  '',
    name: '',
    quantity: '',
  })
  closeSparePopup();
}
function closeSparePopup() {
  showSparePopup.value = false
  selectedPartIndex.value = -1
  searchKeyword.value = ''
}
function handleFileChange({ fileList }) {}
function initRepairRes(id: any) {
@@ -172,6 +300,8 @@
  initRepairRes(options.id)
  const reqList = await getDictInfo(DICT_REPAIR_REQ_TYPE)
  reqTypeList.value = reqList
  const fList = await getDictInfo(DICT_REPAIR_FAULT_TYPE)
  faultList.value = fList
}
function hanldeUpdateRepairRes(data: any, resolve: any) {
@@ -180,6 +310,7 @@
      resolve(true)
      toastSucces()
      uni.$emit('res-list-refresh')
      uni.navigateBack()
    })
    .catch((res) => {
      console.error(res)
@@ -207,9 +338,7 @@
onNavigationBarButtonTap((e) => {
  if (e.index === 0) {
    // ç®¡ç†å‘˜è§’色
    if (isLeader()) {
      toast.warning('当前登录角色不可操作')
    } else if (isOperatorOrRepair()) {
    if (isRepair() || isEquAdmin()) {
      switch (repairRes.status) {
        // å·²æŽ¥å•
        case '1':
@@ -232,9 +361,7 @@
      }
    }
  } else if (e.index === 1) {
    if (isLeader()) {
      toast.warning('当前登录角色不可操作')
    } else if (isOperatorOrRepair()) {
    if (isRepair() || isEquAdmin()) {
      switch (repairRes.status) {
        // å·²æŽ¥å•
        case '1':
eims-ui-mobile/src/pages/repair/res-list.vue
@@ -12,177 +12,38 @@
        title="设备维修"
        left-arrow
        @click-left="goBack"
        right-text="提交"
        @click-right="handleClickRight"
        custom-style="background: #4D80F0;"
        safeAreaInsetTop
      >
        <template #right>
          <text v-if="isOperatorOrRepair()" class="text-white">接单</text>
        </template>
<!--        <template #right>-->
<!--          <text v-if="isLineOrRepair()" class="text-white">接单</text>-->
<!--        </template>-->
      </wd-navbar>
      <wd-drop-menu>
        <wd-drop-menu-item
          v-model="resTypeId"
          label-key="dictLabel"
          value-key="dictValue"
          :options="resTypeList"
          @change="handleResType"
        />
        <wd-drop-menu-item
          v-model="filterDate"
          :options="filterDateList"
          @change="handleFilterDate"
        />
        <wd-drop-menu-item
          v-model="status"
          label-key="dictLabel"
          value-key="dictValue"
          :options="statusList"
          @change="handleResStatu"
        />
      </wd-drop-menu>
<!--      <wd-drop-menu>-->
<!--        <wd-drop-menu-item-->
<!--          v-model="resTypeId"-->
<!--          label-key="dictLabel"-->
<!--          value-key="dictValue"-->
<!--          :options="resTypeList"-->
<!--          @change="handleResType"-->
<!--        />-->
<!--        <wd-drop-menu-item-->
<!--          v-model="filterDate"-->
<!--          :options="filterDateList"-->
<!--          @change="handleFilterDate"-->
<!--        />-->
<!--        <wd-drop-menu-item-->
<!--          v-model="status"-->
<!--          label-key="dictLabel"-->
<!--          value-key="dictValue"-->
<!--          :options="statusList"-->
<!--          @change="handleResStatu"-->
<!--        />-->
<!--      </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-center">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-sm">{{ item.resCode }}</text>
              <text class="text-color-gray ml-2 text-mini">接单:{{ item.createTime }}</text>
            </view>
            <view>
              <wd-tag size="small" v-if="item.status === '1'" type="warning">已接单</wd-tag>
              <wd-tag size="small" v-else-if="item.status === '2'" type="primary">维修中</wd-tag>
              <wd-tag size="small" v-else-if="item.status === '3'" type="success">已完成</wd-tag>
            </view>
          </view>
        </template>
        <view class="flex h-[240rpx] items-center">
          <image
            v-if="item.reqType === '1'"
            class="slot-img text-center"
            src="/static/ico/ico-huiyi.png"
          />
          <image
            v-else-if="item.reqType === '2'"
            class="slot-img text-center"
            src="/static/ico/ico-setting.png"
          />
          <image
            v-else-if="item.reqType === '3'"
            class="slot-img text-center"
            src="/static/ico/ico-faxian.png"
          />
          <view class="flex-1 mt-1">
            <view class="text-color-base">
              <template v-if="item.reqType === '1'">
                <text>设备类型</text>
                <text class="mx-2">|</text>
                <text>{{ item.equName }}</text>
              </template>
              <template v-if="item.reqType === '2'">
                <text>工具类型</text>
                <text class="mx-2">|</text>
                <text>{{ item.fixtureName }}</text>
              </template>
              <template v-if="item.reqType === '3'">
                <text>其他类型</text>
              </template>
              <view class="text-color-gray mt-1 text-mini">
                <text>报修人: {{ item.reqUserName }}</text>
              </view>
              <view class="text-color-gray mt-1 text-mini">
                <text>报修时间: {{ item.reqTime }}</text>
              </view>
              <view class="text-color-gray mt-1 text-mini">
                <text>维修开始: {{ item.startTime }}</text>
              </view>
              <view class="text-color-gray mt-1 text-mini">
                <text>维修结束: {{ item.endTime }}</text>
              </view>
              <view class="text-color-gray mt-1 text-mini">
                <text>维修人: {{ item.resUserName }}</text>
              </view>
            </view>
          </view>
          <view>
            <!--操作工或维修工角色-->
            <template v-if="isOperatorOrRepair()">
              <wd-button
                v-if="item.status === '1'"
                size="small"
                icon="edit-outline"
                @click.stop="handleStartRepair(item)"
              >
                å¼€å§‹ç»´ä¿®
              </wd-button>
              <wd-button
                v-else-if="item.status === '2'"
                size="small"
                icon="edit-outline"
                @click.stop="itemClick(item)"
              >
                ç»´ä¿®ä¸­
              </wd-button>
              <view class="h-full flex flex-col" v-else-if="item.status === '3'">
                <wd-button size="small" icon="warn-bold" @click.stop="itemClick(item)">
                  è¯¦æƒ…
                </wd-button>
                <wd-button
                  class="mt-4"
                  size="small"
                  icon="edit-outline"
                  @click.stop="goToFeedBack(item)"
                >
                  æŸ¥çœ‹è¯„ä»·
                </wd-button>
              </view>
            </template>
            <!--管理员角色-->
            <template v-else-if="isLeader()">
              <wd-button
                v-if="item.status === '1'"
                size="small"
                icon="warn-bold"
                disabled
                @click.stop="itemClick(item)"
              >
                å¾…ç»´ä¿®
              </wd-button>
              <wd-button
                v-else-if="item.status === '2'"
                size="small"
                icon="warn-bold"
                disabled
                @click.stop="itemClick(item)"
              >
                ç»´ä¿®ä¸­
              </wd-button>
              <view class="h-full flex flex-col" v-else-if="item.status === '3'">
                <wd-button size="small" icon="warn-bold" @click.stop="itemClick(item)">
                  è¯¦æƒ…
                </wd-button>
                <wd-button
                  class="mt-4"
                  size="small"
                  icon="edit-outline"
                  @click.stop="goToFeedBack(item)"
                >
                  {{ item.fbId == null ? '写评价' : '查看评价' }}
                </wd-button>
              </view>
            </template>
          </view>
        </view>
      </wd-card>
      <res-card v-for="(item, index) in dataList" :key="item.id" :item="item" @click="handleResClick" />
    </view>
  </z-paging>
</template>
@@ -192,10 +53,11 @@
import { useToast, useMessage } from 'wot-design-uni'
import { getRepairResList, addRepairRes, updateRepairRes } from '@/service/repair'
import { DICT_REPAIR_RES_STATUS, DICT_REPAIR_REQ_TYPE, getDictInfo } from '@/service/dict'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
import { isLeader, isLineOrRepair, isRepair } from "@/utils/RoleUtils";
import { useUserStore } from '@/store'
import { formatDate } from '@/utils/DateUtils'
import dayjs from "dayjs";
import ResCard from "@/components/repair/res-card.vue";
const userStore = useUserStore()
const message = useMessage()
@@ -232,15 +94,15 @@
])
const resTypeList = ref<any>([{ dictLabel: '所有类型', dictValue: -1 }])
const statusList = ref<any>([{ dictLabel: '所有状态', dictValue: -1 }])
function handleResType({ value }) {
  reloadData()
}
function handleResStatu({ value }) {
  reloadData()
}
function handleFilterDate({ value }) {
  reloadData()
}
// function handleResType({ value }) {
//   reloadData()
// }
// function handleResStatu({ value }) {
//   reloadData()
// }
// function handleFilterDate({ value }) {
//   reloadData()
// }
const paging = ref(null)
const dataList = ref([])
@@ -249,35 +111,44 @@
  const queryParams: any = {
    pageNum,
    pageSize,
    reqType: resTypeId.value,
    status: status.value,
    params: {},
    // reqType: resTypeId.value,
    // status: status.value,
  }
  if (resTypeId.value === -1) {
    delete queryParams.reqType
  }
  if (status.value === -1) {
    delete queryParams.status
  }
  if (filterDate.value === '1') {
    // èŽ·å–å½“å‰æ—¥æœŸ
    const now = dayjs()
    queryParams.params = {
      beginReqTime: now.startOf('day').format('YYYY-MM-DD 00:00:00'),
      endReqTime: now.endOf('day').format('YYYY-MM-DD 23:59:59'),
    }
  } else if (filterDate.value === '2') {
    const now = dayjs()
    queryParams.params = {
      beginReqTime: now.startOf('month').format('YYYY-MM-DD 00:00:00'),
      endReqTime: now.endOf('month').format('YYYY-MM-DD 23:59:59'),
    }
  } else {
    delete queryParams.params
  }
  // if (resTypeId.value === -1) {
  //   delete queryParams.reqType
  // }
  // if (status.value === -1) {
  //   delete queryParams.status
  // }
  //
  // if (filterDate.value === '1') {
  //   // èŽ·å–å½“å‰æ—¥æœŸ
  //   const now = dayjs()
  //   queryParams.params = {
  //     beginReqTime: now.startOf('day').format('YYYY-MM-DD 00:00:00'),
  //     endReqTime: now.endOf('day').format('YYYY-MM-DD 23:59:59'),
  //   }
  // } else if (filterDate.value === '2') {
  //   const now = dayjs()
  //   queryParams.params = {
  //     beginReqTime: now.startOf('month').format('YYYY-MM-DD 00:00:00'),
  //     endReqTime: now.endOf('month').format('YYYY-MM-DD 23:59:59'),
  //   }
  // } else {
  //   delete queryParams.params
  // }
  // å¦‚果是从扫码页面过来,只能查询固定设备的数据
  if (option?.from === 'scan') {
    queryParams.assetNo = option.assetNo
  }
  queryParams.params.status = '0,1,2,3'
  queryParams.reqUser = userStore?.userInfo?.userId
  if (isRepair()) {
    queryParams.params.status = null
    queryParams.resUser = userStore?.userInfo?.userId
    delete queryParams.reqUser
    delete queryParams.params
  }
  getRepairResList(queryParams)
    .then((res: any) => {
@@ -291,128 +162,128 @@
  paging.value.reload()
}
/**
 * æ¡ç›®ç‚¹å‡»äº‹ä»¶
 * @param item
 */
function itemClick(item: any) {
  goToDetail(item)
}
/**
 * å¼€å§‹ç»´ä¿®
 * @param item
 */
function handleStartRepair(item: any) {
  // ç¡®è®¤å¼€å§‹ä¿®æ”¹çŠ¶æ€ä¸º2-维修中
  const data = Object.assign({}, item)
  // ç¡®è®¤å¼€å§‹ä¿®æ”¹çŠ¶æ€ä¸º2-维修中
  data.status = '2'
  // è®¾ç½®å¼€å§‹ç»´ä¿®æ—¶é—´
  data.startTime = formatDate(new Date())
  message
    .confirm({
      msg: '确定开始维修?',
      title: '提示',
      beforeConfirm: ({ resolve }) => {
        updateRepair(data, resolve)
      },
    })
    .then(() => {})
    .catch((error) => {
      console.log(error)
    })
}
/**
 * æ›´æ–°ç»´ä¿®å·¥å•
 * @param data
 * @param resolve
 */
function updateRepair(data: any, resolve: any) {
  updateRepairRes(data)
    .then((res: any) => {
      resolve(true)
      if (res?.code === 200) {
        reloadData()
        // ç»´ä¿®ä¸­çŠ¶æ€æ‰éœ€è¦è·³è½¬
        if (data?.status === '2') {
          goToDetail(data)
        }
      }
    })
    .catch((res) => {
      console.error(res)
    })
}
function goToDetail(item) {
  uni.navigateTo({
    url: `/pages/repair/res-detail?id=${item.id}`,
  })
}
function goToFeedBack(item) {
  uni.navigateTo({
    url: `/pages/repair/repair-fb?id=${item.id}`,
  })
}
// /**
//  * æ¡ç›®ç‚¹å‡»äº‹ä»¶
//  * @param item
//  */
// function itemClick(item: any) {
//   goToDetail(item)
// }
//
// /**
//  * å¼€å§‹ç»´ä¿®
//  * @param item
//  */
// function handleStartRepair(item: any) {
//   // ç¡®è®¤å¼€å§‹ä¿®æ”¹çŠ¶æ€ä¸º2-维修中
//   const data = Object.assign({}, item)
//   // ç¡®è®¤å¼€å§‹ä¿®æ”¹çŠ¶æ€ä¸º2-维修中
//   data.status = '2'
//   // è®¾ç½®å¼€å§‹ç»´ä¿®æ—¶é—´
//   data.startTime = formatDate(new Date())
//   message
//     .confirm({
//       msg: '确定开始维修?',
//       title: '提示',
//       beforeConfirm: ({ resolve }) => {
//         updateRepair(data, resolve)
//       },
//     })
//     .then(() => {})
//     .catch((error) => {
//       console.log(error)
//     })
// }
//
// /**
//  * æ›´æ–°ç»´ä¿®å·¥å•
//  * @param data
//  * @param resolve
//  */
// function updateRepair(data: any, resolve: any) {
//   updateRepairRes(data)
//     .then((res: any) => {
//       resolve(true)
//       if (res?.code === 200) {
//         reloadData()
//         // ç»´ä¿®ä¸­çŠ¶æ€æ‰éœ€è¦è·³è½¬
//         if (data?.status === '2') {
//           goToDetail(data)
//         }
//       }
//     })
//     .catch((res) => {
//       console.error(res)
//     })
// }
//
// function goToDetail(item) {
//   uni.navigateTo({
//     url: `/pages/repair/res-detail?id=${item.id}`,
//   })
// }
//
// function goToFeedBack(item) {
//   uni.navigateTo({
//     url: `/pages/repair/repair-fb?id=${item.id}`,
//   })
// }
const goBack = () => {
  uni.navigateBack()
}
function handleClickRight() {
  if (isLeader()) {
    toast.info('请登录维修工账号接单')
  } else if (isOperatorOrRepair()) {
    handleSelectReq()
  }
}
// function handleClickRight() {
//   if (isLineOrRepair()) {
//     handleSelectReq()
//   } else {
//     toast.info('请登录报修工账号接单')
//   }
// }
/**
 * é€‰æ‹©æŠ¥ä¿®å•
 */
function handleSelectReq() {
  uni.navigateTo({
    url: '/pages/repair/req-list',
    events: {
      // ä¸ºæŒ‡å®šäº‹ä»¶æ·»åŠ ä¸€ä¸ªç›‘å¬å™¨ï¼ŒèŽ·å–è¢«æ‰“å¼€é¡µé¢ä¼ é€åˆ°å½“å‰é¡µé¢çš„æ•°æ®
      selectReq: function (data) {
        console.error(data)
        // é€‰æ‹©æŠ¥ä¿®å•后,修改报修单状态和新增维修工单
        const resCode = `WXD${data.data.code.slice(3)}`
        const deptId = userStore?.userInfo?.deptId
        const userId = userStore?.userInfo?.userId
        const resData = {
          reqId: data.data.id,
          reqCode: data.data.code,
          reqUser: data.data.reqUser,
          reqDept: data.data.reqDept,
          resCode,
          status: '1',
          resUser: userId,
          resDept: deptId,
        }
        addRepairRes(resData)
          .then((res: any) => {
            if (res.code === 200) {
              toast.success(res?.msg || '操作成功')
              reloadData()
            } else {
              toast.error(res?.msg || '生成维修工单失败,请重试')
            }
          })
          .catch((res) => {
            toast.error(res?.msg || '生成维修工单失败,请重试')
          })
      },
    },
    success: function (res) {
      // é€šè¿‡eventChannel向被打开页面传送数据
      res.eventChannel.emit('OnSelectReq', { data: '维修单页面选择报修单' })
    },
  })
}
//
// /**
//  * é€‰æ‹©æŠ¥ä¿®å•
//  */
// function handleSelectReq() {
//   uni.navigateTo({
//     url: '/pages/repair/req-list',
//     events: {
//       // ä¸ºæŒ‡å®šäº‹ä»¶æ·»åŠ ä¸€ä¸ªç›‘å¬å™¨ï¼ŒèŽ·å–è¢«æ‰“å¼€é¡µé¢ä¼ é€åˆ°å½“å‰é¡µé¢çš„æ•°æ®
//       selectReq: function (data) {
//         console.error(data)
//         // é€‰æ‹©æŠ¥ä¿®å•后,修改报修单状态和新增维修工单
//         const resCode = `WXD${data.data.code.slice(3)}`
//         const deptId = userStore?.userInfo?.deptId
//         const userId = userStore?.userInfo?.userId
//         const resData = {
//           reqId: data.data.id,
//           reqCode: data.data.code,
//           reqUser: data.data.reqUser,
//           reqDept: data.data.reqDept,
//           resCode,
//           status: '1',
//           resUser: userId,
//           resDept: deptId,
//         }
//         addRepairRes(resData)
//           .then((res: any) => {
//             if (res.code === 200) {
//               toast.success(res?.msg || '操作成功')
//               reloadData()
//             } else {
//               toast.error(res?.msg || '生成维修工单失败,请重试')
//             }
//           })
//           .catch((res) => {
//             toast.error(res?.msg || '生成维修工单失败,请重试')
//           })
//       },
//     },
//     success: function (res) {
//       // é€šè¿‡eventChannel向被打开页面传送数据
//       res.eventChannel.emit('OnSelectReq', { data: '维修单页面选择报修单' })
//     },
//   })
// }
async function initData() {
  const rList: any = await getDictInfo(DICT_REPAIR_REQ_TYPE)
@@ -423,10 +294,10 @@
onLoad((options) => {
  Object.assign(option, options)
  initData()
  uni.$on('res-list-refresh', reloadData)
  uni.$on('list-refresh', reloadData)
})
onUnload(() => {
  uni.$off('res-list-refresh', reloadData)
  uni.$off('list-refresh', reloadData)
})
</script>
eims-ui-mobile/src/pages/scan/index.vue
@@ -57,10 +57,44 @@
        <view class="h-[1px] bg-base"></view>
        <view class="bg-white flex justify-around py-4">
          <wd-button icon="edit-outline" @click.stop="handleInsp">点检</wd-button>
          <wd-button icon="laptop" @click.stop="handMaint">保养</wd-button>
          <wd-button icon="tools" @click.stop="showActions">ç»´ä¿®</wd-button>
          <wd-button icon="laptop" v-if="isRepair() || isLeader()" @click.stop="handMaint">
            ä¿å…»
          </wd-button>
          <wd-button icon="laptop" @click.stop="handleRequest">报修</wd-button>
          <!--          <wd-button icon="tools" @click.stop="showActions">ç»´ä¿®</wd-button>-->
        </view>
      </view>
      <view class="h-[10px] bg-base"></view>
      <wd-tabs v-model="tab">
        <wd-tab title="维修请求">
          <view class="h-[10px] bg-base"></view>
          <!-- ç»´ä¿®è¯·æ±‚区域 -->
          <view class="mt-2" v-if="reqList.length > 0">
            <view class="bg-base">
              <req-card
                v-for="item in reqList"
                :key="item.id"
                :item="item"
                @click="handleReqClick"
              />
            </view>
          </view>
        </wd-tab>
        <wd-tab title="维修单">
          <view class="h-[10px] bg-base"></view>
          <!-- ç»´ä¿®å•区域 -->
          <view class="mt-2" v-if="resList.length > 0">
            <view class="bg-base">
              <res-card v-for="item in resList" :key="item.id" :item="item" />
            </view>
          </view>
        </wd-tab>
      </wd-tabs>
      <wd-action-sheet
        v-model="show"
@@ -79,16 +113,25 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import type { EquVO } from '@/service/equ.d'
import type { RepairReqVO, RepairResVO } from '@/service/repair.d'
import { useToast, useMessage } from 'wot-design-uni'
import { getEquByAssetNo } from '@/service/equ'
import { getInspStByStId } from '@/service/inspect'
import { isLeader, isRepair } from '@/utils/RoleUtils'
import { getRepairReqList, getRepairResList } from '@/service/repair'
import ReqCard from '@/components/repair/req-card.vue'
import ResCard from '@/components/repair/res-card.vue'
import { useUserStore } from '@/store'
const tab = ref<number>(0)
const message = useMessage()
const toast = useToast()
const model = reactive<EquVO>({})
const userStore = useUserStore()
const scanResult = ref<string>('')
const show = ref<boolean>(false)
const exist = ref<boolean>(false)
const reqList = ref<RepairReqVO[]>([])
const resList = ref<RepairResVO[]>([])
const actions = ref([
  {
    name: '新增报修',
@@ -105,6 +148,8 @@
      if (res?.equId) {
        exist.value = true
        Object.assign(model, res)
        // åŠ è½½ç»´ä¿®è¯·æ±‚å’Œç»´ä¿®å•æ•°æ®
        loadRepairData(res.equId)
      } else {
        toast.error('未查询到该资产编号相关数据!')
      }
@@ -114,6 +159,38 @@
      toast.error(res?.data?.msg || '请求失败')
    })
}
// åŠ è½½ç»´ä¿®è¯·æ±‚å’Œç»´ä¿®å•æ•°æ®
function loadRepairData(equId: string | number) {
  // åŠ è½½æœªæŽ¥å•çš„ç»´ä¿®è¯·æ±‚
  getRepairReqList({
    equId,
    status: '0',
  }).then((res: any) => {
    if (res?.rows) {
      reqList.value = res.rows
    }
  })
  // åŠ è½½æœªå®Œæˆçš„ç»´ä¿®å•
  const params = {
    equId,
    params: {
      status: '0,1,2',
    },
  }
  // å¦‚果是维修工则加载本要接单的维修单
  if (isRepair()) {
    params.resUser = userStore?.userInfo?.userId
    params.params.status = '0,1,2'
  }
  getRepairResList(params).then((res: any) => {
    if (res?.rows) {
      resList.value = res.rows
    }
  })
}
function handleInfo() {
  uni.showToast({
    title: '功能开发中',
@@ -152,6 +229,19 @@
  })
}
function handleRequest() {
  if (!model?.assetNo) {
    uni.showToast({
      title: '未查询到设备,请联系管理员!',
      icon: 'none',
    })
    return false
  }
  uni.navigateTo({
    url: `/pages/repair/repair-add?equId=${model?.equId}&equName=${model?.equName}&from=scan`,
  })
}
function showActions() {
  show.value = true
}
@@ -159,6 +249,7 @@
function close() {
  show.value = false
}
function select({ item, index }) {
  console.error(model?.equId)
  console.error(!model?.equId)
@@ -182,10 +273,29 @@
      break
  }
}
;`/pages/repair/req-list`,
  // å¤„理维修请求点击事件
  function handleReqClick(item) {
    uni.navigateTo({
      url: `/pages/repair/req-detail?id=${item.id}`,
    })
  }
// // å¤„理维修单点击事件
// function handleResClick(item) {
//   uni.navigateTo({
//     url: `/pages/repair/res-detail?id=${item.id}`,
//   })
// }
onLoad((options) => {
  uni.$on('list-refresh', loadRepairData)
  scanResult.value = options?.result
  initData(options?.result)
})
onUnload(() => {
  uni.$off('list-refresh', loadRepairData)
})
</script>
<style scoped lang="scss">
eims-ui-mobile/src/types/uni-pages.d.ts
@@ -18,6 +18,7 @@
       "/pages/my/index" |
       "/pages/repair/repair-add" |
       "/pages/repair/repair-fb" |
       "/pages/repair/req-detail" |
       "/pages/repair/req-list" |
       "/pages/repair/res-detail" |
       "/pages/repair/res-list" |
eims-ui-mobile/src/utils/RoleUtils.ts
@@ -23,12 +23,42 @@
export const ROLE_REPAIR = 'repair'
/**
 * è®¾å¤‡ç®¡ç†å‘˜
 */
export const ROLE_EQU_ADMIN = 'equadmin'
/**
 * ç™»å½•角色是操作工或维修工
 */
export const isOperatorOrRepair = () => {
  // const roles = useUserStore()?.userInfo?.roles || []
  // return roles.includes(ROLE_OPERATOR) || roles.includes(ROLE_REPAIR)
  return true
export const isLineOrRepair = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return roles.includes(ROLE_LINE) || roles.includes(ROLE_REPAIR)
}
export const isOperator = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return roles.includes(ROLE_OPERATOR)
}
export const isRepair = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return roles.includes(ROLE_REPAIR)
}
export const isLine = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return roles.includes(ROLE_LINE)
}
export const isSuperAdmin = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return roles.includes(ROLE_SUPER_ADMIN)
}
export const isEquAdmin = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return roles.includes(ROLE_EQU_ADMIN)
}
/**
@@ -37,7 +67,7 @@
export const isLeader = () => {
  const roles = useUserStore()?.userInfo?.roles || []
  return (
    roles.includes(ROLE_LINE) || roles.includes(ROLE_SUPER_ADMIN) || roles.includes(ROLE_LEADER)
    roles.includes(ROLE_SUPER_ADMIN) || roles.includes(ROLE_EQU_ADMIN) || roles.includes(ROLE_LEADER)
  )
}
@@ -58,4 +88,7 @@
  if (roles.includes(ROLE_REPAIR)) {
    return '维修工'
  }
  if (roles.includes(ROLE_EQU_ADMIN)) {
    return '设备管理员'
  }
}
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java
@@ -94,6 +94,11 @@
         * å®Œæˆ
         */
        String WANCHENG = "3";
        /**
         * è¯„ä»·
         */
        String PINGJIA = "4";
    }
    /**
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsRepairRes.java
@@ -106,5 +106,7 @@
     */
    private Long fbId;
    private String faultType;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsInspectPlanBo.java
@@ -59,7 +59,7 @@
    /**
     * ç‚¹æ£€å‘¨æœŸ
     */
    @NotNull(message = "点检周期不能为空", groups = { AddGroup.class, EditGroup.class })
//    @NotNull(message = "点检周期不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long inspCycle;
    /**
@@ -75,7 +75,7 @@
    /**
     * ç‚¹æ£€äºº
     */
    @NotNull(message = "点检人不能为空", groups = { AddGroup.class, EditGroup.class })
//    @NotNull(message = "点检人不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long inspUser;
    /**
@@ -104,7 +104,7 @@
    /**
     * ä¸‹æ¬¡æ‰§è¡Œæ—¶é—´
     */
    @NotNull(message = "下次执行时间不能为空", groups = { AddGroup.class, EditGroup.class })
//    @NotNull(message = "下次执行时间不能为空", groups = { AddGroup.class, EditGroup.class })
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date inspNextTime;
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsRepairReqBo.java
@@ -115,5 +115,7 @@
     */
    private String remark;
    private String statusLt;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsRepairResBo.java
@@ -10,6 +10,8 @@
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
@@ -119,6 +121,12 @@
    private  String assetNo;//设备资产编号
    private Long equId;
    private String statusLt;
    private List<EimsSpareBo> spareParts;
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsRepairResVo.java
@@ -16,7 +16,7 @@
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
@@ -149,6 +149,7 @@
    @Translation(type = TransConstant.REPAIR_REQ_ID_TO_CODE, mapper = "reqId")
    private String reqCode;//报修编码
    private List<EimsSpareVo> spareParts;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsInspectStServiceImpl.java
@@ -256,6 +256,7 @@
        qw.eq(bo.getInspUser() != null, "st.maint_user", bo.getInspUser());
        qw.eq(bo.getVerifyUser() != null, "st.verify_user", bo.getVerifyUser());
        qw.eq(bo.getStatus() != null, "st.status", bo.getStatus());
        qw.eq(bo.getUpdateBy() != null, "st.update_by", bo.getUpdateBy());
        qw.orderByDesc( "st.create_time");
        return qw;
    }
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsRepairFbServiceImpl.java
@@ -1,5 +1,6 @@
package org.dromara.eims.service.impl;
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.service.RepairResService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -103,6 +104,7 @@
            bo.setId(add.getId());
            //新增时更新repair_res表评价id
            EimsRepairRes eimsRepairRes = repairResMapper.selectById(bo.getResId());
            eimsRepairRes.setStatus(DictConstants.REPAIR_RES_STATUS_DETAIL.PINGJIA);
            eimsRepairRes.setFbId(add.getId());
            repairResMapper.updateById(eimsRepairRes);
        }
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsRepairReqServiceImpl.java
@@ -95,11 +95,13 @@
        qw.eq(bo.getRepairUser() != null, "a.repair_user", bo.getRepairUser());
        qw.eq(StringUtils.isNotBlank(bo.getFaultType()), "a,fault_type", bo.getFaultType());
        qw.in(params.containsKey("createBy"), "a.create_by",  (List<Long>) params.get("createBy"));
        qw.eq(params.containsKey("status"), "a.status", params.get("status"));
        Object status = params.get("status") == null ? "" : params.get("status");
        String[] split = status.toString().split(",");
        qw.in(params.containsKey("status"), "a.status", split);
        qw.between(params.get("beginReqTime") != null && params.get("endReqTime") != null,
            "a.req_time", params.get("beginReqTime"), params.get("endReqTime"));
        qw.ne(StringUtils.isNotBlank(bo.getStatusLt()), "a.status", bo.getStatusLt());
        qw.eq(bo.getCreateBy()!=null, "a.create_by", bo.getCreateBy());
        qw.eq(StringUtils.isNotEmpty(bo.getStatus()), "a.status", bo.getStatus());
        qw.orderByDesc("a.create_time");
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsRepairResServiceImpl.java
@@ -7,6 +7,7 @@
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.service.RepairResService;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -18,10 +19,10 @@
import lombok.RequiredArgsConstructor;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.eims.domain.*;
import org.dromara.eims.domain.vo.EimsEquVo;
import org.dromara.eims.domain.vo.EimsFixtureVo;
import org.dromara.eims.domain.vo.EimsRepairReqVo;
import org.dromara.eims.domain.bo.EimsSpareInoutBo;
import org.dromara.eims.domain.vo.*;
import org.dromara.eims.mapper.*;
import org.dromara.eims.service.IEimsSpareInoutService;
import org.dromara.eims.utils.DataFilterUtil;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.vo.SysDeptVo;
@@ -29,7 +30,6 @@
import org.redisson.misc.LogHelper;
import org.springframework.stereotype.Service;
import org.dromara.eims.domain.bo.EimsRepairResBo;
import org.dromara.eims.domain.vo.EimsRepairResVo;
import org.dromara.eims.service.IEimsRepairResService;
import org.springframework.transaction.annotation.Transactional;
@@ -51,6 +51,7 @@
    private final EimsRepairRecordMapper recordMapper;
    private final EimsEquMapper equMapper;
    private final EimsFixtureMapper fixtureMapper;
    private final IEimsSpareInoutService spareInoutService;
    /**
     * æŸ¥è¯¢ç»´ä¿®å·¥å•
@@ -66,6 +67,7 @@
            EimsRepairReqVo reqVo = reqMapper.selectVoById(reqId);
            resVo.setReqType(reqVo.getReqType());
            resVo.setReqCode(reqVo.getCode());
            resVo.setReqDesc(reqVo.getReqDesc());
            if(reqVo.getEquId()!=null){
                EimsEquVo equVo = equMapper.selectVoById(reqVo.getEquId());
@@ -83,9 +85,25 @@
                }
            }
            resVo.setSpareParts(querySpareParts(resVo.getResCode()));
        }
        return resVo;
    }
    public List<EimsSpareVo> querySpareParts(String code) {
        // æŸ¥è¯¢å…³è”的备件,关系如下:备件出入库单的associatedOrder å…³è”传入的code,备件出入库明细通过出入库单的id关联,备件名称通过出入库明细中的spareId关联
        EimsSpareInoutBo bo = new EimsSpareInoutBo();
        bo.setAssociatedOrder(code);
        List<EimsSpareInoutVo> list = spareInoutService.queryList(bo);
        if (!list.isEmpty()) {
            // æŸ¥è¯¢å¤‡ä»¶æ˜Žç»†å’Œåç§°
            EimsSpareInoutVo eimsSpareInoutVo = spareInoutService.queryById(list.get(0).getId());
            return eimsSpareInoutVo.getSpareList();
        }
        return List.of();
    }
    /**
@@ -261,6 +279,23 @@
            recordMapper.insert(record);
        }
        validEntityBeforeSave(update);
        // åˆ¤æ–­å¤‡ä»¶åˆ—表是否为空,不为空则新增一个备件出库单,同时增加出库明细
        if (bo.getSpareParts() != null && bo.getSpareParts().size() > 0) {
            EimsSpareInoutBo spareInoutBo = new EimsSpareInoutBo();
            // æ ¹æ®æ—¥æœŸç”Ÿæˆå•号,格式为CK+日期+流水号
            spareInoutBo.setOrderCode("CK"+ DateUtils.dateTimeNow("yyyyMMddHHmmss"));
            spareInoutBo.setOrderTime(new Date());
            spareInoutBo.setType(DictConstants.SPARE_INOUT_TYPE_DETAIL.CK);
            spareInoutBo.setPartnerName(loginUser.getNickname());
            spareInoutBo.setSpareList(bo.getSpareParts());
            spareInoutBo.setChargeUser(loginUser.getUserId());
            spareInoutBo.setChargeDept(loginUser.getDeptId());
            spareInoutBo.setAssociatedOrder(bo.getResCode());
            // ä¿å­˜è¿›æ•°æ®åº“
            Boolean b = spareInoutService.insertByBo(spareInoutBo);
        }
        return baseMapper.updateById(update) > 0;
    }
@@ -302,6 +337,7 @@
        qw.eq(bo.getReqType() != null, "req.req_type", bo.getReqType());
        qw.eq(bo.getReqUser() != null, "res.req_user", bo.getReqUser());
        qw.eq(bo.getAssetNo() != null, "equ.asset_no", bo.getAssetNo());
        qw.eq(bo.getEquId() != null, "req.equ_id", bo.getEquId());
        qw.between(params.get("beginReqTime") != null && params.get("endReqTime") != null,
            "req.req_time", params.get("beginReqTime"), params.get("endReqTime"));
@@ -314,6 +350,9 @@
        qw.eq(StringUtils.isNotBlank(bo.getStatus()), "res.status", bo.getStatus());
        String s = params.get("status") == null ? "" : params.get("status").toString();
        String[] status = s.split(",");
        qw.in(params.get("status") != null, "res.status", status);
        qw.orderByDesc("res.create_time");
        return qw;
    }