已添加9个文件
已修改27个文件
已删除7个文件
10669 ■■■■■ 文件已修改
multiple/assets/favicon/DHDCico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/DHDCLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/config.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/calibration.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/maintenanceTaskFile.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/repair.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/upkeep.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockIn.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Dialog/FileListDialog.vue 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Dialog/FormDialog.vue 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/enterpriseInfo/index.vue 943 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/amountSummary/index.vue 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/calibration/index.vue 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/defectManagement/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/components/formDia.vue 397 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue 78 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/index.vue 517 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/Form.vue 118 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/Modal.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/index.vue 176 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/components/formDia.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Form/MaintainForm.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Form/RepairForm.vue 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Modal/ApproveModal.vue 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Modal/MaintainModal.vue 127 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Modal/RepairModal.vue 239 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/index.vue 298 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/spareParts/index.vue 149 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/ApproveModal.vue 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/MaintenanceForm.vue 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/PlanForm.vue 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/PlanModal.vue 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/formDia.vue 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Modal/PlanModal.vue 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Modal/formDia.vue 304 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/index.vue 208 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/index.vue 378 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/index.vue 847 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 3583 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/DHDCico.ico

multiple/assets/logo/DHDCLogo.png

multiple/config.json
@@ -129,8 +129,8 @@
  },
  "DHDC": {
    "env": {
      "VITE_APP_TITLE": "敦煌鼎诚信息管理系统",
      "VITE_BASE_API": "http://114.132.189.42:9032",
      "VITE_APP_TITLE": "敦煌市鼎城瑞实业信息管理系统",
      "VITE_BASE_API": "http://114.132.189.42:9018",
      "VITE_JAVA_API": "http://114.132.189.42:9033"
    },
    "screen": "screen/DHDCView.png",
src/api/equipmentManagement/calibration.js
@@ -24,12 +24,4 @@
    method: "post",
    data: query,
  });
}
// åˆ é™¤è®°å½•
export function ledgerRecordDelete(ids) {
  return request({
    url: "/measuringInstrumentLedgerRecord/delete",
    method: "delete",
    data: ids,
  });
}
src/api/equipmentManagement/maintenanceTaskFile.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
import request from "@/utils/request";
// æŸ¥è¯¢ä¿å…»ä»»åŠ¡é™„ä»¶åˆ—è¡¨
export function listMaintenanceTaskFiles(query) {
  return request({
    url: "/maintenanceTaskFile/listPage",
    method: "get",
    params: query,
  });
}
// æ–°å¢žä¿å…»ä»»åС附件
export function addMaintenanceTaskFile(data) {
  return request({
    url: "/maintenanceTaskFile/add",
    method: "post",
    data,
  });
}
// åˆ é™¤ä¿å…»ä»»åС附件
export function delMaintenanceTaskFile(id) {
  return request({
    url: "/maintenanceTaskFile/del",
    method: "delete",
    data: Array.isArray(id) ? id : [id],
  });
}
src/api/equipmentManagement/repair.js
@@ -70,3 +70,19 @@
    data,
  });
};
// æŸ¥è¯¢è®¾å¤‡çš„æŠ¥ä¿®é‡‘额-每月
export const monthlyAmount = (query) => {
  return request({
    url: `/device/repair/monthlyAmount`,
    method: "get",
    params: query,
  });
};
// æŸ¥è¯¢è®¾å¤‡çš„æŠ¥ä¿®é‡‘额-每年
export const yearlyAmount = (query) => {
  return request({
    url: `/device/repair/yearlyAmount`,
    method: "get",
    params: query,
  });
};
src/api/equipmentManagement/upkeep.js
@@ -102,3 +102,20 @@
    data: params,
  });
};
// æŸ¥è¯¢è®¾å¤‡çš„æŠ¥ä¿®é‡‘额-每月
export const monthlyAmount = (query) => {
  return request({
    url: `/device/maintenance/monthlyAmount`,
    method: "get",
    params: query,
  });
};
// æŸ¥è¯¢è®¾å¤‡çš„æŠ¥ä¿®é‡‘额-每年
export const yearlyAmount = (query) => {
  return request({
    url: `/device/maintenance/yearlyAmount`,
    method: "get",
    params: query,
  });
};
src/api/inventoryManagement/stockIn.js
@@ -72,3 +72,12 @@
    })
}
// æ–°å¢žå•†å“å…¥åº“信息
export function addProduct(data) {
    return request({
        url: '/stockin/addProduct',
        method: 'post',
        data: data
    })
}
src/components/Dialog/FileListDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,328 @@
<template>
  <el-dialog v-model="dialogVisible"
             :title="title"
             :width="width"
             :before-close="handleClose">
    <div class="file-list-toolbar"
         v-if="showToolbar">
      <template v-if="useBuiltInUpload">
        <el-upload v-model:file-list="uploadFileList"
                   class="upload-demo"
                   :action="uploadAction"
                   :headers="uploadHeaders"
                   :show-file-list="false"
                   :on-success="handleDefaultUploadSuccess"
                   :on-error="handleDefaultUploadError">
          <el-button v-if="showUploadButton"
                     type="primary"
                     size="small">
            ä¸Šä¼ é™„ä»¶
          </el-button>
        </el-upload>
      </template>
      <template v-else>
        <el-button v-if="showUploadButton"
                   type="primary"
                   size="small"
                   @click="handleUpload">
          æ–°å¢žé™„ä»¶
        </el-button>
      </template>
    </div>
    <el-table :data="tableData"
              border
              :height="tableHeight">
      <el-table-column :label="nameColumnLabel"
                       :prop="nameColumnProp"
                       :min-width="nameColumnMinWidth"
                       show-overflow-tooltip />
      <el-table-column v-if="showActions"
                       fixed="right"
                       label="操作"
                       :width="actionColumnWidth"
                       align="center">
        <template #default="scope">
          <el-button v-if="showDownload"
                     link
                     type="primary"
                     size="small"
                     @click="handleDownload(scope.row)">
            ä¸‹è½½
          </el-button>
          <el-button v-if="showPreview"
                     link
                     type="primary"
                     size="small"
                     @click="handlePreview(scope.row)">
            é¢„览
          </el-button>
          <el-button v-if="showDeleteButton"
                     link
                     type="danger"
                     size="small"
                     @click="handleDelete(scope.row, scope.$index)">
            åˆ é™¤
          </el-button>
          <slot name="actions"
                :row="scope.row"></slot>
        </template>
      </el-table-column>
      <slot name="columns"></slot>
    </el-table>
    <pagination v-if="isShowPagination"
                style="margin-bottom: 20px;"
                :total="page.total"
                :page="page.current"
                :limit="page.size"
                @pagination="paginationSearch"
                @change="handleChange" />
  </el-dialog>
  <filePreview v-if="showPreview"
               ref="filePreviewRef" />
</template>
<script setup>
  import { ref, computed, getCurrentInstance } from "vue";
  import pagination from "@/components/Pagination/index.vue";
  import { ElMessage } from "element-plus";
  import filePreview from "@/components/filePreview/index.vue";
  import { getToken } from "@/utils/auth";
  const props = defineProps({
    modelValue: {
      type: Boolean,
      default: false,
    },
    title: {
      type: String,
      default: "附件",
    },
    width: {
      type: String,
      default: "40%",
    },
    tableHeight: {
      type: String,
      default: "40vh",
    },
    nameColumnLabel: {
      type: String,
      default: "附件名称",
    },
    nameColumnProp: {
      type: String,
      default: "name",
    },
    nameColumnMinWidth: {
      type: [String, Number],
      default: 400,
    },
    actionColumnWidth: {
      type: [String, Number],
      default: 160,
    },
    showActions: {
      type: Boolean,
      default: true,
    },
    showDownload: {
      type: Boolean,
      default: true,
    },
    showPreview: {
      type: Boolean,
      default: true,
    },
    showUploadButton: {
      type: Boolean,
      default: false,
    },
    showDeleteButton: {
      type: Boolean,
      default: false,
    },
    urlField: {
      type: String,
      default: "url",
    },
    downloadMethod: {
      type: Function,
      default: null,
    },
    previewMethod: {
      type: Function,
      default: null,
    },
    uploadMethod: {
      type: Function,
      default: null,
    },
    deleteMethod: {
      type: Function,
      default: null,
    },
    rulesRegulationsManagementId: {
      type: [String, Number],
      default: "",
    },
    uploadUrl: {
      type: String,
      default: `${import.meta.env.VITE_APP_BASE_API}/file/upload`,
    },
    isShowPagination: {
      type: Boolean,
      default: false,
    },
    page: {
      type: Object,
      default: () => ({
        current: 1,
        size: 10,
        total: 0,
      }),
    },
  });
  const emit = defineEmits([
    "update:modelValue",
    "close",
    "download",
    "preview",
    "upload",
    "delete",
  ]);
  const { proxy } = getCurrentInstance();
  const filePreviewRef = ref(null);
  const uploadFileList = ref([]);
  const dialogVisible = computed({
    get: () => props.modelValue,
    set: val => emit("update:modelValue", val),
  });
  const tableData = ref([]);
  const showToolbar = computed(() => props.showUploadButton);
  const useBuiltInUpload = computed(() => !props.uploadMethod);
  const uploadAction = computed(() => props.uploadUrl);
  const uploadHeaders = computed(() => ({
    Authorization: `Bearer ${getToken()}`,
  }));
  const handleClose = () => {
    emit("close");
    dialogVisible.value = false;
  };
  const handleDownload = row => {
    if (props.downloadMethod) {
      props.downloadMethod(row);
    } else {
      // é»˜è®¤ä¸‹è½½æ–¹æ³•
      proxy.$download.name(row[props.urlField]);
    }
    emit("download", row);
  };
  const handlePreview = row => {
    if (props.previewMethod) {
      props.previewMethod(row);
    } else {
      // é»˜è®¤é¢„览方法
      if (filePreviewRef.value) {
        filePreviewRef.value.open(row[props.urlField]);
      }
    }
    emit("preview", row);
  };
  const paginationSearch = page => {
    props.page.current = page.page;
    props.page.size = page.limit;
    emit("pagination", page.page, page.limit);
  };
  const open = list => {
    dialogVisible.value = true;
    tableData.value = list || [];
  };
  const handleUpload = async () => {
    if (props.uploadMethod) {
      // å¦‚果提供了自定义上传方法,由父组件负责更新列表(通过 setList)
      // è¿™é‡Œä¸å†è‡ªåŠ¨æ·»åŠ ï¼Œé¿å…ä¸Žçˆ¶ç»„ä»¶çš„ setList é‡å¤
      await props.uploadMethod();
    }
    emit("upload");
  };
  const handleDelete = async (row, index) => {
    if (props.deleteMethod) {
      const result = await props.deleteMethod(row, index);
      if (result === false) {
        return;
      }
      // å¦‚果提供了 deleteMethod,由父组件负责刷新列表,不在这里删除
    } else {
      // å¦‚果没有提供 deleteMethod,才在组件内部删除
      removeAttachment(index);
    }
    emit("delete", row);
  };
  const addAttachment = item => {
    tableData.value = [...tableData.value, item];
  };
  const handleDefaultUploadSuccess = async (res, file) => {
    if (res?.code !== 200) {
      ElMessage.error(res?.msg || "文件上传失败");
      return;
    }
    if (!props.rulesRegulationsManagementId) {
      ElMessage.error("缺少规章制度ID,无法保存附件");
      return;
    }
    const fileName = res?.data?.originalName || file?.name;
    const fileUrl = res?.data?.tempPath || res?.data?.url;
    const payload = {
      fileName,
      fileUrl,
      rulesRegulationsManagementId: props.rulesRegulationsManagementId,
      raw: res?.data || {},
    };
    emit("upload", payload);
  };
  const handleDefaultUploadError = () => {
    ElMessage.error("文件上传失败");
  };
  const removeAttachment = index => {
    if (index > -1 && index < tableData.value.length) {
      const newList = [...tableData.value];
      newList.splice(index, 1);
      tableData.value = newList;
    }
  };
  const setList = list => {
    tableData.value = list || [];
  };
  defineExpose({
    open,
    addAttachment,
    removeAttachment,
    setList,
    handleUpload,
    handleDelete,
  });
</script>
<style scoped>
  .file-list-toolbar {
    margin-bottom: 8px;
    text-align: right;
  }
</style>
src/components/Dialog/FormDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
<template>
  <el-dialog
    v-model="dialogVisible"
    :title="computedTitle"
    :width="width"
    @close="handleClose"
  >
    <slot></slot>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确认</el-button>
        <el-button @click="handleCancel">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false
  },
  title: {
    type: [String, Function],
    default: ''
  },
  operationType: {
    type: String,
    default: ''
  },
  width: {
    type: String,
    default: '70%'
  }
})
const emit = defineEmits(['update:modelValue', 'close', 'confirm', 'cancel'])
const dialogVisible = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
})
const computedTitle = computed(() => {
  if (typeof props.title === 'function') {
    return props.title(props.operationType)
  }
  return props.title
})
const handleClose = () => {
  emit('close')
}
const handleConfirm = () => {
  emit('confirm')
}
const handleCancel = () => {
  emit('cancel')
  dialogVisible.value = false
}
</script>
<style scoped>
.dialog-footer {
  text-align: center;
}
</style>
src/views/basicData/enterpriseInfo/index.vue
@@ -4,45 +4,74 @@
    <div class="page-header">
      <h2>企业门户</h2>
      <div class="header-actions">
        <el-button @click="handlePreview" :icon="View" type="info" plain>预览</el-button>
        <el-button @click="toggleEdit" :icon="isEdit ? 'Close' : 'Edit'" :type="isEdit ? 'default' : 'primary'">
        <el-button @click="handlePreview"
                   :icon="View"
                   type="info"
                   plain>预览</el-button>
        <el-button @click="toggleEdit"
                   :icon="isEdit ? 'Close' : 'Edit'"
                   :type="isEdit ? 'default' : 'primary'">
          {{ isEdit ? '取消编辑' : '编辑' }}
        </el-button>
      </div>
    </div>
    <!-- ä¼ä¸šä¿¡æ¯å¡ç‰‡ -->
    <div class="enterprise-info-card" v-loading="loading">
    <div class="enterprise-info-card"
         v-loading="loading">
      <!-- åŸºæœ¬ä¿¡æ¯åŒºåŸŸ -->
      <div class="info-section">
        <div class="section-header">
          <h3>基本信息</h3>
        </div>
        <el-descriptions :column="2" border class="info-descriptions">
        <el-descriptions :column="2"
                         border
                         class="info-descriptions">
          <el-descriptions-item label="公司名称">
            <el-input v-if="isEdit" v-model="form.companyName" placeholder="请输入公司名称" clearable />
            <span v-else class="content-text">{{ form.companyName || '-' }}</span>
            <el-input v-if="isEdit"
                      v-model="form.companyName"
                      placeholder="请输入公司名称"
                      clearable />
            <span v-else
                  class="content-text">{{ form.companyName || '-' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="联系人">
            <el-input v-if="isEdit" v-model="form.contactPerson" placeholder="请输入联系人" clearable />
            <span v-else class="content-text">{{ form.contactPerson || '-' }}</span>
            <el-input v-if="isEdit"
                      v-model="form.contactPerson"
                      placeholder="请输入联系人"
                      clearable />
            <span v-else
                  class="content-text">{{ form.contactPerson || '-' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="联系电话">
            <el-input v-if="isEdit" v-model="form.contactPhone" placeholder="请输入联系电话" clearable />
            <span v-else class="content-text">{{ form.contactPhone || '-' }}</span>
            <el-input v-if="isEdit"
                      v-model="form.contactPhone"
                      placeholder="请输入联系电话"
                      clearable />
            <span v-else
                  class="content-text">{{ form.contactPhone || '-' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="公司地址">
            <el-input v-if="isEdit" v-model="form.companyAddress" placeholder="请输入公司地址" clearable />
            <span v-else class="content-text">{{ form.companyAddress || '-' }}</span>
            <el-input v-if="isEdit"
                      v-model="form.companyAddress"
                      placeholder="请输入公司地址"
                      clearable />
            <span v-else
                  class="content-text">{{ form.companyAddress || '-' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="公司网站">
            <el-input v-if="isEdit" v-model="form.website" placeholder="请输入公司网站" clearable />
            <a v-else-if="form.website" :href="form.website" target="_blank" class="link-text">{{ form.website }}</a>
            <span v-else class="content-text">-</span>
            <el-input v-if="isEdit"
                      v-model="form.website"
                      placeholder="请输入公司网站"
                      clearable />
            <a v-else-if="form.website"
               :href="form.website"
               target="_blank"
               class="link-text">{{ form.website }}</a>
            <span v-else
                  class="content-text">-</span>
          </el-descriptions-item>
        </el-descriptions>
      </div>
      <!-- Logo和二维码区域 -->
      <div class="info-section">
        <div class="section-header">
@@ -53,135 +82,148 @@
          <div class="upload-item">
            <span class="upload-label">公司Logo</span>
            <div class="upload-wrapper">
              <el-upload
                v-if="isEdit"
                class="logo-uploader"
                :show-file-list="false"
                :before-upload="(file) => beforeLogoUpload(file, 'companyLogo')"
                action="#">
                <img v-if="form.companyLogo" :src="form.companyLogo" class="uploaded-image" alt="Image Preview" />
                <div v-else class="upload-placeholder">
                  <el-icon class="upload-icon"><Plus /></el-icon>
              <el-upload v-if="isEdit"
                         class="logo-uploader"
                         :show-file-list="false"
                         :before-upload="(file) => beforeLogoUpload(file, 'companyLogo')"
                         action="#">
                <img v-if="form.companyLogo"
                     :src="base + form.companyLogo"
                     class="uploaded-image"
                     alt="Image Preview" />
                <div v-else
                     class="upload-placeholder">
                  <el-icon class="upload-icon">
                    <Plus />
                  </el-icon>
                  <span class="upload-text">上传Logo</span>
                </div>
              </el-upload>
              <img
                v-else-if="form.companyLogo"
                :src="form.companyLogo"
                class="display-image"
                alt="Image Preview"
              />
              <div v-else class="empty-placeholder">
                <el-icon :size="40"><Picture /></el-icon>
              <img v-else-if="form.companyLogo"
                   :src="base + form.companyLogo"
                   class="display-image"
                   alt="Image Preview" />
              <div v-else
                   class="empty-placeholder">
                <el-icon :size="40">
                  <Picture />
                </el-icon>
                <span>暂无Logo</span>
              </div>
            </div>
          </div>
          <!-- äºŒç»´ç  -->
          <div class="upload-item">
            <span class="upload-label">二维码</span>
            <div class="upload-wrapper">
              <el-upload
                v-if="isEdit"
                class="qr-uploader"
                :show-file-list="false"
                :before-upload="(file) => beforeLogoUpload(file, 'qrCode')"
                action="#">
                <img v-if="form.qrCode" :src="form.qrCode" class="uploaded-image" alt="Image Preview" />
                <div v-else class="upload-placeholder">
                  <el-icon class="upload-icon"><Plus /></el-icon>
              <el-upload v-if="isEdit"
                         class="qr-uploader"
                         :show-file-list="false"
                         :before-upload="(file) => beforeLogoUpload(file, 'qrCode')"
                         action="#">
                <img v-if="form.qrCode"
                     :src="base + form.qrCode"
                     class="uploaded-image"
                     alt="Image Preview" />
                <div v-else
                     class="upload-placeholder">
                  <el-icon class="upload-icon">
                    <Plus />
                  </el-icon>
                  <span class="upload-text">上传二维码</span>
                </div>
              </el-upload>
              <img
                v-else-if="form.qrCode"
                :src="form.qrCode"
                class="display-image"
                alt="Image Preview"
              />
              <div v-else class="empty-placeholder">
                <el-icon :size="40"><Picture /></el-icon>
              <img v-else-if="form.qrCode"
                   :src="base + form.qrCode"
                   class="display-image"
                   alt="Image Preview" />
              <div v-else
                   class="empty-placeholder">
                <el-icon :size="40">
                  <Picture />
                </el-icon>
                <span>暂无二维码</span>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- å…¬å¸ç®€ä»‹ -->
      <div class="info-section">
        <div class="section-header">
          <h3>公司简介</h3>
        </div>
        <div class="content-editor">
          <el-input
            v-if="isEdit"
            v-model="form.companyIntro"
            type="textarea"
            :rows="6"
            maxlength="2000"
            show-word-limit
            placeholder="请输入公司简介..."
          />
          <div v-else class="content-display" v-html="form.companyIntro || '<span class=\'empty-text\'>暂无公司简介</span>'"></div>
          <el-input v-if="isEdit"
                    v-model="form.companyIntro"
                    type="textarea"
                    :rows="6"
                    maxlength="2000"
                    show-word-limit
                    placeholder="请输入公司简介..." />
          <div v-else
               class="content-display"
               v-html="form.companyIntro || '<span class=\'empty-text\'>暂无公司简介</span>'"></div>
        </div>
      </div>
      <!-- äº§å“ä»‹ç» -->
      <div class="info-section">
        <div class="section-header">
          <h3>产品介绍</h3>
        </div>
        <div class="content-editor">
          <el-input
            v-if="isEdit"
            v-model="form.productIntro"
            type="textarea"
            :rows="6"
            maxlength="2000"
            show-word-limit
            placeholder="请输入产品介绍..."
          />
          <div v-else class="content-display" v-html="form.productIntro || '<span class=\'empty-text\'>暂无产品介绍</span>'"></div>
          <el-input v-if="isEdit"
                    v-model="form.productIntro"
                    type="textarea"
                    :rows="6"
                    maxlength="2000"
                    show-word-limit
                    placeholder="请输入产品介绍..." />
          <div v-else
               class="content-display"
               v-html="form.productIntro || '<span class=\'empty-text\'>暂无产品介绍</span>'"></div>
        </div>
      </div>
      <!-- è®¾å¤‡ä»‹ç» -->
      <div class="info-section">
        <div class="section-header">
          <h3>设备介绍</h3>
        </div>
        <div class="content-editor">
          <el-input
            v-if="isEdit"
            v-model="form.equipmentIntro"
            type="textarea"
            :rows="6"
            maxlength="2000"
            show-word-limit
            placeholder="请输入设备介绍..."
          />
          <div v-else class="content-display" v-html="form.equipmentIntro || '<span class=\'empty-text\'>暂无设备介绍</span>'"></div>
          <el-input v-if="isEdit"
                    v-model="form.equipmentIntro"
                    type="textarea"
                    :rows="6"
                    maxlength="2000"
                    show-word-limit
                    placeholder="请输入设备介绍..." />
          <div v-else
               class="content-display"
               v-html="form.equipmentIntro || '<span class=\'empty-text\'>暂无设备介绍</span>'"></div>
        </div>
      </div>
      <!-- æ“ä½œæŒ‰é’® -->
      <div v-if="isEdit" class="form-actions">
        <el-button type="primary" @click="handleSave" :loading="saving" size="large">保存</el-button>
        <el-button @click="handleCancel" size="large">取消</el-button>
      <div v-if="isEdit"
           class="form-actions">
        <el-button type="primary"
                   @click="handleSave"
                   :loading="saving"
                   size="large">保存</el-button>
        <el-button @click="handleCancel"
                   size="large">取消</el-button>
      </div>
    </div>
    <!-- é¢„览弹窗 -->
    <el-dialog
      v-model="previewVisible"
      title="企业信息预览"
      width="70%"
      :destroy-on-close="true">
    <el-dialog v-model="previewVisible"
               title="企业信息预览"
               width="70%"
               :destroy-on-close="true">
      <div class="preview-content">
        <div class="preview-header">
          <img v-if="form.companyLogo" :src="form.companyLogo" class="preview-logo" alt="Image Preview" />
          <img v-if="form.companyLogo"
               :src="form.companyLogo"
               class="preview-logo"
               alt="Image Preview" />
          <div class="preview-title">
            <h1>{{ form.companyName || '公司名称' }}</h1>
            <p v-if="form.website">{{ form.website }}</p>
@@ -206,9 +248,12 @@
          <h4>设备介绍</h4>
          <div v-html="form.equipmentIntro || '暂无介绍'"></div>
        </div>
        <div v-if="form.qrCode" class="preview-section preview-qr">
        <div v-if="form.qrCode"
             class="preview-section preview-qr">
          <h4>扫码关注</h4>
          <img :src="form.qrCode" class="qr-image" alt="Image Preview" />
          <img :src="form.qrCode"
               class="qr-image"
               alt="Image Preview" />
        </div>
      </div>
    </el-dialog>
@@ -216,449 +261,463 @@
</template>
<script setup>
  import { ref, reactive, onMounted } from 'vue'
  import { ElMessage } from 'element-plus'
  import { Plus, Picture, View } from '@element-plus/icons-vue'
  import { getEnterpriseInfo, saveEnterpriseInfo, uploadLogo, uploadQrCode } from '@/api/basicData/enterpriseInfo.js'
  import { ref, reactive, onMounted } from "vue";
  import { ElMessage } from "element-plus";
  import { Plus, Picture, View } from "@element-plus/icons-vue";
  import {
    getEnterpriseInfo,
    saveEnterpriseInfo,
    uploadLogo,
    uploadQrCode,
  } from "@/api/basicData/enterpriseInfo.js";
  const { proxy } = getCurrentInstance()
  const { proxy } = getCurrentInstance();
  const loading = ref(false)
  const saving = ref(false)
  const isEdit = ref(false)
  const previewVisible = ref(false)
  const uploadType = ref('')
  const loading = ref(false);
  const saving = ref(false);
  const isEdit = ref(false);
  const previewVisible = ref(false);
  const uploadType = ref("");
  const form = reactive({
    id: null,
    companyName: '',
    companyLogo: '',
    companyIntro: '',
    productIntro: '',
    equipmentIntro: '',
    contactPerson: '',
    contactPhone: '',
    companyAddress: '',
    website: '',
    qrCode: ''
  })
    companyName: "",
    companyLogo: "",
    companyIntro: "",
    productIntro: "",
    equipmentIntro: "",
    contactPerson: "",
    contactPhone: "",
    companyAddress: "",
    website: "",
    qrCode: "",
  });
  // æ·±æ‹·è´åŽŸå§‹æ•°æ®ï¼Œç”¨äºŽå–æ¶ˆæ—¶æ¢å¤
  let originalForm = {}
  let originalForm = {};
  const base = window.location.protocol + "//" + window.location.host;
  // èŽ·å–ä¼ä¸šä¿¡æ¯
  const fetchInfo = () => {
    loading.value = true
    getEnterpriseInfo().then(res => {
      if (res.code === 200 && res.data) {
        // å°†å›¾ç‰‡è·¯å¾„拼接为完整地址
        const base = window.location.protocol + '//' + window.location.host
        if (res.data.companyLogo) {
          res.data.companyLogo = base + res.data.companyLogo
    loading.value = true;
    getEnterpriseInfo()
      .then(res => {
        if (res.code === 200 && res.data) {
          // å°†å›¾ç‰‡è·¯å¾„拼接为完整地址
          if (res.data.companyLogo) {
            res.data.companyLogo = res.data.companyLogo;
          }
          if (res.data.qrCode) {
            res.data.qrCode = res.data.qrCode;
          }
          Object.assign(form, res.data);
          originalForm = JSON.parse(JSON.stringify(res.data));
        }
        if (res.data.qrCode) {
          res.data.qrCode = base + res.data.qrCode
        }
        Object.assign(form, res.data)
        originalForm = JSON.parse(JSON.stringify(res.data))
      }
    }).finally(() => {
      loading.value = false
    })
  }
      })
      .finally(() => {
        loading.value = false;
      });
  };
  // åˆ‡æ¢ç¼–辑模式
  const toggleEdit = () => {
    if (isEdit.value) {
      // å–消编辑,恢复原始数据
      Object.assign(form, originalForm)
      isEdit.value = false
      Object.assign(form, originalForm);
      isEdit.value = false;
    } else {
      // è¿›å…¥ç¼–辑模式
      originalForm = JSON.parse(JSON.stringify(form))
      isEdit.value = true
      originalForm = JSON.parse(JSON.stringify(form));
      isEdit.value = true;
    }
  }
  };
  // é¢„览
  const handlePreview = () => {
    previewVisible.value = true
  }
    previewVisible.value = true;
  };
  // æ–‡ä»¶ä¸Šä¼ å‰æ ¡éªŒ
  const beforeLogoUpload = (file, type) => {
    const isImage = file.type.startsWith('image/')
    const isLt2M = file.size / 1024 / 1024 < 2
    const isImage = file.type.startsWith("image/");
    const isLt2M = file.size / 1024 / 1024 < 2;
    if (!isImage) {
      ElMessage.error('只能上传图片文件!')
      return false
      ElMessage.error("只能上传图片文件!");
      return false;
    }
    if (!isLt2M) {
      ElMessage.error('图片大小不能超过 2MB!')
      return false
      ElMessage.error("图片大小不能超过 2MB!");
      return false;
    }
    uploadType.value = type
    uploadFileReq(file)
    return false
  }
    uploadType.value = type;
    uploadFileReq(file);
    return false;
  };
  // ä¸Šä¼ æ–‡ä»¶è¯·æ±‚
  const uploadFileReq = (file) => {
    const uploadFn = uploadType.value === 'companyLogo' ? uploadLogo : uploadQrCode
    uploadFn(file).then(res => {
      if (res.code === 200) {
        const path = res.data.tempPath
        if (uploadType.value === 'companyLogo') {
          form.companyLogo = path
  const uploadFileReq = file => {
    const uploadFn =
      uploadType.value === "companyLogo" ? uploadLogo : uploadQrCode;
    uploadFn(file)
      .then(res => {
        if (res.code === 200) {
          const path = res.data.tempPath;
          if (uploadType.value === "companyLogo") {
            form.companyLogo = path;
          } else {
            form.qrCode = path;
          }
          ElMessage.success("上传成功");
        } else {
          form.qrCode = path
          ElMessage.error(res.msg || "上传失败");
        }
        ElMessage.success('上传成功')
      } else {
        ElMessage.error(res.msg || '上传失败')
      }
    }).catch(() => {
      ElMessage.error('上传失败')
    })
  }
      })
      .catch(() => {
        ElMessage.error("上传失败");
      });
  };
  // ä¿å­˜
  const handleSave = () => {
    saving.value = true
    saveEnterpriseInfo(form).then(res => {
      if (res.code === 200) {
        ElMessage.success('保存成功')
        isEdit.value = false
        fetchInfo()
      } else {
        ElMessage.error(res.msg || '保存失败')
      }
    }).finally(() => {
      saving.value = false
    })
  }
    saving.value = true;
    console.log(form, "form");
    saveEnterpriseInfo(form)
      .then(res => {
        if (res.code === 200) {
          ElMessage.success("保存成功");
          isEdit.value = false;
          fetchInfo();
        } else {
          ElMessage.error(res.msg || "保存失败");
        }
      })
      .finally(() => {
        saving.value = false;
      });
  };
  // å–消
  const handleCancel = () => {
    Object.assign(form, originalForm)
    isEdit.value = false
  }
    Object.assign(form, originalForm);
    isEdit.value = false;
  };
  onMounted(() => {
    fetchInfo()
  })
    fetchInfo();
  });
