Browse Source

手动集抄

yusheng 5 months ago
parent
commit
fd7d856ef0

+ 76 - 0
api/centralizedMeterReading/index.js

@@ -0,0 +1,76 @@
+// 抽样记录
+import { get, put, postJ, putJ, deleteApi } from "@/utils/request";
+import Vue from "vue";
+
+// 分页
+export async function getList(params) {
+  const res = await get(
+    Vue.prototype.apiUrl + `/ems/remote_meter_reading/page`,
+    params,
+  );
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
+// 取样新建/修改
+export async function save(data) {
+  const res = await postJ(
+    Vue.prototype.apiUrl + `/ems/remote_meter_reading/save`,
+    data,
+  );
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
+// 取样新建/修改
+export async function update(data) {
+  const res = await putJ(
+    Vue.prototype.apiUrl + `/ems/remote_meter_reading/update`,
+    data,
+  );
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
+// 详情
+export async function getById(id) {
+  const res = await get(
+    Vue.prototype.apiUrl + `/ems/remote_meter_reading/getById/${id}`,
+  );
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
+// 删除
+export async function deleteById(data) {
+  const res = await deleteApi(
+    Vue.prototype.apiUrl + `/ems/remote_meter_reading/delete`,
+    data,
+  );
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+/**
+ * 更新发布状态
+ */
+export async function updatePublishStatus(id, publishStatus) {
+  const res = await putJ(
+    Vue.prototype.apiUrl +
+      `/ems/remote_meter_reading/updatePublishStatus/${id}/${publishStatus}`,
+  );
+  if (res.code == 0) {
+    return res.data;
+  }
+
+  return Promise.reject(new Error(res.data.message));
+}

+ 12 - 0
api/classifyManage/itemInformation.js

@@ -23,6 +23,18 @@ export async function bomSubListByVersionId(params) {
   }
   return Promise.reject(res.message);
 }
+// 设备信息列表
+export async function getAssetList(params) {
+  const data = await get(
+    Vue.prototype.apiUrl + "/main/asset/page",
+    params
+  );
+  if (data.code == 0) {
+    return data.data;
+  }
+  return Promise.reject(data.message);
+}
+
 // 实例
 export async function getByRootLevelId(levelId) {
   const res = await get(

+ 51 - 0
components/MaterialSelector/classification-picker.vue

@@ -0,0 +1,51 @@
+<template>
+  <ba-tree-picker
+    ref="treePicker"
+    key="verify"
+    :multiple="false"
+    @select-change="confirm"
+    title="选择分类"
+    :localdata="classificationList"
+    valueKey="id"
+    textKey="name"
+    childrenKey="children"
+  />
+</template>
+
+<script>
+import baTreePicker from "@/components/ba-tree-picker/ba-tree-picker.vue";
+import { getTreeByPid } from "@/api/classifyManage";
+export default {
+  props: {
+    warehousingType: {
+      type: [String, Number],
+      default: "1", //默认原料
+    },
+  },
+  components: { baTreePicker },
+  data() {
+    return {
+      classificationList: [],
+    };
+  },
+  mounted() {
+    this.getClassify();
+  },
+  methods: {
+    show() {
+      this.$refs.treePicker._show();
+    },
+    async getClassify() {
+      const res = await getTreeByPid(this.warehousingType);
+      this.classificationList = res.data;
+      this.$nextTick(() => {
+        this.confirm([res.data[0].id], res.data[0].name);
+      });
+    },
+
+    confirm(...rest) {
+      this.$emit("confirm", ...rest);
+    },
+  },
+};
+</script>

+ 333 - 0
components/MaterialSelector/contentBox.vue

@@ -0,0 +1,333 @@
+<template>
+  <view class="main-box">
+    <view class="title"
+      >选择设备
+      <view class="choose-box" @click="handleClassification"> 选择分类 </view>
+    </view>
+    <view class="wrapper">
+      <view class="searchBox">
+        <input
+          v-model="searchKey"
+          placeholder="请输入关键字搜索"
+          class="searchInput"
+        />
+        <u-button
+          type="icon-shixiangxinzeng"
+          size="30"
+          @click="doSearch"
+          data-icon="Search"
+          class="searchBtn"
+        >
+          <view class="iconfont icon-sousuo"> </view>
+          <view class="text"> 搜索 </view>
+        </u-button>
+      </view>
+    </view>
+    <view class="listContent">
+      <checkbox-group
+        v-for="(item, index) in listData"
+        :key="item.code + '-' + !!item.checked"
+        @change="(e) => selectVal(e, item, index)"
+      >
+        <label>
+          <view class="listBox">
+            <view class="listBox-sel">
+              <checkbox
+                :value="item.code"
+                color="#fff"
+                :disabled="item.disabled"
+                :checked="item.checked"
+              />
+            </view>
+            <view class="listBox-con">
+
+              <view class="listBox-bottom">
+				  <view class="items"> 名称:{{ item.category.name }} </view>
+                <view class="items"> 固资编码:{{ item.fixCode }} </view>
+                <view class="items"> 编号:{{ item.codeNumber }} </view>
+                <view class="items"> 编码:{{ item.code }} </view>
+                <view class="items"> 使用人:{{ item.usePerson }} </view>
+                <view class="items"> 使用岗位:{{ item.postName }} </view>
+                <view class="items"> 片区负责人负责人:{{ item.areaPersonInChargeUserName }} </view>
+                <view class="items"> 型号:{{ item.category.modelType }} </view>
+                <view class="items"> 规格:{{ item.category.specification }} </view>
+              </view>
+            </view>
+          </view>
+        </label>
+      </checkbox-group>
+      <u-empty style="margin-top: 20vh" v-if="!listData.length"> </u-empty>
+    </view>
+    <view class="footer">
+      <view class="footer-cancel" @click="$emit('handleCancel')">关闭</view>
+      <view class="footer-comfirm" @click="handleComfirm">选择</view>
+    </view>
+
+    <classificationPicker
+      :warehousingType="warehousingType"
+      ref="classificationRef"
+      @confirm="classificationConfirm"
+    />
+  </view>
+</template>
+<script>
+import classificationPicker from "./classification-picker.vue";
+import { getAssetList } from "@/api/classifyManage/itemInformation";
+let page = 1;
+export default {
+  components: { classificationPicker },
+  props: {
+    warehousingType: {
+      type: [String, Number],
+      default: "0", //默认原料
+    },
+    chooseList: {
+      type: Array,
+      default: function () {
+        return [];
+      },
+    },
+    multiple: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      searchKey: "",
+      curId: "",
+      listData: [],
+      memoList: [],
+    };
+  },
+
+  methods: {
+    //勾选
+    selectVal(e, val, index) {
+      if (this.multiple) {
+        // 多选模式
+        this.$set(this.listData[index], 'checked', !this.listData[index].checked);
+        this.seletedAll = !this.listData.some((item) => !item.checked);
+        const idx = this.memoList.findIndex(
+          (item) => item.curId === this.listData[index].curId
+        );
+        if (this.listData[index].checked) {
+          if (idx === -1) {
+            this.memoList.push(this.listData[index]);
+          }
+        } else {
+          if (idx > -1) {
+            this.memoList.splice(idx, 1);
+          }
+        }
+      } else {
+        // 单选模式:清空其他选中,仅保留当前项
+        this.listData.forEach((item, i) => {
+          this.$set(item, 'checked', i === index ? !item.checked : false);
+        });
+        if (this.listData[index].checked) {
+          this.memoList = [this.listData[index]];
+        } else {
+          this.memoList = [];
+        }
+      }
+    },
+    handleClassification() {
+      this.$refs.classificationRef.show();
+    },
+    classificationConfirm([id], name) {
+      this.curId = id;
+      page = 1;
+      this.getList();
+    },
+    async getList() {
+      const res = await getAssetList({
+        pageNum: page,
+        size: -1,
+        categoryLevelId: this.curId,
+        searchKey: this.searchKey,
+      });
+      this.listData = res.list;
+      for (var i = 0; i < this.listData.length; i++) {
+        this.$set(this.listData[i], 'checked', false);
+        for (var j = 0; j < this.chooseList.length; j++) {
+          if (this.listData[i].id == this.chooseList[j].id) {
+            this.$set(this.listData[i], 'checked', true);
+          }
+        }
+      }
+      // console.log(res);
+    },
+    doSearch() {
+      page = 1;
+      this.getList();
+    },
+    handleComfirm() {
+      this.$emit("pick", this.memoList);
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.title {
+  line-height: 80rpx;
+  font-size: $uni-font-size-lg;
+  text-align: center;
+  font-weight: bold;
+  position: relative;
+  .choose-box {
+    position: absolute;
+    right: 10rpx;
+    top: 0;
+    font-weight: normal;
+    font-size: $uni-font-size-sm;
+    color: $j-primary-green;
+  }
+}
+
+.main-box {
+  height: 90vh;
+  display: flex;
+  flex-direction: column;
+}
+.wrapper {
+  padding-top: 50px;
+  position: relative;
+}
+.searchBox {
+  position: absolute;
+  top: 0;
+  z-index: 99;
+  background-color: #dedede;
+  height: 50px;
+  width: 100%;
+  line-height: 50px;
+  display: flex;
+  justify-content: space-around;
+  align-items: center;
+
+  input {
+    height: 40px;
+    width: 65%;
+    background: #f9f9f9 !important;
+    margin-left: 20rpx;
+    padding-left: 10rpx;
+    border-radius: 5rpx;
+  }
+
+  .searchBtn {
+    height: 40px;
+    background: #f9f9f9 !important;
+    color: #676767;
+    font-size: 28rpx;
+    width: 260rpx;
+    padding: 0 42rpx;
+    outline: none;
+    border: none;
+
+    .icon-sousuo {
+      font-size: 22px;
+    }
+
+    .text {
+      font-size: 16px;
+      margin-left: 10px;
+    }
+  }
+}
+
+.listContent {
+  // margin-top: 100rpx;
+  padding-bottom: 100rpx;
+  flex: 1;
+  overflow-x: scroll;
+
+  .listBox {
+    display: flex;
+    // height: 180rpx;
+    padding: 20rpx 0;
+    border-bottom: 2rpx solid #e5e5e5;
+
+    .listBox-sel {
+      height: 90rpx;
+      width: 80rpx;
+      line-height: 90rpx;
+      text-align: center;
+
+      checkbox {
+        transform: scale(1.2);
+      }
+    }
+
+    .listBox-con {
+      width: 100%;
+      // display: flex;
+      // flex-wrap: wrap;
+      // justify-content: space-between;
+      align-items: center;
+      padding: 0 18rpx 0 0;
+
+      .listBox-top {
+        width: 100%;
+        display: flex;
+        justify-content: space-between;
+        padding-bottom: 10rpx;
+
+        .listBox-name,
+        .listBox-code {
+          display: inline-block;
+          font-size: $uni-font-size-sm;
+          font-weight: bold;
+        }
+
+        .listBox-code {
+        }
+      }
+
+      .listBox-bottom {
+        width: 100%;
+        display: flex;
+        justify-content: space-between;
+        flex-wrap: wrap;
+        font-size: $uni-font-size-sm;
+
+        .items {
+          width: 50%;
+          margin-bottom: 10rpx;
+        }
+
+        .bot-left,
+        .bot-right {
+          display: inline-block;
+          color: $uni-text-color-grey;
+        }
+
+        .bot-right {
+        }
+      }
+    }
+  }
+}
+.footer {
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+  .footer-cancel {
+    width: 50%;
+    font-size: 36rpx;
+    color: #fff;
+    background: #ccc;
+    text-align: center;
+    line-height: 90rpx;
+  }
+  .footer-comfirm {
+    width: 50%;
+    font-size: 36rpx;
+    color: #fff;
+    background: #33cc66;
+    text-align: center;
+    line-height: 90rpx;
+  }
+}
+</style>

+ 45 - 0
components/MaterialSelector/materialDialog.vue

@@ -0,0 +1,45 @@
+<template>
+  <u-popup :show="searchVisible" mode="bottom" @close="searchVisible = false">
+    <view>
+      <contentBox
+        warehousingType="29"
+        @handleCancel="close"
+        @pick="select"
+        :chooseList="selectList"
+      />
+    </view>
+  </u-popup>
+</template>
+
+<script>
+import contentBox from "./contentBox.vue";
+
+export default {
+  components: { contentBox },
+  props: {
+    selectList: {
+      type: Array,
+      default: function () {
+        return [];
+      },
+    },
+  },
+  data() {
+    return {
+      searchVisible: false,
+    };
+  },
+  methods: {
+    open() {
+      this.searchVisible = true;
+    },
+    close() {
+      this.searchVisible = false;
+    },
+    select(list) {
+      this.$emit("affirm", list);
+      this.searchVisible = false;
+    },
+  },
+};
+</script>

+ 69 - 1
enum/dict.js

@@ -12,6 +12,7 @@ export default {
   取样类型: "quality_method_code",
   计量单位: "measuring_uint",
   记录规则类型: 'record_sheet',
+  表计类型: 'meter_type'
 
 };
 export const numberList = [
@@ -265,4 +266,71 @@ export const materialType = [
 export const relationTypeOption = {
   1: '发货确认单',
   2: '收货确认单'
-};
+};
+
+
+// 用能单位集合
+export const energyConsumingUnitList = [
+  {
+    value: '1',
+    label: '水',
+    unit: [
+      { value: '1', label: 'm³' },
+      { value: '2', label: 'L' },
+      { value: '3', label: 'T' }
+    ],
+    priceUnit: [
+      { value: '1', label: '元/m³' },
+      { value: '2', label: '元/L' },
+      { value: '3', label: '元/T' }
+    ]
+  },
+  {
+    value: '2',
+    label: '电',
+    unit: [
+      { value: '1', label: 'kWh' },
+      { value: '2', label: 'MW' },
+      { value: '3', label: 'VA' }
+    ],
+    priceUnit: [
+      { value: '1', label: '元/kWh' },
+      { value: '2', label: '元/MWh' },
+      { value: '3', label: '元/VA' }
+    ]
+  },
+  {
+    value: '3',
+    label: '气',
+    unit: [
+      // { value: '1', label: 'J' },
+      // { value: '2', label: 'kJ' },
+      // { value: '3', label: 'MJ' },
+      // { value: '4', label: 'GJ' },
+      { value: '5', label: 'm³' }
+    ],
+    priceUnit: [
+      // { value: '1', label: '元/J' },
+      // { value: '2', label: '元/kJ' },
+      // { value: '3', label: '元/MJ' },
+      // { value: '4', label: '元/GJ' },
+      { value: '5', label: '元/m³' }
+    ]
+  },
+  {
+    value: '4',
+    label: '热力',
+    unit: [
+      { value: '1', label: 'J' },
+      { value: '2', label: 'kJ' },
+      { value: '3', label: 'MJ' },
+      { value: '4', label: 'GJ' },
+    ],
+    priceUnit: [
+      { value: '1', label: '元/J' },
+      { value: '2', label: '元/kJ' },
+      { value: '3', label: '元/MJ' },
+      { value: '4', label: '元/GJ' },
+    ]
+  }
+];

+ 1 - 1
manifest.json

@@ -2,7 +2,7 @@
     "name" : "智慧工厂",
     "appid" : "__UNI__45B3907",
     "description" : "",
-    "versionName" : "V1.0.3.95",
+    "versionName" : "V1.0.3.96",
     "versionCode" : "100",
     "transformPx" : false,
     "h5" : {

+ 10 - 3
pages.json

@@ -131,7 +131,7 @@
 				"navigationStyle": "custom",
 				"navigationBarTextStyle": "white"
 			}
-		}, 
+		},
 		{ // 采购收货确认
 			"path": "pages/home/wt/components/purchaseInvoiceConfirm/processTask",
 			"style": {
@@ -1815,7 +1815,6 @@
 				"navigationBarTextStyle": "white"
 			}
 		},
-
 		{
 			"path": "pages/pda/work_order_details/index/index",
 			"style": {
@@ -2667,12 +2666,20 @@
 				"navigationStyle": "custom",
 				"navigationBarTextStyle": "black"
 			}
+		},
+		{
+			"path": "pages/ems/centralizedMeterReading/manualcentralizedMeterReading",
+			"style": {
+				"navigationBarTitleText": "手动集抄",
+				"navigationStyle": "custom",
+				"navigationBarTextStyle": "black"
+			}
 		}
 	],
 	"tabBar": {
 		"color": "#908f8f",
 		"selectedColor": "#157a2c",
-		"borderStyle": "black",
+		"borderStyle": "black", 
 		"backgroundColor": "#ffffff",
 		"fontSize": "14px",
 		"list": [

+ 371 - 0
pages/centralizedMeterReading/components/myCard.vue

@@ -0,0 +1,371 @@
+<template>
+  <view class="card_container">
+    <view class="card_box">
+      <!-- 标题区域 - 独立出来,宽度100% -->
+      <view class="header_box" v-if="title">
+        <view class="title_left">
+          <view class="round" v-if="index">{{ index }}</view>
+          <view class="orderId" :style="{ marginLeft: index ? '16rpx' : '' }">{{
+            title
+          }}</view>
+        </view>
+        <!-- 状态 - 固定在右上角 -->
+        <view class="status-tag" v-if="status">{{ status }}</view>
+      </view>
+
+      <view class="item_box rx-bc" v-for="(_item, i) in columns" :key="i">
+        <template v-for="val in _item">
+          <view
+            class="perce50"
+            :class="[val.className, { 'full-width': val.isFullWidth }]"
+            :style="val.style"
+            :key="val.prop"
+            v-if="!val.isNone"
+          >
+            <!-- 操作行 -->
+            <view class="item_one rx-sc" v-if="val.type == 'action'">
+              <view class="lable">{{ val.label }}</view>
+              <view class="text" style="flex-wrap: wrap">
+                <template v-for="(btn, bI) in btnList">
+                  <u-button
+                    :plain="true"
+                    :hairline="true"
+                    size="mini"
+                    :type="btn.btnType"
+                    v-if="judge(btn)"
+                    :text="btn.name"
+                    @click="action(btn)"
+                    :key="bI"
+                  ></u-button>
+                </template>
+              </view>
+            </view>
+
+            <!-- 普通信息行 -->
+            <view class="item_one rx-sc kk" v-else>
+              <view class="lable">{{ val.label }}</view>
+              <view class="text" :class="val.valueClass" v-if="val.formatter">{{
+                val.formatter(item) || ""
+              }}</view>
+              <view class="text" :class="val.valueClass" v-else-if="val.slot">
+                <slot :name="val.slot"></slot>
+              </view>
+              <view class="text" :class="val.valueClass" v-else>{{
+                item[val.prop] || ""
+              }}</view>
+            </view>
+          </view>
+        </template>
+      </view>
+
+      <!-- 查看详情 -->
+      <view class="footer-link" v-if="showDetail" @click="goDetail">
+        <text>查看详情</text>
+        <u-icon name="arrow-right" size="24" color="#999999"></u-icon>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  props: {
+    btnList: {
+      type: Array,
+      default: () => [],
+    },
+    item: {
+      type: Object,
+      default: () => ({}),
+    },
+    columns: {
+      type: Array,
+      default: () => [],
+    },
+    title: {
+      type: String,
+      default: "",
+    },
+    status: {
+      type: String,
+      default: "",
+    },
+    index: "",
+    showDetail: {
+      type: Boolean,
+      default: true,
+    },
+  },
+  computed: {
+    judge() {
+      return (item) => {
+        if (item.judge) {
+          let is = true;
+          item.judge.forEach(({ key, value, authorities, fn }) => {
+            if (authorities) {
+              is = this.$isAuthorities(authorities);
+            }
+            if (value && !value.includes(this.item[key])) {
+              is = false;
+            }
+            if (fn) {
+              is = fn(this.item);
+            }
+          });
+          return is;
+        } else {
+          return true;
+        }
+      };
+    },
+  },
+  data() {
+    return {};
+  },
+  methods: {
+    action(item) {
+      if (item.type == 1) {
+        uni.navigateTo({
+          url: item.pageUrl + "?id=" + this.item.id + (item.query || ""),
+        });
+      } else {
+        this.$emit(item.apiName);
+      }
+    },
+    goDetail() {
+      this.$emit("goDetail", this.item);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.card_container {
+  padding: 20rpx 24rpx;
+  background: #f5f5f5;
+}
+
+.card_box {
+  background: #ffffff;
+  border-radius: 16rpx;
+  padding: 28rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
+  position: relative;
+
+  .rx-bc {
+    display: flex;
+    align-items: flex-start;
+    flex-flow: row wrap;
+
+    > view {
+      margin-top: 28rpx;
+
+      &:first-child,
+      &:nth-child(2) {
+        margin-top: 0;
+      }
+    }
+  }
+
+  .rx-sc {
+    display: flex;
+    align-items: flex-start;
+  }
+
+  // 头部样式
+  .header_box {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 100%;
+    position: relative;
+    background: linear-gradient(135deg, #e8f5e9 0%, #f1f8e9 100%);
+    padding: 20rpx 24rpx;
+    border-radius: 12rpx 12rpx 0 0;
+    margin: -28rpx -28rpx 28rpx -28rpx;
+    width: calc(100% + 56rpx);
+    box-sizing: border-box;
+    .round {
+      width: 44rpx;
+      height: 44rpx;
+      line-height: 44rpx;
+      border-radius: 50%;
+      background: $theme-color;
+      color: #fff;
+      text-align: center;
+      font-size: 24rpx;
+      font-weight: 600;
+      flex-shrink: 0;
+    }
+
+    .orderId {
+      color: #333333;
+      font-size: 32rpx;
+      font-weight: 600;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    .title_left {
+      display: flex;
+      align-items: center;
+      flex: 1;
+      padding-right: 140rpx;
+      overflow: hidden;
+    }
+
+    // 状态标签
+    .status-tag {
+      background: #e3f2fd;
+      color: #2196f3;
+      font-size: 24rpx;
+      padding: 8rpx 20rpx;
+      border-radius: 24rpx;
+      font-weight: 500;
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      right: 24rpx;
+      z-index: 1;
+    }
+  }
+
+  .item_box {
+    .text {
+      display: flex;
+      flex: 1;
+
+      uni-button:after {
+        border: none;
+      }
+
+      uni-button {
+        width: 100rpx;
+        margin-left: 10rpx;
+        margin-right: 0;
+        color: #fff !important;
+        border: none;
+        background: #157a2c;
+        margin-top: 3px;
+      }
+    }
+
+    .kk .text {
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      display: block;
+    }
+
+    .item_one {
+      width: 100%;
+      font-size: 28rpx;
+      line-height: 44rpx;
+      display: flex;
+      align-items: flex-start;
+
+      .lable {
+        color: #666666;
+        flex-shrink: 0;
+        margin-right: 12rpx;
+        font-size: 28rpx;
+      }
+
+      .text {
+        color: #000;
+        font-size: 28rpx;
+        flex: 1;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        min-width: 0;
+
+        // 高亮值样式
+        &.highlight {
+          color: #4caf50;
+        }
+      }
+    }
+
+    .gylx {
+      color: $theme-color;
+    }
+
+    .perce50 {
+      width: 50%;
+      box-sizing: border-box;
+      padding-right: 20rpx;
+
+      &:nth-child(2n) {
+        padding-right: 0;
+        padding-left: 20rpx;
+      }
+    }
+    .perce100 {
+      width: 100%;
+    }
+
+    .full-width {
+      width: 100% !important;
+      padding-left: 0 !important;
+      padding-right: 0 !important;
+    }
+  }
+
+  // 底部查看详情
+  .footer-link {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin-top: 28rpx;
+    padding-top: 24rpx;
+    border-top: 1rpx solid #f0f0f0;
+    color: #666666;
+    font-size: 28rpx;
+    cursor: pointer;
+
+    &:active {
+      opacity: 0.7;
+    }
+  }
+}
+
+/deep/.u-input {
+  padding: 0 !important;
+  height: 44rpx !important;
+  font-size: 26rpx !important;
+}
+
+/deep/.u-input__content__field-wrapper__field {
+  font-size: 26rpx !important;
+}
+
+/deep/.uni-date-editor--x .uni-date__icon-clear {
+  border: none !important;
+}
+
+/deep/.uni-date__x-input,
+/deep/.uni-date-x {
+  padding: 0 !important;
+  height: 44rpx !important;
+  font-size: 26rpx !important;
+}
+
+/deep/.input-value {
+  font-size: 26rpx !important;
+  height: 44rpx !important;
+
+  uni-text {
+    font-size: 26rpx !important;
+  }
+}
+
+/deep/.u-textarea {
+  padding: 2px !important;
+}
+
+/deep/.u-textarea__field {
+  font-size: 26rpx !important;
+}
+</style>

+ 1039 - 0
pages/centralizedMeterReading/components/workOrderReport.vue

@@ -0,0 +1,1039 @@
+<template>
+	<u-popup :show="visible" :round="0" :closeOnClickOverlay="false" :zIndex="99999" @close="closePopup"
+		class="u-popup-my">
+		<view class="popup-content">
+			<view class="popup-header">
+				<text class="popup-title">{{ title }}</text>
+				<view class="close-btn" @click="closePopup">×</view>
+			</view>
+
+			<scroll-view class="popup-body" scroll-y>
+				<view class="page">
+					<view class="card-a">
+						<!-- 头部 -->
+						<view class="a-header">
+							<text class="a-main-title">{{ form.ruleName }}</text>
+							<view class="a-sub">
+								<text>📋
+									{{
+                    getDictValue("记录规则类型", form.recordRulesClassify)
+                  }}</text>
+							</view>
+						</view>
+						<!-- 基本信息网格 -->
+						<view class="info-grid">
+							<view class="info-item">
+								<text class="info-label">检查时间</text>
+								<uni-datetime-picker type="datetime" v-model="form.checkStartTime"
+									returnType="timestamp"
+									:disabled="title == '详情'">
+									<view class="info-value">{{
+                    form.checkStartTime&& dayjs(form.checkStartTime).format('YYYY-MM-DD HH:mm:ss')|| "请选择" }}</view>
+								</uni-datetime-picker>
+							</view>
+							<view class="info-item">
+								<text class="info-label">报工时间</text>
+
+								<uni-datetime-picker type="datetime" v-model="form.checkFinishTime"
+									:disabled="title == '详情'" returnType="timestamp" >
+									<view class="info-value">
+										{{ form.checkFinishTime&& dayjs(form.checkFinishTime).format('YYYY-MM-DD HH:mm:ss')|| "请选择"}}
+									</view>
+								</uni-datetime-picker>
+							</view>
+							<view class="info-item" v-if="!form.type">
+								<text class="info-label">部门</text>
+								<view class="info-value" :class="{ disabled: title == '详情' }"
+									@click="title != '详情' && $refs.treePicker._show()">{{ form.groupName || "请选择" }}
+								</view>
+							</view>
+							<view class="info-item" v-if="!form.type">
+								<text class="info-label">指定负责人</text>
+								<view class="info-value" :class="{ disabled: title == '详情' }"
+									@click="title != '详情' && $refs.selector.open()">{{
+                    form.executeUsers.map((item) => item.userName).toString() ||
+                    "请选择"
+                  }}</view>
+							</view>
+							<view class="info-item" v-if="form.type">
+								<text class="info-label">班组</text>
+								<view class="info-value">{{ form.teamName }}</view>
+							</view>
+							<view class="info-item" v-if="form.type">
+								<text class="info-label">执行人</text>
+								<view class="info-value" :class="{ disabled: title == '详情' }"
+									@click="title != '详情' && $refs.selector.open()">{{
+                    form.executeUsers.map((item) => item.userName).toString() ||
+                    "请选择"
+                  }}</view>
+							</view>
+							<view class="info-item" v-if="form.type">
+								<text class="info-label">场站</text>
+								<view class="info-value" :class="{ disabled: title == '详情' }"
+									@click="title != '详情' && (showProductLineIdPicker = true)">
+									{{ form.productLineName || "请选择" }}
+								</view>
+							</view>
+							<view class="info-item" v-if="pageName == 'steamInjectionInspectionRecord'">
+								<text class="info-label">甲方检查人</text>
+								<view class="info-value" :class="{ disabled: title == '详情' }"
+									@click="title != '详情' && selectContactShow(17)">{{ form.contactName || "请选择" }}
+								</view>
+							</view>
+							<view class="info-item" v-if="pageName == 'solidWasteRecord'">
+								<text class="info-label">处理方</text>
+								<view class="info-value" :class="{ disabled: title == '详情' }"
+									@click="title != '详情' && selectContactShow(19)">{{ form.supplierName || "请选择" }}
+								</view>
+							</view>
+							<view class="info-item" v-if="pageName == 'qualityTestRecords'">
+								<text class="info-label">联合站检查人</text>
+								<view class="info-value" :class="{ disabled: title == '详情' }"
+									@click="title != '详情' && selectContactShow(17)">{{ form.contactName || "请选择" }}
+								</view>
+							</view>
+						</view>
+						<!-- 运行参数记录区 -->
+						<view class="records-area">
+							<view class="section-header">
+								<view class="section-title">
+									<text>📊 记录数据</text>
+								</view>
+							</view>
+							<!-- 批量操作按钮放在标题下方 -->
+							<view class="batch-bar" v-if="pageName != 'productionRecords' && title != '详情'">
+								<button class="btn btn-primary" @click="batchCheck">
+									✅ 批量检查
+								</button>
+								<button class="btn btn-secondary" @click="batchQualified">
+									🏭 批量合格
+								</button>
+							</view>
+
+							<!-- 检查项列表 -->
+							<view v-for="(item, idx) in form.detailList" :key="idx" class="check-item-a">
+								<view class="item-row1">
+									<view class="item-info">
+										<text class="item-name">{{ idx + 1 }}. {{ item.paramValue }}
+											{{ item.unitName ? "(" + item.unitName + ")" : "" }}</text>
+									</view>
+								</view>
+								<view class="item-row-input" v-if="item.substanceName">
+									<text class="input-label">设备</text>
+									<view class="item-input"> {{ item.substanceName }}</view>
+								</view>
+								<!-- 描述/数量 -->
+								<view class="item-row-input">
+									<text class="input-label">{{
+                    pageName == "qualityTestRecords" ? "描述" : "数量"
+                  }}</text>
+
+									<input class="item-input" v-model="item.num" type="text" placeholder="请输入数量"
+										:disabled="title == '详情'" />
+								</view>
+								<view class="item-row-input" v-if="pageName == 'qualityTestRecords'">
+									<text class="input-label">执行人</text>
+
+									<view class="info-value" style="padding: 6px 10px; flex: 1; border-radius: 28rpx"
+										:class="{ disabled: title == '详情' }"
+										@click="title != '详情' && itemExecuteUsers(idx, item)">{{
+                      (item.checkUsers &&
+                        item.checkUsers
+                          .map((item) => item.userName)
+                          .toString()) ||
+                      "请选择"
+                    }}</view>
+								</view>
+								<view class="item-actions" v-if="pageName != 'productionRecords'">
+									<u-radio-group class="result-group" v-model="item.checkStatus"
+										:disabled="title == '详情'">
+										<u-radio label="已检查" :name="1"></u-radio>
+										<u-radio label="未检查" :name="0"></u-radio>
+									</u-radio-group>
+									<u-radio-group class="result-group" v-model="item.checkResult"
+										:disabled="title == '详情'">
+										<u-radio label="合格" :name="1"></u-radio>
+										<u-radio label="不合格" :name="0"></u-radio>
+									</u-radio-group>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</scroll-view>
+			<view class="popup-footer">
+				<u-button type="default" @click="closePopup">关闭</u-button>
+				<u-button type="primary" @click="submit()" v-if="title == '报工'" :loading="loading">提交</u-button>
+			</view>
+		</view>
+		<u-toast ref="uToast"></u-toast>
+
+		<ba-tree-picker ref="treePicker" key="verify" :multiple="false" @select-change="searchDeptNodeClick"
+			title="选择部门" :localdata="classificationList" valueKey="id" textKey="name" childrenKey="children" />
+		<search-select :multiple="true" ref="selector" v-model="form.executeUsersIds" :data-list="
+        executorList.map((val) => {
+          return {
+            text: val.name,
+            value: val.id,
+          };
+        })
+      " title="选择" @confirm="executeIdListChange">
+		</search-select>
+		<!-- 场站 -->
+		<u-picker :show="showProductLineIdPicker" keyName="label" :columns="[
+        productLineList.map((item) => {
+          return {
+            label: item.name,
+            value: item.id,
+          };
+        }),
+      ]" @confirm="confirmProductLine" @cancel="showProductLineIdPicker = false"></u-picker>
+		<search-select :multiple="true" ref="selector1" v-model="checkUsersIds" :data-list="
+        (form.executeUsers &&
+          form.executeUsers.map((val) => {
+            return {
+              text: val.userName,
+              value: val.userId,
+            };
+          })) ||
+        []
+      " title="选择" @confirm="executeIdListChange1">
+		</search-select>
+		<!-- <u-picker
+      :show="showCheckUsersIds"
+      keyName="label"
+      mode="multiSelector"
+      :columns="[
+        form.executeUsers &&
+          form.executeUsers.map((item) => {
+            return {
+              label: item.userName,
+              value: item.userId,
+            };
+          }),
+      ]"
+      @confirm="confirmCheckUsersIds"
+      @cancel="showCheckUsersIds = false"
+    ></u-picker> -->
+	</u-popup>
+</template>
+
+<script>
+	const formBaseData = {
+		id: null,
+		workshopArea: "",
+		workshopAreaId: null,
+		checkFinishTime: "",
+		checkStartTime: "",
+		checkValidity: null,
+		checkValidityUnit: "",
+		conclusion: null,
+		detailList: [],
+		deviceId: 0,
+		deviceName: "",
+		batchNo: "",
+		executeMethod: 0,
+		formingNum: 0,
+		itemType: 0,
+		produceRoutingId: 0,
+		produceRoutingName: "",
+		produceTaskConfigId: 0,
+		produceTaskId: 0,
+		produceTaskName: "",
+		productCode: "",
+		productModel: "",
+		productName: "",
+		recordRulesClassify: null,
+		recordTemplateStyle: null,
+		ruleId: 0,
+		ruleName: "",
+		reportWorkType: 0,
+		specification: "",
+		workOrderCode: "",
+		workOrderId: 0,
+		itemTaskName: "",
+		brandNo: "",
+		duration: null,
+		// 执行人
+		executeUsersIds: [],
+		executeUsers: [],
+		showProductLineIdPicker: false,
+		// 班组id
+		teamId: "",
+		groupId: null,
+		// 物料字段name
+		pickDetails: [],
+		// 本次产出明细
+		outputDetails: [],
+		recordRulesExecuteMethodId: null,
+		recordRulesExecuteMethodName: "",
+		// 	产出类型 1-原材料,2-在制品,3.BOM标准产出
+		outputType: 1,
+		type: 0,
+		contactName: "",
+		contactId: "",
+		supplierId: "",
+		supplierName: "",
+		productLineId: null,
+		productLineName: "",
+	};
+	import {
+		getById,
+		producetaskrulerecordSaveOrUpdateAndSubmit,
+	} from "@/api/recordRules/index";
+	import {
+		getTeamPage
+	} from "@/api/pda/workOrderHandover.js";
+	import {
+		getUserPage,
+		listOrganizations
+	} from "@/api/common.js";
+	import {
+		toTreeData
+	} from "@/utils/utils.js";
+	import {
+		mapGetters
+	} from "vuex";
+	import searchSelect from "@/pages/salesServiceManagement/accessory/components/searchSelect.vue";
+	import dayjs from 'dayjs'
+	export default {
+		data() {
+			return {
+				visible: false,
+				executeUsersIds: [],
+				title: "",
+				loading: false,
+				butLoading: false,
+				checked: false,
+				teamUserList: [],
+				teamList: [],
+				teamAllList: [],
+				classificationList: [],
+				showProductLineIdPicker: false,
+				showCheckUsersIds: false,
+				executorList: [],
+				allTeamList: [],
+				productLineList: [],
+				dayjs,
+				currentIndex: "",
+				form: JSON.parse(JSON.stringify(formBaseData)),
+			};
+		},
+		computed: {
+			...mapGetters(["getDictValue"]),
+		},
+		components: {
+			searchSelect,
+		},
+		props: {
+			pageName: "",
+		},
+		methods: {
+			async open(row, type) {
+				uni.$off("setSelectList");
+				uni.$on("setSelectList", (data) => {
+					console.log(data, "data");
+					if (data && data.length > 0) {
+						this.form.contactName = data[0].name;
+						this.form.contactId = data[0].id;
+						this.form.supplierName = data[0].name;
+						this.form.supplierId = data[0].id;
+					}
+				});
+
+				if (type == "edit") {
+					this.title = "报工";
+				} else {
+					this.title = "详情";
+				}
+				this.getTreeList();
+				await this.getOrderDetials(row.id);
+				this.visible = true;
+			},
+			cancel() {
+				this.form = {
+					...JSON.parse(JSON.stringify(formBaseData)),
+				};
+
+				this.visible = false;
+			},
+			async getTreeList() {
+				const data = await listOrganizations({});
+				let treeList = toTreeData({
+					data: data || [],
+					idField: "id",
+					parentIdField: "parentId",
+				});
+				this.classificationList = treeList;
+			},
+			selectContactShow(type) {
+				uni.navigateTo({
+					url: "/pages/saleManage/components/selectContact?isAll=" +
+						false +
+						"&contactType=" +
+						type,
+				});
+			},
+			confirmProductLine(e) {
+				this.form.productLineId = e.value[0].value;
+				this.form.productLineName = e.value[0].label;
+				this.showProductLineIdPicker = false;
+			},
+
+			closePopup() {
+				this.cancel();
+			},
+
+			handParent() {
+				this.$refs.parentListRef.open();
+			},
+			changeParent(data) {
+				this.form.contactName = data.name;
+				this.form.contactId = data.id;
+				this.form.supplierName = data.name;
+				this.form.supplierId = data.id;
+			},
+			// 获取工单详情
+			async getOrderDetials(id) {
+				try {
+					const data = await getById(id);
+					data.detailList = data.detailList.map((i) => {
+						i.toolNames = i.tools.map((j) => j.toolName).join(",");
+						if (i.checkUsers && i.checkUsers.length > 0) {
+							console.log("i.checkUsers", i.checkUsers);
+							i.checkUsersIds = i.checkUsers.map((j) => j.userId);
+						}
+
+						if (
+							(!i.checkUsersIds || i.checkUsersIds.length == 0) &&
+							data.executeUsers.length > 0
+						) {
+							// 默认执行人作为检查人
+							i.checkUsersIds = data.executeUsers
+								.filter((i) => i.userId)
+								.map((j) => j.userId);
+							i.checkUsers = data.executeUsers
+								.filter((i) => i.userId)
+								.map((j) => {
+									return {
+										teamId: j.teamId,
+										teamName: j.teamName,
+										userId: j.userId,
+										userName: j.userName,
+									};
+								});
+						}
+						return i;
+					});
+					// this.$util.assignObject(this.form, data);
+					Object.assign(this.form, data);
+
+					this.form.executeUsersIds = this.form.executeUsers
+						.map((i) => i.userId)
+						.filter((i) => i); // 过滤掉 undefined
+
+					if (this.form.executeUsers.length > 0 && !this.form.type) {
+						this.form.groupId = this.form.executeUsers[0]?.groupId + "";
+						this.form.groupName = this.form.executeUsers[0]?.groupName;
+						if (this.form.groupId) {
+							this.searchDeptNodeClick(
+								this.form.groupId,
+								this.form.groupName,
+								"init",
+							);
+						}
+					}
+
+					if (!this.form.teamId && this.teamList.length > 0) {
+						this.form.teamId = this.teamList[0].id;
+					}
+
+					this.form.recordRulesClassify += "";
+
+					// 加载班组人员列表
+					if (this.form.teamId && this.form.type) {
+						await this.getAllTeamList();
+
+						const index = this.allTeamList.findIndex(
+							(item) => item.id == this.form.teamId,
+						);
+						this.executorList = this.allTeamList[index].userVOList;
+
+						this.productLineList = [];
+
+						this.allTeamList[index].factoryWorkstationVOList.forEach((item) => {
+							if (
+								!this.productLineList.find((p) => p.id === item.productionLineId)
+							) {
+								this.productLineList.push({
+									name: item.productionLineName,
+									id: item.productionLineId,
+								});
+							}
+						});
+					}
+
+					this.$nextTick(() => {
+						this.loading = false;
+					});
+				} catch (error) {
+					console.log("error", error);
+					this.loading = false;
+				}
+			},
+			checkTeamList(id) {
+				const index = this.teamList.findIndex((item) => item.id == id);
+				this.teamUserList = this.teamAllList[index];
+				this.form.teamName = this.teamList[index].name;
+				console.log("this.teamUserList", this.teamUserList);
+			},
+
+			// 批量检查
+			batchCheck() {
+				this.form.detailList.forEach((i, index) => {
+					this.$set(this.form.detailList[index], "checkStatus", 1);
+				});
+			},
+			// 批量合格
+			batchQualified() {
+				this.form.detailList.forEach((i, index) => {
+					this.$set(this.form.detailList[index], "checkResult", 1);
+				});
+			},
+			itemExecuteUsers(index, row) {
+				this.currentIndex = index;
+				this.$refs.selector1.open();
+			},
+			executeIdListChange1() {
+				this.$set(
+					this.form.detailList[this.currentIndex],
+					"checkUsersIds",
+					this.checkUsersIds,
+				);
+				const checkUsersIds = this.checkUsersIds;
+				this.checkUsersIds = [];
+				const checkUsers = checkUsersIds.map((i) => {
+					const user = this.form.executeUsers.find((item) => item.userId === i);
+					return user;
+				});
+				console.log(checkUsers, "checkUsers");
+				this.$set(
+					this.form.detailList[this.currentIndex],
+					"checkUsers",
+					checkUsers,
+				);
+			},
+
+			// 获取审核人列表、巡点检人员
+			async getUserList(params) {
+				try {
+					let data = {
+						pageNum: 1,
+						size: -1,
+					};
+					// 如果传了参数就是获取部门人员数据
+					if (params) {
+						data = Object.assign(data, params);
+					}
+					const res = await getUserPage(data);
+					if (params) {
+						this.executorList = res.list;
+					}
+				} catch (error) {}
+			},
+			//选择部门(搜索)
+			async searchDeptNodeClick(id, name, type) {
+				this.form.groupId = id;
+				this.form.groupName = name;
+				if (id) {
+					// 根据部门获取人员
+					const params = {
+						groupId: id,
+					};
+					await this.getUserList(params);
+				} else {
+					this.form.groupId = null;
+				}
+				if (type != "init") {
+					this.form.detailList.forEach((detail) => {
+						detail.checkUsersIds = [];
+						detail.checkUsers = [];
+					});
+					this.form.executeUsers = [];
+					this.form.executeUsersIds = [];
+				}
+			},
+			// 负责人变更 同步执行人列表
+			executeIdListChange() {
+				console.log(this.form.executeUsersIds, "this.form.executeUsersIds");
+				this.form.executeUsers = this.form.executeUsersIds
+					.map((userId) => {
+						const user = this.executorList.find((u) => u.id === userId);
+						if (!user) return null;
+						return {
+							userId: user.id,
+							userName: user.name,
+							groupId: user.groupId,
+							groupName: user.groupName,
+						};
+					})
+					.filter((i) => i);
+				// 同步详情执行人
+				this.form.detailList.forEach((detail, index) => {
+					this.$set(
+						this.form.detailList[index],
+						"checkUsersIds",
+						this.form.executeUsersIds,
+					);
+					this.$set(
+						this.form.detailList[index],
+						"checkUsers",
+						this.form.executeUsers,
+					);
+				});
+				console.log("this.form.executeUsers", this.form.detailList);
+			},
+			async getAllTeamList() {
+				const {
+					list
+				} = await getTeamPage({
+					pageNum: 1,
+					size: -1,
+				});
+				console.log("teamAllList 班组", list);
+				this.allTeamList = list;
+			},
+
+			// 提交
+			async submit() {
+				// 验证检查时间和报工时间必填
+				if (!this.form.checkStartTime) {
+					this.$refs.uToast.show({
+						type: "error",
+						icon: false,
+						message: `请选择检查时间`,
+					});
+					return;
+				}
+				if (!this.form.checkFinishTime) {
+					this.$refs.uToast.show({
+						type: "error",
+						icon: false,
+						message: `请选择报工时间`,
+					});
+					return;
+				}
+				// 验证报工时间必须大于等于检查时间
+				const startTime = new Date(this.form.checkStartTime).getTime();
+				const finishTime = new Date(this.form.checkFinishTime).getTime();
+		
+				if (finishTime < startTime) {
+					this.$refs.uToast.show({
+						type: "error",
+						icon: false,
+						message: `报工时间不能早于检查时间`,
+					});
+					return;
+				}
+
+				if (this.form.executeUsersIds.length == 0) {
+					this.$refs.uToast.show({
+						type: "error",
+						icon: false,
+						message: `请选择执行人`,
+					});
+					return;
+				}
+
+				// 报工需要验证 缓存不验证 排除 recordTemplateStyle ==3 物料添加 == 4生产统计 模板
+				if (this.pageName != "productionRecords") {
+					// 验证检查项目
+					const detailRequired = this.form.detailList.some((i) => {
+						return i.checkResult == null || i.checkStatus == null;
+					});
+
+					if (detailRequired) {
+						this.$refs.uToast.show({
+							type: "error",
+							icon: false,
+							message: `请先完善、检查情况、检查结果`,
+						});
+						return;
+					}
+				}
+
+				this.form.checkStartTime = dayjs(this.form.checkStartTime).format('YYYY-MM-DD HH:mm:ss')
+				this.form.checkFinishTime = dayjs(this.form.checkFinishTime).format('YYYY-MM-DD HH:mm:ss')
+				const body = JSON.parse(JSON.stringify(this.form));
+
+
+				try {
+					await producetaskrulerecordSaveOrUpdateAndSubmit(body);
+					this.$emit("refresh");
+					this.closePopup();
+				} catch (error) {
+					console.log(error, "dsdsd");
+				}
+			},
+		},
+	};
+</script>
+
+<style scoped lang="scss">
+	.popup-content {
+		width: 100vw;
+		height: calc(100vh - 100px);
+		background: #fff;
+		border-radius: 0;
+		display: flex;
+		background-color: #eff2f7;
+		flex-direction: column;
+	}
+
+	.popup-header {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 30rpx;
+		border-bottom: 1rpx solid #e5e5e5;
+
+		.popup-title {
+			font-size: 36rpx;
+			font-weight: bold;
+			color: #333;
+		}
+
+		.close-btn {
+			width: 60rpx;
+			height: 60rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			font-size: 60rpx;
+			color: #999;
+			line-height: 1;
+		}
+	}
+
+	.popup-body {
+		flex: 1;
+		overflow-y: auto;
+		padding: 28rpx;
+	}
+
+	.page {
+		font-family:
+			system-ui,
+			-apple-system,
+			"Segoe UI",
+			Roboto,
+			Helvetica,
+			sans-serif;
+	}
+
+	.design-section {
+		/* margin-bottom: 60rpx; */
+	}
+
+	.design-header {
+		margin-bottom: 20rpx;
+		padding-left: 8rpx;
+	}
+
+	.design-title {
+		font-size: 44rpx;
+		font-weight: 800;
+		color: #1f2b3c;
+		display: block;
+	}
+
+	.design-desc {
+		font-size: 26rpx;
+		color: #6b7280;
+		margin-top: 6rpx;
+	}
+
+	/* 卡片白色风格 */
+	.card-a {
+		background: #ffffff;
+		border-radius: 48rpx;
+		box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.05);
+		overflow: hidden;
+	}
+
+	.a-header {
+		padding: 40rpx 32rpx 24rpx;
+		border-bottom: 2rpx solid #f0f2f5;
+	}
+
+	.a-main-title {
+		font-size: 36rpx;
+		font-weight: 800;
+		background: linear-gradient(135deg, #1f2b3c, #2c3e50);
+		background-clip: text;
+		-webkit-background-clip: text;
+		color: transparent;
+		letter-spacing: -0.5rpx;
+	}
+
+	.a-sub {
+		font-size: 24rpx;
+		color: #8e9aae;
+		margin-top: 12rpx;
+		display: flex;
+		gap: 24rpx;
+	}
+
+	.info-grid {
+		padding: 30rpx 32rpx;
+		display: grid;
+		grid-template-columns: 1fr 1fr;
+		gap: 28rpx 20rpx;
+		// background: #fcfdfe;
+		border-bottom: 2rpx solid #f0f2f5;
+	}
+
+	.info-item {
+		display: flex;
+		flex-direction: column;
+		gap: 8rpx;
+	}
+
+	.info-label {
+		font-size: 26rpx;
+		font-weight: 600;
+		color: #6c7a91;
+		text-transform: uppercase;
+	}
+
+	.records-area {
+		padding: 16rpx 28rpx 32rpx;
+	}
+
+	.section-header {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		margin: 16rpx 0 20rpx 8rpx;
+	}
+
+	.section-title {
+		font-size: 36rpx;
+		font-weight: 700;
+		color: #1f2a44;
+		display: flex;
+		align-items: center;
+		gap: 16rpx;
+	}
+
+	.badge {
+		background: #eff3fa;
+		padding: 6rpx 20rpx;
+		border-radius: 60rpx;
+		font-size: 24rpx;
+		font-weight: normal;
+		color: #2c5f8a;
+	}
+
+	.device-card-a {
+		background: #f8fbfe;
+		border-radius: 36rpx;
+		padding: 20rpx 24rpx;
+		margin-bottom: 30rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		flex-wrap: wrap;
+		border: 2rpx solid #e9edf2;
+	}
+
+	.device-label {
+		font-weight: 700;
+		font-size: 28rpx;
+		color: #1f2a44;
+		background: #eff3fa;
+		padding: 8rpx 24rpx;
+		border-radius: 60rpx;
+	}
+
+	.device-input-a {
+		flex: 1;
+		min-width: 280rpx;
+		background: #ffffff;
+		border: 2rpx solid #dce3ec;
+		border-radius: 36rpx;
+		padding: 16rpx 24rpx;
+		font-size: 28rpx;
+	}
+
+	.check-item-a {
+		background: #ffffff;
+		border-radius: 36rpx;
+		padding: 24rpx 24rpx;
+		margin-bottom: 24rpx;
+		box-shadow:
+			0 4rpx 16rpx rgba(0, 0, 0, 0.02),
+			0 0 0 2rpx #edf2f7;
+	}
+
+	.item-row1 {
+		display: flex;
+		justify-content: space-between;
+		align-items: baseline;
+		flex-wrap: wrap;
+		margin-bottom: 20rpx;
+		gap: 16rpx;
+	}
+
+	.item-name {
+		font-weight: 620;
+		font-size: 30rpx;
+		color: #1f2a44;
+		background: #f5f7fb;
+		padding: 6rpx 20rpx;
+		border-radius: 50rpx;
+	}
+
+	.item-info {
+		display: flex;
+		flex-direction: column;
+		gap: 8rpx;
+	}
+
+	.item-row-input {
+		display: flex;
+		align-items: center;
+		gap: 16rpx;
+		margin-bottom: 20rpx;
+		padding: 0 8rpx;
+	}
+
+	.input-label {
+		font-size: 28rpx;
+		color: #555;
+		font-weight: 500;
+		white-space: nowrap;
+	}
+
+	.item-input {
+		// background: #f8fafe;
+		border: 2rpx solid #e2e8f0;
+		border-radius: 36rpx;
+		padding: 12rpx 24rpx;
+		font-size: 30rpx;
+		min-height: 60rpx;
+		flex: 1;
+		font-family: monospace;
+	}
+
+	.item-actions {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		flex-wrap: wrap;
+		gap: 20rpx;
+	}
+
+	.check-status,
+	.result-group {
+		display: flex;
+		align-items: center;
+		gap: 24rpx;
+		background: #f8fafe;
+		padding: 8rpx 24rpx;
+		border-radius: 60rpx;
+		font-size: 28rpx;
+	}
+
+	/deep/.result-group {
+		.u-radio__text {
+			font-size: 28rpx !important;
+		}
+
+		.u-radio__icon-wrap {
+			width: 30rpx !important;
+			height: 30rpx !important;
+		}
+	}
+
+	.radio-label {
+		display: flex;
+		align-items: center;
+		gap: 8rpx;
+	}
+
+	.batch-bar {
+		display: flex;
+		gap: 16rpx;
+		margin: 16rpx 0 20rpx 8rpx;
+	}
+
+	.btn {
+		flex: 1;
+		text-align: center;
+		border-radius: 80rpx;
+		font-weight: 700;
+		font-size: 30rpx;
+		border: none;
+	}
+
+	.btn-primary {
+		background: #1e6f5c;
+		color: white;
+		box-shadow: 0 4rpx 12rpx rgba(30, 111, 92, 0.2);
+	}
+
+	.btn-secondary {
+		background: #2c7da0;
+		color: white;
+	}
+
+	.footnote {
+		font-size: 22rpx;
+		color: #8e9aab;
+		text-align: center;
+		margin-top: 16rpx;
+		display: block;
+	}
+
+	/* 修复组件样式 */
+	radio,
+	checkbox {
+		transform: scale(0.9);
+		margin-right: 6rpx;
+	}
+
+	button:after {
+		border: none;
+	}
+
+	.info-value {
+		font-size: 28rpx;
+		font-weight: 500;
+		color: #1e2a3a;
+		// background: #f2f5f9;
+		padding: 16rpx 20rpx;
+		border-radius: 24rpx;
+		border: 2rpx solid #e9edf2;
+
+		&.disabled {
+			color: #999;
+			background: #f5f5f5;
+		}
+	}
+
+	.popup-footer {
+		display: flex;
+		padding: 20rpx 30rpx;
+		border-top: 1rpx solid #e5e5e5;
+		gap: 20rpx;
+
+		/deep/ .u-button {
+			flex: 1;
+		}
+	}
+
+	/deep/.uni-select {
+		border-radius: 36rpx;
+	}
+</style>

+ 355 - 0
pages/centralizedMeterReading/list.vue

@@ -0,0 +1,355 @@
+<template>
+	<view class="mainBox">
+		<uni-nav-bar fixed="true" statusBar="true" left-icon="back" title="手动集抄" @clickLeft="back">
+		</uni-nav-bar>
+
+		<view class="top-wrapper">
+			<uni-section>
+				<uni-easyinput prefixIcon="search" style="width: 460rpx" v-model="keyWord"
+					placeholder="工单单号,计划单号,记录规则名称">
+				</uni-easyinput>
+			</uni-section>
+			<button class="search_btn" @click="doSearch">搜索</button>
+		</view>
+		<div style="height: 100rpx; width: 475rpx"></div>
+		<view class="wrapper">
+			<u-list @scrolltolower="scrolltolower" class="listContent">
+				<view v-for="(item, index) in tableList" :key="index" style="position: relative">
+					<myCard :item="item" :index="index + 1" :btnList="btnList" :columns="columns" :title="item.code"
+						:status="statusMap[item.status]" @goDetail="goDetail(item, 'view')"
+						@edit="goDetail(item, 'edit')">
+					</myCard>
+				</view>
+				<view style="width: 100%; height: 40rpx"></view>
+				<view style="margin-top: 20vh" v-if="tableList.length == 0">
+					<u-empty iconSize="150" textSize="32" text="暂无数据"> </u-empty>
+				</view>
+			</u-list>
+		</view>
+
+		<u-toast ref="uToast"></u-toast>
+		<workOrderReport ref="workOrderReportRef" :pageName="pageName" @refresh="successInit"></workOrderReport>
+		<view class="add" @click="add">
+			<u-icon name="plus" color="#fff"></u-icon>
+		</view>
+	</view>
+</template>
+
+<script>
+	import dictMixns from "@/mixins/dictMixins";
+	import myCard from "./components/myCard.vue";
+	import workOrderReport from "./components/workOrderReport.vue";
+	import {
+		page
+	} from "@/api/recordRules/index";
+	export default {
+		components: {
+			myCard,
+			workOrderReport,
+		},
+		mixins: [dictMixns],
+		props: {
+			pageName: {
+				default: "",
+			},
+		},
+		data() {
+			return {
+				statusMap: {
+					0: "未报工",
+					1: "执行中",
+					2: "已执行",
+				},
+				planTypeList: {
+					productionRecords: 2,
+					steamInjectionInspectionRecord: 3,
+					solidWasteRecord: 4,
+					qualityTestRecords: 5,
+					boilerOperationRecord: 6,
+				},
+
+				columns: [
+					[{
+						prop: "substanceName",
+						label: "表计设备名称",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "currentMeterReadingTime",
+						label: "本期抄表时间",
+						className: "perce100",
+					}, ],
+					[{
+							prop: "currentMeterReadingNum",
+							label: "本期抄表数",
+							className: "perce50",
+						},
+						{
+							prop: "unit",
+							label: "单位",
+							className: "perce50",
+						},
+					],
+
+					[{
+						prop: "meterReadingName",
+						label: "抄表人",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "meterEquipmentNumber",
+						label: "表计设备编号",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "meterType",
+						label: "表计类型",
+						className: "perce100",
+
+						formatter: (row) => {
+							return this.getDictValue("表计类型", _row.meterType);
+						},
+					}, ],
+					[{
+						prop: "correlationSubstanceName",
+						label: "关联设备",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "areaName",
+						label: "区域",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "energyClassificationName",
+						label: "用能分类",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "energyTypeName",
+						label: "能源名称",
+						className: "perce100",
+					}, ],
+					[{
+							prop: "approvalStatus",
+							label: "审核状态",
+							className: "perce50",
+							formatter: (_row, _column, cellValue) => {
+								return reviewStatus[_row.approvalStatus] || "-";
+							},
+						},
+						{
+							prop: "publishStatus",
+							label: "发布状态",
+							className: "perce50",
+							formatter: (_row, _column, cellValue) => {
+								return _row.publishStatus == 1 ?
+									"已发布" :
+									_row.publishStatus == 2 ?
+									"已撤销" :
+									"未发布";
+							},
+						},
+					],
+					[{
+						label: "操作:",
+						prop: "action",
+						type: "action",
+						className: "perce100",
+					}, ],
+				].filter((item) => !item.isNone),
+
+				tableList: [],
+				page: 1,
+				size: 10,
+				isEnd: false,
+				keyWord: "",
+				userInfo: {},
+			};
+		},
+		computed: {
+			btnList() {
+				return [{
+						name: "编辑",
+						apiName: "edit",
+						btnType: "primary",
+						judge: [{
+							fn: (item) => {
+								return (
+									[0, 3].includes(item.approvalStatus) &&
+									item.publishStatus != 1
+								);
+							},
+						}, ],
+					},
+					{
+						name: "提交",
+						apiName: "handleSubmit",
+						btnType: "primary",
+						judge: [{
+							fn: (item) => {
+								return (
+									this.approval == 1 && [0, 3].includes(row.approvalStatus)
+								);
+							},
+						}, ],
+					},
+					{
+						name: "删除",
+						apiName: "handleDelete",
+						btnType: "danger",
+						judge: [{
+							fn: (item) => {
+								return (
+									row.publishStatus != 1 && [0].includes(row.approvalStatus)
+								);
+							},
+						}, ],
+					},
+					{
+						name: "发布",
+						apiName: "handlePublish",
+						btnType: "primary",
+						judge: [{
+							fn: (item) => {
+								return row.publishStatus == 0 && this.approval != 1;
+							},
+						}, ],
+					},
+				];
+			},
+		},
+		created() {
+			this.requestDict("表计类型");
+		},
+		onLoad() {},
+
+		methods: {
+			goDetail(item, type) {
+				this.$refs.workOrderReportRef.open(item, type);
+			},
+
+			successInit() {
+				uni.showLoading({
+					title: "加载中",
+				});
+
+				let data = {
+					pageNum: 1,
+					size: this.tableList.length,
+					keyword: this.keyWord,
+					areaId: "",
+					categoryLevelId: "",
+					meterReadingMethod: 1,
+					currentLoginUserId: this.userInfo.userId,
+				};
+
+				page(data)
+					.then((res) => {
+						this.tableList = res.list;
+					})
+					.then(() => {
+						uni.hideLoading();
+					});
+			},
+			doSearch() {
+				this.isEnd = false;
+				this.page = 1;
+				this.getList();
+			},
+			//获取列表信息
+			getList() {
+				this.userInfo = uni.getStorageSync("userInfo");
+				if (this.isEnd || !this.userInfo.userId) {
+					return;
+				}
+				uni.showLoading({
+					title: "加载中",
+				});
+
+				let data = {
+					pageNum: this.page,
+					size: this.size,
+					keyword: this.keyWord,
+					areaId: "",
+					categoryLevelId: "",
+					meterReadingMethod: 1,
+					currentLoginUserId: this.userInfo.userId,
+				};
+
+				page(data)
+					.then((res) => {
+						if (this.page === 1) {
+							this.tableList = res.list;
+						} else {
+							this.tableList.push(...res.list);
+						}
+						this.page += 1;
+						this.isEnd = this.tableList.length >= res.count;
+					})
+					.then(() => {
+						uni.hideLoading();
+					});
+			},
+			scrolltolower() {
+				if (this.isEnd) {
+					return;
+				}
+				this.getList();
+			},
+		},
+	};
+</script>
+
+<style lang="scss" scoped>
+	.mainBox {
+		background-color: #f3f8fb;
+	}
+
+	// .wrapper{
+
+	// }
+	.top-wrapper {
+		background-color: #fff;
+		display: flex;
+		width: 750rpx;
+		height: 88rpx;
+		padding: 16rpx 32rpx;
+		align-items: center;
+		// gap: 16rpx;
+		position: absolute;
+		z-index: 999;
+		// top: 44px;
+		// // #ifdef APP-PLUS
+		// top: 140rpx;
+
+		// // #endif
+		/deep/.uni-section {
+			margin-top: 0px;
+		}
+
+		/deep/.uni-section-header {
+			padding: 0px;
+		}
+
+		.search_btn {
+			width: 120rpx;
+			height: 70rpx;
+			line-height: 70rpx;
+			padding: 0 24rpx;
+			background: $theme-color;
+			font-size: 32rpx;
+			color: #fff;
+			margin: 0;
+			margin-left: 26rpx;
+		}
+
+		.menu_icon {
+			width: 44rpx;
+			height: 44rpx;
+			margin-left: 14rpx;
+		}
+	}
+
+
+</style>

+ 42 - 0
pages/centralizedMeterReading/manualcentralizedMeterReading.vue

@@ -0,0 +1,42 @@
+<template>
+  <view>
+    <list ref="listRef" pageName="manualcentralizedMeterReading"></list>
+  </view>
+</template>
+
+<script>
+import list from "./list.vue";
+
+export default {
+  components: {
+    list,
+  },
+  mixins: [],
+  data() {
+    return {};
+  },
+  computed: {},
+  created() {
+    uni.$off("successInit");
+    uni.$on("successInit", (data) => {
+      this.$nextTick(() => {
+        this.$refs.listRef.successInit();
+      });
+    });
+  },
+  onUnload() {
+    uni.$off("successInit");
+  },
+  onLoad() {
+    this.$nextTick(() => {
+      this.$refs.listRef.getList();
+    });
+    uni.setNavigationBarColor({
+      frontColor: "#000000",
+    });
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 371 - 0
pages/ems/centralizedMeterReading/components/myCard.vue

@@ -0,0 +1,371 @@
+<template>
+  <view class="card_container">
+    <view class="card_box">
+      <!-- 标题区域 - 独立出来,宽度100% -->
+      <view class="header_box" v-if="title">
+        <view class="title_left">
+          <view class="round" v-if="index">{{ index }}</view>
+          <view class="orderId" :style="{ marginLeft: index ? '16rpx' : '' }">{{
+            title
+          }}</view>
+        </view>
+        <!-- 状态 - 固定在右上角 -->
+        <view class="status-tag" v-if="status">{{ status }}</view>
+      </view>
+
+      <view class="item_box rx-bc" v-for="(_item, i) in columns" :key="i">
+        <template v-for="val in _item">
+          <view
+            class="perce50"
+            :class="[val.className, { 'full-width': val.isFullWidth }]"
+            :style="val.style"
+            :key="val.prop"
+            v-if="!val.isNone"
+          >
+            <!-- 操作行 -->
+            <view class="item_one rx-sc" v-if="val.type == 'action'">
+              <view class="lable">{{ val.label }}</view>
+              <view class="text" style="flex-wrap: wrap">
+                <template v-for="(btn, bI) in btnList">
+                  <u-button
+                    :plain="true"
+                    :hairline="true"
+                    size="mini"
+                    :type="btn.btnType"
+                    v-if="judge(btn)"
+                    :text="btn.name"
+                    @click="action(btn)"
+                    :key="bI"
+                  ></u-button>
+                </template>
+              </view>
+            </view>
+
+            <!-- 普通信息行 -->
+            <view class="item_one rx-sc kk" v-else>
+              <view class="lable">{{ val.label }}</view>
+              <view class="text" :class="val.valueClass" v-if="val.formatter">{{
+                val.formatter(item) || ""
+              }}</view>
+              <view class="text" :class="val.valueClass" v-else-if="val.slot">
+                <slot :name="val.slot"></slot>
+              </view>
+              <view class="text" :class="val.valueClass" v-else>{{
+                item[val.prop] || ""
+              }}</view>
+            </view>
+          </view>
+        </template>
+      </view>
+
+      <!-- 查看详情 -->
+      <view class="footer-link" v-if="showDetail" @click="goDetail">
+        <text>查看详情</text>
+        <u-icon name="arrow-right" size="24" color="#999999"></u-icon>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  props: {
+    btnList: {
+      type: Array,
+      default: () => [],
+    },
+    item: {
+      type: Object,
+      default: () => ({}),
+    },
+    columns: {
+      type: Array,
+      default: () => [],
+    },
+    title: {
+      type: String,
+      default: "",
+    },
+    status: {
+      type: String,
+      default: "",
+    },
+    index: "",
+    showDetail: {
+      type: Boolean,
+      default: true,
+    },
+  },
+  computed: {
+    judge() {
+      return (item) => {
+        if (item.judge) {
+          let is = true;
+          item.judge.forEach(({ key, value, authorities, fn }) => {
+            if (authorities) {
+              is = this.$isAuthorities(authorities);
+            }
+            if (value && !value.includes(this.item[key])) {
+              is = false;
+            }
+            if (fn) {
+              is = fn(this.item);
+            }
+          });
+          return is;
+        } else {
+          return true;
+        }
+      };
+    },
+  },
+  data() {
+    return {};
+  },
+  methods: {
+    action(item) {
+      if (item.type == 1) {
+        uni.navigateTo({
+          url: item.pageUrl + "?id=" + this.item.id + (item.query || ""),
+        });
+      } else {
+        this.$emit(item.apiName);
+      }
+    },
+    goDetail() {
+      this.$emit("goDetail", this.item);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.card_container {
+  padding: 20rpx 24rpx;
+  background: #f5f5f5;
+}
+
+.card_box {
+  background: #ffffff;
+  border-radius: 16rpx;
+  padding: 28rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
+  position: relative;
+
+  .rx-bc {
+    display: flex;
+    align-items: flex-start;
+    flex-flow: row wrap;
+
+    > view {
+      margin-top: 28rpx;
+
+      &:first-child,
+      &:nth-child(2) {
+        margin-top: 0;
+      }
+    }
+  }
+
+  .rx-sc {
+    display: flex;
+    align-items: flex-start;
+  }
+
+  // 头部样式
+  .header_box {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 100%;
+    position: relative;
+    background: linear-gradient(135deg, #e8f5e9 0%, #f1f8e9 100%);
+    padding: 20rpx 24rpx;
+    border-radius: 12rpx 12rpx 0 0;
+    margin: -28rpx -28rpx 28rpx -28rpx;
+    width: calc(100% + 56rpx);
+    box-sizing: border-box;
+    .round {
+      width: 44rpx;
+      height: 44rpx;
+      line-height: 44rpx;
+      border-radius: 50%;
+      background: $theme-color;
+      color: #fff;
+      text-align: center;
+      font-size: 24rpx;
+      font-weight: 600;
+      flex-shrink: 0;
+    }
+
+    .orderId {
+      color: #333333;
+      font-size: 32rpx;
+      font-weight: 600;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    .title_left {
+      display: flex;
+      align-items: center;
+      flex: 1;
+      padding-right: 140rpx;
+      overflow: hidden;
+    }
+
+    // 状态标签
+    .status-tag {
+      background: #e3f2fd;
+      color: #2196f3;
+      font-size: 24rpx;
+      padding: 8rpx 20rpx;
+      border-radius: 24rpx;
+      font-weight: 500;
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      right: 24rpx;
+      z-index: 1;
+    }
+  }
+
+  .item_box {
+    .text {
+      display: flex;
+      flex: 1;
+
+      uni-button:after {
+        border: none;
+      }
+
+      uni-button {
+        width: 100rpx;
+        margin-left: 10rpx;
+        margin-right: 0;
+        color: #fff !important;
+        border: none;
+        background: #157a2c;
+        margin-top: 3px;
+      }
+    }
+
+    .kk .text {
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      display: block;
+    }
+
+    .item_one {
+      width: 100%;
+      font-size: 28rpx;
+      line-height: 44rpx;
+      display: flex;
+      align-items: flex-start;
+
+      .lable {
+        color: #666666;
+        flex-shrink: 0;
+        margin-right: 12rpx;
+        font-size: 28rpx;
+      }
+
+      .text {
+        color: #000;
+        font-size: 28rpx;
+        flex: 1;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        min-width: 0;
+
+        // 高亮值样式
+        &.highlight {
+          color: #4caf50;
+        }
+      }
+    }
+
+    .gylx {
+      color: $theme-color;
+    }
+
+    .perce50 {
+      width: 50%;
+      box-sizing: border-box;
+      padding-right: 20rpx;
+
+      &:nth-child(2n) {
+        padding-right: 0;
+        padding-left: 20rpx;
+      }
+    }
+    .perce100 {
+      width: 100%;
+    }
+
+    .full-width {
+      width: 100% !important;
+      padding-left: 0 !important;
+      padding-right: 0 !important;
+    }
+  }
+
+  // 底部查看详情
+  .footer-link {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin-top: 28rpx;
+    padding-top: 24rpx;
+    border-top: 1rpx solid #f0f0f0;
+    color: #666666;
+    font-size: 28rpx;
+    cursor: pointer;
+
+    &:active {
+      opacity: 0.7;
+    }
+  }
+}
+
+/deep/.u-input {
+  padding: 0 !important;
+  height: 44rpx !important;
+  font-size: 26rpx !important;
+}
+
+/deep/.u-input__content__field-wrapper__field {
+  font-size: 26rpx !important;
+}
+
+/deep/.uni-date-editor--x .uni-date__icon-clear {
+  border: none !important;
+}
+
+/deep/.uni-date__x-input,
+/deep/.uni-date-x {
+  padding: 0 !important;
+  height: 44rpx !important;
+  font-size: 26rpx !important;
+}
+
+/deep/.input-value {
+  font-size: 26rpx !important;
+  height: 44rpx !important;
+
+  uni-text {
+    font-size: 26rpx !important;
+  }
+}
+
+/deep/.u-textarea {
+  padding: 2px !important;
+}
+
+/deep/.u-textarea__field {
+  font-size: 26rpx !important;
+}
+</style>

+ 429 - 0
pages/ems/centralizedMeterReading/components/workOrderReport.vue

@@ -0,0 +1,429 @@
+<template>
+	<u-popup :show="visible" :round="0" :closeOnClickOverlay="false" :zIndex="99999" @close="handleCancel"
+		class="u-popup-my">
+		<view class="popup-content">
+			<view class="popup-header">
+				<text class="popup-title">{{ title }}</text>
+				<view class="close-btn" @click="handleCancel">×</view>
+			</view>
+
+			<scroll-view class="popup-body" scroll-y>
+				<view class="page">
+					<view class="card-a">
+						<!-- 头部 -->
+						<view class="a-header">
+							<text class="a-main-title">{{ '抄表记录' }}</text>
+							<!-- <view class="a-sub">
+                <text>📋 {{ title }}</text>
+              </view> -->
+						</view>
+
+						<!-- 设备信息 -->
+						<view class="card-section">
+							<view class="section-title">🔧 设备信息</view>
+							<view class="info-grid">
+								<view class="info-item">
+									<text class="info-label">表计设备名称</text>
+									<view class="info-value" :class="{ disabled: isView }"
+										@click="!isView && openMaterialAdd()">
+										{{ form.substanceName || '请选择' }}
+									</view>
+								</view>
+								<view class="info-item">
+									<text class="info-label">表计设备编号</text>
+									<view class="info-value disabled">
+										{{ form.meterEquipmentNumber || '-' }}
+									</view>
+								</view>
+								<view class="info-item">
+									<text class="info-label">用能分类</text>
+									<view class="info-value disabled">
+										{{ form.energyClassificationName || '-' }}
+									</view>
+								</view>
+								<view class="info-item">
+									<text class="info-label">能源名称</text>
+									<view class="info-value disabled">
+										{{ form.energyTypeName || '-' }}
+									</view>
+								</view>
+								<view class="info-item">
+									<text class="info-label">表计单位</text>
+									<view class="info-value disabled">
+										{{ form.unit || '-' }}
+									</view>
+								</view>
+							</view>
+						</view>
+
+						<!-- 抄表信息 -->
+						<view class="card-section">
+							<view class="section-title">📝 抄表信息</view>
+							<view class="info-grid">
+								<view class="info-item">
+									<text class="info-label">抄表人</text>
+									<input class="info-input" v-model="form.meterReadingName" placeholder="请输入抄表人"
+										:disabled="true" />
+								</view>
+								<view class="info-item">
+									<text class="info-label">本期抄表时间</text>
+									<uni-datetime-picker type="datetime" v-model="form.currentMeterReadingTime"
+										returnType="timestamp" :disabled="isView">
+										<view class="info-value">
+											{{
+                        form.currentMeterReadingTime
+                          ? dayjs(form.currentMeterReadingTime).format('YYYY-MM-DD HH:mm:ss')
+                          : '请选择'
+                      }}
+										</view>
+									</uni-datetime-picker>
+								</view>
+								<view class="info-item">
+									<text class="info-label">本期抄表数</text>
+									<input class="info-input" v-model="form.currentMeterReadingNum" type="number"
+										placeholder="请输入本期抄表数" :disabled="isView" />
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</scroll-view>
+
+			<view class="popup-footer">
+				<u-button type="default" @click="handleCancel">
+					{{ isView ? '关闭' : '取消' }}
+				</u-button>
+				<u-button type="primary" @click="handleSubmit" v-if="!isView" :loading="loading">确定</u-button>
+			</view>
+		</view>
+		<u-toast ref="uToast"></u-toast>
+		<materialDialog ref="materialAddRef" @affirm="handleChooseEquipment"></materialDialog>
+	</u-popup>
+</template>
+
+<script>
+	import {
+		save,
+		update,
+		getById
+	} from '@/api/centralizedMeterReading/index.js';
+	import dayjs from 'dayjs';
+	import {
+		energyConsumingUnitList
+	} from '@/enum/dict.js';
+	import materialDialog from '@/components/MaterialSelector/materialDialog.vue';
+
+	const defForm = {
+		id: '',
+		substanceId: '',
+		substanceName: '',
+		substanceCode: '',
+		meterEquipmentNumber: '',
+		energyClassificationName: '',
+		energyTypeName: '',
+		unit: '',
+		meterReadingName: '',
+		meterReadingId: '',
+		currentMeterReadingTime: '',
+		currentMeterReadingNum: '',
+		meterReadingMethod: 1,
+	};
+
+	export default {
+		name: 'MeterReadingDialog',
+		data() {
+			return {
+				visible: false,
+				loading: false,
+				title: '新增',
+				type: 'add',
+				form: {
+					...defForm
+				},
+				dayjs,
+			};
+		},
+		computed: {
+			isView() {
+				return this.type === 'view';
+			},
+		},
+		components: {
+			materialDialog
+		},
+		methods: {
+			// 打开弹窗
+			async open(row, type = 'add') {
+				this.type = type;
+				if (type === 'add') {
+					this.title = '新增';
+					this.resetForm();
+					const currentUser = uni.getStorageSync("userInfo");
+					this.form.meterReadingName = currentUser?.name || '';
+					this.form.meterReadingId = currentUser?.userId || '';
+					this.form.currentMeterReadingTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
+				} else {
+					this.title = type === 'edit' ? '编辑' : '详情';
+					try {
+						const res = await getById(row.id);
+						this.form = {
+							...defForm,
+							...res
+						};
+					} catch (e) {
+						console.error('获取详情失败', e);
+					}
+				}
+				this.visible = true;
+			},
+			// 关闭弹窗
+			handleCancel() {
+				this.visible = false;
+				this.resetForm();
+			},
+			// 重置表单
+			resetForm() {
+				this.form = {
+					...defForm
+				};
+			},
+			// 打开设备选择(uni-app 中可替换为 navigateTo 或 picker)
+			openMaterialAdd() {
+				if (this.isView) return;
+				// TODO: 接入设备选择页面
+				this.$refs.materialAddRef.open()
+			},
+			// 选择设备回调
+			handleChooseEquipment(data) {
+				let row = data[0] || {}
+				this.form.substanceId = row.id;
+				this.form.substanceName = row?.category?.name;
+				this.form.substanceCode = row.code;
+				this.form.meterEquipmentNumber = row.codeNumber;
+				this.form.energyClassificationName = row.energyClassificationName;
+				this.form.energyClassificationCode = row.energyClassificationCode;
+				this.form.energyTypeName = row.energyTypeName || '';
+				this.form.energyTypeId = row.energyTypeId || '';
+				this.form.energyTypeCode = row.energyTypeCode || '';
+
+				this.form.unit = energyConsumingUnitList
+					.find((item) => item.value == row.energyClassificationCode)
+					?.unit.find((item) => item.value == row.energyUnit)?.label;
+			},
+			// 提交
+			async handleSubmit() {
+				// 必填校验
+				if (!this.form.substanceId) {
+					this.$refs.uToast.show({
+						type: 'error',
+						icon: false,
+						message: '请选择表计设备'
+					});
+					return;
+				}
+				if (!this.form.meterReadingName) {
+					this.$refs.uToast.show({
+						type: 'error',
+						icon: false,
+						message: '请输入抄表人'
+					});
+					return;
+				}
+				if (!this.form.currentMeterReadingTime) {
+					this.$refs.uToast.show({
+						type: 'error',
+						icon: false,
+						message: '请选择本期抄表时间'
+					});
+					return;
+				}
+				if (this.form.currentMeterReadingNum === '' || this.form.currentMeterReadingNum == null) {
+					this.$refs.uToast.show({
+						type: 'error',
+						icon: false,
+						message: '请输入本期抄表数'
+					});
+					return;
+				}
+
+				// 时间格式补齐
+				const formatTime = (timeStr) => {
+					if (!timeStr) return timeStr;
+					if (/^\d{4}-\d{2}-\d{2}$/.test(timeStr)) {
+						return timeStr + ' 00:00:00';
+					}
+					return timeStr;
+				};
+				this.form.currentMeterReadingTime = formatTime(this.form.currentMeterReadingTime);
+
+				const api = this.type === 'edit' ? update : save;
+				this.loading = true;
+				try {
+					await api(this.form);
+					this.$refs.uToast.show({
+						type: 'success',
+						message: this.type === 'edit' ? '编辑成功' : '新增成功',
+					});
+					this.visible = false;
+					this.$emit('refresh');
+					this.resetForm();
+				} catch (error) {
+					console.error(error);
+				} finally {
+					this.loading = false;
+				}
+			},
+		},
+	};
+</script>
+
+<style scoped lang="scss">
+	.popup-content {
+		width: 100vw;
+		height: calc(100vh - 100px);
+		background: #fff;
+		border-radius: 0;
+		display: flex;
+		flex-direction: column;
+		background-color: #eff2f7;
+	}
+
+	.popup-header {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 30rpx;
+		border-bottom: 1rpx solid #e5e5e5;
+		background: #fff;
+
+		.popup-title {
+			font-size: 36rpx;
+			font-weight: bold;
+			color: #333;
+		}
+
+		.close-btn {
+			width: 60rpx;
+			height: 60rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			font-size: 60rpx;
+			color: #999;
+			line-height: 1;
+		}
+	}
+
+	.popup-body {
+		flex: 1;
+		overflow-y: auto;
+		padding: 28rpx;
+	}
+
+	.page {
+		font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, sans-serif;
+	}
+
+	.card-a {
+		background: #ffffff;
+		border-radius: 48rpx;
+		box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.05);
+		overflow: hidden;
+	}
+
+	.a-header {
+		background: linear-gradient(135deg, #e8f5e9 0%, #f1f8e9 100%);
+		padding: 40rpx 32rpx 24rpx;
+		text-align: center;
+	}
+
+	.a-main-title {
+		font-size: 36rpx;
+		font-weight: 800;
+		color: #2e7d32;
+	}
+
+	.a-sub {
+		font-size: 24rpx;
+		color: #558b2f;
+		margin-top: 12rpx;
+	}
+
+	.card-section {
+		padding: 30rpx 32rpx;
+		border-bottom: 2rpx solid #f0f2f5;
+
+		&:last-child {
+			border-bottom: none;
+		}
+	}
+
+	.section-title {
+		font-size: 30rpx;
+		font-weight: 700;
+		color: #1f2a44;
+		margin-bottom: 24rpx;
+		padding-left: 16rpx;
+		border-left: 6rpx solid #4caf50;
+	}
+
+	.info-grid {
+		display: grid;
+		grid-template-columns: 1fr 1fr;
+		gap: 28rpx 20rpx;
+	}
+
+	.info-item {
+		display: flex;
+		flex-direction: column;
+		gap: 8rpx;
+	}
+
+	.info-label {
+		font-size: 26rpx;
+		font-weight: 600;
+		color: #6c7a91;
+	}
+
+	.info-value {
+		font-size: 28rpx;
+		font-weight: 500;
+		color: #1e2a3a;
+		padding: 16rpx 20rpx;
+		border-radius: 24rpx;
+		border: 2rpx solid #e9edf2;
+		background: #fff;
+
+		&.disabled {
+			color: #999;
+			background: #f5f5f5;
+		}
+	}
+
+	.info-input {
+		font-size: 28rpx;
+		color: #1e2a3a;
+		padding: 16rpx 20rpx;
+		border-radius: 24rpx;
+		border: 2rpx solid #e9edf2;
+		background: #fff;
+		height: 76rpx;
+
+		&:disabled {
+			color: #999;
+			background: #f5f5f5;
+		}
+	}
+
+	.popup-footer {
+		display: flex;
+		padding: 20rpx 30rpx;
+		border-top: 1rpx solid #e5e5e5;
+		background: #fff;
+		gap: 20rpx;
+
+		/deep/ .u-button {
+			flex: 1;
+		}
+	}
+</style>

+ 476 - 0
pages/ems/centralizedMeterReading/list.vue

@@ -0,0 +1,476 @@
+<template>
+	<view class="mainBox">
+		<uni-nav-bar fixed="true" statusBar="true" left-icon="back" title="手动集抄" @clickLeft="back">
+			<template slot="right">
+				<u-button type="success" size="small" class="u-reset-button" @click="$refs.treePicker._show()"
+					text="选择分类"></u-button>
+			</template>
+		</uni-nav-bar>
+
+		<view class="top-wrapper">
+			<uni-section>
+
+				<zxz-uni-data-select :localdata="energyConsumingUnitList" v-model="energyClassificationCode"
+					dataValue='value' style="width: 500rpx;" dataKey="label" filterable
+					format='{label}'></zxz-uni-data-select>
+			</uni-section>
+			<button class="search_btn" @click="doSearch">搜索</button>
+		</view>
+		<div style="height: 100rpx; width: 475rpx"></div>
+		<view class="wrapper">
+			<u-list @scrolltolower="scrolltolower" class="listContent">
+				<view v-for="(item, index) in tableList" :key="index" style="position: relative">
+					<myCard :item="item" :index="index + 1" :btnList="btnList" :columns="columns" :title="item.code"
+						:status="statusMap[item.status]" @goDetail="goDetail(item, 'view')"
+						@handlePublish="handlePublish(item, 1)" @handleDelete="handleDelete(item)"
+						@handleSubmit="handleSubmit(item)" @edit="goDetail(item, 'edit')">
+					</myCard>
+				</view>
+				<view style="width: 100%; height: 40rpx"></view>
+				<view style="margin-top: 20vh" v-if="tableList.length == 0">
+					<u-empty iconSize="150" textSize="32" text="暂无数据"> </u-empty>
+				</view>
+			</u-list>
+		</view>
+
+		<u-toast ref="uToast"></u-toast>
+		<ba-tree-picker ref="treePicker" :multiple="false" @select-change="confirm" :localdata="listData" valueKey="id"
+			textKey="name" childrenKey="children" />
+		<workOrderReport ref="workOrderReportRef" :pageName="pageName" @refresh="successInit"></workOrderReport>
+		<view class="add" @click="goDetail({},'add')">
+			<u-icon name="plus" color="#fff"></u-icon>
+		</view>
+	</view>
+</template>
+
+<script>
+	import dictMixns from "@/mixins/dictMixins";
+	import myCard from "./components/myCard.vue";
+	import {
+		getTreeByPid
+	} from "@/api/classifyManage";
+	import workOrderReport from "./components/workOrderReport.vue";
+	import {
+		getList,
+		deleteById,
+		updatePublishStatus
+	} from "@/api/centralizedMeterReading/index";
+	import {
+		parameterGetByCode
+	} from '@/api/mainData/index.js'
+	import {
+		energyConsumingUnitList
+	} from '@/enum/dict.js';
+	import {
+		processInstanceCreateAPI,
+		processInstancePage
+	} from '@/api/wt/index.js'
+	export default {
+		components: {
+			myCard,
+			workOrderReport,
+		},
+		mixins: [dictMixns],
+		props: {
+			pageName: {
+				default: "",
+			},
+		},
+		data() {
+			return {
+				statusMap: {
+					0: "未报工",
+					1: "执行中",
+					2: "已执行",
+				},
+				energyConsumingUnitList,
+				planTypeList: {
+					productionRecords: 2,
+					steamInjectionInspectionRecord: 3,
+					solidWasteRecord: 4,
+					qualityTestRecords: 5,
+					boilerOperationRecord: 6,
+				},
+
+				columns: [
+					[{
+						prop: "substanceName",
+						label: "表计设备名称",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "currentMeterReadingTime",
+						label: "本期抄表时间",
+						className: "perce100",
+					}, ],
+					[{
+							prop: "currentMeterReadingNum",
+							label: "本期抄表数",
+							className: "perce50",
+						},
+						{
+							prop: "unit",
+							label: "单位",
+							className: "perce50",
+						},
+					],
+
+					[{
+						prop: "meterReadingName",
+						label: "抄表人",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "meterEquipmentNumber",
+						label: "表计设备编号",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "meterType",
+						label: "表计类型",
+						className: "perce100",
+
+						formatter: (_row) => {
+							return this.getDictValue("表计类型", _row.meterType);
+						},
+					}, ],
+					[{
+						prop: "correlationSubstanceName",
+						label: "关联设备",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "areaName",
+						label: "区域",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "energyClassificationName",
+						label: "用能分类",
+						className: "perce100",
+					}, ],
+					[{
+						prop: "energyTypeName",
+						label: "能源名称",
+						className: "perce100",
+					}, ],
+					[{
+							prop: "approvalStatus",
+							label: "审核状态",
+							className: "perce50",
+							formatter: (row) => {
+								const reviewStatus = {
+									0: "未提交",
+									1: "审核中",
+									2: "已审核",
+									3: "审核不通过",
+								};
+								return reviewStatus[row.approvalStatus];
+							},
+						},
+						{
+							prop: "publishStatus",
+							label: "发布状态",
+							className: "perce50",
+							formatter: (_row, _column, cellValue) => {
+								return _row.publishStatus == 1 ?
+									"已发布" :
+									_row.publishStatus == 2 ?
+									"已撤销" :
+									"未发布";
+							},
+						},
+					],
+					[{
+						label: "操作:",
+						prop: "action",
+						type: "action",
+						className: "perce100",
+					}, ],
+				].filter((item) => !item.isNone),
+				listData: [],
+				tableList: [],
+				categoryLevelId: '',
+				energyClassificationCode: '',
+				page: 1,
+				size: 10,
+				isEnd: false,
+				keyWord: "",
+				userInfo: {},
+				approval: '1',
+			};
+		},
+		computed: {
+			btnList() {
+				return [{
+						name: "编辑",
+						apiName: "edit",
+						btnType: "primary",
+						judge: [{
+							fn: (item) => {
+								return (
+									[0, 3].includes(item.approvalStatus) &&
+									item.publishStatus != 1
+								);
+							},
+						}, ],
+					},
+					{
+						name: "提交",
+						apiName: "handleSubmit",
+						btnType: "primary",
+						judge: [{
+							fn: (item) => {
+								return (
+									this.approval == 1 && [0, 3].includes(item.approvalStatus) && item
+									.publishStatus != 1
+								);
+							},
+						}, ],
+					},
+					{
+						name: "删除",
+						apiName: "handleDelete",
+						btnType: "danger",
+						judge: [{
+							fn: (item) => {
+								return (
+									item.publishStatus != 1 && [0].includes(item.approvalStatus)
+								);
+							},
+						}, ],
+					},
+					{
+						name: "发布",
+						apiName: "handlePublish",
+						btnType: "primary",
+						judge: [{
+							fn: (item) => {
+								return (
+									item.publishStatus == 0 &&
+									this.approval != 1
+								);
+							},
+						}, ],
+					},
+				];
+			},
+		},
+		created() {
+			//用能集抄人工录入审批:0不审批,1审批
+
+			parameterGetByCode({
+				code: 'ems_manual_entry_for_approval'
+			}).then((res) => {
+				if (res.value) {
+					this.approval = res.value;
+				}
+			});
+			this.requestDict("表计类型");
+			getTreeByPid('29').then((res) => {
+				this.listData = res.data;
+
+			});
+		},
+		onLoad() {},
+
+		methods: {
+
+			confirm(data, name) {
+				if (data[0]) {
+					this.categoryLevelId = data[0].id
+				} else {
+					this.categoryLevelId = ''
+				}
+
+
+				this.doSearch()
+			},
+			goDetail(item, type) {
+				this.$refs.workOrderReportRef.open(item, type);
+			},
+			handleDelete(row) {
+				deleteById([row.id]).then((res) => {
+					uni.showLoading({
+						title: "删除成功",
+					});
+					this.successInit()
+				});
+			},
+
+			async handleSubmit(row) {
+				try {
+					//后台不提供接口
+					let list = await processInstancePage({
+						pageNo: 1,
+						pageSize: 1,
+						reset: true,
+						key: 'ems_remote_meter_reading'
+					})
+					let params = {
+						businessId: row.id,
+						businessKey: 'ems_remote_meter_reading',
+						formCreateUserId: row.createUserId,
+						processDefinitionId: list?.list[0]?.processDefinition.id,
+						variables: {
+							businessCode: row.substanceCode,
+							businessName: row.substanceName,
+							businessType: '人工录入抄表'
+						},
+					}
+
+					await processInstanceCreateAPI(params)
+					uni.showModal({
+						title: `提交成功`,
+						content: '',
+						confirmText: '确认',
+						showCancel: false, // 是否显示取消按钮,默认为 true
+						success: () => {
+							this.successInit()
+						}
+					})
+				} catch {
+
+				}
+			},
+			// 发布/撤销
+			handlePublish(row, status) {
+				updatePublishStatus(row.id, status).then(() => {
+
+					uni.showLoading({
+						title: "发布成功",
+					});
+					this.successInit()
+				});
+			},
+			successInit() {
+				uni.showLoading({
+					title: "加载中",
+				});
+				let data = {
+					pageNum: 1,
+					size: this.tableList.length,
+					keyword: this.keyWord,
+					areaId: "",
+					categoryLevelId: this.categoryLevelId,
+					meterReadingMethod: 1,
+					currentLoginUserId: this.userInfo.userId,
+					energyClassificationCode: this.energyClassificationCode
+				};
+				getList(data)
+					.then((res) => {
+						this.tableList = res.list;
+					})
+					.then(() => {
+						uni.hideLoading();
+					});
+			},
+			doSearch() {
+				this.isEnd = false;
+				this.page = 1;
+				this.getList();
+			},
+			//获取列表信息
+			getList() {
+				this.userInfo = uni.getStorageSync("userInfo");
+				if (this.isEnd || !this.userInfo.userId) {
+					return;
+				}
+				uni.showLoading({
+					title: "加载中",
+				});
+				let data = {
+					pageNum: this.page,
+					size: this.size,
+					keyword: this.keyWord,
+					areaId: "",
+					categoryLevelId: this.categoryLevelId || '',
+					meterReadingMethod: 1,
+					currentLoginUserId: this.userInfo.userId,
+					energyClassificationCode: this.energyClassificationCode
+				};
+
+				getList(data)
+					.then((res) => {
+						if (this.page === 1) {
+							this.tableList = res.list;
+						} else {
+							this.tableList.push(...res.list);
+						}
+						this.page += 1;
+						this.isEnd = this.tableList.length >= res.count;
+					})
+					.then(() => {
+						uni.hideLoading();
+					});
+			},
+			scrolltolower() {
+				if (this.isEnd) {
+					return;
+				}
+				this.getList();
+			},
+		},
+	};
+</script>
+
+<style lang="scss" scoped>
+	.mainBox {
+		background-color: #f3f8fb;
+	}
+
+	.add {
+		width: 96rpx;
+		height: 96rpx;
+		border-radius: 48rpx;
+		background: #3c9cff;
+		position: fixed;
+		bottom: 100rpx;
+		right: 24rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.top-wrapper {
+		background-color: #fff;
+		display: flex;
+		width: 750rpx;
+		height: 88rpx;
+		padding: 16rpx 32rpx;
+		align-items: center;
+		// gap: 16rpx;
+		position: absolute;
+		z-index: 999;
+		// top: 44px;
+		// // #ifdef APP-PLUS
+		// top: 140rpx;
+
+		// // #endif
+		/deep/.uni-section {
+			margin-top: 0px;
+		}
+
+		/deep/.uni-section-header {
+			padding: 0px;
+		}
+
+		.search_btn {
+			width: 120rpx;
+			height: 70rpx;
+			line-height: 70rpx;
+			padding: 0 24rpx;
+			background: $theme-color;
+			font-size: 32rpx;
+			color: #fff;
+			margin: 0;
+			margin-left: 26rpx;
+		}
+
+		.menu_icon {
+			width: 44rpx;
+			height: 44rpx;
+			margin-left: 14rpx;
+		}
+	}
+</style>

+ 42 - 0
pages/ems/centralizedMeterReading/manualcentralizedMeterReading.vue

@@ -0,0 +1,42 @@
+<template>
+  <view>
+    <list ref="listRef" pageName="manualcentralizedMeterReading"></list>
+  </view>
+</template>
+
+<script>
+import list from "./list.vue";
+
+export default {
+  components: {
+    list,
+  },
+  mixins: [],
+  data() {
+    return {};
+  },
+  computed: {},
+  created() {
+    uni.$off("successInit");
+    uni.$on("successInit", (data) => {
+      this.$nextTick(() => {
+        this.$refs.listRef.successInit();
+      });
+    });
+  },
+  onUnload() {
+    uni.$off("successInit");
+  },
+  onLoad() {
+    this.$nextTick(() => {
+      this.$refs.listRef.getList();
+    });
+    uni.setNavigationBarColor({
+      frontColor: "#000000",
+    });
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 462 - 509
pages/index/index.vue

@@ -1,520 +1,473 @@
 <template>
-  <view>
-    <uni-nav-bar
-      fixed="true"
-      statusBar="true"
-      title="工作台"
-      right-icon="scan"
-      @clickRight="HandlScanCode"
-    ></uni-nav-bar>
-    <!-- 顶部横向导航 -->
-    <view class="top-nav-wrapper" v-if="moduleNavList.length > 0">
-      <scroll-view
-        class="top-nav"
-        scroll-x
-        enable-flex
-        :scroll-into-view="scrollIntoViewId"
-        scroll-with-animation
-        @scroll="handleNavScroll"
-      >
-        <view
-          class="nav-tab"
-          v-for="(item, index) in moduleNavList"
-          :key="index"
-          :id="'nav-' + item.id"
-          :class="{ active: currentNavIndex === index }"
-          @click="goToModule(item, index)"
-        >
-          {{ item.name }}
-        </view>
-      </scroll-view>
-    </view>
-    <view class="content-wrapper">
-      <view
-        v-if="internalManagementList.length > 0"
-        :id="'module-xietong'"
-        :data-index="0"
-      >
-        <CellTip title="协同办公"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in internalManagementList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="productionList.length > 0"
-        :id="'module-shengchan'"
-        :data-index="1"
-      >
-        <CellTip title="生产管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="item in productionList"
-              :key="item.link_url"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <label>{{ item.title }}</label>
-              <label>{{ item.num }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="operationsList.length > 0"
-        :id="'module-yunwei'"
-        :data-index="2"
-      >
-        <CellTip title="运维管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in operationsList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="serviceList.length > 0"
-        :id="'module-shouhou'"
-        :data-index="3"
-      >
-        <CellTip title="售后服务管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in serviceList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="warehousingList.length > 0"
-        :id="'module-cangchu'"
-        :data-index="4"
-      >
-        <CellTip title="仓储管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in warehousingList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <label>{{ item.title }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="dispatchList.length > 0"
-        :id="'module-yunshu'"
-        :data-index="5"
-      >
-        <CellTip title="运输管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in dispatchList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="saleManageList.length > 0"
-        :id="'module-yingxiao'"
-        :data-index="6"
-      >
-        <CellTip title="营销管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in saleManageList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="purchaseManageList.length > 0"
-        :id="'module-caigou'"
-        :data-index="7"
-      >
-        <CellTip title="采购管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in purchaseManageList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="pcsList.length > 0"
-        :id="'module-shengchanguanli'"
-        :data-index="8"
-      >
-        <CellTip title="生产管控"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in pcsList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view v-if="qmsList.length > 0" :id="'module-zhiliang'" :data-index="9">
-        <CellTip title="质量管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in qmsList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-      <view
-        v-if="traceabilityList.length > 0"
-        :id="'module-suyuan'"
-        :data-index="10"
-      >
-        <CellTip title="溯源管理"></CellTip>
-        <view class="nav">
-          <view class="nav-content">
-            <view
-              class="nav-item"
-              v-for="(item, index) in traceabilityList"
-              @click="toNav(item.path)"
-            >
-              <span :class="'iconfont ' + item.icon"></span>
-              <i class="badge" v-if="item.badge">{{ item.badge }}</i>
-              <label>{{ item.name }}</label>
-            </view>
-          </view>
-        </view>
-      </view>
-    </view>
-  </view>
+	<view>
+		<uni-nav-bar fixed="true" statusBar="true" title="工作台" right-icon="scan"
+			@clickRight="HandlScanCode"></uni-nav-bar>
+		<!-- 顶部横向导航 -->
+		<view class="top-nav-wrapper" v-if="moduleNavList.length > 0">
+			<scroll-view class="top-nav" scroll-x enable-flex :scroll-into-view="scrollIntoViewId" scroll-with-animation
+				@scroll="handleNavScroll">
+				<view class="nav-tab" v-for="(item, index) in moduleNavList" :key="index" :id="'nav-' + item.id"
+					:class="{ active: currentNavIndex === index }" @click="goToModule(item, index)">
+					{{ item.name }}
+				</view>
+			</scroll-view>
+		</view>
+		<view class="content-wrapper">
+			<view v-if="internalManagementList.length > 0" :id="'module-xietong'" :data-index="0">
+				<CellTip title="协同办公"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in internalManagementList"
+							@click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="productionList.length > 0" :id="'module-shengchan'" :data-index="1">
+				<CellTip title="生产管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="item in productionList" :key="item.link_url"
+							@click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<label>{{ item.title }}</label>
+							<label>{{ item.num }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="operationsList.length > 0" :id="'module-yunwei'" :data-index="2">
+				<CellTip title="运维管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in operationsList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="serviceList.length > 0" :id="'module-shouhou'" :data-index="3">
+				<CellTip title="售后服务管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in serviceList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="warehousingList.length > 0" :id="'module-cangchu'" :data-index="4">
+				<CellTip title="仓储管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in warehousingList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<label>{{ item.title }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="dispatchList.length > 0" :id="'module-yunshu'" :data-index="5">
+				<CellTip title="运输管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in dispatchList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="saleManageList.length > 0" :id="'module-yingxiao'" :data-index="6">
+				<CellTip title="营销管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in saleManageList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="purchaseManageList.length > 0" :id="'module-caigou'" :data-index="7">
+				<CellTip title="采购管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in purchaseManageList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="pcsList.length > 0" :id="'module-shengchanguanli'" :data-index="8">
+				<CellTip title="生产管控"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in pcsList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="qmsList.length > 0" :id="'module-zhiliang'" :data-index="9">
+				<CellTip title="质量管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in qmsList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="energyManagement.length > 0" :id="'module-zhiliang'" :data-index="10">
+				<CellTip title="能源管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in energyManagement" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<view v-if="traceabilityList.length > 0" :id="'module-suyuan'" :data-index="11">
+				<CellTip title="溯源管理"></CellTip>
+				<view class="nav">
+					<view class="nav-content">
+						<view class="nav-item" v-for="(item, index) in traceabilityList" @click="toNav(item.path)">
+							<span :class="'iconfont ' + item.icon"></span>
+							<i class="badge" v-if="item.badge">{{ item.badge }}</i>
+							<label>{{ item.name }}</label>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
 </template>
 
 <script>
-import CellTip from "./CellTip.vue";
-import { statistics, qualityTodoByPda } from "@/api/myTicket";
-export default {
-  components: {
-    CellTip,
-  },
-  data() {
-    return {
-      timer: null,
-      src1: "https://cdn.uviewui.com/uview/album/1.jpg",
-      workOrder: {}, // 工单统计数据
-      qmsList: [],
-      //生产类
-      productionList: [],
-      //仓储管理
-      warehousingList: [],
-      //运维类
-      operationsList: [],
-      dispatchList: [],
-      internalManagementList: [],
-      treeList: [],
-      saleManageList: [],
-      // 售后服务
-      serviceList: [],
-      // 溯源管理
-      traceabilityList: [],
-      // 采购管理
-      purchaseManageList: [],
-      pcsList: [],
-      // 导航相关
-      moduleNavList: [],
-      scrollIntoViewId: "",
-      currentNavIndex: 0,
-      moduleObserver: null,
-      // 顶部固定区域高度(状态栏 + nav-bar + 横向导航)
-      topNavHeight: 0,
-    };
-  },
-  created() {
-    this.getTree();
-  },
-  onShow() {
-    this.getStatistics();
-    this.$nextTick(() => {
-      this.calculateTopNavHeight();
-    });
-  },
-  onReady() {
-    // 页面准备完成后,滚动到第一个模块
-    this.$nextTick(() => {
-      if (this.moduleNavList.length > 0) {
-        this.goToModule(this.moduleNavList[0], 0);
-      }
-    });
-  },
-  onHide() {
-    clearTimeout(this.timer);
-  },
-  onUnload() {
-    clearTimeout(this.timer);
-  },
-  methods: {
-    // 计算顶部固定区域高度(动态获取实际高度)
-    calculateTopNavHeight() {
-      const query = uni.createSelectorQuery().in(this);
-      query
-        .select(".top-nav-wrapper")
-        .boundingClientRect((rect) => {
-          if (rect) {
-            this.topNavHeight = rect.bottom;
-          }
-        })
-        .exec();
-    },
-    getStatistics() {
-      // 获取工单统计数
-      statistics()
-        .then((res) => {
-          this.workOrder = res;
-          this.operationsList.forEach((item) => {
-            if (item.name == "保养工单") item.badge = res.maintenanceNum;
-            if (item.name == "巡点检工单") item.badge = res.patrolInspection;
-            if (item.name == "维修工单") item.badge = res.repairsNum;
-          });
-        })
-        .finally(() => {
-          this.timer = setTimeout(() => {
-            this.getStatistics();
-          }, 12000);
-        });
-    },
-    getTree() {
-      let _list = uni.getStorageSync("treeList");
-      let list = JSON.parse(_list) || [];
-      console.log("-----------list--------------");
-      console.log(list);
-      // 收集所有异步请求的 Promise
-      const asyncPromises = [];
+	import CellTip from "./CellTip.vue";
+	import {
+		statistics,
+		qualityTodoByPda
+	} from "@/api/myTicket";
+	export default {
+		components: {
+			CellTip,
+		},
+		data() {
+			return {
+				timer: null,
+				src1: "https://cdn.uviewui.com/uview/album/1.jpg",
+				workOrder: {}, // 工单统计数据
+				qmsList: [],
+				//生产类
+				productionList: [],
+				//仓储管理
+				warehousingList: [],
+				//运维类
+				operationsList: [],
+				dispatchList: [],
+				internalManagementList: [],
+				treeList: [],
+				saleManageList: [],
+				// 售后服务
+				serviceList: [],
+				// 溯源管理
+				traceabilityList: [],
+				// 采购管理
+				purchaseManageList: [],
+				pcsList: [],
+				// 导航相关
+				moduleNavList: [],
+				energyManagement:[],
+				scrollIntoViewId: "",
+				currentNavIndex: 0,
+				moduleObserver: null,
+				// 顶部固定区域高度(状态栏 + nav-bar + 横向导航)
+				topNavHeight: 0,
+			};
+		},
+		created() {
+			this.getTree();
+		},
+		onShow() {
+			this.getStatistics();
+			this.$nextTick(() => {
+				this.calculateTopNavHeight();
+			});
+		},
+		onReady() {
+			// 页面准备完成后,滚动到第一个模块
+			this.$nextTick(() => {
+				if (this.moduleNavList.length > 0) {
+					this.goToModule(this.moduleNavList[0], 0);
+				}
+			});
+		},
+		onHide() {
+			clearTimeout(this.timer);
+		},
+		onUnload() {
+			clearTimeout(this.timer);
+		},
+		methods: {
+			// 计算顶部固定区域高度(动态获取实际高度)
+			calculateTopNavHeight() {
+				const query = uni.createSelectorQuery().in(this);
+				query
+					.select(".top-nav-wrapper")
+					.boundingClientRect((rect) => {
+						if (rect) {
+							this.topNavHeight = rect.bottom;
+						}
+					})
+					.exec();
+			},
+			getStatistics() {
+				// 获取工单统计数
+				statistics()
+					.then((res) => {
+						this.workOrder = res;
+						this.operationsList.forEach((item) => {
+							if (item.name == "保养工单") item.badge = res.maintenanceNum;
+							if (item.name == "巡点检工单") item.badge = res.patrolInspection;
+							if (item.name == "维修工单") item.badge = res.repairsNum;
+						});
+					})
+					.finally(() => {
+						this.timer = setTimeout(() => {
+							this.getStatistics();
+						}, 12000);
+					});
+			},
+			getTree() {
+				let _list = uni.getStorageSync("treeList");
+				let list = JSON.parse(_list) || [];
+				console.log("-----------list--------------");
+				console.log(list);
+				// 收集所有异步请求的 Promise
+				const asyncPromises = [];
+
+				if (list[0] && list[0].children.length > 0) {
+					// console.log(list[0].children, "list[0].children ----");
+					list[0].children.forEach((f) => {
+						if (f.path == "productionManage") {
+							this.productionList = f.children;
+						} else if (f.path == "operationsList") {
+							const promise = statistics().then((res) => {
+								this.workOrder = res;
+								f.children.forEach((item) => {
+									if (item.name == "保养工单") item.badge = res.maintenanceNum;
+									if (item.name == "巡点检工单")
+										item.badge = res.patrolInspection;
+									if (item.name == "维修工单") item.badge = res.repairsNum;
+								});
+								this.operationsList = f.children;
+							});
+							asyncPromises.push(promise);
+						} else if (f.path == "warehouseManagement") {
+							this.warehousingList = f.children;
+						} else if (f.path == "saleManageList") {
+							this.saleManageList = f.children;
+							this.saleManageList.forEach((item) => {
+								item.name = item.name.replace("员工", "");
+								item.name = item.name.replace("管理员", "");
+							});
+						} else if (f.path == "internalManagement") {
+							// console.log(f, "f");
+							this.internalManagementList = f.children;
+						} else if (f.path == "serviceList") {
+							this.serviceList = f.children;
+						} else if (f.path == "traceability") {
+							this.traceabilityList = f.children;
+						} else if (f.path == "purchasingManage") {
+							this.purchaseManageList = f.children;
+						} else if (f.path == "qualityManage") {
+							const promise = qualityTodoByPda().then((res) => {
+								f.children.forEach((item) => {
+									if (item.name == "质检工单") item.badge = res.workOrderNum;
+									if (item.name == "我的质检工单")
+										item.badge = res.myWorkOrderNum;
+									if (item.name == "我的任务单") item.badge = res.myTaskMonadNum;
+									if (item.name == "我的受托单")
+										item.badge = res.myRequestEntrustNum;
+								});
+								this.qmsList = f.children;
+							});
+							asyncPromises.push(promise);
+						} else if (f.path == "productionControlManagement") {
+							this.pcsList = f.children;
+						} else if (f.path == "dispatchManage") {
+							this.dispatchList = f.children;
+						} else if (f.path == 'energyManagement') {
+							this.energyManagement = f.children
 
-      if (list[0] && list[0].children.length > 0) {
-        // console.log(list[0].children, "list[0].children ----");
-        list[0].children.forEach((f) => {
-          if (f.path == "productionManage") {
-            this.productionList = f.children;
-          } else if (f.path == "operationsList") {
-            const promise = statistics().then((res) => {
-              this.workOrder = res;
-              f.children.forEach((item) => {
-                if (item.name == "保养工单") item.badge = res.maintenanceNum;
-                if (item.name == "巡点检工单")
-                  item.badge = res.patrolInspection;
-                if (item.name == "维修工单") item.badge = res.repairsNum;
-              });
-              this.operationsList = f.children;
-            });
-            asyncPromises.push(promise);
-          } else if (f.path == "warehouseManagement") {
-            this.warehousingList = f.children;
-          } else if (f.path == "saleManageList") {
-            this.saleManageList = f.children;
-            this.saleManageList.forEach((item) => {
-              item.name = item.name.replace("员工", "");
-              item.name = item.name.replace("管理员", "");
-            });
-          } else if (f.path == "internalManagement") {
-            // console.log(f, "f");
-            this.internalManagementList = f.children;
-          } else if (f.path == "serviceList") {
-            this.serviceList = f.children;
-          } else if (f.path == "traceability") {
-            this.traceabilityList = f.children;
-          } else if (f.path == "purchasingManage") {
-            this.purchaseManageList = f.children;
-          } else if (f.path == "qualityManage") {
-            const promise = qualityTodoByPda().then((res) => {
-              f.children.forEach((item) => {
-                if (item.name == "质检工单") item.badge = res.workOrderNum;
-                if (item.name == "我的质检工单")
-                  item.badge = res.myWorkOrderNum;
-                if (item.name == "我的任务单") item.badge = res.myTaskMonadNum;
-                if (item.name == "我的受托单")
-                  item.badge = res.myRequestEntrustNum;
-              });
-              this.qmsList = f.children;
-            });
-            asyncPromises.push(promise);
-          } else if (f.path == "productionControlManagement") {
-            this.pcsList = f.children;
-          } else if (f.path == "dispatchManage") {
-            this.dispatchList = f.children;
-          }
-        });
-      }
+						}
+					});
+				}
 
-      // 等待所有异步数据加载完成后再生成导航
-      if (asyncPromises.length > 0) {
-        Promise.all(asyncPromises).then(() => {
-          this.buildNavList();
-        });
-      } else {
-        this.buildNavList();
-      }
-    },
-    // 构建导航列表
-    buildNavList() {
-      const navMap = {
-        internalManagementList: { name: "协同办公", id: "xietong" },
-        productionList: { name: "生产管理", id: "shengchan" },
-        operationsList: { name: "运维管理", id: "yunwei" },
-        serviceList: { name: "售后服务", id: "shouhou" },
-        warehousingList: { name: "仓储管理", id: "cangchu" },
-        dispatchList: { name: "运输管理", id: "yunshu" },
-        saleManageList: { name: "营销管理", id: "yingxiao" },
-        purchaseManageList: { name: "采购管理", id: "caigou" },
-        pcsList: { name: "生产管控", id: "shengchanguanli" },
-        qmsList: { name: "质量管理", id: "zhiliang" },
-        traceabilityList: { name: "溯源管理", id: "suyuan" },
-      };
+				// 等待所有异步数据加载完成后再生成导航
+				if (asyncPromises.length > 0) {
+					Promise.all(asyncPromises).then(() => {
+						this.buildNavList();
+					});
+				} else {
+					this.buildNavList();
+				}
+			},
+			// 构建导航列表
+			buildNavList() {
+				const navMap = {
+					internalManagementList: {
+						name: "协同办公",
+						id: "xietong"
+					},
+					productionList: {
+						name: "生产管理",
+						id: "shengchan"
+					},
+					operationsList: {
+						name: "运维管理",
+						id: "yunwei"
+					},
+					serviceList: {
+						name: "售后服务",
+						id: "shouhou"
+					},
+					warehousingList: {
+						name: "仓储管理",
+						id: "cangchu"
+					},
+					dispatchList: {
+						name: "运输管理",
+						id: "yunshu"
+					},
+					saleManageList: {
+						name: "营销管理",
+						id: "yingxiao"
+					},
+					purchaseManageList: {
+						name: "采购管理",
+						id: "caigou"
+					},
+					pcsList: {
+						name: "生产管控",
+						id: "shengchanguanli"
+					},
+					qmsList: {
+						name: "质量管理",
+						id: "zhiliang"
+					},
+					traceabilityList: {
+						name: "溯源管理",
+						id: "suyuan"
+					},
+				};
 
-      this.moduleNavList = Object.entries(navMap)
-        .filter(([key]) => this[key]?.length > 0)
-        .map(([, value]) => value);
-    },
-    // 点击导航跳转 - 使用动态获取的顶部高度进行精确定位
-    goToModule(item, index) {
-      this.currentNavIndex = index;
-      this.scrollIntoViewId = "nav-" + item.id;
-      setTimeout(() => {
-        this.scrollIntoViewId = "";
-        const query = uni.createSelectorQuery().in(this);
-        query.select("#module-" + item.id).boundingClientRect();
-        query.select(".top-nav-wrapper").boundingClientRect();
-        query.selectViewport().scrollOffset();
-        query.exec((res) => {
-          if (res[0] && res[1] && res[2]) {
-            const targetTop = res[0].top;
-            const fixedBottom = res[1].bottom;
-            const currentScrollTop = res[2].scrollTop;
-            const scrollTop = currentScrollTop + targetTop - fixedBottom - 10;
-            uni.pageScrollTo({
-              scrollTop: Math.max(0, scrollTop),
-              duration: 300,
-            });
-          }
-        });
-      }, 200);
-    },
-    // 监听导航滚动
-    handleNavScroll(e) {
-      // 可以在这里添加滚动时的逻辑
-    },
-    onUnload() {
-      if (this.moduleObserver) {
-        this.moduleObserver.disconnect();
-      }
-      clearTimeout(this.timer);
-    },
+				this.moduleNavList = Object.entries(navMap)
+					.filter(([key]) => this[key]?.length > 0)
+					.map(([, value]) => value);
+			},
+			// 点击导航跳转 - 使用动态获取的顶部高度进行精确定位
+			goToModule(item, index) {
+				this.currentNavIndex = index;
+				this.scrollIntoViewId = "nav-" + item.id;
+				setTimeout(() => {
+					this.scrollIntoViewId = "";
+					const query = uni.createSelectorQuery().in(this);
+					query.select("#module-" + item.id).boundingClientRect();
+					query.select(".top-nav-wrapper").boundingClientRect();
+					query.selectViewport().scrollOffset();
+					query.exec((res) => {
+						if (res[0] && res[1] && res[2]) {
+							const targetTop = res[0].top;
+							const fixedBottom = res[1].bottom;
+							const currentScrollTop = res[2].scrollTop;
+							const scrollTop = currentScrollTop + targetTop - fixedBottom - 10;
+							uni.pageScrollTo({
+								scrollTop: Math.max(0, scrollTop),
+								duration: 300,
+							});
+						}
+					});
+				}, 200);
+			},
+			// 监听导航滚动
+			handleNavScroll(e) {
+				// 可以在这里添加滚动时的逻辑
+			},
+			onUnload() {
+				if (this.moduleObserver) {
+					this.moduleObserver.disconnect();
+				}
+				clearTimeout(this.timer);
+			},
 
-    toNav(url) {
-      // console.log(url);
-      uni.navigateTo({
-        url: url,
-      });
-    },
-    HandlScanCode() {
-      let _this = this;
-      uni.scanCode({
-        success: function (res) {
-          console.log(res);
-          // 获取扫描结果中的URL
-          const scanResult = res.result;
-          // 检查URL是否包含traceability
-          if (scanResult && scanResult.includes("/traceability")) {
-            // 从扫描结果中提取id参数
-            let id = "";
-            if (scanResult.includes("id=")) {
-              // 分割URL获取id参数值
-              const idPart = scanResult.split("id=")[1];
-              // 如果id后面还有其他参数,只取到第一个&之前的部分
-              if (idPart.includes("&")) {
-                id = idPart.split("&")[0];
-              } else {
-                id = idPart;
-              }
-            }
-            // 跳转到溯源页面并传递id
-            uni.navigateTo({
-              url: `/pages/traceability/scanCode/index?id=${id}`,
-            });
-            return;
-          }
-          _this.scantoRouter(scanResult);
-        },
-      });
-    },
-    scantoRouter(result) {
-      uni.navigateTo({
-        url: `/pages/pda/workOrder/extrusionMolding/index?id=${result}`,
-      });
-    },
-  },
-};
+			toNav(url) {
+				// console.log(url);
+				uni.navigateTo({
+					url: url,
+				});
+			},
+			HandlScanCode() {
+				let _this = this;
+				uni.scanCode({
+					success: function(res) {
+						console.log(res);
+						// 获取扫描结果中的URL
+						const scanResult = res.result;
+						// 检查URL是否包含traceability
+						if (scanResult && scanResult.includes("/traceability")) {
+							// 从扫描结果中提取id参数
+							let id = "";
+							if (scanResult.includes("id=")) {
+								// 分割URL获取id参数值
+								const idPart = scanResult.split("id=")[1];
+								// 如果id后面还有其他参数,只取到第一个&之前的部分
+								if (idPart.includes("&")) {
+									id = idPart.split("&")[0];
+								} else {
+									id = idPart;
+								}
+							}
+							// 跳转到溯源页面并传递id
+							uni.navigateTo({
+								url: `/pages/traceability/scanCode/index?id=${id}`,
+							});
+							return;
+						}
+						_this.scantoRouter(scanResult);
+					},
+				});
+			},
+			scantoRouter(result) {
+				uni.navigateTo({
+					url: `/pages/pda/workOrder/extrusionMolding/index?id=${result}`,
+				});
+			},
+		},
+	};
 </script>
 
 <style lang="scss" scoped>
-@import "index.scss";
-</style>
+	@import "index.scss";
+</style>

+ 25 - 0
pages/pcs/components/workOrderReport.vue

@@ -623,6 +623,31 @@
 					});
 					return;
 				}
+				if (this.pageName == 'steamInjectionInspectionRecord'&&!this.form.contactName) {
+					this.$refs.uToast.show({
+						type: "error",
+						icon: false,
+						message: `请选择甲方检查人!`,
+					});
+					return;
+				}
+				if (this.pageName == 'solidWasteRecord'&&!this.form.supplierName) {
+					this.$refs.uToast.show({
+						type: "error",
+						icon: false,
+						message: `请选择处理方!`,
+					});
+					return;
+				}
+				if (this.pageName == 'qualityTestRecords'&&!this.form.contactName) {
+					this.$refs.uToast.show({
+						type: "error",
+						icon: false,
+						message: `请选择联合站检查人!`,
+					});
+					return;
+				}
+				
 				// 验证报工时间必须大于等于检查时间
 				const startTime = new Date(this.form.checkStartTime).getTime();
 				const finishTime = new Date(this.form.checkFinishTime).getTime();