</script>
<style scoped lang="scss">
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 24px;
  background: #fff;
  border-radius: 8px;
  margin-bottom: 16px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  h2 {
    margin: 0;
    font-size: 20px;
    font-weight: 600;
    color: #303133;
  }
  .header-actions {
  .page-header {
    display: flex;
    gap: 10px;
  }
}
.enterprise-info-card {
  background: #fff;
  padding: 24px;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.info-section {
  margin-bottom: 32px;
  &:last-of-type {
    margin-bottom: 0;
  }
  .section-header {
    justify-content: space-between;
    align-items: center;
    padding: 16px 24px;
    background: #fff;
    border-radius: 8px;
    margin-bottom: 16px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
    h3 {
    h2 {
      margin: 0;
      font-size: 16px;
      font-size: 20px;
      font-weight: 600;
      color: #303133;
      padding-bottom: 10px;
      border-bottom: 2px solid #409eff;
      display: inline-block;
      position: relative;
    }
      &::after {
        content: '';
        position: absolute;
        left: 0;
        bottom: -2px;
        width: 60px;
        height: 2px;
        background-color: #409eff;
    .header-actions {
      display: flex;
      gap: 10px;
    }
  }
  .enterprise-info-card {
    background: #fff;
    padding: 24px;
    border-radius: 8px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
  }
  .info-section {
    margin-bottom: 32px;
    &:last-of-type {
      margin-bottom: 0;
    }
    .section-header {
      margin-bottom: 16px;
      h3 {
        margin: 0;
        font-size: 16px;
        font-weight: 600;
        color: #303133;
        padding-bottom: 10px;
        border-bottom: 2px solid #409eff;
        display: inline-block;
        position: relative;
        &::after {
          content: "";
          position: absolute;
          left: 0;
          bottom: -2px;
          width: 60px;
          height: 2px;
          background-color: #409eff;
        }
      }
    }
  }
}
.info-descriptions {
  :deep(.el-descriptions__label) {
    width: 120px;
    background-color: #f5f7fa;
    font-weight: 500;
  .info-descriptions {
    :deep(.el-descriptions__label) {
      width: 120px;
      background-color: #f5f7fa;
      font-weight: 500;
      color: #606266;
    }
    :deep(.el-descriptions__content) {
      color: #303133;
    }
  }
  .content-text {
    color: #606266;
  }
  :deep(.el-descriptions__content) {
    color: #303133;
  }
}
  .link-text {
    color: #409eff;
    text-decoration: none;
.content-text {
  color: #606266;
}
.link-text {
  color: #409eff;
  text-decoration: none;
  &:hover {
    text-decoration: underline;
  }
}
.logo-qr-container {
  display: flex;
  gap: 40px;
}
.upload-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  .upload-label {
    font-size: 14px;
    color: #606266;
    margin-bottom: 12px;
    font-weight: 500;
    &:hover {
      text-decoration: underline;
    }
  }
  .upload-wrapper {
  .logo-qr-container {
    display: flex;
    gap: 40px;
  }
  .upload-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    .upload-label {
      font-size: 14px;
      color: #606266;
      margin-bottom: 12px;
      font-weight: 500;
    }
    .upload-wrapper {
      width: 140px;
      height: 140px;
    }
  }
  /* el-upload æ ¹èŠ‚ç‚¹æ˜¯å¤–å±‚ div,真正触发区在内层 .el-upload,需让内层铺满外层虚线框 */
  .logo-uploader,
  .qr-uploader {
    display: flex;
    flex-direction: column;
    box-sizing: border-box;
    width: 140px;
    height: 140px;
  }
}
    border: 1px dashed #d9d9d9;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.3s;
    overflow: hidden;
/* el-upload æ ¹èŠ‚ç‚¹æ˜¯å¤–å±‚ div,真正触发区在内层 .el-upload,需让内层铺满外层虚线框 */
.logo-uploader,
.qr-uploader {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: 140px;
  height: 140px;
  border: 1px dashed #d9d9d9;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s;
  overflow: hidden;
    :deep(.el-upload) {
      flex: 1;
      min-height: 0;
      width: 100%;
      display: flex !important;
      flex-direction: column;
      align-items: stretch;
      box-sizing: border-box;
      border: none;
      outline: none;
    }
  :deep(.el-upload) {
    flex: 1;
    min-height: 0;
    width: 100%;
    display: flex !important;
    flex-direction: column;
    align-items: stretch;
    box-sizing: border-box;
    border: none;
    outline: none;
    :deep(.uploaded-image),
    :deep(.upload-placeholder) {
      flex: 1 1 auto;
      min-height: 0;
      min-width: 0;
      width: 100%;
    }
    &:hover {
      border-color: #409eff;
    }
    :deep(.uploaded-image) {
      object-fit: contain;
    }
    :deep(.upload-placeholder) {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      background: #fafafa;
      color: #8c939d;
      .upload-icon {
        font-size: 32px;
        margin-bottom: 8px;
      }
      .upload-text {
        font-size: 12px;
      }
    }
  }
  :deep(.uploaded-image),
  :deep(.upload-placeholder) {
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
    width: 100%;
  }
  &:hover {
    border-color: #409eff;
  }
  :deep(.uploaded-image) {
  .display-image {
    width: 140px;
    height: 140px;
    object-fit: contain;
    border-radius: 8px;
    border: 1px solid #e4e7ed;
  }
  :deep(.upload-placeholder) {
  .empty-placeholder {
    width: 140px;
    height: 140px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background: #fafafa;
    color: #8c939d;
    border-radius: 8px;
    border: 1px dashed #e4e7ed;
    color: #c0c4cc;
    .upload-icon {
      font-size: 32px;
      margin-bottom: 8px;
    }
    .upload-text {
    span {
      margin-top: 8px;
      font-size: 12px;
    }
  }
}
.display-image {
  width: 140px;
  height: 140px;
  object-fit: contain;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
}
.empty-placeholder {
  width: 140px;
  height: 140px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background: #fafafa;
  border-radius: 8px;
  border: 1px dashed #e4e7ed;
  color: #c0c4cc;
  span {
    margin-top: 8px;
    font-size: 12px;
  .content-editor {
    :deep(.el-textarea__inner) {
      border-radius: 6px;
      font-size: 14px;
      line-height: 1.8;
    }
  }
}
.content-editor {
  :deep(.el-textarea__inner) {
  .content-display {
    padding: 16px 20px;
    background: #fafafa;
    border-radius: 6px;
    font-size: 14px;
    border: 1px solid #ebeef5;
    line-height: 1.8;
    color: #606266;
    min-height: 120px;
    white-space: pre-wrap;
    :deep(.empty-text) {
      color: #c0c4cc;
      font-style: italic;
    }
  }
}
.content-display {
  padding: 16px 20px;
  background: #fafafa;
  border-radius: 6px;
  border: 1px solid #ebeef5;
  line-height: 1.8;
  color: #606266;
  min-height: 120px;
  white-space: pre-wrap;
  :deep(.empty-text) {
    color: #c0c4cc;
    font-style: italic;
  }
}
.form-actions {
  display: flex;
  justify-content: center;
  gap: 16px;
  padding-top: 24px;
  margin-top: 16px;
  border-top: 1px solid #ebeef5;
}
/* é¢„览弹窗样式 */
.preview-content {
  .preview-header {
  .form-actions {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 20px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border-radius: 8px;
    color: #fff;
    margin-bottom: 20px;
    justify-content: center;
    gap: 16px;
    padding-top: 24px;
    margin-top: 16px;
    border-top: 1px solid #ebeef5;
  }
    .preview-logo {
      width: 80px;
      height: 80px;
      object-fit: contain;
      background: #fff;
  /* é¢„览弹窗样式 */
  .preview-content {
    .preview-header {
      display: flex;
      align-items: center;
      gap: 20px;
      padding: 20px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      border-radius: 8px;
      padding: 8px;
      color: #fff;
      margin-bottom: 20px;
      .preview-logo {
        width: 80px;
        height: 80px;
        object-fit: contain;
        background: #fff;
        border-radius: 8px;
        padding: 8px;
      }
      .preview-title {
        h1 {
          margin: 0 0 8px 0;
          font-size: 28px;
          font-weight: 600;
        }
        p {
          margin: 0;
          opacity: 0.9;
          font-size: 14px;
        }
      }
    }
    .preview-title {
      h1 {
        margin: 0 0 8px 0;
        font-size: 28px;
    .preview-section {
      margin-bottom: 24px;
      h4 {
        font-size: 16px;
        font-weight: 600;
        color: #303133;
        margin: 0 0 12px 0;
        padding-bottom: 8px;
        border-bottom: 1px solid #ebeef5;
      }
      p {
        margin: 0;
        opacity: 0.9;
        font-size: 14px;
        margin: 8px 0;
        color: #606266;
        line-height: 1.6;
      }
      :deep(div) {
        line-height: 1.8;
        color: #606266;
        white-space: pre-wrap;
      }
    }
    .preview-qr {
      text-align: center;
      .qr-image {
        width: 150px;
        height: 150px;
        margin-top: 12px;
        border-radius: 8px;
        border: 1px solid #ebeef5;
      }
    }
  }
  .preview-section {
    margin-bottom: 24px;
    h4 {
      font-size: 16px;
      font-weight: 600;
      color: #303133;
      margin: 0 0 12px 0;
      padding-bottom: 8px;
      border-bottom: 1px solid #ebeef5;
    }
    p {
      margin: 8px 0;
      color: #606266;
      line-height: 1.6;
    }
    :deep(div) {
      line-height: 1.8;
      color: #606266;
      white-space: pre-wrap;
    }
  :deep(.el-divider) {
    margin: 16px 0;
  }
  .preview-qr {
    text-align: center;
    .qr-image {
      width: 150px;
      height: 150px;
      margin-top: 12px;
      border-radius: 8px;
      border: 1px solid #ebeef5;
    }
  }
}
:deep(.el-divider) {
  margin: 16px 0;
}
</style>
src/views/equipmentManagement/amountSummary/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,180 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab">
      <el-tab-pane label="报修" name="repair">
        <el-form :inline="true" :model="filtersRepair">
          <el-form-item label="查看模式">
            <el-radio-group v-model="modeRepair" @change="buildColumnsRepair">
              <el-radio-button :value="'month'">按月</el-radio-button>
              <el-radio-button :value="'year'">按年</el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item v-if="modeRepair === 'month' || modeRepair === 'year'" label="年份">
            <el-date-picker v-model="yearRepair" type="year" value-format="YYYY" format="YYYY" placeholder="请选择年份" @change="refreshRepair" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="refreshRepair">查询</el-button>
            <el-button @click="resetRepair">重置</el-button>
          </el-form-item>
        </el-form>
        <div class="table_list">
          <PIMTable
            rowKey="deviceId"
            :column="columnsRepair"
            :tableData="tableDataRepair"
            :page="{ current: 1, size: tableDataRepair.length || 10, total: tableDataRepair.length }"
          />
        </div>
      </el-tab-pane>
      <el-tab-pane label="保养" name="maintain">
        <el-form :inline="true" :model="filtersMaintain">
          <el-form-item label="查看模式">
            <el-radio-group v-model="modeMaintain" @change="buildColumnsMaintain">
              <el-radio-button :value="'month'">按月</el-radio-button>
              <el-radio-button :value="'year'">按年</el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item v-if="modeMaintain === 'month' || modeMaintain === 'year'" label="年份">
            <el-date-picker v-model="yearMaintain" type="year" value-format="YYYY" format="YYYY" placeholder="请选择年份" @change="refreshMaintain" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="refreshMaintain">查询</el-button>
            <el-button @click="resetMaintain">重置</el-button>
          </el-form-item>
        </el-form>
        <div class="table_list">
          <PIMTable
            rowKey="deviceId"
            :column="columnsMaintain"
            :tableData="tableDataMaintain"
            :page="{ current: 1, size: tableDataMaintain.length || 10, total: tableDataMaintain.length }"
          />
        </div>
      </el-tab-pane>
    </el-tabs>
  </div>
  </template>
<script setup>
import { ref } from "vue";
import { monthlyAmount, yearlyAmount } from "@/api/equipmentManagement/repair";
import { monthlyAmount as monthlyAmountMaintain, yearlyAmount as yearlyAmountMaintain } from "@/api/equipmentManagement/upkeep";
defineOptions({ name: "金额汇总" });
const activeTab = ref("repair");
// æŠ¥ä¿®
const modeRepair = ref("month");
const yearRepair = ref(new Date().getFullYear().toString());
const filtersRepair = ref({});
const columnsRepair = ref([]);
const tableDataRepair = ref([]);
const fetchRepairData = async () => {
  try {
    const query = { year: yearRepair.value };
    const res = modeRepair.value === "month" ? await monthlyAmount(query) : await yearlyAmount(query);
    const list = res?.records || res?.data || res || [];
    tableDataRepair.value = Array.isArray(list) ? list : [];
  } catch (e) {
    tableDataRepair.value = [];
  }
};
const buildColumnsRepair = async () => {
  const base = [{ label: "设备名称", align: "center", prop: "deviceName", width: 180 }];
  if (modeRepair.value === "month") {
    const monthCols = Array.from({ length: 12 }, (_, i) => ({
      label: `${i + 1}月`,
      align: "center",
      prop: `month${i + 1}`,
    }));
    columnsRepair.value = [
      ...base,
      ...monthCols,
      { label: "总计", align: "center", prop: "total" },
    ];
  } else {
    columnsRepair.value = [
      ...base,
      { label: "金额", align: "center", prop: "totalRepairPrice" },
    ];
  }
  await fetchRepairData();
};
const refreshRepair = async () => {
  await buildColumnsRepair();
};
const resetRepair = () => {
  modeRepair.value = "month";
  yearRepair.value = new Date().getFullYear().toString();
  refreshRepair();
};
// ä¿å…»
const modeMaintain = ref("month");
const yearMaintain = ref(new Date().getFullYear().toString());
const filtersMaintain = ref({});
const columnsMaintain = ref([]);
const tableDataMaintain = ref([]);
const fetchMaintainData = async () => {
  try {
    const query = { year: yearMaintain.value };
    const res = modeMaintain.value === "month" ? await monthlyAmountMaintain(query) : await yearlyAmountMaintain(query);
    const list = res?.records || res?.data || res || [];
    tableDataMaintain.value = Array.isArray(list) ? list : [];
  } catch (e) {
    tableDataMaintain.value = [];
  }
};
const buildColumnsMaintain = async () => {
  const base = [{ label: "设备名称", align: "center", prop: "deviceName", width: 180 }];
  if (modeMaintain.value === "month") {
    const monthCols = Array.from({ length: 12 }, (_, i) => ({
      label: `${i + 1}月`,
      align: "center",
      prop: `month${i + 1}`,
    }));
    columnsMaintain.value = [
      ...base,
      ...monthCols,
      { label: "总计", align: "center", prop: "total" },
    ];
  } else {
    columnsMaintain.value = [
      ...base,
      { label: "金额", align: "center", prop: "totalRepairPrice" },
    ];
  }
  await fetchMaintainData();
};
const refreshMaintain = async () => {
  await buildColumnsMaintain();
};
const resetMaintain = () => {
  modeMaintain.value = "month";
  yearMaintain.value = new Date().getFullYear().toString();
  refreshMaintain();
};
buildColumnsRepair();
refreshRepair();
buildColumnsMaintain();
refreshMaintain();
</script>
<style scoped>
.table_list {
  margin-top: 10px;
}
</style>
src/views/equipmentManagement/calibration/index.vue
@@ -57,10 +57,10 @@
<script setup>
import {onMounted, ref} from "vue";
import {ElMessageBox, ElMessage} from "element-plus";
import {ElMessageBox} from "element-plus";
import useUserStore from "@/store/modules/user.js";
import CalibrationDia from "@/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue";
import {ledgerRecordListPage, ledgerRecordDelete} from "@/api/equipmentManagement/calibration.js";
import {ledgerRecordListPage} from "@/api/equipmentManagement/calibration.js";
const { proxy } = getCurrentInstance();
const userStore = useUserStore()
@@ -134,7 +134,6 @@
    {
        dataType: "action",
        label: "操作",
        width: 140,
        align: "center",
        fixed: 'right',
        operation: [
@@ -144,16 +143,9 @@
                clickFun: (row) => {
                    openCalibrationDia("edit", row);
                },
            },
            {
                name: "删除",
                type: "text",
                style: {
                    color: "#F56C6C"
                },
                clickFun: (row) => {
                    handleDelete(row);
                },
                disabled: (row) => {
                    return row.userId !== userStore.id
                }
            },
        ],
    },
@@ -201,26 +193,6 @@
        calibrationDia.value?.openDialog(type, row)
    })
}
// åˆ é™¤è®°å½•
const handleDelete = (row) => {
    ElMessageBox.confirm(`确认删除计量器具编号为"${row.code}"的检定记录吗?`, "删除确认", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            ledgerRecordDelete([row.id]).then(() => {
                ElMessage.success("删除成功");
                getList();
            }).catch(() => {
                ElMessage.error("删除失败");
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消删除");
        });
};
// å¯¼å‡º
const handleOut = () => {
src/views/equipmentManagement/defectManagement/index.vue
@@ -43,7 +43,7 @@
    <el-dialog title="登记设备缺陷" v-model="showRegisterDialog" width="50%">
      <el-form :model="defectForm" :rules="defectRules" ref="defectFormRef" label-width="100px">
        <el-form-item label="设备名称" prop="deviceName">
          <el-select v-model="defectForm.deviceLedgerId" @change="setDeviceModel">
          <el-select v-model="defectForm.deviceLedgerId" @change="setDeviceModel" filterable>
            <el-option
              v-for="(item, index) in deviceOptions"
              :key="index"
src/views/equipmentManagement/inspectionManagement/components/formDia.vue
@@ -1,116 +1,102 @@
<template>
    <div>
        <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'"
                             v-model="dialogVisitable" width="800px" @close="cancel">
            <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="设备名称" prop="taskId">
                            <el-select v-model="form.taskId" @change="setDeviceModel" filterable>
                                <el-option
                                    v-for="(item, index) in deviceOptions"
                                    :key="index"
                                    :label="item.deviceName"
                                    :value="item.id"
                                ></el-option>
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="巡检人" prop="inspector">
                            <el-select v-model="form.inspector"                 filterable
                                                 default-first-option
                                                 :reserve-keyword="false" placeholder="请选择" multiple clearable>
                                <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="备注" prop="remarks">
                            <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="登记时间" prop="dateStr">
                            <el-date-picker
                                v-model="form.dateStr"
                                type="date"
                                placeholder="选择登记日期"
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                                style="width: 100%"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="任务频率" prop="frequencyType">
                            <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                                <el-option label="每日" value="DAILY"/>
                                <el-option label="每周" value="WEEKLY"/>
                                <el-option label="每月" value="MONTHLY"/>
                                <!-- <el-option label="季度" value="QUARTERLY"/> -->
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                                <el-option label="周一" value="MON"/>
                                <el-option label="周二" value="TUE"/>
                                <el-option label="周三" value="WED"/>
                                <el-option label="周四" value="THU"/>
                                <el-option label="周五" value="FRI"/>
                                <el-option label="周六" value="SAT"/>
                                <el-option label="周日" value="SUN"/>
                            </el-select>
                            <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm"  style="width: 50%"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="DD,HH:mm"
                                value-format="DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="MM,DD,HH:mm"
                                value-format="MM,DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button @click="cancel">取消</el-button>
                    <el-button type="primary" @click="submitForm">保存</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
  <div>
    <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'"
               v-model="dialogVisitable" width="800px" @close="cancel">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="设备名称" prop="taskIds">
              <el-select v-model="form.taskIds" @change="setDeviceModel" multiple filterable>
                <el-option
                  v-for="(item, index) in deviceOptions"
                  :key="index"
                  :label="item.deviceName"
                  :value="item.id"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="巡检人" prop="inspector">
              <el-select v-model="form.inspector" placeholder="请选择" multiple clearable>
                <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="备注" prop="remarks">
              <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="任务频率" prop="frequencyType">
              <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                <el-option label="每日" value="DAILY"/>
                <el-option label="每周" value="WEEKLY"/>
                <el-option label="每月" value="MONTHLY"/>
                <!-- <el-option label="季度" value="QUARTERLY"/> -->
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                              value-format="HH:mm" />
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                <el-option label="周一" value="MON"/>
                <el-option label="周二" value="TUE"/>
                <el-option label="周三" value="WED"/>
                <el-option label="周四" value="THU"/>
                <el-option label="周五" value="FRI"/>
                <el-option label="周六" value="SAT"/>
                <el-option label="周日" value="SUN"/>
              </el-select>
              <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                              value-format="HH:mm"  style="width: 50%"/>
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-date-picker
                  v-model="form.frequencyDetail"
                  type="datetime"
                  clearable
                  placeholder="选择开始日期"
                  format="DD,HH:mm"
                  value-format="DD,HH:mm"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-date-picker
                  v-model="form.frequencyDetail"
                  type="datetime"
                  clearable
                  placeholder="选择开始日期"
                  format="MM,DD,HH:mm"
                  value-format="MM,DD,HH:mm"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">保存</el-button>
          <el-button @click="cancel">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
@@ -127,117 +113,128 @@
const operationType = ref('add');
const deviceOptions = ref([]);
const data = reactive({
    form: {
        taskId: undefined,
        taskName: undefined,
        inspector: '',
        inspectorIds: '',
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: '',
        dateStr: ''
    },
    rules: {
        taskId: [{ required: true, message: "请选择设备", trigger: "change" },],
        inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" },],
        dateStr: [{ required: true, message: "请选择登记时间", trigger: "change" }]
    }
  form: {
    taskIds: [],
    taskName: undefined,
    inspector: '',
    inspectorIds: '',
    remarks: '',
    frequencyType: '',
    frequencyDetail: '',
    week: '',
    time: ''
  },
  rules: {
    taskIds: [{ required: true, message: "请选择设备", trigger: "change" },],
    inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" },],
  }
})
const { form, rules } = toRefs(data)
const userList = ref([])
const loadDeviceName = async () => {
    const { data } = await getDeviceLedger();
    deviceOptions.value = data;
  const { data } = await getDeviceLedger();
  deviceOptions.value = data;
};
const setDeviceModel = (id) => {
    const option = deviceOptions.value.find((item) => item.id === id);
    if (option) {
        form.value.taskName = option.deviceName;
    }
const setDeviceModel = (ids) => {
  if (!ids || ids.length === 0) {
    form.value.taskIds = []
    form.value.taskName = undefined
    return
  }
  const selectedDevices = deviceOptions.value.filter((item) => ids.includes(item.id))
  if (selectedDevices.length > 0) {
    form.value.taskIds = ids
    form.value.taskName = selectedDevices.map(d => d.deviceName).join(',')
  }
}
// æ‰“开弹框
const openDialog = async (type, row) => {
    dialogVisitable.value = true
    operationType.value = type
    // é‡ç½®è¡¨å•
    resetForm();
    // åŠ è½½ç”¨æˆ·åˆ—è¡¨
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
    // åŠ è½½è®¾å¤‡åˆ—è¡¨
    await loadDeviceName();
    if (type === 'edit' && row) {
        form.value = {...row}
        form.value.inspector = form.value.inspectorIds.split(',').map(Number)
        // å¦‚果有设备ID,自动设置设备信息
        if (form.value.taskId) {
            setDeviceModel(form.value.taskId);
        }
    }
  dialogVisitable.value = true
  operationType.value = type
  // é‡ç½®è¡¨å•
  resetForm();
  // åŠ è½½ç”¨æˆ·åˆ—è¡¨
  userListNoPageByTenantId().then((res) => {
    userList.value = res.data;
  });
  // åŠ è½½è®¾å¤‡åˆ—è¡¨
  await loadDeviceName();
  if (type === 'edit' && row) {
    form.value = {...row}
    form.value.inspector = form.value.inspectorIds.split(',').map(Number)
    // å¦‚果有设备ID数组,转换为数组并设置设备信息
    if (row.taskIds) {
      form.value.taskIds = row.taskIds.split(',').map(id => parseInt(id.trim()))
      setDeviceModel(form.value.taskIds)
    }
  }
}
// å…³é—­å¯¹è¯æ¡†
const cancel = () => {
    resetForm()
    dialogVisitable.value = false
    emit('closeDia')
  resetForm()
  dialogVisitable.value = false
  emit('closeDia')
}
// é‡ç½®è¡¨å•函数
const resetForm = () => {
    if (proxy.$refs.formRef) {
        proxy.$refs.formRef.resetFields()
    }
    // é‡ç½®è¡¨å•数据确保设备信息正确重置
    form.value = {
        taskId: undefined,
        taskName: undefined,
        inspector: '',
        inspectorIds: '',
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: ''
    }
  if (proxy.$refs.formRef) {
    proxy.$refs.formRef.resetFields()
  }
  // é‡ç½®è¡¨å•数据确保设备信息正确重置
  form.value = {
    taskIds: [],
    taskName: undefined,
    inspector: '',
    inspectorIds: '',
    remarks: '',
    frequencyType: '',
    frequencyDetail: '',
    week: '',
    time: ''
  }
}
// æäº¤è¡¨å•
const submitForm = () => {
    proxy.$refs["formRef"].validate(async valid => {
        if (valid) {
            try {
                form.value.inspectorIds = form.value.inspector.join(',')
                delete form.value.inspector
                if (form.value.frequencyType === 'WEEKLY') {
                    let frequencyDetail = ''
                    frequencyDetail = form.value.week + ',' + form.value.time
                    form.value.frequencyDetail = frequencyDetail
                }
                let res = await userStore.getInfo()
                form.value.registrantId = res.user.userId
                await addOrEditTimingTask(form.value)
                cancel()
                proxy.$modal.msgSuccess('提交成功')
            } catch (error) {
                proxy.$modal.msgError('提交失败,请重试')
            }
        }
    })
  proxy.$refs["formRef"].validate(async valid => {
    if (valid) {
      try {
        form.value.inspectorIds = form.value.inspector.join(',')
        delete form.value.inspector
        // å¤„理 taskIds å’Œ taskName
        if (form.value.taskIds && Array.isArray(form.value.taskIds)) {
          form.value.taskIds = form.value.taskIds.join(',')
        }
        if (form.value.frequencyType === 'WEEKLY') {
          let frequencyDetail = ''
          frequencyDetail = form.value.week + ',' + form.value.time
          form.value.frequencyDetail = frequencyDetail
        }
        let res = await userStore.getInfo()
        form.value.registrantId = res.user.userId
        await addOrEditTimingTask(form.value)
        cancel()
        proxy.$modal.msgSuccess('提交成功')
      } catch (error) {
        proxy.$modal.msgError('提交失败,请重试')
      }
    }
  })
}
defineExpose({ openDialog })
</script>
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
@@ -32,7 +32,7 @@
        
        <!-- ç”Ÿäº§åŽ -->
        <div class="form-container">
          <div class="title">生产中</div>
          <div class="title">生产后</div>
          
          <!-- å›¾ç‰‡åˆ—表 -->
          <div style="display: flex; flex-wrap: wrap;">
@@ -59,7 +59,7 @@
        
        <!-- ç”Ÿäº§é—®é¢˜ -->
        <div class="form-container">
          <div class="title">生产后</div>
          <div class="title">生产问题</div>
          
          <!-- å›¾ç‰‡åˆ—表 -->
          <div style="display: flex; flex-wrap: wrap;">
@@ -100,7 +100,7 @@
        
        <!-- è§†é¢‘ -->
        <div v-else-if="mediaType === 'video'" style="position: relative;">
          <Video
          <video
              :src="mediaList[currentMediaIndex]"
              autoplay
              controls
@@ -114,6 +114,7 @@
<script setup>
import { ref } from 'vue';
import VueEasyLightbox from 'vue-easy-lightbox';
const { proxy } = getCurrentInstance();
// æŽ§åˆ¶å¼¹çª—显示
const dialogVisitable = ref(false);
@@ -133,26 +134,83 @@
const currentMediaIndex = ref(0);
const mediaList = ref([]); // å­˜å‚¨å½“前要查看的媒体列表(含图片和视频对象)
const mediaType = ref('image'); // image | video
const javaApi = proxy.javaApi;
// å¤„理 URL:将 Windows è·¯å¾„转换为可访问的 URL
function processFileUrl(fileUrl) {
  if (!fileUrl) return '';
  // å¦‚æžœ URL æ˜¯ Windows è·¯å¾„格式(包含反斜杠),需要转换
  if (fileUrl && fileUrl.indexOf('\\') > -1) {
    // æŸ¥æ‰¾ uploads å…³é”®å­—的位置,从那里开始提取相对路径
    const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
    if (uploadsIndex > -1) {
      // ä»Ž uploads å¼€å§‹æå–路径,并将反斜杠替换为正斜杠
      const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
      fileUrl = '/' + relativePath;
    } else {
      // å¦‚果没有找到 uploads,提取最后一个目录和文件名
      const parts = fileUrl.split('\\');
      const fileName = parts[parts.length - 1];
      fileUrl = '/uploads/' + fileName;
    }
  }
  // ç¡®ä¿æ‰€æœ‰éž http å¼€å¤´çš„ URL éƒ½æ‹¼æŽ¥ baseUrl
  if (fileUrl && !fileUrl.startsWith('http')) {
    // ç¡®ä¿è·¯å¾„以 / å¼€å¤´
    if (!fileUrl.startsWith('/')) {
      fileUrl = '/' + fileUrl;
    }
    // æ‹¼æŽ¥ baseUrl
    fileUrl = javaApi + fileUrl;
  }
  return fileUrl;
}
// å¤„理每一类数据:分离图片和视频
function processItems(items) {
  const images = [];
  const videos = [];
  // æ£€æŸ¥ items æ˜¯å¦å­˜åœ¨ä¸”为数组
  if (!items || !Array.isArray(items)) {
    return { images, videos };
  }
  items.forEach(item => {
    if (item.contentType?.startsWith('image/')) {
      images.push(item.url);
    } else if (item.contentType?.startsWith('video/')) {
      videos.push(item.url);
    if (!item || !item.url) return;
    // å¤„理文件 URL
    const fileUrl = processFileUrl(item.url);
    // æ ¹æ®æ–‡ä»¶æ‰©å±•名判断是图片还是视频
    const urlLower = fileUrl.toLowerCase();
    if (urlLower.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/)) {
      images.push(fileUrl);
    } else if (urlLower.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/)) {
      videos.push(fileUrl);
    } else if (item.contentType) {
      // å¦‚果有 contentType,使用 contentType åˆ¤æ–­
      if (item.contentType.startsWith('image/')) {
        images.push(fileUrl);
      } else if (item.contentType.startsWith('video/')) {
        videos.push(fileUrl);
      }
    }
  });
  return { images, videos };
}
// æ‰“开弹窗并加载数据
const openDialog = async (row) => {
  const { images: beforeImgs, videos: beforeVids } = processItems(row.beforeProduction);
  const { images: afterImgs, videos: afterVids } = processItems(row.afterProduction);
  const { images: issueImgs, videos: issueVids } = processItems(row.productionIssues);
  // ä½¿ç”¨æ­£ç¡®çš„字段名:commonFileListBefore, commonFileListAfter
  // productionIssues å¯èƒ½ä¸å­˜åœ¨ï¼Œä½¿ç”¨ç©ºæ•°ç»„
  const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBefore || []);
  const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfter || []);
  const { images: issueImgs, videos: issueVids } = processItems(row.productionIssues || []);
  
  beforeProductionImgs.value = beforeImgs;
  beforeProductionVideos.value = beforeVids;
src/views/equipmentManagement/inspectionManagement/index.vue
@@ -1,78 +1,77 @@
<template>
    <div class="app-container">
        <el-form :inline="true" :model="queryParams" class="search-form">
            <el-form-item label="搜索">
                <el-input
                    v-model="queryParams.searchAll"
                    placeholder="请输入关键字"
                    clearable
                    :style="{ width: '100%' }"
                />
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="handleQuery">查询</el-button>
                <el-button @click="resetQuery">重置</el-button>
            </el-form-item>
        </el-form>
        <el-card>
            <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
                <el-radio-group v-model="activeRadio" @change="radioChange">
                    <el-radio-button v-for="tab in radios"
                                                     :key="tab.name"
                                                     :label="tab.label"
                                                     :value="tab.name"/>
                </el-radio-group>
                <!-- æ“ä½œæŒ‰é’®åŒº -->
                <el-space v-if="activeRadio !== 'task'">
                    <el-button type="primary" :icon="Plus" @click="handleAdd(undefined)">新建</el-button>
                    <el-button type="danger" :icon="Delete" @click="handleDelete">删除</el-button>
                    <el-button @click="handleOut">导出</el-button>
                </el-space>
                <el-space v-else>
                    <el-button @click="handleOut">导出</el-button>
                </el-space>
            </div>
            <div>
                <div>
                    <PIMTable :table-loading="tableLoading"
                                        :table-data="tableData"
                                        :column="tableColumns"
                                        @selection-change="handleSelectionChange"
                                        :is-selection="true"
                                        :border="true"
                                        :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }"
                                        :page="{
          current: pageNum,
          size: pageSize,
          total: total,
        }"
                                        @pagination="pagination"
                    >
                        <template #inspector="{ row }">
                            <div class="person-tags">
                                <!-- è°ƒè¯•信息,上线时删除 -->
                                <!-- {{ console.log('inspector data:', row.inspector) }} -->
                                <template v-if="row.inspector && row.inspector.length > 0">
                                    <el-tag
                                        v-for="(person, index) in row.inspector"
                                        :key="index"
                                        size="small"
                                        type="primary"
                                        class="person-tag"
                                    >
                                        {{ person }}
                                    </el-tag>
                                </template>
                                <span v-else class="no-data">--</span>
                            </div>
                        </template>
                    </PIMTable>
                </div>
            </div>
        </el-card>
        <form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
        <view-files ref="viewFiles"></view-files>
    </div>
  <div class="app-container">
    <el-form :inline="true" :model="queryParams" class="search-form">
      <el-form-item label="搜索">
        <el-input
            v-model="queryParams.searchAll"
            placeholder="请输入关键字"
            clearable
            :style="{ width: '100%' }"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleQuery">查询</el-button>
        <el-button @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <el-card>
      <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
        <el-radio-group v-model="activeRadio" @change="radioChange">
          <el-radio-button v-for="tab in radios"
                           :key="tab.name"
                           :label="tab.label"
                           :value="tab.name"/>
        </el-radio-group>
        <!-- æ“ä½œæŒ‰é’®åŒº -->
        <el-space v-if="activeRadio !== 'task'">
          <el-button type="primary" :icon="Plus" @click="handleAdd(undefined)">新建</el-button>
          <el-button type="danger" :icon="Delete" @click="handleDelete">删除</el-button>
          <el-button @click="handleOut">导出</el-button>
        </el-space>
        <el-space v-else>
          <el-button @click="handleOut">导出</el-button>
        </el-space>
      </div>
      <div>
        <PIMTable :table-loading="tableLoading"
                :table-data="tableData"
                :column="tableColumns"
                @selection-change="handleSelectionChange"
                @pagination="handlePagination"
                :is-selection="true"
                :border="true"
                :page="{
                  current: pageNum,
                  size: pageSize,
                  total: total,
                  layout: 'total, sizes, prev, pager, next, jumper'
                }"
                :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }"
        >
          <template #inspector="{ row }">
            <div class="person-tags">
              <!-- è°ƒè¯•信息,上线时删除 -->
              <!-- {{ console.log('inspector data:', row.inspector) }} -->
              <template v-if="row.inspector && row.inspector.length > 0">
                <el-tag
                  v-for="(person, index) in row.inspector"
                  :key="index"
                  size="small"
                  type="primary"
                  class="person-tag"
                >
                  {{ person }}
                </el-tag>
              </template>
              <span v-else class="no-data">--</span>
            </div>
          </template>
        </PIMTable>
      </div>
    </el-card>
    <form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
    <view-files ref="viewFiles"></view-files>
  </div>
</template>
<script setup>
@@ -81,16 +80,15 @@
import { ElMessageBox } from "element-plus";
// ç»„件引入
import Pagination from "@/components/Pagination/index.vue";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue";
import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue";
// æŽ¥å£å¼•å…¥
import {
    delTimingTask,
    inspectionTaskList,
    timingTaskList
  delTimingTask,
  inspectionTaskList,
  timingTaskList
} from "@/api/inspectionManagement/index.js";
// å…¨å±€å˜é‡
@@ -100,14 +98,14 @@
// æŸ¥è¯¢å‚æ•°
const queryParams = reactive({
    searchAll: "",
  searchAll: "",
});
// å•选框配置
const activeRadio = ref("taskManage");
const radios = reactive([
    { name: "taskManage", label: "定时任务管理" },
    { name: "task", label: "定时任务记录" },
  { name: "taskManage", label: "定时任务管理" },
  { name: "task", label: "定时任务记录" },
]);
// è¡¨æ ¼æ•°æ®
@@ -122,233 +120,244 @@
// åˆ—配置
const columns = ref([
    { prop: "taskName", label: "巡检任务名称", minWidth: 160 },
    { prop: "remarks", label: "备注", minWidth: 150 },
    { prop: "inspector", label: "执行巡检人", minWidth: 150, slot: "inspector" },
    {
        prop: "frequencyType",
        label: "频次",
        minWidth: 150,
        formatData: (cell) => ({
            DAILY: "每日",
            WEEKLY: "每周",
            MONTHLY: "每月",
            QUARTERLY: "季度"
        }[cell] || "")
    },
    {
        prop: "frequencyDetail",
        label: "开始日期与时间",
        minWidth: 150,
        formatter: (row, column, cellValue) => {
            // å…ˆåˆ¤æ–­æ˜¯å¦æ˜¯å­—符串
            if (typeof cellValue !== 'string') return '';
            let val = cellValue;
            const replacements = {
                MON: '周一',
                TUE: '周二',
                WED: '周三',
                THU: '周四',
                FRI: '周五',
                SAT: '周六',
                SUN: '周日'
            };
            // ä½¿ç”¨æ­£åˆ™ä¸€æ¬¡æ€§æ›¿æ¢æ‰€æœ‰åŒ¹é…é¡¹
            return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
        }
    },
    { prop: "registrant", label: "登记人", minWidth: 100 },
    { prop: "dateStr", label: "登记日期", minWidth: 100 },
  { prop: "taskName", label: "巡检任务名称", minWidth: 160 },
  { prop: "remarks", label: "备注", minWidth: 150 },
  { prop: "inspector", label: "执行巡检人", minWidth: 150, slot: "inspector" },
  {
    prop: "frequencyType",
    label: "频次",
    minWidth: 150,
    formatter: (_, __, val) => ({
      DAILY: "每日",
      WEEKLY: "每周",
      MONTHLY: "每月",
      QUARTERLY: "季度"
    }[val] || "")
  },
  {
    prop: "frequencyDetail",
    label: "开始日期与时间",
    minWidth: 150,
    formatter: (row, column, cellValue) => {
      // å…ˆåˆ¤æ–­æ˜¯å¦æ˜¯å­—符串
      if (typeof cellValue !== 'string') return '';
      let val = cellValue;
      const replacements = {
        MON: '周一',
        TUE: '周二',
        WED: '周三',
        THU: '周四',
        FRI: '周五',
        SAT: '周六',
        SUN: '周日'
      };
      // ä½¿ç”¨æ­£åˆ™ä¸€æ¬¡æ€§æ›¿æ¢æ‰€æœ‰åŒ¹é…é¡¹
      return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
    }
  },
  { prop: "registrant", label: "登记人", minWidth: 100 },
  { prop: "createTime", label: "登记日期", minWidth: 100 },
]);
// æ“ä½œåˆ—配置
const getOperationColumn = (operations) => {
    if (!operations || operations.length === 0) return null;
    const operationConfig = {
        label: "操作",
        width: 130,
        fixed: "right",
        dataType: "action",
        operation: operations.map(op => {
            switch (op) {
                case 'edit':
                    return {
                        name: "编辑",
                        clickFun: handleAdd,
                        color: "#409EFF"
                    };
                case 'viewFile':
                    return {
                        name: "查看附件",
                        clickFun: viewFile,
                        color: "#67C23A"
                    };
                default:
                    return null;
            }
        }).filter(Boolean)
    };
    return operationConfig;
  if (!operations || operations.length === 0) return null;
  const operationConfig = {
    label: "操作",
    width: 130,
    fixed: "right",
    dataType: "action",
    operation: operations.map(op => {
      switch (op) {
        case 'edit':
          return {
            name: "编辑",
            clickFun: handleAdd,
            color: "#409EFF"
          };
        case 'viewFile':
          return {
            name: "查看附件",
            clickFun: viewFile,
            color: "#67C23A"
          };
        default:
          return null;
      }
    }).filter(Boolean)
  };
  return operationConfig;
};
onMounted(() => {
    radioChange('taskManage');
  radioChange('taskManage');
});
// å•选变化
const radioChange = (value) => {
    if (value === "taskManage") {
        const operationColumn = getOperationColumn(['edit']);
        tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
        operationsArr.value = ['edit'];
    } else if (value === "task") {
        const operationColumn = getOperationColumn(['viewFile']);
        tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
        operationsArr.value = ['viewFile'];
    }
    pageNum.value = 1;
    pageSize.value = 10;
    getList();
  if (value === "taskManage") {
    const operationColumn = getOperationColumn(['edit']);
    tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
    operationsArr.value = ['edit'];
  } else if (value === "task") {
    const operationColumn = getOperationColumn(['viewFile']);
    const statusColumn = {
      prop: "status",
      label: "任务状态",
      minWidth: 100,
      dataType: "tag",
      formatType: (row) => {
        if (row.status === '已巡检') return 'success';
        return 'warning';
      }
    };
    tableColumns.value = [...columns.value, statusColumn, ...(operationColumn ? [operationColumn] : [])];
    operationsArr.value = ['viewFile'];
  }
  pageNum.value = 1;
  pageSize.value = 10;
  getList();
};
// æŸ¥è¯¢æ“ä½œ
const handleQuery = () => {
    pageNum.value = 1;
    pageSize.value = 10;
    getList();
  pageNum.value = 1;
  pageSize.value = 10;
  getList();
};
const pagination = (obj) => {
    pageNum.value = obj.page;
    pageSize.value = obj.limit;
// åˆ†é¡µå¤„理
const handlePagination = (val) => {
    pageNum.value = val.page;
    pageSize.value = val.limit;
    getList();
};
// èŽ·å–åˆ—è¡¨æ•°æ®
const getList = () => {
    tableLoading.value = true;
    const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
    let apiCall;
    if (activeRadio.value === "task") {
        apiCall = inspectionTaskList(params);
    } else {
        apiCall = timingTaskList(params);
    }
    apiCall.then(res => {
        const rawData = res.data.records || [];
        // å¤„理 inspector å­—段,将字符串转换为数组(适用于所有情况)
        tableData.value = rawData.map(item => {
            const processedItem = { ...item };
            // å¤„理 inspector å­—段
            if (processedItem.inspector) {
                if (typeof processedItem.inspector === 'string') {
                    // å­—符串按逗号分割
                    processedItem.inspector = processedItem.inspector.split(',').map(s => s.trim()).filter(s => s);
                } else if (!Array.isArray(processedItem.inspector)) {
                    // éžæ•°ç»„转为数组
                    processedItem.inspector = [processedItem.inspector];
                }
            } else {
                // ç©ºå€¼è®¾ä¸ºç©ºæ•°ç»„
                processedItem.inspector = [];
            }
            return processedItem;
        });
        total.value = res.data.total || 0;
    }).finally(() => {
        tableLoading.value = false;
    });
  tableLoading.value = true;
  const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
  let apiCall;
  if (activeRadio.value === "task") {
    apiCall = inspectionTaskList(params);
  } else {
    apiCall = timingTaskList(params);
  }
  apiCall.then(res => {
    const rawData = res.data.records || [];
    // å¤„理 inspector å­—段,将字符串转换为数组(适用于所有情况)
    tableData.value = rawData.map(item => {
      const processedItem = { ...item };
      // å¤„理 inspector å­—段
      if (processedItem.inspector) {
        if (typeof processedItem.inspector === 'string') {
          // å­—符串按逗号分割
          processedItem.inspector = processedItem.inspector.split(',').map(s => s.trim()).filter(s => s);
        } else if (!Array.isArray(processedItem.inspector)) {
          // éžæ•°ç»„转为数组
          processedItem.inspector = [processedItem.inspector];
        }
      } else {
        // ç©ºå€¼è®¾ä¸ºç©ºæ•°ç»„
        processedItem.inspector = [];
      }
      return processedItem;
    });
    total.value = res.data.total || 0;
  }).finally(() => {
    tableLoading.value = false;
  });
};
// é‡ç½®æŸ¥è¯¢
const resetQuery = () => {
    for (const key in queryParams) {
        if (!["pageNum", "pageSize"].includes(key)) {
            queryParams[key] = "";
        }
    }
    handleQuery();
  for (const key in queryParams) {
    if (!["pageNum", "pageSize"].includes(key)) {
      queryParams[key] = "";
    }
  }
  handleQuery();
};
// æ–°å¢ž / ç¼–辑
const handleAdd = (row) => {
    const type = row ? 'edit' : 'add';
    nextTick(() => {
        formDia.value?.openDialog(type, row);
    });
  const type = row ? 'edit' : 'add';
  nextTick(() => {
    formDia.value?.openDialog(type, row);
  });
};
// æŸ¥çœ‹é™„ä»¶
const viewFile = (row) => {
    nextTick(() => {
        viewFiles.value?.openDialog(row);
    });
  nextTick(() => {
    viewFiles.value?.openDialog(row);
  });
};
// åˆ é™¤æ“ä½œ
const handleDelete = () => {
    if (!selectedRows.value.length) {
        proxy.$modal.msgWarning("请选择要删除的数据");
        return;
    }
    const deleteIds = selectedRows.value.map(item => item.id);
    proxy.$modal.confirm('是否确认删除所选数据项?').then(() => {
        return delTimingTask(deleteIds);
    }).then(() => {
        proxy.$modal.msgSuccess("删除成功");
        handleQuery();
    }).catch(() => {});
  if (!selectedRows.value.length) {
    proxy.$modal.msgWarning("请选择要删除的数据");
    return;
  }
  const deleteIds = selectedRows.value.map(item => item.id);
  proxy.$modal.confirm('是否确认删除所选数据项?').then(() => {
    return delTimingTask(deleteIds);
  }).then(() => {
    proxy.$modal.msgSuccess("删除成功");
    handleQuery();
  }).catch(() => {});
};
// å¤šé€‰å˜æ›´
const handleSelectionChange = (selection) => {
    selectedRows.value = selection;
  selectedRows.value = selection;
};
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            // æ ¹æ®å½“前选中的标签页调用不同的导出接口
            if (activeRadio.value === "taskManage") {
                // å®šæ—¶ä»»åŠ¡ç®¡ç†
                proxy.download("/timingTask/export", {}, "定时任务管理.xlsx");
            } else if (activeRadio.value === "task") {
                // å®šæ—¶ä»»åŠ¡è®°å½•
                proxy.download("/inspectionTask/export", {}, "定时任务记录.xlsx");
            }
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      // æ ¹æ®å½“前选中的标签页调用不同的导出接口
      if (activeRadio.value === "taskManage") {
        // å®šæ—¶ä»»åŠ¡ç®¡ç†
        proxy.download("/timingTask/export", {}, "定时任务管理.xlsx");
      } else if (activeRadio.value === "task") {
        // å®šæ—¶ä»»åŠ¡è®°å½•
        proxy.download("/inspectionTask/export", {}, "定时任务记录.xlsx");
      }
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
</script>
<style scoped>
.person-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.person-tag {
    margin-right: 4px;
    margin-bottom: 2px;
  margin-right: 4px;
  margin-bottom: 2px;
}
.no-data {
    color: #909399;
    font-size: 14px;
  color: #909399;
  font-size: 14px;
}
</style>
src/views/equipmentManagement/ledger/Form.vue
@@ -32,71 +32,29 @@
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="启用折旧" prop="enableDepreciation">
          <el-switch v-model="form.enableDepreciation" :active-value="true" :inactive-value="false" />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="数量" prop="number">
          <el-input-number :min="1" style="width: 100%"
            v-model="form.number"
                                                     disabled
                                             disabled
            placeholder="请输入数量"
            @change="mathNum"
          />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="含税单价" prop="taxIncludingPriceUnit">
          <el-input-number :step="0.01" :min="0" style="width: 100%"
        <el-form-item label="资产原值" prop="taxIncludingPriceUnit">
          <el-input-number :min="0" style="width: 100%"
                                                     :precision="2"
            v-model="form.taxIncludingPriceUnit"
            placeholder="请输入含税单价"
            placeholder="请输入资产原值"
            maxlength="10"
            @change="mathNum"
          />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="含税总价" prop="taxIncludingPriceTotal">
          <el-input
            v-model="form.taxIncludingPriceTotal"
            placeholder="自动生成"
            type="number"
            disabled
          />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="税率(%)" prop="taxRate">
          <!-- <el-input
            v-model="form.taxRate"
            placeholder="请输入税率"
            type="number"
          >
            <template #append> % </template>
          </el-input> -->
          <el-select
            v-model="form.taxRate"
            placeholder="请选择"
            clearable
            @change="mathNum"
          >
            <el-option label="1" :value="1" />
            <el-option label="6" :value="6" />
            <el-option label="13" :value="13" />
          </el-select>
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="不含税总价" prop="unTaxIncludingPriceTotal">
          <el-input
            v-model="form.unTaxIncludingPriceTotal"
            placeholder="自动生成"
            type="number"
            disabled
          />
        </el-form-item>
      </el-col>
            <el-col :span="12">
                <el-form-item label="启用折旧" prop="enableDepreciation">
                    <el-switch v-model="form.enableDepreciation" :active-value="true" :inactive-value="false" />
                </el-form-item>
            </el-col>
      <!-- <el-col :span="12">
        <el-form-item label="录入人" prop="createUser">
          <el-input v-model="form.createUser" placeholder="请输入录入人" />
@@ -137,11 +95,6 @@
// import useUserStore from "@/store/modules/user";
import { getLedgerById } from "@/api/equipmentManagement/ledger";
import dayjs from "dayjs";
import {
  calculateTaxIncludeTotalPrice,
  calculateTaxExclusiveTotalPrice,
} from "@/utils/summarizeTable";
import { ElMessage } from "element-plus";
import {ref} from "vue";
defineOptions({
@@ -151,13 +104,25 @@
const operationType = ref('');
const formRules = {
    deviceName: [{ required: true, trigger: "blur", message: "请输入" }],
    deviceModel: [{ required: true, trigger: "blur", message: "请输入" }],
    supplierName: [{ required: true, trigger: "blur", message: "请输入" }],
    unit: [{ required: true, trigger: "blur", message: "请输入" }],
    number: [{ required: true, trigger: "blur", message: "请输入" }],
    taxIncludingPriceUnit: [{ required: true, trigger: "blur", message: "请输入" }],
    taxRate: [{ required: true, trigger: "change", message: "请输入" }],
    planRuntimeTime: [{ required: true, trigger: "change", message: "请选择" }],
    deviceModel: [{ trigger: "blur", message: "请输入" }],
    supplierName: [{ trigger: "blur", message: "请输入" }],
    unit: [{ trigger: "blur", message: "请输入" }],
    taxIncludingPriceUnit: [
        {
            required: true,
            trigger: "blur",
            validator: (rule, value, callback) => {
                if (value === undefined || value === null || value === '') {
                    callback(new Error("请输入资产原值"));
                } else if (typeof value === 'number' && value >= 0) {
                    callback();
                } else {
                    callback(new Error("请输入有效的资产原值"));
                }
            }
        }
    ],
    planRuntimeTime: [{ trigger: "change", message: "请选择" }],
}
const { form, resetForm } = useFormData({
@@ -169,10 +134,7 @@
  enableDepreciation: false, // æ˜¯å¦å¯ç”¨æŠ˜æ—§
  unit: undefined, // å•位
  number: 1, // æ•°é‡
  taxIncludingPriceUnit: undefined, // å«ç¨Žå•ä»·
  taxIncludingPriceTotal: undefined, // å«ç¨Žæ€»ä»·
  taxRate: undefined, // ç¨Žçއ
  unTaxIncludingPriceTotal: undefined, // ä¸å«ç¨Žæ€»ä»·
    taxIncludingPriceUnit: undefined, // èµ„产原值
  // createUser: useUserStore().nickName, // å½•入人
  createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // å½•入日期
    planRuntimeTime: dayjs().format("YYYY-MM-DD"), // å½•入日期
@@ -193,29 +155,11 @@
    form.unit = data.unit;
    form.number = 1;
    form.taxIncludingPriceUnit = data.taxIncludingPriceUnit;
    form.taxIncludingPriceTotal = data.taxIncludingPriceTotal;
    form.taxRate = data.taxRate;
    form.unTaxIncludingPriceTotal = data.unTaxIncludingPriceTotal;
    form.createTime = data.createTime;
  }
};
const mathNum = () => {
  if (!form.taxIncludingPriceUnit) {
    ElMessage.error("请输入单价");
    return;
  }
  form.taxIncludingPriceTotal = calculateTaxIncludeTotalPrice(
    form.taxIncludingPriceUnit,
    form.number
  );
  if (form.taxRate) {
    form.unTaxIncludingPriceTotal = calculateTaxExclusiveTotalPrice(
      form.taxIncludingPriceTotal,
      form.taxRate
    );
  }
};
// æ¸…除表单校验状态
const clearValidate = () => {
src/views/equipmentManagement/ledger/Modal.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog :title="modalOptions.title" v-model="visible" @close="close" draggable>
  <el-dialog :title="modalOptions.title" v-model="visible" @close="close">
    <Form ref="formRef"></Form>
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
src/views/equipmentManagement/ledger/index.vue
@@ -55,6 +55,7 @@
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus"> æ–°å¢ž </el-button>
          <el-button plain icon="Upload" @click="handleImport">导入</el-button>
          <el-button @click="handleOut" icon="download">导出</el-button>
          <el-button
            type="danger"
@@ -82,13 +83,58 @@
      </PIMTable>
    </div>
    <Modal ref="modalRef" @success="getTableData"></Modal>
    <el-dialog v-model="qrDialogVisible" title="二维码" width="300px" draggable>
    <el-dialog v-model="qrDialogVisible" title="二维码" width="300px">
      <div style="text-align:center;">
        <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" />
        <div style="margin-top:6px;font-size:14px;color:#333;">{{ qrRowData?.deviceName }}</div>
        <div style="margin:10px 0;">
          <el-button type="primary" @click="downloadQRCode">下载二维码图片</el-button>
        </div>
      </div>
    </el-dialog>
    <!-- ç”¨æˆ·å¯¼å…¥å¯¹è¯æ¡† -->
    <el-dialog
      :title="upload.title"
      v-model="upload.open"
      width="400px"
      append-to-body
    >
      <el-upload
        ref="uploadRef"
        :limit="1"
        accept=".xlsx, .xls"
        :headers="upload.headers"
        :action="upload.url + '?updateSupport=' + upload.updateSupport"
        :disabled="upload.isUploading"
        :before-upload="upload.beforeUpload"
        :on-progress="upload.onProgress"
        :on-success="upload.onSuccess"
        :on-error="upload.onError"
        :on-change="upload.onChange"
        :auto-upload="false"
        drag
      >
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <template #tip>
          <div class="el-upload__tip text-center">
            <span>仅允许导入xls、xlsx格式文件。</span>
            <el-link
              type="primary"
              :underline="false"
              style="font-size: 12px; vertical-align: baseline"
              @click="importTemplate"
              >下载模板</el-link
            >
          </div>
        </template>
      </el-upload>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
          <el-button @click="upload.open = false">取 æ¶ˆ</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
@@ -103,6 +149,7 @@
import dayjs from "dayjs";
import QRCode from "qrcode";
import { ref } from "vue";
import { getToken } from "@/utils/auth.js";
defineOptions({
  name: "设备台账",
@@ -171,24 +218,9 @@
      prop: "number",
    },
    {
      label: "含税单价",
      label: "资产原值",
      align: "center",
      prop: "taxIncludingPriceUnit",
    },
    {
      label: "含税总价",
      align: "center",
      prop: "taxIncludingPriceTotal",
    },
    {
      label: "税率",
      align: "center",
      prop: "taxRate",
    },
    {
      label: "不含税总价",
      align: "center",
      prop: "unTaxIncludingPriceTotal",
    },
    {
      label: "启用折旧",
@@ -231,6 +263,69 @@
        },
  ]
);
const upload = reactive({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(客户导入)
  open: false,
  // å¼¹å‡ºå±‚标题(客户导入)
  title: "",
  // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
  isUploading: false,
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/device/ledger/import",
  // æ–‡ä»¶ä¸Šä¼ å‰çš„回调
  beforeUpload: (file) => {
    console.log('文件即将上传', file);
    // å¯ä»¥åœ¨æ­¤å¤„做文件类型或大小校验
    const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
    if (!isValid) {
      proxy.$modal.msgError("只能上传 Excel æ–‡ä»¶");
    }
    return isValid;
  },
  // æ–‡ä»¶çŠ¶æ€æ”¹å˜æ—¶çš„å›žè°ƒ
  onChange: (file, fileList) => {
    console.log('文件状态改变', file, fileList);
  },
  // æ–‡ä»¶ä¸Šä¼ æˆåŠŸæ—¶çš„å›žè°ƒ
  onSuccess: (response, file, fileList) => {
    console.log('上传成功', response, file, fileList);
    upload.isUploading = false;
    if(response.code === 200){
      proxy.$modal.msgSuccess("文件上传成功");
      upload.open = false;
      proxy.$refs["uploadRef"].clearFiles();
      getTableData();
    }else if(response.code === 500){
      proxy.$modal.msgError(response.msg);
    }else{
      proxy.$modal.msgWarning(response.msg);
    }
  },
  // æ–‡ä»¶ä¸Šä¼ å¤±è´¥æ—¶çš„回调
  onError: (error, file, fileList) => {
    console.error('上传失败', error, file, fileList);
    upload.isUploading = false;
    proxy.$modal.msgError("文件上传失败");
  },
  // æ–‡ä»¶ä¸Šä¼ è¿›åº¦å›žè°ƒ
  onProgress: (event, file, fileList) => {
    console.log('上传中...', event.percent);
  }
});
/** æäº¤ä¸Šä¼ æ–‡ä»¶ */
const submitFileForm = () => {
  upload.isUploading = true;
  proxy.$refs["uploadRef"].submit();
}
/** ä¸‹è½½æ¨¡æ¿ */
const importTemplate = () => {
  proxy.download("/device/ledger/downloadTemplate", {}, "设备台账模板.xlsx");
}
// å¤šé€‰åŽåšä»€ä¹ˆ
const handleSelectionChange = (selectionList) => {
@@ -275,6 +370,11 @@
  }
  getTableData();
};
/** å¯¼å…¥æŒ‰é’®æ“ä½œ */
const handleImport = () => {
  upload.title = "设备台账导入";
  upload.open = true;
}
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
@@ -299,10 +399,44 @@
};
const downloadQRCode = () => {
  const a = document.createElement("a");
  a.href = qrCodeUrl.value;
  a.download = `${qrRowData.value.deviceName || "二维码"}.png`;
  a.click();
  const name = qrRowData.value?.deviceName || "二维码";
  const img = new Image();
  img.src = qrCodeUrl.value;
  img.onload = () => {
    const padding = 10;
    const qrSize = 200;
    const textHeight = 24; // space for text
    const width = qrSize + padding * 2;
    const height = qrSize + padding * 2 + textHeight;
    const canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext("2d");
    // background
    ctx.fillStyle = "#ffffff";
    ctx.fillRect(0, 0, width, height);
    // draw QR centered
    ctx.drawImage(img, padding, padding, qrSize, qrSize);
    // draw name centered below
    ctx.fillStyle = "#333";
    ctx.font = "14px Arial";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    const maxTextWidth = width - padding * 2;
    let displayName = name;
    // ellipsis if too long
    while (ctx.measureText(displayName).width > maxTextWidth && displayName.length > 0) {
      displayName = displayName.slice(0, -1);
    }
    if (displayName !== name) displayName = displayName + "…";
    ctx.fillText(displayName, width / 2, qrSize + padding + textHeight / 2);
    const dataUrl = canvas.toDataURL("image/png");
    const a = document.createElement("a");
    a.href = dataUrl;
    a.download = `${name}.png`;
    a.click();
  };
};
onMounted(() => {
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -4,7 +4,6 @@
            v-model="dialogFormVisible"
            title="计量器具"
            width="50%"
            draggable
            @close="closeDia"
        >
            <el-form
@@ -68,9 +67,7 @@
                            <el-select
                                v-model="form.userId"
                                placeholder="请选择"
                filterable
                default-first-option
                :reserve-keyword="false"
                                disabled
                                clearable
                            >
                                <el-option
@@ -132,7 +129,6 @@
import {getToken} from "@/utils/auth.js";
import {ledgerRecordUpdate, ledgerRecordVerifying} from "@/api/equipmentManagement/calibration.js";
import {delLedgerFile} from "@/api/salesManagement/salesLedger.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -252,6 +248,14 @@
    dialogFormVisible.value = false;
    emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
    openDialog,
});
src/views/equipmentManagement/measurementEquipment/components/formDia.vue
@@ -4,7 +4,6 @@
        v-model="dialogFormVisible"
        title="计量器具"
        width="50%"
                draggable
        @close="closeDia"
    >
            <el-form
@@ -65,9 +64,7 @@
                                v-model="form.userId"
                                placeholder="请选择"
                                clearable
                filterable
                default-first-option
                :reserve-keyword="false"
                                disabled
                            >
                                <el-option
                                    v-for="item in userList"
@@ -127,7 +124,6 @@
import {afterSalesServiceAdd, afterSalesServiceUpdate} from "@/api/customerService/index.js";
import {getToken} from "@/utils/auth.js";
import {measuringInstrumentAdd, measuringInstrumentUpdate} from "@/api/equipmentManagement/measurementEquipment.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -238,6 +234,14 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
  openDialog,
});
src/views/equipmentManagement/measurementEquipment/index.vue
@@ -148,13 +148,13 @@
                    openCalibrationDia("verifying", row);
                },
            },
            // {
            //     name: "附件",
            //     type: "text",
            //     clickFun: (row) => {
      //     openFilesFormDia(row);
            //     },
            // },
            {
                name: "附件",
                type: "text",
                clickFun: (row) => {
          openFilesFormDia(row);
                },
            },
        ],
    },
]);
@@ -221,6 +221,12 @@
const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.userId !== userStore.id);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
src/views/equipmentManagement/repair/Form/MaintainForm.vue
ÎļþÒÑɾ³ý
src/views/equipmentManagement/repair/Form/RepairForm.vue
ÎļþÒÑɾ³ý
src/views/equipmentManagement/repair/Modal/ApproveModal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
<template>
  <FormDialog
    v-model="visible"
    title="报修审批"
    width="800px"
    @confirm="handleSubmit"
    @cancel="handleClose"
    @close="handleClose"
  >
    <el-descriptions :column="2" border>
      <el-descriptions-item label="设备名称">
        {{ detail.deviceName || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="规格型号">
        {{ detail.deviceModel || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="报修日期">
        {{ detail.repairTime || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="报修人">
        {{ detail.repairName || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="审批人">
        {{ detail.auditName || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="当前状态">
        {{ statusText(detail.status) }}
      </el-descriptions-item>
      <el-descriptions-item label="故障现象" :span="2">
        {{ detail.remark || "-" }}
      </el-descriptions-item>
    </el-descriptions>
    <div style="margin-top: 16px">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
        <el-form-item label="审批结果" prop="decision">
          <el-radio-group v-model="form.decision">
            <el-radio :value="0">通过</el-radio>
            <el-radio :value="3">不通过</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="监督人" prop="supervisoryName">
          <el-input v-model="form.supervisoryName" placeholder="请输入监督人" clearable style="width: 100%" />
        </el-form-item>
      </el-form>
    </div>
  </FormDialog>
</template>
<script setup>
import { nextTick, ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { editRepair, getRepairById } from "@/api/equipmentManagement/repair";
defineOptions({
  name: "报修审批弹窗",
});
const emits = defineEmits(["ok"]);
const visible = ref(false);
const loading = ref(false);
const id = ref();
const detail = ref({});
const formRef = ref();
const form = ref({
  decision: undefined, // 0 é€šè¿‡ 3 ä¸é€šè¿‡
  supervisoryName: undefined, // ç›‘督人
});
const rules = {
  decision: [{ required: true, message: "请选择审批结果", trigger: "change" }],
  supervisoryName: [{ required: true, message: "请选择监督人", trigger: "change" }],
};
const statusText = (status) => {
  const map = {
    0: "待维修",
    1: "完结",
    2: "待审核",
    3: "审核不通过",
  };
  return map[status] ?? "-";
};
const loadDetail = async (repairId) => {
  const { data } = await getRepairById(repairId);
  detail.value = data ?? {};
};
const open = async (repairId) => {
  id.value = repairId;
  visible.value = true;
  await nextTick();
  await loadDetail(repairId);
  form.value.decision = undefined;
  form.value.supervisoryName = undefined;
};
const handleClose = () => {
  visible.value = false;
  id.value = undefined;
  detail.value = {};
  form.value.decision = undefined;
  form.value.supervisoryName = undefined;
};
const updateStatus = async (status) => {
  loading.value = true;
  try {
    const { code } = await editRepair({ id: id.value, status, supervisoryName: form.value.supervisoryName });
    if (code === 200) {
      ElMessage.success("审批成功");
      emits("ok");
      handleClose();
    }
  } finally {
    loading.value = false;
  }
};
const handleSubmit = async () => {
  if (detail.value?.status !== 2) {
    ElMessage.warning("仅待审核状态可审批");
    return;
  }
  await formRef.value?.validate(async (valid) => {
    if (!valid) return;
    const isApprove = form.value.decision === 0;
    ElMessageBox.confirm(`确认审批${isApprove ? "通过" : "不通过"}?`, "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => updateStatus(form.value.decision));
  });
};
defineExpose({ open });
</script>
<style scoped></style>
src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -1,53 +1,116 @@
<template>
  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr" draggable>
    <MaintainForm ref="maintainFormRef" />
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
                {{ modalOptions.confirmText }}
            </el-button>
      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
    </template>
  </el-dialog>
  <FormDialog
    v-model="visible"
    :title="'设备维修'"
    width="500px"
    @confirm="sendForm"
    @cancel="handleCancel"
    @close="handleClose"
  >
    <el-form :model="form" :rules="rules" label-width="110px" ref="formRef">
      <el-form-item label="维修人" prop="maintenanceName">
        <el-input v-model="form.maintenanceName" placeholder="请输入维修人" />
      </el-form-item>
      <el-form-item label="维修结果" prop="maintenanceResult">
        <el-input v-model="form.maintenanceResult" placeholder="请输入维修结果" />
      </el-form-item>
            <el-form-item label="本次维修金额" prop="repairPrice">
                <el-input-number v-model="form.repairPrice" :min="0" :precision="2" style="width: 100%" />
            </el-form-item>
      <el-form-item label="维修日期" prop="maintenanceTime">
        <el-date-picker
          v-model="form.maintenanceTime"
          placeholder="请选择维修日期"
          format="YYYY-MM-DD HH:mm:ss"
          value-format="YYYY-MM-DD HH:mm:ss"
          type="datetime"
          clearable
          style="width: 100%"
        />
      </el-form-item>
    </el-form>
  </FormDialog>
</template>
<script setup>
import { useModal } from "@/hooks/useModal";
import MaintainForm from "../Form/MaintainForm.vue";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { addMaintain } from "@/api/equipmentManagement/repair";
import useFormData from "@/hooks/useFormData";
import useUserStore from "@/store/modules/user";
import dayjs from "dayjs";
import { ElMessage } from "element-plus";
defineOptions({
  name: "维修模态框",
});
const maintainFormRef = ref();
const emits = defineEmits(["ok"]);
const {
  id,
  visible,
  loading,
  openModal,
  modalOptions,
  handleConfirm,
  closeModal,
} = useModal({ title: "设备维修" });
// ä¿å­˜æŠ¥ä¿®è®°å½•çš„id
const repairId = ref();
const visible = ref(false);
const loading = ref(false);
const formRef = ref();
const userStore = useUserStore();
const { form, resetForm } = useFormData({
  maintenanceName: undefined, // ç»´ä¿®åç§°
  maintenanceResult: undefined, // ç»´ä¿®ç»“æžœ
  maintenanceTime: undefined, // ç»´ä¿®æ—¥æœŸ
    repairPrice: undefined, // ç»´ä¿®é‡‘额
  status: 0,
});
const rules = {
  maintenanceName: [{ required: true, message: "请输入维修人", trigger: "blur" }],
  maintenanceResult: [{ required: true, message: "请输入维修结果", trigger: "blur" }],
  repairPrice: [{ required: true, message: "请输入本次维修金额", trigger: "change" }],
  maintenanceTime: [{ required: true, message: "请选择维修日期", trigger: "change" }],
};
const setForm = (data) => {
  form.maintenanceName = data.maintenanceName ?? userStore.nickName;
  form.maintenanceResult = data.maintenanceResult;
  form.maintenanceTime =
    data.maintenanceTime
      ? dayjs(data.maintenanceTime).format("YYYY-MM-DD HH:mm:ss")
      : dayjs().format("YYYY-MM-DD HH:mm:ss");
  form.status = 2; // ç»´ä¿®åŽå›ºå®šè¿›å…¥å¾…审核(不在界面展示)
};
const sendForm = async () => {
  loading.value = true;
  const form = await maintainFormRef.value.getForm();
  const { code } = await addMaintain({ id: id.value, ...form });
  if (code == 200) {
    emits("ok");
    maintainFormRef.value.resetForm();
    closeModal();
  }
  loading.value = false;
    await formRef.value.validate(async (valid) => {
        if (!valid) return;
        loading.value = true;
        try {
            const { code } = await addMaintain({ id: repairId.value, ...form });
            if (code == 200) {
                ElMessage.success("维修成功");
                emits("ok");
                resetForm();
                visible.value = false;
            }
        } finally {
            loading.value = false;
        }
    })
};
const handleCancel = () => {
  resetForm();
  visible.value = false;
};
const handleClose = () => {
  resetForm();
  visible.value = false;
};
const open = async (id, row) => {
  openModal(id);
  repairId.value = id; // ä¿å­˜æŠ¥ä¿®è®°å½•çš„id
  visible.value = true;
  await nextTick();
  maintainFormRef.value.setForm(row);
  setForm(row);
};
defineExpose({
src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -1,24 +1,113 @@
<template>
  <el-dialog v-model="visible" :title="modalOptions.title" @close="close" draggable>
    <RepairForm ref="repairFormRef" :id="id" />
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
                {{ modalOptions.confirmText }}
            </el-button>
      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
    </template>
  </el-dialog>
  <FormDialog
    v-model="visible"
    :title="id ? '编辑设备报修' : '新增设备报修'"
    width="800px"
    @confirm="sendForm"
    @cancel="handleCancel"
    @close="handleClose"
  >
    <el-form :model="form" :rules="rules" label-width="100px" ref="formRef">
      <el-row>
        <el-col :span="12">
          <el-form-item label="设备名称">
            <el-select v-model="form.deviceLedgerId" @change="setDeviceModel" filterable>
              <el-option
                v-for="(item, index) in deviceOptions"
                :key="index"
                :label="item.deviceName"
                :value="item.id"
              ></el-option>
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="规格型号">
            <el-input
              v-model="form.deviceModel"
              placeholder="请输入规格型号"
              disabled
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="报修日期">
            <el-date-picker
              v-model="form.repairTime"
              placeholder="请选择报修日期"
              format="YYYY-MM-DD"
              value-format="YYYY-MM-DD"
              type="date"
              clearable
              style="width: 100%"
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="报修人">
            <el-input v-model="form.repairName" placeholder="请输入报修人" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="审批人" prop="auditName">
            <el-select
              v-model="form.auditName"
              placeholder="请选择审批人"
              filterable
              clearable
              style="width: 100%"
            >
              <el-option
                v-for="user in userOptions"
                :key="user.userId"
                :label="user.nickName"
                :value="user.nickName"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row v-if="id">
        <el-col :span="12">
          <el-form-item label="报修状态">
            <el-select v-model="form.status">
              <el-option label="待维修" :value="0"></el-option>
              <el-option label="完结" :value="1"></el-option>
              <el-option label="待审核" :value="2"></el-option>
              <el-option label="审核不通过" :value="3"></el-option>
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="24">
          <el-form-item label="故障现象">
            <el-input
              v-model="form.remark"
              :rows="2"
              type="textarea"
              placeholder="请输入故障现象"
            />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </FormDialog>
</template>
<script setup>
import { useModal } from "@/hooks/useModal";
import RepairForm from "../Form/RepairForm.vue";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import {
  addRepair,
  editRepair,
  getRepairById,
} from "@/api/equipmentManagement/repair";
import { ElMessage } from "element-plus";
import dayjs from "dayjs";
import useFormData from "@/hooks/useFormData";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import useUserStore from "@/store/modules/user";
import { userListNoPageByTenantId } from "@/api/system/user";
defineOptions({
  name: "设备报修弹窗",
@@ -26,63 +115,101 @@
const emits = defineEmits(["ok"]);
const repairFormRef = ref();
const {
  id,
  visible,
  loading,
  openModal,
  modalOptions,
  handleConfirm,
  closeModal,
} = useModal({ title: "设备报修" });
const id = ref();
const visible = ref(false);
const loading = ref(false);
const formRef = ref();
const userStore = useUserStore();
const deviceOptions = ref([]);
const userOptions = ref([]);
const loadDeviceName = async () => {
  const { data } = await getDeviceLedger();
  deviceOptions.value = data;
};
const loadUserOptions = async () => {
  const res = await userListNoPageByTenantId();
  userOptions.value = res?.data ?? [];
};
const { form, resetForm } = useFormData({
  deviceLedgerId: undefined, // è®¾å¤‡Id
  deviceName: undefined, // è®¾å¤‡åç§°
  deviceModel: undefined, // è§„格型号
  repairTime: dayjs().format("YYYY-MM-DD"), // æŠ¥ä¿®æ—¥æœŸï¼Œé»˜è®¤å½“天
  repairName: userStore.nickName, // æŠ¥ä¿®äºº
  auditName: undefined, // å®¡æ‰¹äºº
  remark: undefined, // æ•…障现象
  status: 0, // æŠ¥ä¿®çŠ¶æ€
});
const rules = {
  auditName: [
    { required: true, message: "请选择审批人", trigger: "change" },
  ],
};
const setDeviceModel = (deviceId) => {
  const option = deviceOptions.value.find((item) => item.id === deviceId);
  form.deviceModel = option.deviceModel;
};
const setForm = (data) => {
  form.deviceLedgerId = data.deviceLedgerId;
  form.deviceName = data.deviceName;
  form.deviceModel = data.deviceModel;
  form.repairTime = data.repairTime;
  form.repairName = data.repairName;
  form.auditName = data.auditName;
  form.remark = data.remark;
  form.status = data.status;
};
const sendForm = async () => {
  try {
    // å¼€å§‹åŠ è½½
  await formRef.value?.validate(async (valid) => {
    if (!valid) return;
    loading.value = true;
    // æäº¤è¡¨å•并获取校验结果
    const submitStatus = await repairFormRef.value.submitForm();
    if (!submitStatus) {
      // å¦‚果表单验证失败,取消加载状态
      loading.value = false;
      return;
    }
    // èŽ·å–è¡¨å•æ•°æ®
    const form = await repairFormRef.value.getForm();
    // æ ¹æ®æ˜¯å¦æœ‰ID决定是编辑还是新增
    const { code } = id.value
    try {
      const { code } = id.value
        ? await editRepair({ id: unref(id), ...form })
        : await addRepair(form);
    if (code === 200) {
      ElMessage.success(`${id ? "编辑" : "新增"}报修成功`);
      emits("ok");
      if (code == 200) {
        ElMessage.success(`${id.value ? "编辑" : "新增"}报修成功`);
        visible.value = false;
        emits("ok");
      }
    } finally {
      loading.value = false;
    }
  } catch (error) {
  } finally {
    // æ— è®ºæˆåŠŸè¿˜æ˜¯å¤±è´¥ï¼Œéƒ½å–æ¶ˆåŠ è½½çŠ¶æ€
    loading.value = false;
    closeModal();
  }
  });
};
const handleCancel = () => {
  resetForm();
  visible.value = false;
};
const handleClose = () => {
  resetForm();
  visible.value = false;
};
const openAdd = async () => {
  openModal();
  id.value = undefined;
  visible.value = true;
  await nextTick();
  await repairFormRef.value.loadDeviceName();
  await Promise.all([loadDeviceName(), loadUserOptions()]);
};
const openEdit = async (id) => {
  const { data } = await getRepairById(id);
  openModal(id);
const openEdit = async (editId) => {
  const { data } = await getRepairById(editId);
  id.value = editId;
  visible.value = true;
  await nextTick();
  await repairFormRef.value.loadDeviceName();
  await repairFormRef.value.setForm(data);
};
const close = () => {
  repairFormRef.value.resetForm();
  closeModal();
  await Promise.all([loadDeviceName(), loadUserOptions()]);
  setForm(data);
};
defineExpose({
@@ -90,3 +217,5 @@
  openEdit,
});
</script>
<style lang="scss" scoped></style>
src/views/equipmentManagement/repair/index.vue
@@ -7,7 +7,6 @@
            style="width: 240px"
            placeholder="请输入设备名称"
            clearable
            :prefix-icon="Search"
            @change="getTableData"
        />
      </el-form-item>
@@ -15,9 +14,8 @@
        <el-input
            v-model="filters.deviceModel"
            style="width: 240px"
            placeholder="请选择规格型号"
            placeholder="请输入规格型号"
            clearable
            :prefix-icon="Search"
            @change="getTableData"
        />
      </el-form-item>
@@ -27,7 +25,6 @@
            style="width: 240px"
            placeholder="请输入故障现象"
            clearable
            :prefix-icon="Search"
            @change="getTableData"
        />
      </el-form-item>
@@ -37,7 +34,6 @@
            style="width: 240px"
            placeholder="请输入维修人"
            clearable
            :prefix-icon="Search"
            @change="getTableData"
        />
      </el-form-item>
@@ -68,15 +64,6 @@
      <div class="actions">
        <el-text class="mx-1" size="large">设备报修</el-text>
        <div>
          <el-button
            type="primary"
            icon="Plus"
            :disabled="multipleList.length !== 1 || multipleList[0]?.status !== 1"
            @click="addMaintain"
          >
            æ–°å¢žç»´ä¿®
          </el-button>
          <el-button type="success" icon="Van" @click="addRepair">
            æ–°å¢žæŠ¥ä¿®
          </el-button>
@@ -86,79 +73,94 @@
          <el-button
            type="danger"
            icon="Delete"
            :disabled="multipleList.length <= 0"
            @click="delRepairByIds(multipleList)"
            :disabled="multipleList.length <= 0 || hasFinishedStatus"
            @click="delRepairByIds(multipleList.map((item) => item.id))"
          >
            æ‰¹é‡åˆ é™¤
          </el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        isSelection
        :column="columns"
        :tableData="dataList"
        :page="{
          rowKey="id"
          isSelection
          :column="columns"
          :tableData="dataList"
          :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @selection-change="handleSelectionChange"
        @pagination="changePage"
          @selection-change="handleSelectionChange"
          @pagination="changePage"
      >
        <template #statusRef="{ row }">
          <el-tag v-if="row.status === 5" type="danger">维修失败</el-tag>
          <el-tag v-if="row.status === 4" type="danger">维修成功</el-tag>
          <el-tag v-if="row.status === 3" type="danger">维修中</el-tag>
          <el-tag v-if="row.status === 2" type="danger">审核失败</el-tag>
          <el-tag v-if="row.status === 1" type="success">审核通过</el-tag>
          <el-tag v-if="row.status === 0" type="warning">审核中</el-tag>
          <el-tag v-if="row.status === 0" type="warning">待维修</el-tag>
          <el-tag v-else-if="row.status === 1" type="success">完结</el-tag>
          <el-tag v-else-if="row.status === 2" type="info">待审核</el-tag>
          <el-tag v-else-if="row.status === 3" type="danger">审核不通过</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button
            type="primary"
            text
            icon="editPen"
            link
            :disabled="row.status === 1"
            @click="editRepair(row.id)"
            :disabled="row.status !== 0"
          >
            ç¼–辑
          </el-button>
          <el-button
            type="danger"
            text
            icon="delete"
            @click="delRepairByIds(row)"
            type="warning"
            link
            :disabled="row.status !== 2"
            @click="openApprove(row.id)"
          >
            å®¡æ‰¹
          </el-button>
          <el-button
            type="success"
            link
            :disabled="row.status !== 0"
            @click="addMaintain(row)"
          >
            ç»´ä¿®
          </el-button>
          <el-button
            type="danger"
            link
            :disabled="row.status === 1"
            @click="delRepairByIds(row.id)"
          >
            åˆ é™¤
          </el-button>
        </template>
      </PIMTable>
    </div>
    <RepairModal ref="repairModalRef" @ok="getTableData" />
    <MaintainModal ref="maintainModalRef" @ok="getTableData" />
    <RepairModal ref="repairModalRef" @ok="getTableData"/>
    <MaintainModal ref="maintainModalRef" @ok="getTableData"/>
    <ApproveModal ref="approveModalRef" @ok="getTableData"/>
  </div>
</template>
<script setup>
import { usePaginationApi } from "@/hooks/usePaginationApi";
import { getRepairPage, delRepair } from "@/api/equipmentManagement/repair";
import { onMounted, getCurrentInstance } from "vue";
import { onMounted, getCurrentInstance, computed } from "vue";
import {usePaginationApi} from "@/hooks/usePaginationApi";
import {getRepairPage, delRepair} from "@/api/equipmentManagement/repair";
import RepairModal from "./Modal/RepairModal.vue";
import { ElMessageBox, ElMessage } from "element-plus";
import {ElMessageBox, ElMessage} from "element-plus";
import dayjs from "dayjs";
import MaintainModal from "./Modal/MaintainModal.vue";
import ApproveModal from "./Modal/ApproveModal.vue";
defineOptions({
  name: "设备报修",
});
const { proxy } = getCurrentInstance();
const {proxy} = getCurrentInstance();
// æ¨¡æ€æ¡†å®žä¾‹
const repairModalRef = ref();
const maintainModalRef = ref();
const approveModalRef = ref();
// è¡¨æ ¼å¤šé€‰æ¡†é€‰ä¸­é¡¹
const multipleList = ref([]);
@@ -173,85 +175,87 @@
  resetFilters,
  onCurrentChange,
} = usePaginationApi(
  getRepairPage,
  {
    deviceName: undefined,
    deviceModel: undefined,
    remark: undefined,
    maintenanceName: undefined,
    repairTimeStr: undefined,
    maintenanceTimeStr: undefined,
  },
  [
    getRepairPage,
    {
      label: "设备名称",
      align: "center",
      prop: "deviceName",
      deviceName: undefined,
      deviceModel: undefined,
      remark: undefined,
      maintenanceName: undefined,
      repairTimeStr: undefined,
      maintenanceTimeStr: undefined,
    },
    {
      label: "规格型号",
      align: "center",
      prop: "deviceModel",
    },
    {
      label: "报修日期",
      align: "center",
      prop: "repairTime",
      formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
    },
    {
      label: "报修人",
      align: "center",
      prop: "repairName",
    },
    {
      label: "故障现象",
      align: "center",
      prop: "remark",
    },
    {
      label: "维修人",
      align: "center",
      prop: "maintenanceName",
    },
    {
      label: "维修结果",
      align: "center",
      prop: "maintenanceResult",
    },
    {
      label: "维修日期",
      align: "center",
      prop: "maintenanceTime",
      formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
    },
    {
      label: "状态",
      align: "center",
      prop: "status",
      dataType: "slot",
      slot: "statusRef",
    },
    {
      fixed: "right",
      label: "操作",
      dataType: "slot",
      slot: "operation",
      align: "center",
      width: "200px",
    },
  ]
    [
      {
        label: "设备名称",
        align: "center",
        prop: "deviceName",
      },
      {
        label: "规格型号",
        align: "center",
        prop: "deviceModel",
      },
      {
        label: "报修日期",
        align: "center",
        prop: "repairTime",
        formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
      },
      {
        label: "报修人",
        align: "center",
        prop: "repairName",
      },
      {
        label: "故障现象",
        align: "center",
        prop: "remark",
      },
      {
        label: "维修人",
        align: "center",
        prop: "maintenanceName",
      },
      {
        label: "维修结果",
        align: "center",
        prop: "maintenanceResult",
      },
      {
        label: "维修日期",
        align: "center",
        prop: "maintenanceTime",
        formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
      },
      { prop: "auditName", label: "审核人", width: 120 },
    { prop: "supervisoryName", label: "监督人", width: 120 },
      {
        label: "状态",
        align: "center",
        prop: "status",
        dataType: "slot",
        slot: "statusRef",
      },
      {
        fixed: "right",
        label: "操作",
        dataType: "slot",
        slot: "operation",
        align: "center",
        width: "300px",
      },
    ]
);
// type === 1 ç»´ä¿® 2报修间
const handleDateChange = (value,type) => {
const handleDateChange = (value, type) => {
  filters.maintenanceTimeStr = null
  filters.c = null
  if(type === 1){
  if (type === 1) {
    if (value) {
      filters.maintenanceTimeStr = dayjs(value).format("YYYY-MM-DD");
    }
  }else{
  } else {
    if (value) {
      filters.repairTimeStr = dayjs(value).format("YYYY-MM-DD");
    }
@@ -264,6 +268,11 @@
  multipleList.value = selectionList;
};
// æ£€æŸ¥é€‰ä¸­çš„记录中是否有完结状态的
const hasFinishedStatus = computed(() => {
  return multipleList.value.some(item => item.status === 1)
})
// æ–°å¢žæŠ¥ä¿®
const addRepair = () => {
  repairModalRef.value.openAdd();
@@ -275,36 +284,33 @@
};
// æ–°å¢žç»´ä¿®
const addMaintain = () => {
  const row = multipleList.value[0];
const addMaintain = (row) => {
  maintainModalRef.value.open(row.id, row);
};
const changePage = ({ page, limit }) => {
    pagination.currentPage = page;
    pagination.pageSize = limit;
    onCurrentChange(page);
// å®¡æ‰¹
const openApprove = (id) => {
  approveModalRef.value.open(id);
};
const changePage = ({page, limit}) => {
  pagination.currentPage = page;
  pagination.pageSize = limit;
  onCurrentChange(page);
};
// å•行删除
const delRepairByIds = async (ids) => {
  let isDel = false
  if(Array.isArray(ids)){
    ids.forEach((item)=>{
      if(item.status !== 0){
        isDel = true
      }
    })
  }else{
    if(ids.status !== 0){
      isDel = true
    }
  }
  // æ£€æŸ¥æ˜¯å¦æœ‰å®Œç»“状态的记录
  const idsArray = Array.isArray(ids) ? ids : [ids];
  const hasFinished = idsArray.some(id => {
    const record = dataList.value.find(item => item.id === id);
    return record && record.status === 1;
  });
  if(isDel){
    ElMessage.warning("只能删除审核中的报修数据");
    return
  if (hasFinished) {
    ElMessage.warning('不能删除状态为完结的记录');
    return;
  }
  ElMessageBox.confirm("确认删除报修数据, æ­¤æ“ä½œä¸å¯é€†?", "警告", {
@@ -312,17 +318,10 @@
    cancelButtonText: "取消",
    type: "warning",
  }).then(async () => {
    let idsList = ""
    if(Array.isArray(ids)){
      idsList = multipleList.value.map((item) => item.id);
      console.log(idsList)
    }else{
      idsList = ids.id
    }
    const { code } = await delRepair(idsList);
    const {code} = await delRepair(ids);
    if (code === 200) {
      ElMessage.success("删除成功");
      await getTableData();
      getTableData();
    }
  });
};
@@ -334,12 +333,12 @@
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/device/repair/export", {}, "设备报修.xlsx");
    })
    .catch(() => {
      ElMessage.info("已取消");
    });
      .then(() => {
        proxy.download("/device/repair/export", {}, "设备报修.xlsx");
      })
      .catch(() => {
        ElMessage.info("已取消");
      });
};
onMounted(() => {
@@ -351,6 +350,7 @@
.table_list {
  margin-top: unset;
}
.actions {
  display: flex;
  justify-content: space-between;
src/views/equipmentManagement/spareParts/index.vue
@@ -19,47 +19,21 @@
                <el-button type="primary" @click="addCategory" >新增</el-button>
            </div>
        </div>
    <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="renderTableData"
        :tableLoading="loading"
        :page="pagination"
        :isShowPagination="true"
        @pagination="handleSizeChange"
    >
      <template #status="{ row }">
        <el-tag type="success" size="small">{{ row.status }}</el-tag>
      </template>
    </PIMTable>
    
    <div class="table_list">
      <el-table
        v-loading="loading"
        :data="renderTableData"
        style="width: 100%; margin-top: 10px;"
        border
        row-key="id"
      >
      <el-table-column prop="deviceNameStr" label="设备名称"  width="300"></el-table-column>
        <el-table-column prop="name" label="备件名称" width="200"></el-table-column>
        <el-table-column prop="sparePartsNo" label="备件编号" width="200"></el-table-column>
        <el-table-column prop="status" label="状态" width="100">
          <template #default="{ row }">
            <el-tag type="success" size="small">{{ row.status }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="price" label="ä»·æ ¼" width="140"></el-table-column>
        <el-table-column prop="description" label="描述" width="150"></el-table-column>
        <el-table-column label="操作" width="150" fixed="right" align="center">
          <template #default="{ row }">
            <el-button
              link
                            type="primary"
              @click="() => editCategory(row)"
              :disabled="loading"
            >
              ç¼–辑
            </el-button>
            <el-button
                            link
              @click="() => deleteCategory(row.id)"
              style="color: #f56c6c;"
              :disabled="loading"
            >
              åˆ é™¤
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <el-dialog title="分类管理" v-model="dialogVisible" width="60%">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-form-item label="设备" prop="deviceLedgerIds">
@@ -85,6 +59,9 @@
        </el-form-item>
        <el-form-item label="备件编号" prop="sparePartsNo">
          <el-input v-model="form.sparePartsNo"></el-input>
        </el-form-item>
        <el-form-item label="数量" prop="quantity">
          <el-input type="number" v-model="form.quantity"></el-input>
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-select v-model="form.status" placeholder="请选择状态">
@@ -143,6 +120,66 @@
const queryParams = reactive({
  name: ''
});
// åˆ†é¡µå‚æ•°
const pagination = reactive({
  current: 1,
  size: 10,
  total: 0
});
const columns = ref([
  {
    label: "设备名称",
    prop: "deviceNameStr",
  },
  {
    label: "备件名称",
    prop: "name",
  },
  {
    label: "备件编号",
    prop: "sparePartsNo",
  },
  {
    label: "状态",
    prop: "status",
    slot: "status",
    dataType: "slot",
  },
  {
    label: "ä»·æ ¼",
    prop: "price",
  },
  {
    label: "数量",
    prop: "quantity",
  },
  {
    label: "描述",
    prop: "description",
  },
  {
    label: "操作",
    prop: "operation",
    width: 150,
    fixed: 'right',
    align: "center",
    dataType: "action",
    operation: [
      {
        name: "编辑",
        clickFun: (row) => {
          editCategory(row)
        },
      },
      {
        name: "删除",
        clickFun: (row) => {
          deleteCategory(row.id)
        },
      },
    ],
  },
]);
// è¡¨å•数据
const form = reactive({
  id:'',
@@ -161,6 +198,9 @@
  ],
  sparePartsNo: [
    { required: true, message: '请输入备件编号', trigger: 'blur' }
  ],
  quantity:[
    { required: true, message: '请输入数量', trigger: 'blur' }
  ],
  status: [
    { required: true, message: '请选择状态', trigger: 'change' }
@@ -208,7 +248,10 @@
const fetchListData = async () => {
  loading.value = true;
  try {
    const params = {};
    const params = {
      current: pagination.current,
      size: pagination.size
    };
    if (queryParams.name) {
      params.name = queryParams.name;
    }
@@ -216,6 +259,7 @@
    if (res.code === 200) {
      renderTableData.value = res.data.records || [];
      categories.value = res.data.records || [];
      pagination.total = res.data.total || 0;
    }
  } catch (error) {
        loading.value = false;
@@ -226,12 +270,27 @@
// æŸ¥è¯¢
const handleQuery = () => {
  pagination.current = 1;
  fetchListData();
}
// é‡ç½®æŸ¥è¯¢
const resetQuery = () => {
  queryParams.name = '';
  pagination.current = 1;
  fetchListData();
}
// åˆ†é¡µå¤§å°æ”¹å˜
const handleSizeChange = (size) => {
  pagination.size = size;
  pagination.current = 1;
  fetchListData();
}
// å½“前页改变
const handleCurrentChange = (current) => {
  pagination.current = current;
  fetchListData();
}
@@ -254,6 +313,7 @@
  form.status = '';
  form.description = '';
  form.deviceLedgerIds = [];
  form.quantity = undefined;
  form.price = null;
  operationType.value = 'add'
  dialogVisible.value = true;
@@ -366,6 +426,13 @@
  margin-top: unset;
}
.pagination-container {
  margin-top: 20px;
  display: flex;
  justify-content: flex-end;
  padding: 16px 0;
}
.el-table__header-wrapper th {
  background-color: #f5f7fa;
  font-weight: 600;
src/views/equipmentManagement/upkeep/Form/ApproveModal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,144 @@
<template>
  <FormDialog
    v-model="visible"
    title="定时任务审批"
    width="800px"
    @confirm="handleSubmit"
    @cancel="handleClose"
    @close="handleClose"
  >
    <el-descriptions :column="2" border>
      <el-descriptions-item label="任务名称">
        {{ detail.taskName || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="规格型号">
        {{ detail.deviceModel || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="频次">
        {{ frequencyText(detail.frequencyType) }}
      </el-descriptions-item>
      <el-descriptions-item label="开始日期与时间">
        {{ detail.frequencyDetail || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="登记人">
        {{ detail.registrant || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="登记日期">
        {{ detail.registrationDate || "-" }}
      </el-descriptions-item>
      <el-descriptions-item label="当前状态">
        {{ statusText(detail.status) }}
      </el-descriptions-item>
      <el-descriptions-item label="备注" :span="2">
        {{ detail.remarks || "-" }}
      </el-descriptions-item>
    </el-descriptions>
    <div style="margin-top: 16px">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
        <el-form-item label="审批结果" prop="decision">
          <el-radio-group v-model="form.decision">
            <el-radio label="审核通过">审核通过</el-radio>
            <el-radio label="审核不通过">审核不通过</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="监督人" prop="supervisoryName">
          <el-input v-model="form.supervisoryName" placeholder="请输入监督人" clearable style="width: 100%" />
        </el-form-item>
      </el-form>
    </div>
  </FormDialog>
</template>
<script setup>
import { nextTick, ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { deviceMaintenanceTaskEdit } from "@/api/equipmentManagement/upkeep";
defineOptions({
  name: "定时任务审批弹窗",
});
const emits = defineEmits(["ok"]);
const visible = ref(false);
const loading = ref(false);
const detail = ref({});
const formRef = ref();
const form = ref({
  decision: undefined, // å®¡æ ¸é€šè¿‡ / å®¡æ ¸ä¸é€šè¿‡
  supervisoryName: undefined, // ç›‘督人
});
const rules = {
  decision: [{ required: true, message: "请选择审批结果", trigger: "change" }],
  supervisoryName: [{ required: true, message: "请选择监督人", trigger: "change" }],
};
const statusText = (status) => status || "-";
const frequencyText = (type) => {
  const map = {
    DAILY: "每日",
    WEEKLY: "每周",
    MONTHLY: "每月",
    QUARTERLY: "季度",
  };
  return map[type] ?? "-";
};
const open = async (row) => {
  detail.value = { ...(row || {}) };
  visible.value = true;
  await nextTick();
  form.value.decision = undefined;
  form.value.supervisoryName = undefined;
};
const handleClose = () => {
  visible.value = false;
  detail.value = {};
  form.value.decision = undefined;
  form.value.supervisoryName = undefined;
};
const updateStatus = async (status) => {
  loading.value = true;
  try {
    const payload = { ...(detail.value || {}), status, supervisoryName: form.value.supervisoryName };
    const { code } = await deviceMaintenanceTaskEdit(payload);
    if (code === 200) {
      ElMessage.success("审批成功");
      emits("ok");
      handleClose();
    }
  } finally {
    loading.value = false;
  }
};
const handleSubmit = async () => {
  if (detail.value?.status === "审核通过") {
    ElMessage.warning("审核通过后不可再次审批");
    return;
  }
  await formRef.value?.validate(async (valid) => {
    if (!valid) return;
    const isApprove = form.value.decision === "审核通过";
    ElMessageBox.confirm(
      `确认审批${isApprove ? "通过" : "不通过"}?`,
      "提示",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }
    ).then(() => updateStatus(form.value.decision));
  });
};
defineExpose({ open });
</script>
<style scoped></style>
src/views/equipmentManagement/upkeep/Form/MaintenanceForm.vue
ÎļþÒÑɾ³ý
src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,146 @@
<template>
  <FormDialog
    v-model="visible"
    :title="'设备保养'"
    width="500px"
    @confirm="sendForm"
    @cancel="handleCancel"
    @close="handleClose"
  >
    <el-form :model="form" :rules="rules" label-width="120px" ref="formRef">
      <el-form-item label="实际保养人" prop="maintenanceActuallyName">
        <el-input
          v-model="form.maintenanceActuallyName"
          placeholder="请输入实际保养人"
        ></el-input>
      </el-form-item>
      <el-form-item label="实际保养日期" prop="maintenanceActuallyTime">
        <el-date-picker
          v-model="form.maintenanceActuallyTime"
          placeholder="请选择实际保养日期"
          format="YYYY-MM-DD HH:mm:ss"
          value-format="YYYY-MM-DD HH:mm:ss"
          type="datetime"
          clearable
          style="width: 100%"
        />
      </el-form-item>
      <el-form-item label="保养状态" prop="status">
        <el-select v-model="form.status">
          <el-option label="待保养" :value="0"></el-option>
          <el-option label="完结" :value="1"></el-option>
          <el-option label="失败" :value="2"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="保养结果" prop="maintenanceResult">
        <el-input
          v-model="form.maintenanceResult"
          placeholder="请输入保养结果"
          type="text" />
      </el-form-item>
            <el-form-item label="本次保养金额" prop="maintenancePrice">
                <el-input-number v-model="form.maintenancePrice" :min="0" :precision="2" style="width: 100%" />
            </el-form-item>
    </el-form>
  </FormDialog>
</template>
<script setup>
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { addMaintenance } from "@/api/equipmentManagement/upkeep";
import useFormData from "@/hooks/useFormData";
import dayjs from "dayjs";
import useUserStore from "@/store/modules/user";
import { ElMessage } from "element-plus";
defineOptions({
  name: "保养模态框",
});
const emits = defineEmits(["ok"]);
// ä¿å­˜è®¡åˆ’保养记录的id
const planId = ref();
const visible = ref(false);
const loading = ref(false);
const formRef = ref();
const userStore = useUserStore();
const { form, resetForm } = useFormData({
  maintenanceActuallyName: undefined, // å®žé™…保养人
  maintenanceActuallyTime: undefined, // å®žé™…保养日期
  maintenanceResult: undefined, // ä¿å…»ç»“æžœ
    maintenancePrice: undefined, // ä¿å…»é‡‘额
  status: 0, // ä¿å…»çŠ¶æ€
});
const rules = {
  maintenanceActuallyName: [
    { required: true, message: "请输入实际保养人", trigger: "blur" },
  ],
  maintenanceActuallyTime: [
    { required: true, message: "请选择实际保养日期", trigger: "change" },
  ],
  maintenanceResult: [
    { required: true, message: "请输入保养结果", trigger: "blur" },
  ],
  maintenancePrice: [
    { required: true, message: "请输入本次保养金额", trigger: "change" },
  ],
};
const setForm = (data) => {
  form.maintenanceActuallyName =
    data.maintenanceActuallyName ?? userStore.nickName;
  form.maintenanceActuallyTime =
    data.maintenanceActuallyTime
      ? dayjs(data.maintenanceActuallyTime).format("YYYY-MM-DD HH:mm:ss")
      : dayjs().format("YYYY-MM-DD HH:mm:ss");
  form.maintenanceResult = data.maintenanceResult;
  form.status = 1; // é»˜è®¤çŠ¶æ€ä¸ºå®Œç»“
};
/**
 * @desc ä¿å­˜ä¿å…»
 */
const sendForm = async () => {
  await formRef.value?.validate(async (valid) => {
    if (!valid) return;
    loading.value = true;
    try {
      const { code } = await addMaintenance({ id: planId.value, ...form });
      if (code == 200) {
        ElMessage.success("保养成功");
        emits("ok");
        resetForm();
        visible.value = false;
      }
    } finally {
      loading.value = false;
    }
  });
};
const handleCancel = () => {
  resetForm();
  visible.value = false;
};
const handleClose = () => {
  resetForm();
  visible.value = false;
};
const open = async (id, row) => {
  planId.value = id; // ä¿å­˜è®¡åˆ’保养记录的id
  visible.value = true;
  await nextTick();
  setForm(row);
};
defineExpose({
  open,
});
</script>
<style lang="scss" scoped></style>
src/views/equipmentManagement/upkeep/Form/PlanForm.vue
ÎļþÒÑɾ³ý
src/views/equipmentManagement/upkeep/Form/PlanModal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,188 @@
<template>
  <FormDialog
    v-model="visible"
    :title="id ? '编辑设备保养计划' : '新增设备保养计划'"
    width="500px"
    @confirm="sendForm"
    @cancel="handleCancel"
    @close="handleClose"
  >
    <el-form :model="form" label-width="100px">
      <el-form-item label="设备名称">
        <el-select
          v-model="form.deviceLedgerId"
          @change="setDeviceModel"
          placeholder="请选择设备"
          filterable
          default-first-option
          :reserve-keyword="false"
        >
          <el-option
            v-for="(item, index) in deviceOptions"
            :key="index"
            :label="item.deviceName"
            :value="item.id"
          ></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="规格型号">
        <el-input
          v-model="form.deviceModel"
          placeholder="请输入规格型号"
          disabled
        />
      </el-form-item>
      <el-form-item label="录入人">
        <el-select
          v-model="form.createUser"
          placeholder="请选择"
          filterable
          default-first-option
          :reserve-keyword="false"
          clearable
        >
          <el-option
            v-for="item in userList"
            :key="item.userId"
            :label="item.nickName"
            :value="item.userId"
          />
        </el-select>
      </el-form-item>
      <el-form-item v-if="id" label="保修状态">
        <el-select v-model="form.status">
          <el-option label="待保修" :value="0"></el-option>
          <el-option label="完结" :value="1"></el-option>
          <el-option label="失败" :value="2"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="计划保养日期">
        <el-date-picker
          style="width: 100%"
          v-model="form.maintenancePlanTime"
          format="YYYY-MM-DD"
          value-format="YYYY-MM-DD HH:mm:ss"
          type="date"
          placeholder="请选择计划保养日期日期"
          clearable
        />
      </el-form-item>
    </el-form>
  </FormDialog>
</template>
<script setup>
import FormDialog from "@/components/Dialog/FormDialog.vue";
import {
  addUpkeep,
  editUpkeep,
  getUpkeepById,
} from "@/api/equipmentManagement/upkeep";
import { ElMessage } from "element-plus";
import useFormData from "@/hooks/useFormData";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import { onMounted } from "vue";
import dayjs from "dayjs";
import { userListNoPage } from "@/api/system/user.js";
defineOptions({
  name: "设备保养新增计划",
});
const emits = defineEmits(["ok"]);
const id = ref();
const visible = ref(false);
const loading = ref(false);
const deviceOptions = ref([]);
const loadDeviceName = async () => {
  const { data } = await getDeviceLedger();
  deviceOptions.value = data;
};
const { form, resetForm } = useFormData({
  deviceLedgerId: undefined, // è®¾å¤‡Id
  deviceName: undefined, // è®¾å¤‡åç§°
  deviceModel: undefined, // è§„格型号
  maintenancePlanTime: undefined, // è®¡åˆ’保养日期
  createUser: undefined, // å½•入人
  status: 0, //保修状态
});
const setDeviceModel = (deviceId) => {
  const option = deviceOptions.value.find((item) => item.id === deviceId);
  form.deviceModel = option.deviceModel;
};
/**
 * @desc è®¾ç½®è¡¨å•内容
 * @param data è®¾å¤‡ä¿¡æ¯
 */
const setForm = (data) => {
  form.deviceLedgerId = data.deviceLedgerId;
  form.deviceName = data.deviceName;
  form.deviceModel = data.deviceModel;
  form.createUser = Number(data.createUser);
  form.status = data.status;
  form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
    "YYYY-MM-DD HH:mm:ss"
  );
};
// ç”¨æˆ·åˆ—表
const userList = ref([]);
onMounted(() => {
  loadDeviceName();
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
});
const openEdit = async (editId) => {
  const { data } = await getUpkeepById(editId);
  id.value = editId;
  visible.value = true;
  await nextTick();
  setForm(data);
};
const sendForm = async () => {
  loading.value = true;
  try {
    const { code } = id.value
      ? await editUpkeep({ id: unref(id), ...form })
      : await addUpkeep(form);
    if (code == 200) {
      ElMessage.success(`${id.value ? "编辑" : "新增"}计划成功`);
      visible.value = false;
      emits("ok");
    }
  } finally {
    loading.value = false;
  }
};
const handleCancel = () => {
  resetForm();
  visible.value = false;
};
const handleClose = () => {
  resetForm();
  visible.value = false;
};
const openModal = () => {
  id.value = undefined;
  visible.value = true;
};
defineExpose({
  openModal,
  openEdit,
});
</script>
<style lang="scss" scoped></style>
src/views/equipmentManagement/upkeep/Form/formDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,337 @@
<template>
    <FormDialog
        v-model="dialogVisitable"
        :title="operationType === 'add' ? '新增保养任务' : '编辑保养任务'"
        width="800px"
        :operation-type="operationType"
        @confirm="submitForm"
        @cancel="cancel"
        @close="cancel"
    >
        <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="设备名称" prop="taskIds">
                        <el-select v-model="form.taskIds" @change="setDeviceModel" multiple filterable>
                            <el-option
                                v-for="(item, index) in deviceOptions"
                                :key="index"
                                :label="item.deviceName"
                                :value="item.id"
                            ></el-option>
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="规格型号">
                        <el-input
                            v-model="form.deviceModel"
                            placeholder="请输入规格型号"
                            disabled
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="录入人" prop="inspector">
                        <el-select
                            v-model="form.inspector"
                            filterable
                            default-first-option
                            :reserve-keyword="false"
                            placeholder="请选择"
                            clearable
                        >
                            <el-option
                                v-for="item in userList"
                                :label="item.nickName"
                                :value="item.userId"
                                :key="item.userId"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="登记时间" prop="registrationDate">
                        <el-date-picker
                            v-model="form.registrationDate"
                            type="date"
                            placeholder="选择登记日期"
                            format="YYYY-MM-DD"
                            value-format="YYYY-MM-DD"
                            style="width: 100%"
                        />
                    </el-form-item>
                </el-col>
            </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="审批人" prop="auditName">
            <el-select
              v-model="form.auditName"
              filterable
              placeholder="请选择审批人"
              clearable
            >
              <el-option
                v-for="item in userList"
                :key="item.userId"
                :label="item.nickName"
                :value="item.nickName"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="任务频率" prop="frequencyType">
                        <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                            <el-option label="每日" value="DAILY"/>
                            <el-option label="每周" value="WEEKLY"/>
                            <el-option label="每月" value="MONTHLY"/>
                            <el-option label="季度" value="QUARTERLY"/>
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
                    <el-form-item label="日期" prop="frequencyDetail">
                        <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                                                        value-format="HH:mm" />
                    </el-form-item>
                </el-col>
                <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
                    <el-form-item label="日期" prop="frequencyDetail">
                        <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                            <el-option label="周一" value="MON"/>
                            <el-option label="周二" value="TUE"/>
                            <el-option label="周三" value="WED"/>
                            <el-option label="周四" value="THU"/>
                            <el-option label="周五" value="FRI"/>
                            <el-option label="周六" value="SAT"/>
                            <el-option label="周日" value="SUN"/>
                        </el-select>
                        <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                                                        value-format="HH:mm"  style="width: 50%"/>
                    </el-form-item>
                </el-col>
                <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
                    <el-form-item label="日期" prop="frequencyDetail">
                        <el-date-picker
                            v-model="form.frequencyDetail"
                            type="datetime"
                            clearable
                            placeholder="选择开始日期"
                            format="DD,HH:mm"
                            value-format="DD,HH:mm"
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
                    <el-form-item label="日期" prop="frequencyDetail">
                        <el-date-picker
                            v-model="form.frequencyDetail"
                            type="datetime"
                            clearable
                            placeholder="选择开始日期"
                            format="MM,DD,HH:mm"
                            value-format="MM,DD,HH:mm"
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="备注" prop="remarks">
                        <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
    </FormDialog>
</template>
<script setup>
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { reactive, ref, getCurrentInstance, toRefs } from "vue";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import { deviceMaintenanceTaskAdd, deviceMaintenanceTaskEdit } from "@/api/equipmentManagement/upkeep";
import { getCurrentDate } from "@/utils/index.js";
import useUserStore from "@/store/modules/user.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits()
const dialogVisitable = ref(false);
const operationType = ref('add');
const deviceOptions = ref([]);
const userStore = useUserStore();
const data = reactive({
    form: {
        taskIds: [],
        taskName: undefined,
        // å½•入人:单选一个用户 id
        inspector: undefined,
    auditName: undefined,
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: '',
        deviceModel: undefined, // è§„格型号
        registrationDate: ''
    },
    rules: {
        taskIds: [{ required: true, message: "请选择设备", trigger: "change" },],
        inspector: [{ required: true, message: "请选择录入人", trigger: "blur" },],
        registrationDate: [{ required: true, message: "请选择登记时间", trigger: "change" }],
    auditName: [{ required: true, message: "请选择审批人", trigger: "change" }],
    }
})
const { form, rules } = toRefs(data)
const userList = ref([])
const loadDeviceName = async () => {
    const { data } = await getDeviceLedger();
    deviceOptions.value = data;
};
// é€‰æ‹©è®¾å¤‡æ—¶ï¼Œå›žå¡«è®¾å¤‡åç§°(taskName)和规格型号(deviceModel)
const setDeviceModel = (ids) => {
    if (!ids || ids.length === 0) {
        form.value.taskIds = []
        form.value.taskName = undefined
        form.value.deviceModel = undefined
        return
    }
    const selectedDevices = deviceOptions.value.filter((item) => ids.includes(item.id))
    if (selectedDevices.length > 0) {
        form.value.taskIds = ids
        form.value.taskName = selectedDevices.map(d => d.deviceName).join(',')
        form.value.deviceModel = selectedDevices.map(d => d.deviceModel || '-').join(',')
    }
}
// æ‰“开弹框
const openDialog = async (type, row) => {
    dialogVisitable.value = true
    operationType.value = type
    // é‡ç½®è¡¨å•
    resetForm();
    // åŠ è½½ç”¨æˆ·åˆ—è¡¨
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
    // åŠ è½½è®¾å¤‡åˆ—è¡¨
    await loadDeviceName();
    if (type === 'edit' && row) {
        form.value = { ...row }
        // ç¼–辑时用接口返回的 registrantId å›žæ˜¾å½•入人
        if (row.registrantId) {
            form.value.inspector = row.registrantId
        }
        // å¦‚果有设备ID数组,转换为数组并设置设备信息
        if (row.taskIds) {
            form.value.taskIds = row.taskIds.split(',').map(id => parseInt(id.trim()))
            setDeviceModel(form.value.taskIds)
        }
    } else if (type === 'add') {
        // æ–°å¢žæ—¶è®¾ç½®ç™»è®°æ—¥æœŸä¸ºå½“天
        form.value.registrationDate = getCurrentDate();
        // æ–°å¢žæ—¶è®¾ç½®å½•入人为当前登录账户
        form.value.inspector = userStore.id;
    }
}
// å…³é—­å¯¹è¯æ¡†
const cancel = () => {
    resetForm()
    dialogVisitable.value = false
    emit('closeDia')
}
// é‡ç½®è¡¨å•函数
const resetForm = () => {
    if (proxy.$refs.formRef) {
        proxy.$refs.formRef.resetFields()
    }
    // é‡ç½®è¡¨å•数据确保设备信息正确重置
    form.value = {
        taskIds: [],
        taskName: undefined,
        inspector: undefined,
        auditName: undefined,
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: '',
        deviceModel: undefined,
        registrationDate: ''
    }
}
// æäº¤è¡¨å•
const submitForm = () => {
    proxy.$refs["formRef"].validate(async valid => {
        if (valid) {
            try {
                const payload = { ...form.value }
                // ä¸å†å‘后端传保养人字段,仅使用接口要求的 registrant / registrantId
                // æ ¹æ®é€‰æ‹©çš„"录入人"设置 registrant / registrantId
                if (payload.inspector) {
                    const selectedUser = userList.value.find(
                        (u) => String(u.userId) === String(payload.inspector)
                    )
                    if (selectedUser) {
                        payload.registrantId = selectedUser.userId
                        payload.registrant = selectedUser.nickName
                    }
                }
                delete payload.inspector
                delete payload.inspectorIds
                // å¤„理 taskIds å’Œ taskName
                if (payload.taskIds && Array.isArray(payload.taskIds)) {
                    payload.taskIds = payload.taskIds.join(',')
                }
                if (payload.frequencyType === 'WEEKLY') {
                    let frequencyDetail = ''
                    frequencyDetail = payload.week + ',' + payload.time
                    payload.frequencyDetail = frequencyDetail
                }
                // å½•入日期:直接使用表单里的 registrationDate å­—段
                // ä¸€äº›é»˜è®¤çŠ¶æ€å­—æ®µ
                if (payload.status === undefined || payload.status === null || payload.status === '') {
                    payload.status = '待审核' // é»˜è®¤çŠ¶æ€ï¼šå¾…å®¡æ ¸
                }
                payload.active = true
                payload.deleted = 0
                if (operationType.value === 'edit') {
                    await deviceMaintenanceTaskEdit(payload)
                } else {
                    await deviceMaintenanceTaskAdd(payload)
                }
                cancel()
                proxy.$modal.msgSuccess('提交成功')
            } catch (error) {
                proxy.$modal.msgError('提交失败,请重试')
            }
        }
    })
}
defineExpose({ openDialog })
</script>
<style scoped>
</style>
src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue
ÎļþÒÑɾ³ý
src/views/equipmentManagement/upkeep/Modal/PlanModal.vue
ÎļþÒÑɾ³ý
src/views/equipmentManagement/upkeep/Modal/formDia.vue
ÎļþÒÑɾ³ý
src/views/equipmentManagement/upkeep/index.vue
@@ -17,8 +17,9 @@
            </el-form-item>
            <el-form-item label="任务状态">
              <el-select v-model="scheduledFilters.status" placeholder="请选择任务状态" clearable style="width: 200px">
                <el-option label="启用" value="1" />
                <el-option label="停用" value="0" />
                <el-option label="待审核" value="待审核" />
                <el-option label="审核通过" value="审核通过" />
                <el-option label="审核不通过" value="审核不通过" />
              </el-select>
            </el-form-item>
            <el-form-item>
@@ -58,22 +59,30 @@
            @pagination="changeScheduledPage"
          >
            <template #statusRef="{ row }">
              <el-tag v-if="row.status === 1" type="success">启用</el-tag>
              <el-tag v-if="row.status === 0" type="danger">停用</el-tag>
              <el-tag v-if="row.status === '待审核'" type="warning">待审核</el-tag>
              <el-tag v-else-if="row.status === '审核通过'" type="success">审核通过</el-tag>
              <el-tag v-else-if="row.status === '审核不通过'" type="danger">审核不通过</el-tag>
              <span v-else>{{ row.status }}</span>
            </template>
            <template #operation="{ row }">
              <el-button
                type="primary"
                text
                icon="editPen"
                link
                @click="editScheduledTask(row)"
              >
                ç¼–辑
              </el-button>
              <el-button
                type="warning"
                link
                :disabled="row.status === '审核通过'"
                @click="openScheduledApprove(row)"
              >
                å®¡æ‰¹
              </el-button>
              <el-button
                type="danger"
                text
                icon="delete"
                link
                @click="delScheduledTaskByIds(row.id)"
              >
                åˆ é™¤
@@ -135,24 +144,13 @@
          <div class="actions">
            <el-text class="mx-1" size="large">任务记录</el-text>
            <div>
              <el-button
                type="primary"
                icon="Plus"
                :disabled="multipleList.length !== 1"
                @click="addMaintain"
              >
                æ–°å¢žä¿å…»
              </el-button>
              <el-button type="success" icon="Van" @click="addPlan">
                æ–°å¢žè®¡åˆ’
              </el-button>
              <el-button @click="handleOut">
                å¯¼å‡º
              </el-button>
              <el-button
                type="danger"
                icon="Delete"
                :disabled="multipleList.length <= 0"
                :disabled="multipleList.length <= 0 || hasFinishedStatus"
                @click="delRepairByIds(multipleList.map((item) => item.id))"
              >
                æ‰¹é‡åˆ é™¤
@@ -181,21 +179,44 @@
          <el-tag v-if="row.status === 0" type="warning">待保养</el-tag>
        </template>
        <template #operation="{ row }">
          <!-- è¿™ä¸ªåŠŸèƒ½è·Ÿæ–°å¢žä¿å…»åŠŸèƒ½ä¸€æ¨¡ä¸€æ ·ï¼Œæœ‰å•¥æ„ä¹‰ï¼Ÿ -->
          <!-- <el-button
              type="primary"
              text
              @click="addMaintain(row)"
          >
            æ–°å¢žä¿å…»
          </el-button> -->
          <el-button
            type="primary"
            text
            icon="editPen"
            link
            :disabled="row.status === 1"
            @click="editPlan(row.id)"
          >
            ç¼–辑
          </el-button>
          <el-button
            type="success"
            link
            :disabled="row.status === 1"
            @click="addMaintain(row)"
          >
            ä¿å…»
          </el-button>
          <el-button
            type="danger"
            text
            icon="delete"
            link
            :disabled="row.status === 1"
            @click="delRepairByIds(row.id)"
          >
            åˆ é™¤
          </el-button>
          <el-button
            type="primary"
            link
            @click="openFileDialog(row)"
          >
            é™„ä»¶
          </el-button>
        </template>
      </PIMTable>
@@ -203,24 +224,41 @@
      </el-tab-pane>
    </el-tabs>
    <PlanModal ref="planModalRef" @ok="getTableData" />
        <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
        <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" />
    <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
    <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" />
    <ApproveModal ref="approveModalRef" @ok="getScheduledTableData" />
    <FileListDialog
      ref="fileListDialogRef"
      v-model="fileDialogVisible"
      :show-upload-button="true"
      :show-delete-button="true"
      :delete-method="handleAttachmentDelete"
      :name-column-label="'附件名称'"
      :rulesRegulationsManagementId="currentMaintenanceTaskId"
      @upload="handleAttachmentUpload" />
  </div>
</template>
<script setup>
import { ref, onMounted, reactive, getCurrentInstance, nextTick } from 'vue'
import { ref, onMounted, reactive, getCurrentInstance, nextTick, computed } from 'vue'
import { Search } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import PlanModal from './Modal/PlanModal.vue'
import MaintenanceModal from './Modal/MaintenanceModal.vue'
import FormDia from './Modal/formDia.vue'
import PlanModal from './Form/PlanModal.vue'
import MaintenanceModal from './Form/MaintenanceModal.vue'
import FormDia from './Form/formDia.vue'
import ApproveModal from './Form/ApproveModal.vue'
import FileListDialog from '@/components/Dialog/FileListDialog.vue'
import {
  getUpkeepPage,
  delUpkeep,
  deviceMaintenanceTaskList,
  deviceMaintenanceTaskDel,
} from '@/api/equipmentManagement/upkeep'
import {
  listMaintenanceTaskFiles,
  addMaintenanceTaskFile,
  delMaintenanceTaskFile,
} from '@/api/equipmentManagement/maintenanceTaskFile'
import dayjs from 'dayjs'
const { proxy } = getCurrentInstance()
@@ -234,6 +272,12 @@
const maintainModalRef = ref()
// å®šæ—¶ä»»åŠ¡å¼¹çª—æŽ§åˆ¶å™¨
const formDiaRef = ref()
// ä¿å…»å®¡æ‰¹å¼¹çª—
const approveModalRef = ref()
// é™„件弹窗
const fileListDialogRef = ref(null)
const fileDialogVisible = ref(false)
const currentMaintenanceTaskId = ref(null)
// ä»»åŠ¡è®°å½•tab(原设备保养页面)相关变量
const filters = reactive({
@@ -307,6 +351,15 @@
    },
    { prop: "registrant", label: "登记人", minWidth: 100 },
    { prop: "registrationDate", label: "登记日期", minWidth: 100 },
    { prop: "auditName", label: "审核人", width: 120 },
    { prop: "supervisoryName", label: "监督人", width: 120 },
  {
    label: "状态",
    align: "center",
    prop: "status",
    dataType: "slot",
    slot: "statusRef",
  },
    {
        fixed: "right",
        label: "操作",
@@ -379,7 +432,7 @@
        dataType: "slot",
        slot: "operation",
        align: "center",
        width: "200px",
        width: "350px",
    },
])
@@ -494,15 +547,24 @@
  multipleList.value = selection
}
// æ£€æŸ¥é€‰ä¸­çš„记录中是否有完结状态的
const hasFinishedStatus = computed(() => {
  return multipleList.value.some(item => item.status === 1)
})
const changePage = (page) => {
  pagination.value.currentPage = page.page
  pagination.value.pageSize = page.limit
  getTableData()
}
const addMaintain = () => {
  const row = multipleList.value[0]
const addMaintain = (row) => {
  maintainModalRef.value.open(row.id, row)
}
// å®šæ—¶ä»»åŠ¡å®¡æ‰¹
const openScheduledApprove = (row) => {
  approveModalRef.value.open(row)
}
const addPlan = () => {
@@ -514,6 +576,13 @@
}
const delRepairByIds = async (ids) => {
  // æ£€æŸ¥æ˜¯å¦æœ‰å®Œç»“状态的记录
  const hasFinished = multipleList.value.some(item => item.status === 1)
  if (hasFinished) {
    ElMessage.warning('不能删除状态为完结的记录')
    return
  }
  try {
    await ElMessageBox.confirm('确认删除保养数据, æ­¤æ“ä½œä¸å¯é€†?', '警告', {
      confirmButtonText: '确定',
@@ -554,6 +623,79 @@
  getTableData()
}
// é™„件相关方法
// æŸ¥è¯¢é™„件列表
const fetchMaintenanceTaskFiles = async (deviceMaintenanceId) => {
  try {
    const params = {
      current: 1,
      size: 100,
      deviceMaintenanceId,
      rulesRegulationsManagementId:deviceMaintenanceId
    }
    const res = await listMaintenanceTaskFiles(params)
    const records = res?.data?.records || []
    const mapped = records.map(item => ({
      id: item.id,
      name: item.fileName || item.name,
      url: item.fileUrl || item.url,
      raw: item,
    }))
    fileListDialogRef.value?.setList(mapped)
  } catch (error) {
    ElMessage.error('获取附件列表失败')
  }
}
// æ‰“开附件弹窗
const openFileDialog = async (row) => {
  currentMaintenanceTaskId.value = row.id
  fileDialogVisible.value = true
  await fetchMaintenanceTaskFiles(row.id)
}
// åˆ·æ–°é™„件列表
const refreshFileList = async () => {
  if (!currentMaintenanceTaskId.value) return
  await fetchMaintenanceTaskFiles(currentMaintenanceTaskId.value)
}
// ä¸Šä¼ é™„ä»¶
const handleAttachmentUpload = async (filePayload) => {
  if (!currentMaintenanceTaskId.value) return
  try {
    const payload = {
      name: filePayload?.fileName || filePayload?.name,
      url: filePayload?.fileUrl || filePayload?.url,
      deviceMaintenanceId: currentMaintenanceTaskId.value,
    }
    await addMaintenanceTaskFile(payload)
    ElMessage.success('文件上传成功')
    await refreshFileList()
  } catch (error) {
    ElMessage.error('文件上传失败')
  }
}
// åˆ é™¤é™„ä»¶
const handleAttachmentDelete = async (row) => {
  if (!row?.id) return false
  try {
    await ElMessageBox.confirm('确认删除该附件?', '提示', { type: 'warning' })
  } catch {
    return false
  }
  try {
    await delMaintenanceTaskFile(row.id)
    ElMessage.success('删除成功')
    await refreshFileList()
    return true
  } catch (error) {
    ElMessage.error('删除失败')
    return false
  }
}
onMounted(() => {
  // æ ¹æ®é»˜è®¤æ¿€æ´»çš„ Tab è°ƒç”¨å¯¹åº”的查询接口
  if (activeTab.value === 'scheduled') {
src/views/inventoryManagement/receiptManagement/index.vue
@@ -23,7 +23,9 @@
      </div>
      <div>
        <el-button type="primary"
                   @click="openForm('add')">新增入库</el-button>
                   @click="openFormadd('add')">订单入库</el-button>
        <el-button type="primary"
                   @click="openDirectForm('add')">新增入库</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
@@ -231,6 +233,72 @@
        </div>
      </template>
    </el-dialog>
    <!-- ç›´æŽ¥æ–°å¢žå…¥åº“对话框 -->
    <el-dialog v-model="directDialogVisible"
               title="新增入库"
               width="70%"
               @close="closeDirectDia">
      <el-form :model="directForm"
               label-width="140px"
               label-position="top"
               :rules="directRules"
               ref="directFormRef">
        <el-form :model="directQuery"
                 class="mb-2">
          <el-form-item label="产品大类">
            <el-select v-model="directQuery.productCategory"
                       placeholder="请选择产品大类"
                       clearable
                       filterable
                       @change="handleProductCategoryChange">
              <el-option v-for="item in productList"
                         :key="item.id"
                         :label="item.productName"
                         :value="item.productName" />
            </el-select>
          </el-form-item>
          <el-form-item label="规格型号">
            <el-select v-model="directQuery.productModelId"
                       placeholder="请先选择产品大类"
                       clearable
                       filterable
                       :disabled="!directQuery.productCategory">
              <el-option v-for="item in productModelList"
                         :key="item.id"
                         :label="item.model"
                         :value="item.id" />
            </el-select>
          </el-form-item>
          <el-form-item label="入库数量">
            <el-input v-model="directQuery.inboundQuantity"
                      placeholder="输入入库数量"
                      clearable />
          </el-form-item>
          <el-form-item label="预警数量">
            <el-input v-model="directQuery.warnNum"
                      placeholder="输入预警数量"
                      clearable />
          </el-form-item>
          <el-form-item label="缺货数量">
            <el-input v-model="directQuery.outStockQuantity"
                      placeholder="输入缺货数量"
                      clearable />
          </el-form-item>
          <el-form-item label="缺货情况">
            <el-input v-model="directQuery.shortageDescription"
                      placeholder="输入缺货情况"
                      clearable />
          </el-form-item>
        </el-form>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitDirectForm">确认</el-button>
          <el-button @click="closeDirectDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
@@ -245,8 +313,10 @@
    addSutockIn,
    delStockIn,
    selectProductRecordListByPuechaserId,
    addProduct,
  } from "@/api/inventoryManagement/stockIn.js";
  import { purchaseListPage } from "@/api/procurementManagement/procurementLedger.js";
  import { productTreeList, modelList } from "@/api/basicData/product.js";
  const userStore = useUserStore();
  const { proxy } = getCurrentInstance();
@@ -304,8 +374,56 @@
        { required: true, message: "请输入入库批次", trigger: "blur" },
      ],
    },
    // ç›´æŽ¥æ–°å¢žå…¥åº“相关
    directDialogVisible: false,
    directForm: {
      id: null,
      productId: null,
      productModel: "",
      inboundQuantity: 0,
      warnNum: 0,
      outStockQuantity: 0,
      shortageDescription: "",
    },
    directRules: {
      supplierId: [
        { required: true, message: "请选择供应商", trigger: "change" },
      ],
      inboundTime: [
        { required: true, message: "请选择入库时间", trigger: "change" },
      ],
      inboundBatch: [
        { required: true, message: "请输入入库批次", trigger: "blur" },
      ],
    },
    directQuery: {
      productId: null,
      productModelId: null,
      inboundQuantity: 0,
      warnNum: 0,
      outStockQuantity: 0,
      shortageDescription: "",
    },
    directProductList: [],
    directLoading: false,
    directSelectedRows: [],
    // äº§å“ç›¸å…³
    productList: [],
    productModelList: [],
  });
  const { searchForm, form, rules } = toRefs(data);
  const {
    searchForm,
    form,
    rules,
    directDialogVisible,
    directForm,
    directRules,
    directQuery,
    directProductList,
    directLoading,
    directSelectedRows,
    productModelList,
  } = toRefs(data);
  const formatPurchaseOption = (item = {}) => {
    const contract = item.purchaseContractNumber || "--";
@@ -441,9 +559,7 @@
      loadingProducts.value = false;
    }
  };
  // æ‰“开弹框
  const openForm = async (type, row) => {
  const openFormadd = async (type, row) => {
    operationType.value = type;
    dialogFormVisible.value = true;
    selectedRows.value = [];
@@ -491,6 +607,215 @@
        loadingProducts.value = false;
      }
    }
  };
  // æ‰“开订单入库弹框
  const openForm = async (type, row) => {
    if (row.salesLedgerProductId) {
      operationType.value = type;
      dialogFormVisible.value = true;
      selectedRows.value = [];
      await loadPurchaseOptions();
      if (type === "add") {
        // æ–°å¢žæ—¶åˆå§‹åŒ–表单
        form.value = {
          id: null,
          purchaseContractNumber: "",
          supplierId: null,
          supplierName: "",
          inboundTime: "",
          inboundBatch: "",
          recorderId: userStore.userId,
          recorderName: userStore.name,
          entryDate: getCurrentDate(),
          remark: "",
        };
        productList.value = []; // æ¸…空产品列表
      } else {
        form.value = JSON.parse(JSON.stringify(row));
        try {
          loadingProducts.value = true;
          // æ ¹æ®åˆåŒå·åŠ è½½å¯¹åº”çš„äº§å“åˆ—è¡¨ï¼ˆå‡è®¾ getProductByContract æ˜¯å¯ç”¨æŽ¥å£ï¼‰
          const res = await selectProductRecordListByPuechaserId({
            purchaseContractNumber: form.value.purchaseContractNumber,
            id: row.id,
          });
          productList.value = res.data.map(item => ({
            ...item,
            quantityStock: Number(
              item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0
            ),
            originalQuantityStock: Number(
              item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0
            ),
          }));
          selectedRows.value = productList.value;
        } catch (error) {
          console.error("加载产品失败:", error);
          proxy.$modal.msgError("加载产品失败");
          productList.value = [];
        } finally {
          loadingProducts.value = false;
        }
      }
    } else {
      // ç›´æŽ¥æ–°å¢žå…¥åº“
      openDirectForm(type, row);
    }
  };
  // æ‰“开直接新增入库弹框
  const openDirectForm = (type, row) => {
    directDialogVisible.value = true;
    directSelectedRows.value = [];
    directQuery.value = {
      productId: null,
      productModelId: null,
      inboundQuantity: 0,
      warnNum: 0,
      outStockQuantity: 0,
      shortageDescription: "",
    };
    directProductList.value = [];
    directForm.value = {
      id: null,
      supplierId: null,
      supplierName: "",
      inboundTime: "",
      inboundBatch: "",
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: "",
    };
    if (type === "edit" && row) {
      // ç¼–辑模式,回显数据
      directForm.value = { ...row };
      // å›žæ˜¾äº§å“å¤§ç±»
      const selectedProduct = productList.value.find(
        item => item.productName === row.productCategory
      );
      if (selectedProduct) {
        directQuery.value.productId = selectedProduct.id;
        // æ ¹æ®äº§å“å¤§ç±»åŠ è½½è§„æ ¼åž‹å·åˆ—è¡¨å¹¶å›žæ˜¾
        loadProductModelList(selectedProduct.id).then(() => {
          // å›žæ˜¾è§„格型号
          directQuery.value.productCategory = row.productCategory || "";
          directQuery.value.productModelId = row.productModelId;
          // å›žæ˜¾å…¶ä»–字段
          directQuery.value.inboundQuantity =
            row.inboundNum || row.inboundQuantity;
          directQuery.value.warnNum = row.warnNum || 0;
          directQuery.value.outStockQuantity = row.outStockQuantity || 0;
          directQuery.value.shortageDescription = row.shortageDescription || "";
        });
      }
    }
  };
  // åŠ è½½äº§å“å¤§ç±»åˆ—è¡¨
  const loadProductList = async () => {
    try {
      const res = await productTreeList();
      productList.value = res;
    } catch (error) {
      console.error("加载产品大类失败:", error);
      proxy.$modal.msgError("加载产品大类失败");
    }
  };
  // å¤„理产品大类变化
  const handleProductCategoryChange = value => {
    directQuery.value.specificationModel = "";
    productModelList.value = [];
    if (value) {
      // æ ¹æ®äº§å“å¤§ç±»åŠ è½½è§„æ ¼åž‹å·åˆ—è¡¨
      const selectedProduct = productList.value.find(
        item => item.productName === value
      );
      if (selectedProduct) {
        loadProductModelList(selectedProduct.id);
      }
    }
  };
  // åŠ è½½äº§å“åž‹å·åˆ—è¡¨
  const loadProductModelList = async productId => {
    try {
      const res = await modelList({ id: productId });
      productModelList.value = res;
    } catch (error) {
      console.error("加载规格型号失败:", error);
      proxy.$modal.msgError("加载规格型号失败");
    }
  };
  // ç›´æŽ¥æ–°å¢žå…¥åº“搜索产品
  const onDirectSearch = async () => {
    try {
      directLoading.value = true;
      // è¿™é‡Œéœ€è¦è°ƒç”¨äº§å“æœç´¢API,暂时模拟数据
      // å®žé™…项目中应该调用真实的产品查询接口
      directProductList.value = [
        {
          id: 1,
          productCategory: "电子产品",
          specificationModel: "A-100",
          unit: "个",
          quantityStock: 0,
          outStockQuantity: 0,
          shortageDescription: "",
          taxRate: 13,
          taxInclusiveUnitPrice: 100,
        },
        {
          id: 2,
          productCategory: "电子产品",
          specificationModel: "A-200",
          unit: "个",
          quantityStock: 0,
          outStockQuantity: 0,
          shortageDescription: "",
          taxRate: 13,
          taxInclusiveUnitPrice: 200,
        },
        {
          id: 3,
          productCategory: "办公用品",
          specificationModel: "B-100",
          unit: "套",
          quantityStock: 0,
          outStockQuantity: 0,
          shortageDescription: "",
          taxRate: 13,
          taxInclusiveUnitPrice: 50,
        },
      ];
    } catch (error) {
      console.error("搜索产品失败:", error);
      proxy.$modal.msgError("搜索产品失败");
      directProductList.value = [];
    } finally {
      directLoading.value = false;
    }
  };
  // ç›´æŽ¥æ–°å¢žå…¥åº“重置搜索
  const onDirectReset = () => {
    directQuery.value = {
      productId: null,
      productModelId: null,
      inboundQuantity: 0,
      warnNum: 0,
      outStockQuantity: 0,
      shortageDescription: "",
    };
    directProductList.value = [];
  };
  // å¤„理直接新增入库产品选择
  const handleDirectSelectionChange = selection => {
    directSelectedRows.value = selection.filter(item => item.id);
  };
  const updatePro = async () => {
@@ -583,10 +908,50 @@
    }
  };
  // å…³é—­å¼¹æ¡†
  // å…³é—­è®¢å•入库弹框
  const closeDia = () => {
    proxy.$refs.formRef.resetFields();
    dialogFormVisible.value = false;
  };
  // å…³é—­ç›´æŽ¥æ–°å¢žå…¥åº“弹框
  const closeDirectDia = () => {
    if (proxy.$refs.directFormRef) {
      proxy.$refs.directFormRef.resetFields();
    }
    directDialogVisible.value = false;
  };
  // æäº¤ç›´æŽ¥æ–°å¢žå…¥åº“表单
  const submitDirectForm = async () => {
    // éªŒè¯è‡³å°‘选择了一个产品
    console.log(directQuery.value, "directQuery.value");
    if (!directQuery.value.productModelId) {
      proxy.$modal.msgWarning("请先选择产品及规格型号");
      return;
    }
    await proxy.$refs.directFormRef.validate();
    if (directQuery.value.inboundQuantity <= 0) {
      proxy.$modal.msgError("本次入库数量需大于0");
      return;
    }
    // å‡†å¤‡æäº¤æ•°æ®
    const submitData = {
      ...directQuery.value,
      // å¦‚果是编辑模式,添加id
      ...(directForm.value.id && { id: directForm.value.id }),
    };
    addProduct(submitData).then(res => {
      if (res.code === 200) {
        proxy.$modal.msgSuccess("操作成功");
        closeDirectDia();
        getList(); // åˆ·æ–°åˆ—表
      } else {
        proxy.$modal.msgError(res.msg || "操作失败");
      }
    });
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
@@ -679,6 +1044,7 @@
  onMounted(() => {
    getList();
    loadProductList();
  });
</script>
src/views/inventoryManagement/stockManagement/index.vue
@@ -3,67 +3,148 @@
    <div class="search_form">
      <div>
        <span class="search_title">供应商名称:</span>
        <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请输入" @change="handleQuery"
          clearable prefix-icon="Search" />
                <span class="search_title ml10">入库日期:</span>
                <el-date-picker
                    v-model="searchForm.timeStr"
                    type="date"
                    placeholder="请选择日期"
                    value-format="YYYY-MM-DD"
                    format="YYYY-MM-DD"
                    clearable
                    @change="handleQuery"
                />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
        <el-input v-model="searchForm.supplierName"
                  style="width: 240px"
                  placeholder="请输入"
                  @change="handleQuery"
                  clearable
                  prefix-icon="Search" />
        <span class="search_title ml10">入库日期:</span>
        <el-date-picker v-model="searchForm.timeStr"
                        type="date"
                        placeholder="请选择日期"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        clearable
                        @change="handleQuery" />
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <!-- <el-button type="primary" @click="openForm('add')">新增</el-button> -->
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
        :row-class-name="tableRowClassName"
        :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="入库日期" prop="createTime" width="100" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" width="240" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" width="100" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" width="200" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="80" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="inboundNum0" width="100" show-overflow-tooltip />
        <el-table-column label="库存预警数量" prop="warnNum" width="130" show-overflow-tooltip />
        <el-table-column label="含税单价" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip />
        <el-table-column label="含税总价" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="税率(%)" prop="taxRate" width="100" show-overflow-tooltip />
        <el-table-column label="不含税总价" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                :expand-row-keys="expandedRowKeys"
                :row-key="row => row.id"
                show-summary
                style="width: 100%"
                :row-class-name="tableRowClassName"
                :summary-method="summarizeMainTable"
                height="calc(100vh - 18.5em)">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="入库日期"
                         prop="createTime"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="供应商名称"
                         prop="supplierName"
                         width="240"
                         show-overflow-tooltip />
        <el-table-column label="产品大类"
                         prop="productCategory"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="规格型号"
                         prop="specificationModel"
                         width="200"
                         show-overflow-tooltip />
        <el-table-column label="单位"
                         prop="unit"
                         width="80"
                         show-overflow-tooltip />
        <el-table-column label="库存数量"
                         prop="inboundNum0"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="库存预警数量"
                         prop="warnNum"
                         width="130"
                         show-overflow-tooltip />
        <el-table-column label="含税单价"
                         prop="taxInclusiveUnitPrice"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="含税总价"
                         prop="taxInclusiveTotalPrice"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="税率(%)"
                         prop="taxRate"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="不含税总价"
                         prop="taxExclusiveTotalPrice"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="入库人"
                         prop="createBy"
                         width="80"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
                         label="操作"
                         min-width="60"
                         align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row);">编辑</el-button>
            <el-button link
                       type="primary"
                       size="small"
                       @click="openForm('edit', scope.row);">编辑</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增库存' : '编辑库存'" width="70%"
      @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
    <el-dialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增库存' : '编辑库存'"
               width="70%"
               @close="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="供应商名称:" prop="supplierName">
              <el-input disabled v-model="form.supplierName" placeholder="请输入" clearable />
            <el-form-item label="供应商名称:"
                          prop="supplierName">
              <el-input disabled
                        v-model="form.supplierName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="产品大类:" prop="productId">
              <el-select disabled v-model="form.productCategory" placeholder="请选择" clearable filterable>
                <el-option v-for="item in productList" :key="item.id" :label="item.productName"
            <el-form-item label="产品大类:"
                          prop="productId">
              <el-select disabled
                         v-model="form.productCategory"
                         placeholder="请选择"
                         clearable
                         filterable>
                <el-option v-for="item in productList"
                           :key="item.id"
                           :label="item.productName"
                           :value="item.productName" />
              </el-select>
            </el-form-item>
@@ -71,77 +152,122 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="productManageId">
              <el-select disabled v-model="form.specificationModel" placeholder="请先选择产品大类" clearable filterable :disabled="!form.productCategory">
                <el-option v-for="item in productModelList" :key="item.id" :label="item.model"
            <el-form-item label="规格型号:"
                          prop="productManageId">
              <el-select disabled
                         v-model="form.specificationModel"
                         placeholder="请先选择产品大类"
                         clearable
                         filterable
                         :disabled="!form.productCategory">
                <el-option v-for="item in productModelList"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item  label="单位:" prop="customerId">
              <el-input disabled v-model="form.unit" placeholder="请输入" clearable />
            <el-form-item label="单位:"
                          prop="customerId">
              <el-input disabled
                        v-model="form.unit"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="库存时间:" prop="projectName">
              <el-date-picker style="width: 100%" v-model="form.updateTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                type="date" placeholder="请选择" clearable />
            <el-form-item label="库存时间:"
                          prop="projectName">
              <el-date-picker style="width: 100%"
                              v-model="form.updateTime"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="入库时间:" prop="projectName">
              <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item  label="含税单价:" prop="customerId">
              <el-input disabled v-model="form.taxInclusiveUnitPrice" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item  label="含税总价:" prop="customerContractNo">
              <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item  label="税率:" prop="customerId">
              <el-input disabled v-model="form.taxRate" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价:" prop="entryDate">
              <el-input disabled v-model="form.taxExclusiveTotalPrice" placeholder="请输入" clearable />
            <el-form-item label="入库时间:"
                          prop="projectName">
              <el-date-picker style="width: 100%"
                              v-model="form.createTime"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="出库人:" prop="entryPerson">
              <el-select v-model="form.createUser" placeholder="请选择" clearable>
                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
            <el-form-item label="含税单价:"
                          prop="customerId">
              <el-input disabled
                        v-model="form.taxInclusiveUnitPrice"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="含税总价:"
                          prop="customerContractNo">
              <el-input disabled
                        v-model="form.taxInclusiveTotalPrice"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="税率:"
                          prop="customerId">
              <el-input disabled
                        v-model="form.taxRate"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价:"
                          prop="entryDate">
              <el-input disabled
                        v-model="form.taxExclusiveTotalPrice"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="出库人:"
                          prop="entryPerson">
              <el-select v-model="form.createUser"
                         placeholder="请选择"
                         clearable>
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
<!--          <el-col :span="12">-->
<!--          <el-form-item label="库存预警数量:" prop="warnNum">-->
<!--            <el-input v-model="form.warnNum" placeholder="请输入最低库存" clearable />-->
<!--          </el-form-item>-->
<!--        </el-col>-->
          <!--          <el-col :span="12">-->
          <!--          <el-form-item label="库存预警数量:" prop="warnNum">-->
          <!--            <el-input v-model="form.warnNum" placeholder="请输入最低库存" clearable />-->
          <!--          </el-form-item>-->
          <!--        </el-col>-->
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
@@ -150,277 +276,298 @@
</template>
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import { productTreeList,modelList } from "@/api/basicData/product.js"
import {
  getStockManagePage,
  delStockManage,
} from "@/api/inventoryManagement/stockManage.js";
import {
  updateManagement,updateStockIn
} from "@/api/inventoryManagement/stockIn.js";
  import pagination from "@/components/PIMTable/Pagination.vue";
  import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue";
  import { ElMessageBox } from "element-plus";
  import useUserStore from "@/store/modules/user";
  import { userListNoPageByTenantId } from "@/api/system/user.js";
  import { productTreeList, modelList } from "@/api/basicData/product.js";
  import {
    getStockManagePage,
    delStockManage,
  } from "@/api/inventoryManagement/stockManage.js";
  import {
    updateManagement,
    updateStockIn,
  } from "@/api/inventoryManagement/stockIn.js";
  const userStore = useUserStore();
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const productData = ref([]);
  const selectedRows = ref([]);
  const userList = ref([]);
  const productList = ref([]);
  const productModelList = ref([]);
  // const customerOption = ref([])
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const fileList = ref([]);
  const loading = ref(false);
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const data = reactive({
    searchForm: {
      supplierName: "",
      timeStr: "",
    },
    form: {
      supplierId: null,
      supplierName: "",
      productId: null,
      productName: "",
      userId: userStore.userId,
      nickName: "",
      productModelId: null,
      model: "",
      unit: "",
      productrecordId: null,
      taxInclusiveUnitPrice: "",
      taxInclusiveTotalPrice: "",
      taxRate: "",
      taxExclusiveTotalPrice: "",
      inboundTime: "",
      inboundBatch: "",
      stockQuantity: "",
      boundTime: "",
      warnNum: "", // æ–°å¢žæœ€ä½Žåº“存字段
      salesLedgerProductId: null,
    },
    rules: {
      // supplierName: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }],
      productCategory: [
        { required: true, message: "请选择产品大类", trigger: "change" },
      ],
      specificationModel: [
        { required: true, message: "请输入规格型号", trigger: "blur" },
      ],
      unit: [{ required: true, message: "请输入单位", trigger: "blur" }],
      stockQuantity: [
        { required: true, message: "请输入出库数量", trigger: "blur" },
      ],
      taxInclusiveUnitPrice: [
        { required: true, message: "请输入含税单价", trigger: "blur" },
      ],
      taxInclusiveTotalPrice: [
        { required: true, message: "请输入含税总价", trigger: "blur" },
      ],
      taxRate: [{ required: true, message: "请输入税率", trigger: "blur" }],
      taxExclusiveTotalPrice: [
        { required: true, message: "请输入不含税总价", trigger: "blur" },
      ],
      boundTime: [
        { required: true, message: "请选择库存时间", trigger: "change" },
      ],
      inboundTime: [
        { required: true, message: "请选择入库时间", trigger: "change" },
      ],
      inboundPerson: [
        { required: true, message: "请选择出库人", trigger: "change" },
      ],
      warnNum: [{ required: true, message: "请输入最低库存", trigger: "blur" }],
    },
  });
  const { searchForm, form, rules } = toRefs(data);
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const tableData = ref([])
const productData = ref([])
const selectedRows = ref([])
const userList = ref([])
const productList = ref([])
const productModelList = ref([])
// const customerOption = ref([])
const tableLoading = ref(false)
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
const fileList = ref([])
const loading = ref(false);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref('')
const dialogFormVisible = ref(false)
const data = reactive({
  searchForm: {
    supplierName: '',
        timeStr: '',
  },
  form: {
    supplierId: null,
    supplierName: '',
    productId: null,
    productName: '',
    userId: userStore.userId,
    nickName: '',
    productModelId: null,
    model: '',
    unit: '',
    productrecordId: null,
    taxInclusiveUnitPrice: '',
    taxInclusiveTotalPrice: '',
    taxRate: '',
    taxExclusiveTotalPrice: '',
    inboundTime: '',
    inboundBatch: '',
    stockQuantity: '',
    boundTime: '',
        warnNum: '', // æ–°å¢žæœ€ä½Žåº“存字段
    salesLedgerProductId: null,
  },
  rules: {
    supplierName: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }],
    productCategory: [{ required: true, message: '请选择产品大类', trigger: 'change' }],
    specificationModel: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
    unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
    stockQuantity: [{ required: true, message: '请输入出库数量', trigger: 'blur' }],
    taxInclusiveUnitPrice: [{ required: true, message: '请输入含税单价', trigger: 'blur' }],
    taxInclusiveTotalPrice: [{ required: true, message: '请输入含税总价', trigger: 'blur' }],
    taxRate: [{ required: true, message: '请输入税率', trigger: 'blur' }],
    taxExclusiveTotalPrice: [{ required: true, message: '请输入不含税总价', trigger: 'blur' }],
    boundTime: [{ required: true, message: '请选择库存时间', trigger: 'change' }],
    inboundTime: [{ required: true, message: '请选择入库时间', trigger: 'change' }],
    inboundPerson: [{ required: true, message: '请选择出库人', trigger: 'change' }],
        warnNum: [{ required: true, message: '请输入最低库存', trigger: 'blur' }],
  }
})
const { searchForm, form, rules } = toRefs(data)
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1
  getList()
}
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const getList = () => {
  tableLoading.value = true
  getStockManagePage({ ...searchForm.value, ...page }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
    // æ•°æ®åŠ è½½å®ŒæˆåŽæ£€æŸ¥åº“å­˜
    // checkStockAndCreatePurchase();
  }).catch(() => {
    tableLoading.value = false
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter(item => item.id);
  console.log('selection', selectedRows.value)
}
const expandedRowKeys = ref([])
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']);
};
// è¡¨æ ¼è¡Œç±»å
const tableRowClassName = ({ row }) => {
  const stock = Number(row?.inboundNum0 ?? 0);
  const warn = Number(row?.warnNum ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
    return '';
  }
  return stock < warn ? 'row-low-stock' : '';
};
// æ‰“开弹框
const openForm = async (type, row) => {
  operationType.value = type
  form.value = {}
  productData.value = []
  let userLists = await userListNoPageByTenantId()
  userList.value = userLists.data
  if (type === 'edit') {
    form.value = { ...row }
    productTreeList().then(res =>{
      productList.value = res
      productList.value.forEach(i =>{
        if (i.label === row.productCategory) {
          modelList({ id: i.id }).then((res) => {
            productModelList.value = res;
          });
        }
      })
    })
  }
  form.value.entryDate = getCurrentDate() // è®¾ç½®é»˜è®¤å½•入日期为当前日期
  dialogFormVisible.value = true
}
// æäº¤è¡¨å•
const submitForm = () => {
  console.log(form.value)
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      updateManagement(form.value).then(res => {
        proxy.$modal.msgSuccess("提交成功")
        closeDia()
        getList()
        // æäº¤åŽæ£€æŸ¥åº“存并尝试创建请购单
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    getStockManagePage({ ...searchForm.value, ...page })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        total.value = res.data.total;
        // æ•°æ®åŠ è½½å®ŒæˆåŽæ£€æŸ¥åº“å­˜
        // checkStockAndCreatePurchase();
      })
    }
  })
}
// æ£€æŸ¥åº“存并创建请购单
// const checkStockAndCreatePurchase = async () => {
//   const stockList = tableData.value;
//   // handList()
//   for (const item of stockList) {
//     if (item.inboundNum0 < item.warnNum) {
//       try {
//                 const stockInData = {
//                     id: item.id,
//                     quantityStock: item.warnNum + item.totalInboundNum,// ä½¿ç”¨æ–°æ ¼å¼åŒ–函数
//                 };
//                 loading.value = true
//                 await updateStockIn(stockInData)
//                 proxy.$modal.msgSuccess(`产品 ${item.productCategory} ä¿®æ”¹å…¥åº“成功`)
//                 loading.value = false
//       } catch (error) {
//         proxy.$modal.msgError(`产品 ${item.productCategory} ç”Ÿæˆè¯·è´­å•失败,请手动处理`);
//
//       }
//     }
//   }
// };
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef")
  dialogFormVisible.value = false
}
      .catch(() => {
        tableLoading.value = false;
      });
  };
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm(
    '是否确认导出?',
    '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }
  ).then(() => {
    proxy.download("/stockin/exportCopy", {}, '库存信息.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
// åˆ é™¤
const handleDelete = () => {
  let ids = []
  if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.createUser !== userStore.id);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
    ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning('请选择数据')
    return
  }
  ElMessageBox.confirm(
    '选中的内容将被删除,是否确认删除?',
    '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }
  ).then(() => {
    delStockManage({ids:ids}).then(res => {
      proxy.$modal.msgSuccess("删除成功")
      getList()
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    // è¿‡æ»¤æŽ‰å­æ•°æ®
    selectedRows.value = selection.filter(item => item.id);
    console.log("selection", selectedRows.value);
  };
  const expandedRowKeys = ref([]);
  // ä¸»è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable = param => {
    return proxy.summarizeTable(param, [
      "contractAmount",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
    ]);
  };
  // è¡¨æ ¼è¡Œç±»å
  const tableRowClassName = ({ row }) => {
    const stock = Number(row?.inboundNum0 ?? 0);
    const warn = Number(row?.warnNum ?? 0);
    if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
      return "";
    }
    return stock < warn ? "row-low-stock" : "";
  };
  // æ‰“开弹框
  const openForm = async (type, row) => {
    operationType.value = type;
    form.value = {};
    productData.value = [];
    let userLists = await userListNoPageByTenantId();
    userList.value = userLists.data;
    if (type === "edit") {
      form.value = { ...row };
      productTreeList().then(res => {
        productList.value = res;
        productList.value.forEach(i => {
          if (i.label === row.productCategory) {
            modelList({ id: i.id }).then(res => {
              productModelList.value = res;
            });
          }
        });
      });
    }
    form.value.entryDate = getCurrentDate(); // è®¾ç½®é»˜è®¤å½•入日期为当前日期
    dialogFormVisible.value = true;
  };
  // æäº¤è¡¨å•
  const submitForm = () => {
    console.log(form.value);
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        updateManagement(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
          getList();
          // æäº¤åŽæ£€æŸ¥åº“存并尝试创建请购单
          // checkStockAndCreatePurchase();
        });
      }
    });
  };
  // æ£€æŸ¥åº“存并创建请购单
  // const checkStockAndCreatePurchase = async () => {
  //   const stockList = tableData.value;
  //   // handList()
  //   for (const item of stockList) {
  //     if (item.inboundNum0 < item.warnNum) {
  //       try {
  //                 const stockInData = {
  //                     id: item.id,
  //                     quantityStock: item.warnNum + item.totalInboundNum,// ä½¿ç”¨æ–°æ ¼å¼åŒ–函数
  //                 };
  //                 loading.value = true
  //                 await updateStockIn(stockInData)
  //                 proxy.$modal.msgSuccess(`产品 ${item.productCategory} ä¿®æ”¹å…¥åº“成功`)
  //                 loading.value = false
  //       } catch (error) {
  //         proxy.$modal.msgError(`产品 ${item.productCategory} ç”Ÿæˆè¯·è´­å•失败,请手动处理`);
  //
  //       }
  //     }
  //   }
  // };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  };
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0'); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
onMounted(() => {
  getList()
  // checkStockAndCreatePurchase();
      .then(() => {
        proxy.download("/stockin/exportCopy", {}, "库存信息.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // åˆ é™¤
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
      const unauthorizedData = selectedRows.value.filter(
        item => item.createUser !== userStore.id
      );
      if (unauthorizedData.length > 0) {
        proxy.$modal.msgWarning("不可删除他人维护的数据");
        return;
      }
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delStockManage({ ids: ids }).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
  function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  }
  onMounted(() => {
    getList();
    // checkStockAndCreatePurchase();
    // æ¯å°æ—¶æ£€æŸ¥ä¸€æ¬¡åº“å­˜
    // const intervalId = setInterval(checkStockAndCreatePurchase, 60 * 60 * 1000);
// onUnmounted(() => {
//   // ç»„件卸载时清除定时器
//   clearInterval(intervalId);
// });
})
    // onUnmounted(() => {
    //   // ç»„件卸载时清除定时器
    //   clearInterval(intervalId);
    // });
  });
</script>
<style scoped lang="scss">
:deep(.row-low-stock td) {
  background-color: #fde2e2;
  color: #c45656;
}
  :deep(.row-low-stock td) {
    background-color: #fde2e2;
    color: #c45656;
  }
:deep(.row-low-stock:hover > td) {
  background-color: #fcd4d4;
}
  :deep(.row-low-stock:hover > td) {
    background-color: #fcd4d4;
  }
</style>
src/views/salesManagement/salesLedger/index.vue
@@ -1,29 +1,48 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="客户名称:">
          <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
          <el-input v-model="searchForm.customerName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="客户合同号:">
          <el-input v-model="searchForm.customerContractNo" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
          <el-input v-model="searchForm.customerContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="销售合同号:">
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
          <el-input v-model="searchForm.salesContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="项目名称:">
          <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
          <el-input v-model="searchForm.projectName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="录入日期:">
          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
            placeholder="请选择" clearable @change="changeDaterange" />
          <el-date-picker v-model="searchForm.entryDate"
                          value-format="YYYY-MM-DD"
                          format="YYYY-MM-DD"
                          type="daterange"
                          placeholder="请选择"
                          clearable
                          @change="changeDaterange" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
          <el-button type="primary"
                     @click="handleQuery"> æœç´¢ </el-button>
        </el-form-item>
      </el-form>
    </div>
@@ -31,125 +50,225 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="openForm('add')">
          <el-button type="primary"
                     @click="openForm('add')">
            æ–°å¢žå°è´¦
          </el-button>
          <el-button @click="handleOut">导出</el-button>
          <el-button type="danger" plain @click="handleDelete">删除</el-button>
          <el-button type="primary" plain @click="handlePrint">打印</el-button>
          <el-button type="danger"
                     plain
                     @click="handleDelete">删除</el-button>
          <el-button type="primary"
                     plain
                     @click="handlePrint">打印</el-button>
        </div>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                :expand-row-keys="expandedRowKeys"
                :row-key="(row) => row.id"
                show-summary
                style="width: 100%"
                :summary-method="summarizeMainTable"
                @expand-change="expandChange"
                height="calc(100vh - 18.5em)">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
        <el-table-column type="expand">
          <template #default="props">
            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable" style="min-width: 1200px;"
            <el-table :data="props.row.children"
                      border
                      show-summary
                      :summary-method="summarizeChildrenTable"
                      style="min-width: 1200px;"
                      scrollbar-always-on>
              <el-table-column align="center" label="序号" type="index" width="70" />
              <el-table-column label="产品大类" prop="productCategory" width="160" />
              <el-table-column label="规格型号" prop="specificationModel" width="220" />
              <el-table-column label="单位" prop="unit" width="100" />
              <el-table-column label="数量" prop="quantity" width="120" />
              <el-table-column label="税率(%)" prop="taxRate" width="120" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="160" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="180" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="180" />
<!--              <el-table-column fixed="right" label="操作" width="150" align="center">-->
<!--                <template #default="scope">-->
<!--                  <el-button link type="primary" size="small" @click="openDeliveryForm(props.row, scope.row)">发货</el-button>-->
<!--                </template>-->
<!--              </el-table-column>-->
              <el-table-column align="center"
                               label="序号"
                               type="index"
                               width="70" />
              <el-table-column label="产品大类"
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="单位"
                               prop="unit" />
              <el-table-column label="数量"
                               prop="quantity" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
              <el-table-column label="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="formattedNumber" />
              <el-table-column fixed="right"
                               label="操作"
                               width="150"
                               align="center">
                <template #default="scope">
                  <el-button link
                             type="primary"
                             size="small"
                             @click="openDeliveryForm(props.row, scope.row)">发货</el-button>
                </template>
              </el-table-column>
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户合同号" prop="customerContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip />
        <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
        <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip />
        <el-table-column label="审核状态" width="140">
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="销售合同号"
                         prop="salesContractNo"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="客户合同号"
                         prop="customerContractNo"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="客户名称"
                         prop="customerName"
                         width="300"
                         show-overflow-tooltip />
        <el-table-column label="业务员"
                         prop="salesman"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="项目名称"
                         prop="projectName"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="审核状态"
                         width="140">
          <template #default="scope">
              <el-tag
                v-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 0"
                type="warning"
              >待审核</el-tag>
              <el-tag
                v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 1"
                type="primary"
              >审核中</el-tag>
              <el-tag
                v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 2"
                type="success"
              >审核完成</el-tag>
              <el-tag
                v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 3"
                type="danger"
              >审核未通过</el-tag>
              <el-tag
                v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 4"
                type="info"
              >已重新提交</el-tag>
              <el-tag v-else type="info">-</el-tag>
            <el-tag v-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 0"
                    type="warning">待审核</el-tag>
            <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 1"
                    type="primary">审核中</el-tag>
            <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 2"
                    type="success">审核完成</el-tag>
            <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 3"
                    type="danger">审核未通过</el-tag>
            <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 4"
                    type="info">已重新提交</el-tag>
            <el-tag v-else
                    type="info">-</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="销售类型" width="120">
        <el-table-column label="销售类型"
                         width="120">
          <template #default="scope">
            <el-tag
              :type="scope.row.salesType === '紧急' ? 'danger' : 'info'"
            >{{ scope.row.salesType || '-' }}</el-tag>
            <el-tag :type="scope.row.salesType === '紧急' ? 'danger' : 'info'">{{ scope.row.salesType || '-' }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
          :formatter="formattedNumber" />
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="150" align="center">
        <el-table-column label="付款方式"
                         prop="paymentMethod"
                         show-overflow-tooltip />
        <el-table-column label="合同金额(元)"
                         prop="contractAmount"
                         width="220"
                         show-overflow-tooltip
                         :formatter="formattedNumber" />
        <el-table-column label="录入人"
                         prop="entryPersonName"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="录入日期"
                         prop="entryDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="签订日期"
                         prop="executionDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
                         label="操作"
                         min-width="150"
                         align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
<!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详情</el-button>-->
            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>
            <el-button link
                       type="primary"
                       size="small"
                       @click="openForm('edit', scope.row)">编辑</el-button>
            <!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详情</el-button>-->
            <el-button link
                       type="primary"
                       size="small"
                       @click="downLoadFile(scope.row)">附件</el-button>
            <!-- <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button> -->
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" width="70%"
      @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
    <el-dialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'"
               width="70%"
               @close="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="销售合同号:" prop="salesContractNo">
              <el-input v-model="form.salesContractNo" placeholder="自动生成" clearable disabled />
            <el-form-item label="销售合同号:"
                          prop="salesContractNo">
              <el-input v-model="form.salesContractNo"
                        placeholder="自动生成"
                        clearable
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                  :value="item.nickName" />
            <el-form-item label="业务员:"
                          prop="salesman">
              <el-select v-model="form.salesman"
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'">
                <el-option v-for="item in userList"
                           :key="item.nickName"
                           :label="item.nickName"
                           :value="item.nickName" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户合同号:" prop="customerContractNo">
              <el-input v-model="form.customerContractNo" placeholder="请输入" clearable :disabled="operationType === 'view'"/>
            <el-form-item label="客户合同号:"
                          prop="customerContractNo">
              <el-input v-model="form.customerContractNo"
                        placeholder="请输入"
                        clearable
                        :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
            <el-form-item label="客户名称:"
                          prop="customerId">
              <el-select v-model="form.customerId"
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'">
                <el-option v-for="item in customerOption"
                           :key="item.id"
                           :label="item.customerName"
                           :value="item.id">
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
                  }}
@@ -160,49 +279,78 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            <el-form-item label="项目名称:"
                          prop="projectName">
              <el-input v-model="form.projectName"
                        placeholder="请输入"
                        clearable
                        :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="签订日期:" prop="executionDate">
                            <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"
                                                            format="YYYY-MM-DD" type="date" placeholder="请选择" clearable :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="签订日期:"
                          prop="executionDate">
              <el-date-picker style="width: 100%"
                              v-model="form.executionDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable
                              :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入人:" prop="entryPerson">
                            <el-select v-model="form.entryPerson" placeholder="请选择" clearable @change="changs" disabled>
                                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
                            </el-select>
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="录入日期:" prop="entryDate">
              <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                type="date" placeholder="请选择" clearable />
            <el-form-item label="录入人:"
                          prop="entryPerson">
              <el-select v-model="form.entryPerson"
                         placeholder="请选择"
                         clearable
                         @change="changs"
                         disabled>
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="录入日期:"
                          prop="entryDate">
              <el-date-picker style="width: 100%"
                              v-model="form.entryDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" />
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable
                        :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="销售类型:" prop="salesType">
              <el-select
                v-model="form.salesType"
                placeholder="请选择"
                clearable
                :disabled="operationType === 'view'"
                style="width: 100%"
              >
                <el-option label="普通" value="普通" />
                <el-option label="紧急" value="紧急" />
            <el-form-item label="销售类型:"
                          prop="salesType">
              <el-select v-model="form.salesType"
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'"
                         style="width: 100%">
                <el-option label="普通"
                           value="普通" />
                <el-option label="紧急"
                           value="紧急" />
              </el-select>
            </el-form-item>
          </el-col>
@@ -212,37 +360,31 @@
            <el-form-item v-if="operationType !== 'view'">
              <template #label>
                <span>审批人选择:</span>
                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button>
                <el-button type="primary"
                           @click="addApproverNode"
                           style="margin-left: 8px;">新增节点</el-button>
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  style="margin-right: 30px; text-align: center; margin-bottom: 10px;"
                >
                <div v-for="(node, index) in approverNodes"
                     :key="node.id"
                     style="margin-right: 30px; text-align: center; margin-bottom: 10px;">
                  <div>
                    <span>审批人</span>
                    â†’
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="选择人员"
                    style="width: 140px; margin-bottom: 8px;"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                  <el-select v-model="node.userId"
                             placeholder="选择人员"
                             style="width: 140px; margin-bottom: 8px;">
                    <el-option v-for="user in userList"
                               :key="user.userId"
                               :label="user.nickName"
                               :value="user.userId" />
                  </el-select>
                  <div>
                    <el-button
                      type="danger"
                      size="small"
                      @click="removeApproverNode(index)"
                      v-if="approverNodes.length > 1"
                    >删除</el-button>
                    <el-button type="danger"
                               size="small"
                               @click="removeApproverNode(index)"
                               v-if="approverNodes.length > 1">删除</el-button>
                  </div>
                </div>
              </div>
@@ -250,44 +392,93 @@
          </el-col>
        </el-row>
        <el-row>
          <el-form-item label="产品信息:" prop="entryDate">
            <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
            <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button>
          <el-form-item label="产品信息:"
                        prop="entryDate">
            <el-button v-if="operationType !== 'view'"
                       type="primary"
                       @click="openProductForm('add')">添加</el-button>
            <el-button v-if="operationType !== 'view'"
                       plain
                       type="danger"
                       @click="deleteProduct">删除</el-button>
          </el-form-item>
        </el-row>
        <el-table :data="productData" border @selection-change="productSelected" show-summary
          :summary-method="summarizeMainTable">
          <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
          <el-table-column align="center" label="序号" type="index" width="60" />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="单位" prop="unit" />
          <el-table-column label="数量" prop="quantity" />
          <el-table-column label="税率(%)" prop="taxRate" />
          <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
          <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
        <el-table :data="productData"
                  border
                  @selection-change="productSelected"
                  show-summary
                  :summary-method="summarizeMainTable">
          <el-table-column align="center"
                           type="selection"
                           width="55"
                           v-if="operationType !== 'view'" />
          <el-table-column align="center"
                           label="序号"
                           type="index"
                           width="60" />
          <el-table-column label="产品大类"
                           prop="productCategory" />
          <el-table-column label="规格型号"
                           prop="specificationModel" />
          <el-table-column label="单位"
                           prop="unit" />
          <el-table-column label="数量"
                           prop="quantity" />
          <el-table-column label="税率(%)"
                           prop="taxRate" />
          <el-table-column label="含税单价(元)"
                           prop="taxInclusiveUnitPrice"
                           :formatter="formattedNumber" />
          <el-table-column label="含税总价(元)"
                           prop="taxInclusiveTotalPrice"
                           :formatter="formattedNumber" />
          <el-table-column label="不含税总价(元)"
                           prop="taxExclusiveTotalPrice"
                           :formatter="formattedNumber" />
          <el-table-column fixed="right"
                           label="操作"
                           min-width="60"
                           align="center"
                           v-if="operationType !== 'view'">
            <template #default="scope">
              <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
              <el-button link
                         type="primary"
                         size="small"
                         @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
            </template>
          </el-table-column>
        </el-table>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="备注·:" prop="remark">
              <el-input v-model="form.remark" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
            <el-form-item label="备注·:"
                          prop="remark">
              <el-input v-model="form.remark"
                        placeholder="请输入"
                        clearable
                        type="textarea"
                        :rows="2"
                        :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:" prop="remark">
              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                :on-success="handleUploadSuccess" :on-remove="handleRemove">
                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                <template #tip v-if="operationType !== 'view'">
            <el-form-item label="附件材料:"
                          prop="remark">
              <el-upload v-model:file-list="fileList"
                         :action="upload.url"
                         multiple
                         ref="fileUpload"
                         auto-upload
                         :headers="upload.headers"
                         :before-upload="handleBeforeUpload"
                         :on-error="handleUploadError"
                         :on-success="handleUploadSuccess"
                         :on-remove="handleRemove">
                <el-button type="primary"
                           v-if="operationType !== 'view'">上传</el-button>
                <template #tip
                          v-if="operationType !== 'view'">
                  <div class="el-upload__tip">
                    æ–‡ä»¶æ ¼å¼æ”¯æŒ
                    doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
@@ -300,84 +491,140 @@
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <el-dialog v-model="productFormVisible" :title="productOperationType === 'add' ? '新增产品' : '编辑产品'" width="40%"
      @close="closeProductDia">
      <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
    <el-dialog v-model="productFormVisible"
               :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
               width="40%"
               @close="closeProductDia">
      <el-form :model="productForm"
               label-width="140px"
               label-position="top"
               :rules="productRules"
               ref="productFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品大类:" prop="productCategory">
            <el-form-item label="产品大类:"
                          prop="productCategory">
              <!-- <el-select v-model="productForm.productCategory" placeholder="请选择" clearable>
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
              </el-select> -->
              <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable check-strictly
                @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
              <el-tree-select v-model="productForm.productCategory"
                              placeholder="请选择"
                              clearable
                              check-strictly
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
            <el-form-item label="规格型号:"
                          prop="productModelId">
              <el-select v-model="productForm.productModelId"
                         placeholder="请选择"
                         clearable
                         @change="getProductModel">
                <el-option v-for="item in modelOptions"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="单位:" prop="unit">
              <el-input v-model="productForm.unit" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="税率(%):" prop="taxRate">
                            <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate">
                                <el-option label="1" value="1" />
                                <el-option label="6" value="6" />
                                <el-option label="13" value="13" />
                            </el-select>
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
              <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
                                                             :precision="2"
                                                             placeholder="请输入" clearable @change="calculateFromUnitPrice" />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number  :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable
                                                                :precision="2"
                                                                @change="calculateFromQuantity" style="width: 100%" />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
              <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
            <el-form-item label="单位:"
                          prop="unit">
              <el-input v-model="productForm.unit"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
              <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
            <el-form-item label="税率(%):"
                          prop="taxRate">
              <el-select v-model="productForm.taxRate"
                         placeholder="请选择"
                         clearable
                         @change="calculateFromTaxRate">
                <el-option label="1"
                           value="1" />
                <el-option label="6"
                           value="6" />
                <el-option label="13"
                           value="13" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="发票类型:" prop="invoiceType">
              <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable>
                <el-option label="增普票" value="增普票" />
                <el-option label="增专票" value="增专票" />
            <el-form-item label="含税单价(元):"
                          prop="taxInclusiveUnitPrice">
              <el-input-number :step="0.01"
                               :min="0"
                               v-model="productForm.taxInclusiveUnitPrice"
                               style="width: 100%"
                               :precision="2"
                               placeholder="请输入"
                               clearable
                               @change="calculateFromUnitPrice" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.1"
                               :min="0"
                               v-model="productForm.quantity"
                               placeholder="请输入"
                               clearable
                               :precision="2"
                               @change="calculateFromQuantity"
                               style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税总价(元):"
                          prop="taxInclusiveTotalPrice">
              <el-input v-model="productForm.taxInclusiveTotalPrice"
                        placeholder="请输入"
                        clearable
                        @change="calculateFromTotalPrice" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价(元):"
                          prop="taxExclusiveTotalPrice">
              <el-input v-model="productForm.taxExclusiveTotalPrice"
                        placeholder="请输入"
                        clearable
                        @change="calculateFromExclusiveTotalPrice" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="发票类型:"
                          prop="invoiceType">
              <el-select v-model="productForm.invoiceType"
                         placeholder="请选择"
                         clearable>
                <el-option label="增普票"
                           value="增普票" />
                <el-option label="增专票"
                           value="增专票" />
              </el-select>
            </el-form-item>
          </el-col>
@@ -385,661 +632,719 @@
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitProduct">确认</el-button>
          <el-button type="primary"
                     @click="submitProduct">确认</el-button>
          <el-button @click="closeProductDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
        <!-- æ‰“印预览弹窗 -->
        <el-dialog
            v-model="printPreviewVisible"
            title="打印预览"
            width="90%"
            :close-on-click-modal="false"
            class="print-preview-dialog"
        >
            <div class="print-preview-container">
                <div class="print-preview-header">
                    <el-button type="primary" @click="executePrint">执行打印</el-button>
                    <el-button @click="printPreviewVisible = false">关闭预览</el-button>
                </div>
                <div class="print-preview-content">
                    <div v-if="printData.length === 0" style="text-align: center; padding: 50px; color: #999;">
                        æš‚无打印数据
                    </div>
                    <div v-else style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;">
                        å…± {{ printData.length }} æ¡æ•°æ®å¾…打印
                    </div>
                    <div v-for="(item, index) in printData" :key="index" class="print-page">
                        <div class="delivery-note">
                            <div class="header">
                                <div class="company-name">鼎诚瑞实业有限责任公司</div>
                                <div class="document-title">零售发货单</div>
                            </div>
                            <div class="info-section">
                                <div class="info-row">
                                    <div>
                                        <span class="label">发货日期:</span>
                                        <span class="value">{{ formatDate(item.createTime) }}</span>
                                    </div>
                                    <div>
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.customerName || '张爱有' }}</span>
                                    </div>
                                </div>
                                <div class="info-row">
                                    <span class="label">单号:</span>
                                    <span class="value">{{ item.salesContractNo }}</span>
                                </div>
                            </div>
                            <div class="table-section">
                                <table class="product-table">
                                    <thead>
                                    <tr>
                                        <th>产品名称</th>
                                        <th>规格型号</th>
                                        <th>单位</th>
                                        <th>单价</th>
                                        <th>零售数量</th>
                                        <th>零售金额</th>
                                    </tr>
                                    </thead>
                                    <tbody>
                                    <tr v-for="product in item.products" :key="product.id">
                                        <td>{{ product.productCategory || '' }}</td>
                                        <td>{{ product.specificationModel || '' }}</td>
                                        <td>{{ product.unit || '' }}</td>
                                        <td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
                                        <td>{{ product.quantity || '0' }}</td>
                                        <td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
                                    </tr>
                                    <tr v-if="!item.products || item.products.length === 0">
                                        <td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td>
                                    </tr>
                                    </tbody>
                                    <tfoot>
                                    <tr>
                                        <td class="label">合计</td>
                                        <td class="total-value"></td>
                                        <td class="total-value"></td>
                                        <td class="total-value"></td>
                                        <td class="total-value">{{ getTotalQuantity(item.products) }}</td>
                                        <td class="total-value">{{ getTotalAmount(item.products) }}</td>
                                    </tr>
                                    </tfoot>
                                </table>
                            </div>
                            <div class="footer-section">
                                <div class="footer-row">
                                    <div class="footer-item">
                                        <span class="label">收货电话:</span>
                                        <span class="value"></span>
                                    </div>
                                    <div class="footer-item">
                                        <span class="label">收货人:</span>
                                        <span class="value"></span>
                                    </div>
                                    <div class="footer-item address-item">
                                        <span class="label">收货地址:</span>
                                        <span class="value address-value"></span>
                                    </div>
                                </div>
                                <div class="footer-row">
                                    <div class="footer-item">
                                        <span class="label">操作员:</span>
                                        <span class="value">{{ userStore.nickName || '撕开前' }}</span>
                                    </div>
                                    <div class="footer-item">
                                        <span class="label">打印日期:</span>
                                        <span class="value">{{ formatDateTime(new Date()) }}</span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </el-dialog>
        <!-- å‘货弹框 -->
        <el-dialog
            v-model="deliveryFormVisible"
            title="发货信息"
            width="40%"
            @close="closeDeliveryDia"
        >
            <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef">
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货日期:" prop="shippingDate">
                            <el-date-picker
                                style="width: 100%"
                                v-model="deliveryForm.shippingDate"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                type="date"
                                placeholder="请选择发货日期"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货车牌号:" prop="shippingCarNumber">
                            <el-input
                                v-model="deliveryForm.shippingCarNumber"
                                placeholder="请输入发货车牌号"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitDelivery">确认发货</el-button>
                    <el-button @click="closeDeliveryDia">取消</el-button>
                </div>
            </template>
        </el-dialog>
    <!-- æ‰“印预览弹窗 -->
    <el-dialog v-model="printPreviewVisible"
               title="打印预览"
               width="90%"
               :close-on-click-modal="false"
               class="print-preview-dialog">
      <div class="print-preview-container">
        <div class="print-preview-header">
          <el-button type="primary"
                     @click="executePrint">执行打印</el-button>
          <el-button @click="printPreviewVisible = false">关闭预览</el-button>
        </div>
        <div class="print-preview-content">
          <div v-if="printData.length === 0"
               style="text-align: center; padding: 50px; color: #999;">
            æš‚无打印数据
          </div>
          <div v-else
               style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;">
            å…± {{ printData.length }} æ¡æ•°æ®å¾…打印
          </div>
          <div v-for="(item, index) in printData"
               :key="index"
               class="print-page">
            <div class="delivery-note">
              <div class="header">
                <div class="company-name">鼎诚瑞实业有限责任公司</div>
                <div class="document-title">零售发货单</div>
              </div>
              <div class="info-section">
                <div class="info-row">
                  <div>
                    <span class="label">发货日期:</span>
                    <span class="value">{{ formatDate(item.createTime) }}</span>
                  </div>
                  <div>
                    <span class="label">客户名称:</span>
                    <span class="value">{{ item.customerName || '张爱有' }}</span>
                  </div>
                </div>
                <div class="info-row">
                  <span class="label">单号:</span>
                  <span class="value">{{ item.salesContractNo }}</span>
                </div>
              </div>
              <div class="table-section">
                <table class="product-table">
                  <thead>
                    <tr>
                      <th>产品名称</th>
                      <th>规格型号</th>
                      <th>单位</th>
                      <th>单价</th>
                      <th>零售数量</th>
                      <th>零售金额</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr v-for="product in item.products"
                        :key="product.id">
                      <td>{{ product.productCategory || '' }}</td>
                      <td>{{ product.specificationModel || '' }}</td>
                      <td>{{ product.unit || '' }}</td>
                      <td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
                      <td>{{ product.quantity || '0' }}</td>
                      <td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
                    </tr>
                    <tr v-if="!item.products || item.products.length === 0">
                      <td colspan="6"
                          style="text-align: center; color: #999;">暂无产品数据</td>
                    </tr>
                  </tbody>
                  <tfoot>
                    <tr>
                      <td class="label">合计</td>
                      <td class="total-value"></td>
                      <td class="total-value"></td>
                      <td class="total-value"></td>
                      <td class="total-value">{{ getTotalQuantity(item.products) }}</td>
                      <td class="total-value">{{ getTotalAmount(item.products) }}</td>
                    </tr>
                  </tfoot>
                </table>
              </div>
              <div class="footer-section">
                <div class="footer-row">
                  <div class="footer-item">
                    <span class="label">收货电话:</span>
                    <span class="value"></span>
                  </div>
                  <div class="footer-item">
                    <span class="label">收货人:</span>
                    <span class="value"></span>
                  </div>
                  <div class="footer-item address-item">
                    <span class="label">收货地址:</span>
                    <span class="value address-value"></span>
                  </div>
                </div>
                <div class="footer-row">
                  <div class="footer-item">
                    <span class="label">操作员:</span>
                    <span class="value">{{ userStore.nickName || '撕开前' }}</span>
                  </div>
                  <div class="footer-item">
                    <span class="label">打印日期:</span>
                    <span class="value">{{ formatDateTime(new Date()) }}</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- å‘货弹框 -->
    <el-dialog v-model="deliveryFormVisible"
               title="发货信息"
               width="40%"
               @close="closeDeliveryDia">
      <el-form :model="deliveryForm"
               label-width="120px"
               label-position="top"
               :rules="deliveryRules"
               ref="deliveryFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货日期:"
                          prop="shippingDate">
              <el-date-picker style="width: 100%"
                              v-model="deliveryForm.shippingDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择发货日期"
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货车牌号:"
                          prop="shippingCarNumber">
              <el-input v-model="deliveryForm.shippingCarNumber"
                        placeholder="请输入发货车牌号"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitDelivery">确认发货</el-button>
          <el-button @click="closeDeliveryDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <FileList ref="fileListRef" />
  </div>
</template>
<script setup>
import { getToken } from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.vue";
import {onMounted, ref} from "vue";
import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
import {ElMessage, ElMessageBox} from "element-plus";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
import FileList from "./fileList.vue";
import {
  ledgerListPage,
  productList,
  customerList,
  addOrUpdateSalesLedger,
  getSalesLedgerWithProducts,
  delLedger,
  addOrUpdateSalesLedgerProduct,
  delProduct,
  delLedgerFile, getProductInventory,
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import {
  getStockInPage
} from "@/api/inventoryManagement/stockIn.js";
  import { getToken } from "@/utils/auth";
  import pagination from "@/components/PIMTable/Pagination.vue";
  import { onMounted, ref } from "vue";
  import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
  import { ElMessage, ElMessageBox } from "element-plus";
  import useUserStore from "@/store/modules/user";
  import { userListNoPage } from "@/api/system/user.js";
  import FileList from "./fileList.vue";
  import {
    ledgerListPage,
    productList,
    customerList,
    addOrUpdateSalesLedger,
    getSalesLedgerWithProducts,
    delLedger,
    addOrUpdateSalesLedgerProduct,
    delProduct,
    delLedgerFile,
    getProductInventory,
  } from "@/api/salesManagement/salesLedger.js";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import useFormData from "@/hooks/useFormData.js";
  import dayjs from "dayjs";
  import { getStockInPage } from "@/api/inventoryManagement/stockIn.js";
  const userStore = useUserStore();
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const productData = ref([]);
  const selectedRows = ref([]);
  const productSelectedRows = ref([]);
  const userList = ref([]);
  const customerOption = ref([]);
  const productOptions = ref([]);
  const modelOptions = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const fileList = ref([]);
  const approverNodes = ref([{ id: 1, userId: null }]);
  let nextApproverId = 2;
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const productData = ref([]);
const selectedRows = ref([]);
const productSelectedRows = ref([]);
const userList = ref([]);
const customerOption = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const fileList = ref([]);
const approverNodes = ref([{ id: 1, userId: null }]);
let nextApproverId = 2;
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const data = reactive({
    searchForm: {
      customerName: "", // å®¢æˆ·åç§°
      customerContractNo: "", // å®¢æˆ·åˆåŒç¼–号
      salesContractNo: "", // é”€å”®åˆåŒç¼–号
      projectName: "", // é¡¹ç›®åç§°
      entryDate: null, // å½•入日期
      entryDateStart: undefined,
      entryDateEnd: undefined,
    },
    form: {
      salesContractNo: "",
      salesman: "",
      customerContractNo: "",
      customerId: "",
      projectName: "",
      entryPerson: "",
      entryDate: "",
      maintenanceTime: "",
      productData: [],
      executionDate: "",
      paymentMethod: "",
      salesType: "普通",
    },
    rules: {
      salesType: [
        { required: true, message: "请选择销售类型", trigger: "change" },
      ],
      salesman: [{ required: true, message: "请选择", trigger: "change" }],
      customerContractNo: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      customerId: [{ required: true, message: "请选择", trigger: "change" }],
      projectName: [{ required: true, message: "请输入", trigger: "blur" }],
      entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
      executionDate: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
  const { form, rules } = toRefs(data);
  const { form: searchForm } = useFormData(data.searchForm);
  const addApproverNode = () => {
    approverNodes.value.push({ id: nextApproverId++, userId: null });
  };
  const removeApproverNode = index => {
    approverNodes.value.splice(index, 1);
  };
  // äº§å“è¡¨å•弹框数据
  const productFormVisible = ref(false);
  const productOperationType = ref("");
  const currentId = ref("");
  const productFormData = reactive({
    productForm: {
      productCategory: "",
      specificationModel: "",
      unit: "",
      quantity: "",
      taxInclusiveUnitPrice: "",
      taxRate: "",
      taxInclusiveTotalPrice: "",
      taxExclusiveTotalPrice: "",
      invoiceType: "",
    },
    productRules: {
      productCategory: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      specificationModel: [
        { required: true, message: "请选择", trigger: "change" },
      ],
      unit: [{ required: true, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      taxInclusiveUnitPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      taxRate: [{ required: true, message: "请选择", trigger: "change" }],
      taxInclusiveTotalPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      taxExclusiveTotalPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
  const { productForm, productRules } = toRefs(productFormData);
  // é˜²æ­¢å¾ªçŽ¯è®¡ç®—çš„æ ‡å¿—
  const isCalculating = ref(false);
  const upload = reactive({
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
    // è®¾ç½®ä¸Šä¼ çš„请求头部
    headers: { Authorization: "Bearer " + getToken() },
  });
  // æ‰“印相关
  const printPreviewVisible = ref(false);
  const printData = ref([]);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
  searchForm: {
    customerName: "", // å®¢æˆ·åç§°
    customerContractNo: "", // å®¢æˆ·åˆåŒç¼–号
    salesContractNo: "", // é”€å”®åˆåŒç¼–号
    projectName: "", // é¡¹ç›®åç§°
    entryDate: null, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  form: {
    salesContractNo: "",
    salesman: "",
    customerContractNo: "",
    customerId: "",
    projectName: "",
    entryPerson: "",
    entryDate: "",
    maintenanceTime: "",
    productData: [],
    executionDate: "",
    paymentMethod: "",
    salesType: "普通",
  },
  rules: {
    salesType: [{ required: true, message: "请选择销售类型", trigger: "change" }],
    salesman: [{ required: true, message: "请选择", trigger: "change" }],
    customerContractNo: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    customerId: [{ required: true, message: "请选择", trigger: "change" }],
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
    entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    executionDate: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const { form, rules } = toRefs(data);
const { form: searchForm } = useFormData(data.searchForm);
const addApproverNode = () => {
  approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = (index) => {
  approverNodes.value.splice(index, 1);
};
// äº§å“è¡¨å•弹框数据
const productFormVisible = ref(false);
const productOperationType = ref("");
const currentId = ref("");
const productFormData = reactive({
  productForm: {
    productCategory: "",
    specificationModel: "",
    unit: "",
    quantity: "",
    taxInclusiveUnitPrice: "",
    taxRate: "",
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
  },
  productRules: {
    productCategory: [{ required: true, message: "请选择", trigger: "change" }],
        productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    specificationModel: [
      { required: true, message: "请选择", trigger: "change" },
    ],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    taxInclusiveUnitPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxRate: [{ required: true, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxExclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const { productForm, productRules } = toRefs(productFormData);
// é˜²æ­¢å¾ªçŽ¯è®¡ç®—çš„æ ‡å¿—
const isCalculating = ref(false);
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
// æ‰“印相关
const printPreviewVisible = ref(false);
const printData = ref([]);
  // å‘货相关
  const deliveryFormVisible = ref(false);
  const currentDeliveryContext = ref(null);
  const deliveryFormData = reactive({
    deliveryForm: {
      shippingDate: "",
      shippingCarNumber: "",
    },
    deliveryRules: {
      shippingDate: [
        { required: true, message: "请选择发货日期", trigger: "change" },
      ],
      shippingCarNumber: [
        { required: true, message: "请输入发货车牌号", trigger: "blur" },
      ],
    },
  });
  const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
// å‘货相关
const deliveryFormVisible = ref(false);
const currentDeliveryContext = ref(null);
const deliveryFormData = reactive({
  deliveryForm: {
    shippingDate: "",
    shippingCarNumber: "",
  },
  deliveryRules: {
    shippingDate: [
      { required: true, message: "请选择发货日期", trigger: "change" }
    ],
    shippingCarNumber: [
      { required: true, message: "请输入发货车牌号", trigger: "blur" }
    ],
  },
});
const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
  const changeDaterange = value => {
    if (value) {
      searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
      searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
    } else {
      searchForm.entryDateStart = undefined;
      searchForm.entryDateEnd = undefined;
    }
    handleQuery();
  };
const changeDaterange = (value) => {
  if (value) {
    searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    searchForm.entryDateStart = undefined;
    searchForm.entryDateEnd = undefined;
  }
  handleQuery();
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
    expandedRowKeys.value = [];
  getList();
};
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  ledgerListPage({ ...rest, ...page })
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.records;
      tableData.value.map((item) => {
        item.children = [];
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    expandedRowKeys.value = [];
    getList();
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    const { entryDate, ...rest } = searchForm;
    ledgerListPage({ ...rest, ...page })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.records;
        tableData.value.map(item => {
          item.children = [];
        });
        total.value = res.total;
      })
      .catch(() => {
        tableLoading.value = false;
      });
      total.value = res.total;
    })
    .catch(() => {
      tableLoading.value = false;
  };
  // èŽ·å–äº§å“å¤§ç±»tree数据
  const getProductOptions = () => {
    productTreeList().then(res => {
      productOptions.value = convertIdToValue(res);
    });
};
// èŽ·å–äº§å“å¤§ç±»tree数据
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
};
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
};
// èŽ·å–tree子数据
const getModels = (value) => {
  productForm.value.productCategory = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  });
};
const getProductModel = (value) => {
  console.log("value", value);
  const index = modelOptions.value.findIndex((item) => item.id === value);
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model;
    productForm.value.unit = modelOptions.value[index].unit;
  } else {
    productForm.value.specificationModel = null;
    productForm.value.unit = null;
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
  };
  const formattedNumber = (row, column, cellValue) => {
    return parseFloat(cellValue).toFixed(2);
  };
  // èŽ·å–tree子数据
  const getModels = value => {
    productForm.value.productCategory = findNodeById(productOptions.value, value);
    modelList({ id: value }).then(res => {
      modelOptions.value = res;
    });
  };
  const getProductModel = value => {
    console.log("value", value);
    const index = modelOptions.value.findIndex(item => item.id === value);
    if (index !== -1) {
      productForm.value.specificationModel = modelOptions.value[index].model;
      productForm.value.unit = modelOptions.value[index].unit;
    } else {
      productForm.value.specificationModel = null;
      productForm.value.unit = null;
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
  };
  const findNodeById = (nodes, productId) => {
    for (let i = 0; i < nodes.length; i++) {
      if (nodes[i].value === productId) {
        return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
      }
    }
  }
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter((item) => item.children !== undefined);
  console.log("selection", selectedRows.value);
};
const productSelected = (selectedRows) => {
  productSelectedRows.value = selectedRows;
};
const expandedRowKeys = ref([]);
// å±•开行
const expandChange = (row, expandedRows) => {
  if (expandedRows.length > 0) {
    expandedRowKeys.value = [];
    try {
      productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
        const index = tableData.value.findIndex((item) => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
        expandedRowKeys.value.push(row.id);
      });
    } catch (error) {
      console.log(error);
    }
  } else {
    expandedRowKeys.value = [];
  }
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, [
    "contractAmount",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeChildrenTable = (param) => {
  return proxy.summarizeTable(param, [
    "taxInclusiveUnitPrice",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// æ‰“开弹框
const openForm = async (type, row) => {
  operationType.value = type;
  form.value = {};
  productData.value = [];
  approverNodes.value = [{ id: 1, userId: null }];
  nextApproverId = 2;
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  customerList().then((res) => {
    customerOption.value = res;
  });
  form.value.entryPerson = userStore.id;
  if (type !== "add") {
    currentId.value = row.id;
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
      form.value = { ...res };
      form.value.entryPerson = Number(res.entryPerson);
      if (!form.value.salesType) {
        form.value.salesType = "普通";
      }
      productData.value = form.value.productData;
      fileList.value = form.value.salesLedgerFiles;
      const approveUserIds = form.value.approveUserIds || form.value.approverIds;
      if (approveUserIds) {
        const ids = String(approveUserIds)
          .split(",")
          .map((id) => Number(id.trim()))
          .filter((id) => !Number.isNaN(id));
        if (ids.length > 0) {
          approverNodes.value = ids.map((id, idx) => ({ id: idx + 1, userId: id }));
          nextApproverId = ids.length + 1;
      if (nodes[i].children && nodes[i].children.length > 0) {
        const foundNode = findNodeById(nodes[i].children, productId);
        if (foundNode) {
          return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
        }
      }
    }
    return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
  };
  function convertIdToValue(data) {
    return data.map(item => {
      const { id, children, ...rest } = item;
      const newItem = {
        ...rest,
        value: id, // å°† id æ”¹ä¸º value
      };
      if (children && children.length > 0) {
        newItem.children = convertIdToValue(children);
      }
      return newItem;
    });
  }
  // let userAll = await userStore.getInfo()
  // userList.value.forEach(element => {
  //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
  //     form.value.entryPerson = userAll.user.userId // è®¾ç½®é»˜è®¤ä¸šåŠ¡å‘˜ä¸ºå½“å‰ç”¨æˆ·
  //   }
  // });
  form.value.entryDate = getCurrentDate(); // è®¾ç½®é»˜è®¤å½•入日期为当前日期
  if (type === "add") {
    form.value.salesType = "普通";
  }
  dialogFormVisible.value = true;
};
function changs(val) {
  console.log(val);
}
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  // if (file.size > 1024 * 1024 * 10) {
  //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
  //   return false;
  // }
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
}
// ä¸Šä¼ å¤±è´¥
function handleUploadError(err) {
  proxy.$modal.msgError("上传文件失败");
  proxy.$modal.closeLoading();
}
// ä¸Šä¼ æˆåŠŸå›žè°ƒ
function handleUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.fileUpload.handleRemove(file);
  }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    // è¿‡æ»¤æŽ‰å­æ•°æ®
    selectedRows.value = selection.filter(item => item.children !== undefined);
    console.log("selection", selectedRows.value);
  };
  const productSelected = selectedRows => {
    productSelectedRows.value = selectedRows;
  };
  const expandedRowKeys = ref([]);
  // å±•开行
  const expandChange = (row, expandedRows) => {
    if (expandedRows.length > 0) {
      expandedRowKeys.value = [];
      try {
        productList({ salesLedgerId: row.id, type: 1 }).then(res => {
          const index = tableData.value.findIndex(item => item.id === row.id);
          if (index > -1) {
            tableData.value[index].children = res.data;
          }
          expandedRowKeys.value.push(row.id);
        });
      } catch (error) {
        console.log(error);
      }
    } else {
      expandedRowKeys.value = [];
    }
  };
  // ä¸»è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable = param => {
    return proxy.summarizeTable(param, [
      "contractAmount",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
    ]);
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeChildrenTable = param => {
    return proxy.summarizeTable(param, [
      "taxInclusiveUnitPrice",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
    ]);
  };
  // æ‰“开弹框
  const openForm = async (type, row) => {
    operationType.value = type;
    form.value = {};
    productData.value = [];
    approverNodes.value = [{ id: 1, userId: null }];
    nextApproverId = 2;
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    customerList().then(res => {
      customerOption.value = res;
    });
    form.value.entryPerson = userStore.id;
    if (type !== "add") {
      currentId.value = row.id;
      getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
        form.value = { ...res };
        form.value.entryPerson = Number(res.entryPerson);
        if (!form.value.salesType) {
          form.value.salesType = "普通";
        }
        productData.value = form.value.productData;
        fileList.value = form.value.salesLedgerFiles;
        const approveUserIds =
          form.value.approveUserIds || form.value.approverIds;
        if (approveUserIds) {
          const ids = String(approveUserIds)
            .split(",")
            .map(id => Number(id.trim()))
            .filter(id => !Number.isNaN(id));
          if (ids.length > 0) {
            approverNodes.value = ids.map((id, idx) => ({
              id: idx + 1,
              userId: id,
            }));
            nextApproverId = ids.length + 1;
          }
        }
      });
    }
    // let userAll = await userStore.getInfo()
    // userList.value.forEach(element => {
    //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
    //     form.value.entryPerson = userAll.user.userId // è®¾ç½®é»˜è®¤ä¸šåŠ¡å‘˜ä¸ºå½“å‰ç”¨æˆ·
    //   }
    // });
    form.value.entryDate = getCurrentDate(); // è®¾ç½®é»˜è®¤å½•入日期为当前日期
    if (type === "add") {
      form.value.salesType = "普通";
    }
    dialogFormVisible.value = true;
  };
  function changs(val) {
    console.log(val);
  }
}
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      if (operationType.value !== "view") {
        const hasEmptyApprover = approverNodes.value.some((node) => !node.userId);
        if (hasEmptyApprover) {
          proxy.$modal.msgWarning("请为所有审批节点选择审批人");
  // ä¸Šä¼ å‰æ ¡æ£€
  function handleBeforeUpload(file) {
    // æ ¡æ£€æ–‡ä»¶å¤§å°
    // if (file.size > 1024 * 1024 * 10) {
    //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
    //   return false;
    // }
    proxy.$modal.loading("正在上传文件,请稍候...");
    return true;
  }
  // ä¸Šä¼ å¤±è´¥
  function handleUploadError(err) {
    proxy.$modal.msgError("上传文件失败");
    proxy.$modal.closeLoading();
  }
  // ä¸Šä¼ æˆåŠŸå›žè°ƒ
  function handleUploadSuccess(res, file, uploadFiles) {
    proxy.$modal.closeLoading();
    if (res.code === 200) {
      file.tempId = res.data.tempId;
      proxy.$modal.msgSuccess("上传成功");
    } else {
      proxy.$modal.msgError(res.msg);
      proxy.$refs.fileUpload.handleRemove(file);
    }
  }
  // ç§»é™¤æ–‡ä»¶
  function handleRemove(file) {
    if (operationType.value === "edit") {
      let ids = [];
      ids.push(file.id);
      delLedgerFile(ids).then(res => {
        proxy.$modal.msgSuccess("删除成功");
      });
    }
  }
  // æäº¤è¡¨å•
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        if (operationType.value !== "view") {
          const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
          if (hasEmptyApprover) {
            proxy.$modal.msgWarning("请为所有审批节点选择审批人");
            return;
          }
          form.value.approveUserIds = approverNodes.value
            .map(node => node.userId)
            .join(",");
        }
        console.log("productData.value--", productData.value);
        if (productData.value !== null && productData.value.length > 0) {
          form.value.productData = proxy.HaveJson(productData.value);
        } else {
          proxy.$modal.msgWarning("请添加产品信息");
          return;
        }
        form.value.approveUserIds = approverNodes.value.map((node) => node.userId).join(",");
      }
            console.log('productData.value--', productData.value)
      if (productData.value !== null && productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
      } else {
        proxy.$modal.msgWarning("请添加产品信息");
        return;
      }
      let tempFileIds = [];
      if (fileList.value !== null && fileList.value.length > 0) {
        tempFileIds = fileList.value.map((item) => item.tempId);
      }
      form.value.tempFileIds = tempFileIds;
      form.value.type = 1;
      addOrUpdateSalesLedger(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        getList();
      });
    }
  });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
};
const productIndex = ref(0);
// æ‰“开产品弹框
const openProductForm = (type, row,index) => {
  productOperationType.value = type;
  productForm.value = {};
  proxy.resetForm("productFormRef");
  if (type === "edit") {
    productForm.value = { ...row };
    productIndex.value = index;
  }
  productFormVisible.value = true;
  getProductOptions();
};
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
  proxy.$refs["productFormRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitProductEdit();
      } else {
        if(productOperationType.value === "add"){
          productData.value.push({ ...productForm.value });
        }else{
          productData.value[productIndex.value] = { ...productForm.value }
        let tempFileIds = [];
        if (fileList.value !== null && fileList.value.length > 0) {
          tempFileIds = fileList.value.map(item => item.tempId);
        }
        closeProductDia();
        form.value.tempFileIds = tempFileIds;
        form.value.type = 1;
        addOrUpdateSalesLedger(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
          getList();
        });
      }
    });
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  };
  const productIndex = ref(0);
  // æ‰“开产品弹框
  const openProductForm = (type, row, index) => {
    productOperationType.value = type;
    productForm.value = {};
    proxy.resetForm("productFormRef");
    if (type === "edit") {
      productForm.value = { ...row };
      productIndex.value = index;
    }
  });
};
const submitProductEdit = () => {
  productForm.value.salesLedgerId = currentId.value;
  productForm.value.type = 1
  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeProductDia();
    getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
      productData.value = res.productData;
    });
  });
};
// åˆ é™¤äº§å“
const deleteProduct = () => {
  if (productSelectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  if (operationType.value === "add") {
    productSelectedRows.value.forEach((selectedRow) => {
      const index = productData.value.findIndex(
        (product) => product.id === selectedRow.id
      );
      if (index !== -1) {
        productData.value.splice(index, 1);
    productFormVisible.value = true;
    getProductOptions();
  };
  // æäº¤äº§å“è¡¨å•
  const submitProduct = () => {
    proxy.$refs["productFormRef"].validate(valid => {
      if (valid) {
        if (operationType.value === "edit") {
          submitProductEdit();
        } else {
          if (productOperationType.value === "add") {
            productData.value.push({ ...productForm.value });
          } else {
            productData.value[productIndex.value] = { ...productForm.value };
          }
          closeProductDia();
        }
      }
    });
  } else {
  };
  const submitProductEdit = () => {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 1;
    addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeProductDia();
      getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(res => {
        productData.value = res.productData;
      });
    });
  };
  // åˆ é™¤äº§å“
  const deleteProduct = () => {
    if (productSelectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    if (operationType.value === "add") {
      productSelectedRows.value.forEach(selectedRow => {
        const index = productData.value.findIndex(
          product => product.id === selectedRow.id
        );
        if (index !== -1) {
          productData.value.splice(index, 1);
        }
      });
    } else {
      let ids = [];
      if (productSelectedRows.value.length > 0) {
        ids = productSelectedRows.value.map(item => item.id);
      }
      ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delProduct(ids).then(res => {
            proxy.$modal.msgSuccess("删除成功");
            closeProductDia();
            getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
              res => {
                productData.value = res.productData;
              }
            );
          });
        })
        .catch(() => {
          proxy.$modal.msg("已取消");
        });
    }
  };
  // å…³é—­äº§å“å¼¹æ¡†
  const closeProductDia = () => {
    proxy.resetForm("productFormRef");
    productFormVisible.value = false;
  };
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        proxy.download("/sales/ledger/export", {}, "销售台账.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // åˆ é™¤
  const handleDelete = () => {
    let ids = [];
    if (productSelectedRows.value.length > 0) {
      ids = productSelectedRows.value.map((item) => item.id);
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
@@ -1047,843 +1352,831 @@
      type: "warning",
    })
      .then(() => {
        delProduct(ids).then((res) => {
        delLedger(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          closeProductDia();
          getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
            (res) => {
              productData.value = res.productData;
            }
          );
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  }
};
// å…³é—­äº§å“å¼¹æ¡†
const closeProductDia = () => {
  proxy.resetForm("productFormRef");
  productFormVisible.value = false;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/sales/ledger/export", {}, "销售台账.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      delLedger(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
  };
// æ‰“印功能
const handlePrint = async () => {
    if (selectedRows.value.length === 0) {
        proxy.$modal.msgWarning("请选择要打印的数据");
        return;
    }
    // æ˜¾ç¤ºåŠ è½½çŠ¶æ€
    proxy.$modal.loading("正在获取产品数据,请稍候...");
    try {
        // ä¸ºæ¯ä¸ªé€‰ä¸­çš„销售台账记录查询对应的产品数据
        const printDataWithProducts = [];
        for (const row of selectedRows.value) {
            try {
                // è°ƒç”¨productList接口查询产品数据
                const productRes = await productList({ salesLedgerId: row.id, type: 1 });
                // å°†äº§å“æ•°æ®æ•´åˆåˆ°é”€å”®å°è´¦è®°å½•中
                const rowWithProducts = {
                    ...row,
                    products: productRes.data || []
                };
                printDataWithProducts.push(rowWithProducts);
            } catch (error) {
                console.error(`获取销售台账 ${row.id} çš„产品数据失败:`, error);
                // å³ä½¿æŸä¸ªè®°å½•的产品数据获取失败,也要包含该记录
                printDataWithProducts.push({
                    ...row,
                    products: []
                });
            }
        }
        printData.value = printDataWithProducts;
        console.log('打印数据(包含产品):', printData.value);
        printPreviewVisible.value = true;
    } catch (error) {
        console.error('获取产品数据失败:', error);
        proxy.$modal.msgError("获取产品数据失败,请重试");
    } finally {
        proxy.$modal.closeLoading();
    }
};
// æ‰§è¡Œæ‰“印
const executePrint = () => {
    console.log('开始执行打印,数据条数:', printData.value.length);
    console.log('打印数据:', printData.value);
    // åˆ›å»ºä¸€ä¸ªæ–°çš„æ‰“印窗口
    const printWindow = window.open('', '_blank', 'width=800,height=600');
    // æž„建打印内容
    let printContent = `
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>打印预览</title>
      <style>
        body {
          margin: 0;
          padding: 0;
          font-family: "SimSun", serif;
          background: white;
        }
                                                     .print-page {
            width: 200mm;
            height: 75mm;
            padding: 10mm;
            padding-left: 20mm;
            background: white;
            box-sizing: border-box;
            page-break-after: always;
            page-break-inside: avoid;
          }
         .print-page:last-child {
           page-break-after: avoid;
         }
        .delivery-note {
          width: 100%;
          height: 100%;
          font-size: 12px;
          line-height: 1.2;
          display: flex;
          flex-direction: column;
          color: #000;
        }
        .header {
          text-align: center;
          margin-bottom: 8px;
        }
        .company-name {
          font-size: 18px;
          font-weight: bold;
          margin-bottom: 4px;
        }
        .document-title {
          font-size: 16px;
          font-weight: bold;
        }
        .info-section {
          margin-bottom: 8px;
          display: flex;
          justify-content: space-between;
          align-items: center;
        }
        .info-row {
          line-height: 20px;
        }
        .label {
          font-weight: bold;
          width: 60px;
          font-size: 12px;
        }
        .value {
          margin-right: 20px;
          min-width: 80px;
          font-size: 12px;
        }
                 .table-section {
                 margin-bottom: 40px;
          //  flex: 0.6;
         }
        .product-table {
          width: 100%;
          border-collapse: collapse;
          border: 1px solid #000;
        }
                 .product-table th, .product-table td {
           border: 1px solid #000;
           padding: 6px;
           text-align: center;
           font-size: 12px;
           line-height: 1.4;
         }
        .product-table th {
          font-weight: bold;
        }
        .total-value {
          font-weight: bold;
        }
        .footer-section {
          margin-top: auto;
        }
        .footer-row {
          display: flex;
          margin-bottom: 3px;
          line-height: 22px;
          justify-content: space-between;
        }
        .footer-item {
          display: flex;
          margin-right: 20px;
        }
        .footer-item .label {
          font-weight: bold;
          width: 80px;
          font-size: 12px;
        }
        .footer-item .value {
          min-width: 80px;
          font-size: 12px;
        }
        .address-item .address-value {
          min-width: 200px;
        }
        @media print {
          body {
            margin: 0;
            padding: 0;
          }
                     .print-page {
             margin: 0;
             padding: 10mm;
             /* padding-left: 20mm; */
             page-break-inside: avoid;
             page-break-after: always;
           }
           .print-page:last-child {
             page-break-after: avoid;
           }
        }
      </style>
    </head>
    <body>
  `;
    // ä¸ºæ¯æ¡æ•°æ®ç”Ÿæˆæ‰“印页面
    printData.value.forEach((item, index) => {
        printContent += `
      <div class="print-page">
        <div class="delivery-note">
          <div class="header">
            <div class="company-name">鼎诚瑞实业有限责任公司</div>
            <div class="document-title">零售发货单</div>
          </div>
          <div class="info-section">
            <div class="info-row">
              <div>
                <span class="label">发货日期:</span>
                <span class="value">${formatDate(item.createTime)}</span>
              </div>
              <div>
                <span class="label">客户名称:</span>
                <span class="value">${item.customerName || '张爱有'}</span>
              </div>
            </div>
            <div class="info-row">
              <span class="label">单号:</span>
              <span class="value">${item.salesContractNo || ''}</span>
            </div>
          </div>
          <div class="table-section">
            <table class="product-table">
              <thead>
                <tr>
                  <th>产品名称</th>
                  <th>规格型号</th>
                  <th>单位</th>
                  <th>单价</th>
                  <th>零售数量</th>
                  <th>零售金额</th>
                </tr>
              </thead>
              <tbody>
                ${item.products && item.products.length > 0 ?
                  item.products.map(product => `
                    <tr>
                      <td>${product.productCategory || ''}</td>
                      <td>${product.specificationModel || ''}</td>
                      <td>${product.unit || ''}</td>
                      <td>${product.taxInclusiveUnitPrice || '0'}</td>
                      <td>${product.quantity || '0'}</td>
                      <td>${product.taxInclusiveTotalPrice || '0'}</td>
                    </tr>
                  `).join('') :
                  '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
                }
              </tbody>
              <tfoot>
                <tr>
                  <td class="label">合计</td>
                  <td class="total-value"></td>
                  <td class="total-value"></td>
                  <td class="total-value"></td>
                  <td class="total-value">${getTotalQuantityForPrint(item.products)}</td>
                  <td class="total-value">${getTotalAmountForPrint(item.products)}</td>
                </tr>
              </tfoot>
            </table>
          </div>
          <div class="footer-section">
            <div class="footer-row">
              <div class="footer-item">
                <span class="label">收货电话:</span>
                <span class="value"></span>
              </div>
              <div class="footer-item">
                <span class="label">收货人:</span>
                <span class="value"></span>
              </div>
              <div class="footer-item address-item">
                <span class="label">收货地址:</span>
                <span class="value address-value"></span>
              </div>
            </div>
            <div class="footer-row">
              <div class="footer-item">
                <span class="label">操作员:</span>
                <span class="value">${userStore.nickName || '撕开前'}</span>
              </div>
              <div class="footer-item">
                <span class="label">打印日期:</span>
                <span class="value">${formatDateTime(new Date())}</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    `;
    });
    printContent += `
    </body>
    </html>
  `;
    // å†™å…¥å†…容到新窗口
    printWindow.document.write(printContent);
    printWindow.document.close();
    // ç­‰å¾…内容加载完成后打印
    printWindow.onload = () => {
        setTimeout(() => {
            printWindow.print();
            printWindow.close();
            printPreviewVisible.value = false;
        }, 500);
    };
};
// æ ¼å¼åŒ–日期
const formatDate = (dateString) => {
    if (!dateString) return getCurrentDate();
    const date = new Date(dateString);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}/${month}/${day}`;
};
// æ ¼å¼åŒ–日期时间
const formatDateTime = (date) => {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// è®¡ç®—产品总数量
const getTotalQuantity = (products) => {
  if (!products || products.length === 0) return '0';
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.quantity) || 0);
  }, 0);
  return total.toFixed(2);
};
// è®¡ç®—产品总金额
const getTotalAmount = (products) => {
  if (!products || products.length === 0) return '0';
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
  }, 0);
  return total.toFixed(2);
};
// ç”¨äºŽæ‰“印的计算函数
const getTotalQuantityForPrint = (products) => {
  if (!products || products.length === 0) return '0';
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.quantity) || 0);
  }, 0);
  return total.toFixed(2);
};
const getTotalAmountForPrint = (products) => {
  if (!products || products.length === 0) return '0';
  const total = products.reduce((sum, product) => {
    return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
  }, 0);
  return total.toFixed(2);
};
const mathNum = () => {
  console.log("productForm.value", productForm.value);
  if (!productForm.value.taxInclusiveUnitPrice) {
    return;
  }
  if (!productForm.value.quantity) {
    return;
  }
  // å«ç¨Žæ€»ä»·è®¡ç®—
  productForm.value.taxInclusiveTotalPrice =
    proxy.calculateTaxIncludeTotalPrice(
      productForm.value.taxInclusiveUnitPrice,
      productForm.value.quantity
    );
  if (productForm.value.taxRate) {
    // ä¸å«ç¨Žæ€»ä»·è®¡ç®—
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        productForm.value.taxRate
      );
  }
};
// æ ¹æ®å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
const calculateFromTotalPrice = () => {
  if (isCalculating.value) return;
  const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
  const quantity = parseFloat(productForm.value.quantity);
  if (!totalPrice || !quantity || quantity <= 0) {
    return;
  }
  isCalculating.value = true;
  // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
  productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
  // å¦‚果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        totalPrice,
        productForm.value.taxRate
      );
  }
  isCalculating.value = false;
};
// æ ¹æ®ä¸å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
const calculateFromExclusiveTotalPrice = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
  const quantity = parseFloat(productForm.value.quantity);
  const taxRate = parseFloat(productForm.value.taxRate);
  if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
    return;
  }
  isCalculating.value = true;
  // å…ˆè®¡ç®—含税总价 = ä¸å«ç¨Žæ€»ä»· / (1 - ç¨Žçއ/100)
  const taxRateDecimal = taxRate / 100;
  const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
  productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
  // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
  productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
  isCalculating.value = false;
};
// æ ¹æ®æ•°é‡å˜åŒ–计算总价
const calculateFromQuantity = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  const quantity = parseFloat(productForm.value.quantity);
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
  if (!quantity || quantity <= 0 || !unitPrice) {
    return;
  }
  isCalculating.value = true;
  // è®¡ç®—含税总价
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
  // å¦‚果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        productForm.value.taxRate
      );
  }
  isCalculating.value = false;
};
// æ ¹æ®å«ç¨Žå•价变化计算总价
const calculateFromUnitPrice = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  const quantity = parseFloat(productForm.value.quantity);
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
  if (!quantity || quantity <= 0 || !unitPrice) {
    return;
  }
  isCalculating.value = true;
  // è®¡ç®—含税总价
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
  // å¦‚果有税率,计算不含税总价
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        productForm.value.taxRate
      );
  }
  isCalculating.value = false;
};
// æ ¹æ®ç¨ŽçŽ‡å˜åŒ–è®¡ç®—ä¸å«ç¨Žæ€»ä»·
const calculateFromTaxRate = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
  const taxRate = parseFloat(productForm.value.taxRate);
  if (!inclusiveTotalPrice || !taxRate) {
    return;
  }
  isCalculating.value = true;
  // è®¡ç®—不含税总价
  productForm.value.taxExclusiveTotalPrice =
    proxy.calculateTaxExclusiveTotalPrice(
      inclusiveTotalPrice,
      taxRate
    );
  isCalculating.value = false;
};
/**
 * ä¸‹è½½æ–‡ä»¶
 *
 * @param row ä¸‹è½½æ–‡ä»¶çš„相关信息对象
 */
const fileListRef = ref(null)
const downLoadFile = (row) => {
  getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
    fileListRef.value.open(res.salesLedgerFiles)
  });
}
// æ‰“开发货弹框
const openDeliveryForm = (row) => {
  getProductInventory({ salesLedgerId: row.id, type:1 }).then((res) => {
    currentDeliveryRow.value = row;
    deliveryForm.value = {
      shippingDate: getCurrentDate(),
      shippingCarNumber: "",
    };
    deliveryFormVisible.value = true;
  }).catch(err => {
    // ElMessage.error(err);
  });
};
// æäº¤å‘货表单
const submitDelivery = () => {
  proxy.$refs["deliveryFormRef"].validate((valid) => {
    if (valid) {
      addShippingInfo({
        salesLedgerId: currentDeliveryContext.value.parentRow.id,
        salesLedgerProductId: currentDeliveryContext.value.productRow.id,
        shippingDate: deliveryForm.value.shippingDate,
        shippingCarNumber: deliveryForm.value.shippingCarNumber,
      })
        .then(() => {
          proxy.$modal.msgSuccess("发货成功");
          closeDeliveryDia();
        })
        .catch(() => {
          proxy.$modal.msgError("发货失败,请重试");
        });
  // æ‰“印功能
  const handlePrint = async () => {
    if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择要打印的数据");
      return;
    }
    // æ˜¾ç¤ºåŠ è½½çŠ¶æ€
    proxy.$modal.loading("正在获取产品数据,请稍候...");
    try {
      // ä¸ºæ¯ä¸ªé€‰ä¸­çš„销售台账记录查询对应的产品数据
      const printDataWithProducts = [];
      for (const row of selectedRows.value) {
        try {
          // è°ƒç”¨productList接口查询产品数据
          const productRes = await productList({
            salesLedgerId: row.id,
            type: 1,
          });
          // å°†äº§å“æ•°æ®æ•´åˆåˆ°é”€å”®å°è´¦è®°å½•中
          const rowWithProducts = {
            ...row,
            products: productRes.data || [],
          };
          printDataWithProducts.push(rowWithProducts);
        } catch (error) {
          console.error(`获取销售台账 ${row.id} çš„产品数据失败:`, error);
          // å³ä½¿æŸä¸ªè®°å½•的产品数据获取失败,也要包含该记录
          printDataWithProducts.push({
            ...row,
            products: [],
          });
        }
      }
      printData.value = printDataWithProducts;
      console.log("打印数据(包含产品):", printData.value);
      printPreviewVisible.value = true;
    } catch (error) {
      console.error("获取产品数据失败:", error);
      proxy.$modal.msgError("获取产品数据失败,请重试");
    } finally {
      proxy.$modal.closeLoading();
    }
  };
  // æ‰§è¡Œæ‰“印
  const executePrint = () => {
    console.log("开始执行打印,数据条数:", printData.value.length);
    console.log("打印数据:", printData.value);
    // åˆ›å»ºä¸€ä¸ªæ–°çš„æ‰“印窗口
    const printWindow = window.open("", "_blank", "width=800,height=600");
    // æž„建打印内容
    let printContent = `
                      <!DOCTYPE html>
                      <html>
                      <head>
                        <meta charset="UTF-8">
                        <title>打印预览</title>
                        <style>
                          body {
                            margin: 0;
                            padding: 0;
                            font-family: "SimSun", serif;
                            background: white;
                          }
                                                                       .print-page {
                              width: 200mm;
                              height: 75mm;
                              padding: 10mm;
                              padding-left: 20mm;
                              background: white;
                              box-sizing: border-box;
                              page-break-after: always;
                              page-break-inside: avoid;
                            }
                           .print-page:last-child {
                             page-break-after: avoid;
                           }
                          .delivery-note {
                            width: 100%;
                            height: 100%;
                            font-size: 12px;
                            line-height: 1.2;
                            display: flex;
                            flex-direction: column;
                            color: #000;
                          }
                          .header {
                            text-align: center;
                            margin-bottom: 8px;
                          }
                          .company-name {
                            font-size: 18px;
                            font-weight: bold;
                            margin-bottom: 4px;
                          }
                          .document-title {
                            font-size: 16px;
                            font-weight: bold;
                          }
                          .info-section {
                            margin-bottom: 8px;
                            display: flex;
                            justify-content: space-between;
                            align-items: center;
                          }
                          .info-row {
                            line-height: 20px;
                          }
                          .label {
                            font-weight: bold;
                            width: 60px;
                            font-size: 12px;
                          }
                          .value {
                            margin-right: 20px;
                            min-width: 80px;
                            font-size: 12px;
                          }
                                   .table-section {
                                   margin-bottom: 40px;
                            //  flex: 0.6;
                           }
                          .product-table {
                            width: 100%;
                            border-collapse: collapse;
                            border: 1px solid #000;
                          }
                                   .product-table th, .product-table td {
                             border: 1px solid #000;
                             padding: 6px;
                             text-align: center;
                             font-size: 12px;
                             line-height: 1.4;
                           }
                          .product-table th {
                            font-weight: bold;
                          }
                          .total-value {
                            font-weight: bold;
                          }
                          .footer-section {
                            margin-top: auto;
                          }
                          .footer-row {
                            display: flex;
                            margin-bottom: 3px;
                            line-height: 22px;
                            justify-content: space-between;
                          }
                          .footer-item {
                            display: flex;
                            margin-right: 20px;
                          }
                          .footer-item .label {
                            font-weight: bold;
                            width: 80px;
                            font-size: 12px;
                          }
                          .footer-item .value {
                            min-width: 80px;
                            font-size: 12px;
                          }
                          .address-item .address-value {
                            min-width: 200px;
                          }
                          @media print {
                            body {
                              margin: 0;
                              padding: 0;
                            }
                                       .print-page {
                               margin: 0;
                               padding: 10mm;
                               /* padding-left: 20mm; */
                               page-break-inside: avoid;
                               page-break-after: always;
                             }
                             .print-page:last-child {
                               page-break-after: avoid;
                             }
                          }
                        </style>
                      </head>
                      <body>
                    `;
    // ä¸ºæ¯æ¡æ•°æ®ç”Ÿæˆæ‰“印页面
    printData.value.forEach((item, index) => {
      printContent += `
                        <div class="print-page">
                          <div class="delivery-note">
                            <div class="header">
                              <div class="company-name">鼎诚瑞实业有限责任公司</div>
                              <div class="document-title">零售发货单</div>
                            </div>
                            <div class="info-section">
                              <div class="info-row">
                                <div>
                                  <span class="label">发货日期:</span>
                                  <span class="value">${formatDate(
                                    item.createTime
                                  )}</span>
                                </div>
                                <div>
                                  <span class="label">客户名称:</span>
                                  <span class="value">${
                                    item.customerName || "张爱有"
                                  }</span>
                                </div>
                              </div>
                              <div class="info-row">
                                <span class="label">单号:</span>
                                <span class="value">${
                                  item.salesContractNo || ""
                                }</span>
                              </div>
                            </div>
                            <div class="table-section">
                              <table class="product-table">
                                <thead>
                                  <tr>
                                    <th>产品名称</th>
                                    <th>规格型号</th>
                                    <th>单位</th>
                                    <th>单价</th>
                                    <th>零售数量</th>
                                    <th>零售金额</th>
                                  </tr>
                                </thead>
                                <tbody>
                                  ${
                                    item.products && item.products.length > 0
                                      ? item.products
                                          .map(
                                            product => `
                                      <tr>
                                        <td>${product.productCategory || ""}</td>
                                        <td>${
                                          product.specificationModel || ""
                                        }</td>
                                        <td>${product.unit || ""}</td>
                                        <td>${
                                          product.taxInclusiveUnitPrice || "0"
                                        }</td>
                                        <td>${product.quantity || "0"}</td>
                                        <td>${
                                          product.taxInclusiveTotalPrice || "0"
                                        }</td>
                                      </tr>
                                    `
                                          )
                                          .join("")
                                      : '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
                                  }
                                </tbody>
                                <tfoot>
                                  <tr>
                                    <td class="label">合计</td>
                                    <td class="total-value"></td>
                                    <td class="total-value"></td>
                                    <td class="total-value"></td>
                                    <td class="total-value">${getTotalQuantityForPrint(
                                      item.products
                                    )}</td>
                                    <td class="total-value">${getTotalAmountForPrint(
                                      item.products
                                    )}</td>
                                  </tr>
                                </tfoot>
                              </table>
                            </div>
                            <div class="footer-section">
                              <div class="footer-row">
                                <div class="footer-item">
                                  <span class="label">收货电话:</span>
                                  <span class="value"></span>
                                </div>
                                <div class="footer-item">
                                  <span class="label">收货人:</span>
                                  <span class="value"></span>
                                </div>
                                <div class="footer-item address-item">
                                  <span class="label">收货地址:</span>
                                  <span class="value address-value"></span>
                                </div>
                              </div>
                              <div class="footer-row">
                                <div class="footer-item">
                                  <span class="label">操作员:</span>
                                  <span class="value">${
                                    userStore.nickName || "撕开前"
                                  }</span>
                                </div>
                                <div class="footer-item">
                                  <span class="label">打印日期:</span>
                                  <span class="value">${formatDateTime(
                                    new Date()
                                  )}</span>
                                </div>
                              </div>
                            </div>
                          </div>
                        </div>
                      `;
    });
    printContent += `
                      </body>
                      </html>
                    `;
    // å†™å…¥å†…容到新窗口
    printWindow.document.write(printContent);
    printWindow.document.close();
    // ç­‰å¾…内容加载完成后打印
    printWindow.onload = () => {
      setTimeout(() => {
        printWindow.print();
        printWindow.close();
        printPreviewVisible.value = false;
      }, 500);
    };
  };
  // æ ¼å¼åŒ–日期
  const formatDate = dateString => {
    if (!dateString) return getCurrentDate();
    const date = new Date(dateString);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}/${month}/${day}`;
  };
  // æ ¼å¼åŒ–日期时间
  const formatDateTime = date => {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
  };
  // èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
  function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  }
  // è®¡ç®—产品总数量
  const getTotalQuantity = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.quantity) || 0);
    }, 0);
    return total.toFixed(2);
  };
  // è®¡ç®—产品总金额
  const getTotalAmount = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
    }, 0);
    return total.toFixed(2);
  };
  // ç”¨äºŽæ‰“印的计算函数
  const getTotalQuantityForPrint = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.quantity) || 0);
    }, 0);
    return total.toFixed(2);
  };
  const getTotalAmountForPrint = products => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
    }, 0);
    return total.toFixed(2);
  };
  const mathNum = () => {
    console.log("productForm.value", productForm.value);
    if (!productForm.value.taxInclusiveUnitPrice) {
      return;
    }
    if (!productForm.value.quantity) {
      return;
    }
    // å«ç¨Žæ€»ä»·è®¡ç®—
    productForm.value.taxInclusiveTotalPrice =
      proxy.calculateTaxIncludeTotalPrice(
        productForm.value.taxInclusiveUnitPrice,
        productForm.value.quantity
      );
    if (productForm.value.taxRate) {
      // ä¸å«ç¨Žæ€»ä»·è®¡ç®—
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          productForm.value.taxRate
        );
    }
  };
  // æ ¹æ®å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
  const calculateFromTotalPrice = () => {
    if (isCalculating.value) return;
    const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
    const quantity = parseFloat(productForm.value.quantity);
    if (!totalPrice || !quantity || quantity <= 0) {
      return;
    }
    isCalculating.value = true;
    // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
    productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          totalPrice,
          productForm.value.taxRate
        );
    }
    isCalculating.value = false;
  };
  // æ ¹æ®ä¸å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
  const calculateFromExclusiveTotalPrice = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (isCalculating.value) return;
    const exclusiveTotalPrice = parseFloat(
      productForm.value.taxExclusiveTotalPrice
    );
    const quantity = parseFloat(productForm.value.quantity);
    const taxRate = parseFloat(productForm.value.taxRate);
    if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
      return;
    }
    isCalculating.value = true;
    // å…ˆè®¡ç®—含税总价 = ä¸å«ç¨Žæ€»ä»· / (1 - ç¨Žçއ/100)
    const taxRateDecimal = taxRate / 100;
    const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
    productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
    // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
    productForm.value.taxInclusiveUnitPrice = (
      inclusiveTotalPrice / quantity
    ).toFixed(2);
    isCalculating.value = false;
  };
  // æ ¹æ®æ•°é‡å˜åŒ–计算总价
  const calculateFromQuantity = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (isCalculating.value) return;
    const quantity = parseFloat(productForm.value.quantity);
    const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
      return;
    }
    isCalculating.value = true;
    // è®¡ç®—含税总价
    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          productForm.value.taxRate
        );
    }
    isCalculating.value = false;
  };
  // æ ¹æ®å«ç¨Žå•价变化计算总价
  const calculateFromUnitPrice = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (isCalculating.value) return;
    const quantity = parseFloat(productForm.value.quantity);
    const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
      return;
    }
    isCalculating.value = true;
    // è®¡ç®—含税总价
    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          productForm.value.taxRate
        );
    }
    isCalculating.value = false;
  };
  // æ ¹æ®ç¨ŽçŽ‡å˜åŒ–è®¡ç®—ä¸å«ç¨Žæ€»ä»·
  const calculateFromTaxRate = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (isCalculating.value) return;
    const inclusiveTotalPrice = parseFloat(
      productForm.value.taxInclusiveTotalPrice
    );
    const taxRate = parseFloat(productForm.value.taxRate);
    if (!inclusiveTotalPrice || !taxRate) {
      return;
    }
    isCalculating.value = true;
    // è®¡ç®—不含税总价
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate);
    isCalculating.value = false;
  };
  /**
   * ä¸‹è½½æ–‡ä»¶
   *
   * @param row ä¸‹è½½æ–‡ä»¶çš„相关信息对象
   */
  const fileListRef = ref(null);
  const downLoadFile = row => {
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
      fileListRef.value.open(res.salesLedgerFiles);
    });
  };
  // æ‰“开发货弹框
  const openDeliveryForm = (props, scope) => {
    getProductInventory({ salesLedgerId: props.id, type: 1 })
      .then(res => {
        currentDeliveryContext.value = {
          id: props.id,
          productId: scope.id,
        };
        deliveryForm.value = {
          shippingDate: getCurrentDate(),
          shippingCarNumber: "",
        };
        // currentDeliveryRow.value = props.row;
        deliveryFormVisible.value = true;
      })
      .catch(err => {
        // ElMessage.error(err);
      });
  };
  // æäº¤å‘货表单
  const submitDelivery = () => {
    proxy.$refs["deliveryFormRef"].validate(valid => {
      if (valid) {
        addShippingInfo({
          salesLedgerId: currentDeliveryContext.value.id,
          salesLedgerProductId: currentDeliveryContext.value.productId,
          shippingDate: deliveryForm.value.shippingDate,
          shippingCarNumber: deliveryForm.value.shippingCarNumber,
        })
          .then(() => {
            proxy.$modal.msgSuccess("发货成功");
            closeDeliveryDia();
          })
          .catch(() => {
            proxy.$modal.msgError("发货失败,请重试");
          });
      }
    });
  };
  // å…³é—­å‘货弹框
  const closeDeliveryDia = () => {
    proxy.resetForm("deliveryFormRef");
    deliveryFormVisible.value = false;
    currentDeliveryContext.value = null;
  };
  onMounted(() => {
    getList();
  });
};
// å…³é—­å‘货弹框
const closeDeliveryDia = () => {
  proxy.resetForm("deliveryFormRef");
  deliveryFormVisible.value = false;
  currentDeliveryContext.value = null;
};
onMounted(() => {
    getList();
});
</script>
<style scoped lang="scss">
.ml-10 {
  margin-left: 10px;
}
  .ml-10 {
    margin-left: 10px;
  }
.table_list {
  margin-top: unset;
}
  .table_list {
    margin-top: unset;
  }
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
}
.print-preview-dialog {
    .el-dialog__body {
        padding: 0;
        max-height: 80vh;
        overflow-y: auto;
    }
}
  .actions {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
  }
  .print-preview-dialog {
    .el-dialog__body {
      padding: 0;
      max-height: 80vh;
      overflow-y: auto;
    }
  }
.print-preview-container {
    .print-preview-header {
        padding: 15px;
        border-bottom: 1px solid #e4e7ed;
        text-align: center;
  .print-preview-container {
    .print-preview-header {
      padding: 15px;
      border-bottom: 1px solid #e4e7ed;
      text-align: center;
        .el-button {
            margin: 0 10px;
        }
    }
      .el-button {
        margin: 0 10px;
      }
    }
    .print-preview-content {
        padding: 20px;
        background-color: #f5f5f5;
        min-height: 400px;
    }
}
    .print-preview-content {
      padding: 20px;
      background-color: #f5f5f5;
      min-height: 400px;
    }
  }
.print-page {
    width: 220mm;
    height: 90mm;
    padding: 10mm;
    margin: 0 auto;
    background: white;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    margin-bottom: 10px;
    box-sizing: border-box;
}
  .print-page {
    width: 220mm;
    height: 90mm;
    padding: 10mm;
    margin: 0 auto;
    background: white;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    margin-bottom: 10px;
    box-sizing: border-box;
  }
.delivery-note {
    width: 100%;
    height: 100%;
    font-family: "SimSun", serif;
    font-size: 10px;
    line-height: 1.2;
    display: flex;
    flex-direction: column;
}
  .delivery-note {
    width: 100%;
    height: 100%;
    font-family: "SimSun", serif;
    font-size: 10px;
    line-height: 1.2;
    display: flex;
    flex-direction: column;
  }
.header {
    text-align: center;
    margin-bottom: 8px;
  .header {
    text-align: center;
    margin-bottom: 8px;
    .company-name {
        font-size: 18px;
        font-weight: bold;
        margin-bottom: 4px;
    }
    .company-name {
      font-size: 18px;
      font-weight: bold;
      margin-bottom: 4px;
    }
    .document-title {
        font-size: 16px;
        font-weight: bold;
    }
}
    .document-title {
      font-size: 16px;
      font-weight: bold;
    }
  }
.info-section {
    margin-bottom: 8px;
    display: flex;
    justify-content: space-between;
    align-items: center;
  .info-section {
    margin-bottom: 8px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .info-row {
        line-height: 20px;
    .info-row {
      line-height: 20px;
        .label {
            font-weight: bold;
            width: 60px;
            font-size: 14px;
        }
      .label {
        font-weight: bold;
        width: 60px;
        font-size: 14px;
      }
        .value {
            margin-right: 20px;
            min-width: 80px;
            font-size: 14px;
        }
    }
}
      .value {
        margin-right: 20px;
        min-width: 80px;
        font-size: 14px;
      }
    }
  }
.table-section {
    margin-bottom: 4px;
    flex: 1;
  .table-section {
    margin-bottom: 4px;
    flex: 1;
    .product-table {
        width: 100%;
        border-collapse: collapse;
        border: 1px solid #000;
    .product-table {
      width: 100%;
      border-collapse: collapse;
      border: 1px solid #000;
        th, td {
            border: 1px solid #000;
            padding: 6px;
            text-align: center;
            font-size: 14px;
            line-height: 1.4;
        }
      th,
      td {
        border: 1px solid #000;
        padding: 6px;
        text-align: center;
        font-size: 14px;
        line-height: 1.4;
      }
        th {
            font-weight: bold;
        }
      th {
        font-weight: bold;
      }
        .total-label {
            text-align: right;
            font-weight: bold;
        }
      .total-label {
        text-align: right;
        font-weight: bold;
      }
        .total-value {
            font-weight: bold;
        }
    }
}
      .total-value {
        font-weight: bold;
      }
    }
  }
.footer-section {
    .footer-row {
        display: flex;
        margin-bottom: 3px;
        line-height: 20px;
        justify-content: space-between;
  .footer-section {
    .footer-row {
      display: flex;
      margin-bottom: 3px;
      line-height: 20px;
      justify-content: space-between;
        .footer-item {
            display: flex;
            margin-right: 20px;
      .footer-item {
        display: flex;
        margin-right: 20px;
            .label {
                font-weight: bold;
                width: 80px;
                font-size: 14px;
            }
        .label {
          font-weight: bold;
          width: 80px;
          font-size: 14px;
        }
            .value {
                min-width: 80px;
                font-size: 14px;
            }
        .value {
          min-width: 80px;
          font-size: 14px;
        }
            &.address-item {
                .address-value {
                    min-width: 200px;
                }
            }
        }
    }
}
        &.address-item {
          .address-value {
            min-width: 200px;
          }
        }
      }
    }
  }
@media print {
    .app-container {
        display: none;
    }
  @media print {
    .app-container {
      display: none;
    }
    .print-page {
        box-shadow: none;
        margin: 0;
        padding: 10mm;
        padding-left: 20mm;
        page-break-inside: avoid;
        page-break-after: always;
    }
    .print-page:last-child {
        page-break-after: avoid;
    }
}
    .print-page {
      box-shadow: none;
      margin: 0;
      padding: 10mm;
      padding-left: 20mm;
      page-break-inside: avoid;
      page-break-after: always;
    }
    .print-page:last-child {
      page-break-after: avoid;
    }
  }
</style>