Răsfoiți Sursa

新增物品审批界面

695593266@qq.com 23 ore în urmă
părinte
comite
b25ce68ee1

+ 57 - 0
src/api/bpm/components/itemPublishApproval/index.js

@@ -0,0 +1,57 @@
+import request from '@/utils/request';
+
+// 物品/产品详情
+export async function getCategoryDetails(id) {
+  const res = await request.get(`/main/category/info/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 字段模型
+export async function fieldModel(params) {
+  const res = await request.get(`/main/fieldmodel/list`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 根据物品 ID 获取 BOM 列表
+export async function getBomAssociationList(params) {
+  const res = await request.get('/main/bomCategory/getList', { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 根据物品 ID 获取工艺路线
+export async function getBomRoutingList(categoryId) {
+  const res = await request.get(
+    `/main/bomCategory/bomRoutingListByCategoryId/${categoryId}`
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 获取关联模具、设备
+export async function getRelatesInformationList(data) {
+  const res = await request.post('/main/category/getRelatesInformationList', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 获取客户、供应商、生产厂家
+export async function getContactsByCategoryIds(data) {
+  const res = await request.post('/eom/contact/queryByCategoryIds', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 677 - 0
src/views/bpm/handleTask/components/itemPublishApproval/detailDialog.vue

@@ -0,0 +1,677 @@
+<template>
+  <div class="item-publish-approval" v-loading="loading">
+    <headerTitle title="物品发布审批"></headerTitle>
+
+    <div class="section">
+      <div class="section-title">基本信息</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item
+          v-for="item in basicFields"
+          :key="item.prop"
+          :label="item.label"
+        >
+          {{ formatValue(form, item) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section" v-if="dynamicFields.length">
+      <div class="section-title">扩展字段</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item
+          v-for="item in dynamicFields"
+          :key="item.prop"
+          :label="item.label"
+        >
+          {{ formatValue(form.extField, item) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section" v-if="industryAttribute == 1">
+      <div class="section-title">药品信息</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item
+          v-for="item in medicineFields"
+          :key="item.prop"
+          :label="item.label"
+        >
+          {{ formatValue(form, item) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section">
+      <div class="section-title">标签</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item label="是否消耗品">
+          {{ yesNo(form.extTagField && form.extTagField.isConsumables) }}
+        </el-descriptions-item>
+        <el-descriptions-item label="需要序列号" v-if="clientEnvironmentId == 5">
+          {{ yesNo(form.extTagField && form.extTagField.needProductSequence) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section" v-if="imageList.length">
+      <div class="section-title">产品图片</div>
+      <el-image
+        v-for="(item, index) in imageList"
+        :key="item.id || item.url || index"
+        class="product-image"
+        :src="getFileUrl(item)"
+        :preview-src-list="previewList"
+        fit="cover"
+      />
+    </div>
+
+    <div class="section">
+      <div class="section-title">仓储配置</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item
+          v-for="item in warehouseFields"
+          :key="item.prop"
+          :label="item.label"
+        >
+          {{ formatValue(categoryWms, item) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section" v-if="packageDispositionVOList.length">
+      <div class="section-title">包装组</div>
+      <el-table :data="packageDispositionVOList" border size="small">
+        <el-table-column prop="code" label="编码" min-width="120" />
+        <el-table-column prop="name" label="名称" min-width="120" />
+        <el-table-column label="是否启用" min-width="90">
+          <template slot-scope="scope">
+            {{ yesNo(scope.row.status) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="最小包装单元" min-width="150">
+          <template slot-scope="scope">
+            {{ formatPackageUnit(scope.row, 'min') }}
+          </template>
+        </el-table-column>
+        <el-table-column label="内包装单元" min-width="150">
+          <template slot-scope="scope">
+            {{ formatPackageUnit(scope.row, 'in') }}
+          </template>
+        </el-table-column>
+        <el-table-column label="外包装单元" min-width="150">
+          <template slot-scope="scope">
+            {{ formatPackageUnit(scope.row, 'out') }}
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <div class="section">
+      <div class="section-title">销售配置</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item label="计价方式">
+          {{ pricingWayText(categorySales.pricingWay) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section">
+      <div class="section-title">采购信息</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item
+          v-for="item in purchaseFields"
+          :key="item.prop"
+          :label="item.label"
+        >
+          {{ formatValue(categoryPurchase, item) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section">
+      <div class="section-title">生产信息</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item
+          v-for="item in productionFields"
+          :key="item.prop"
+          :label="item.label"
+        >
+          {{ formatValue(categoryMes, item) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section">
+      <div class="section-title">计划</div>
+      <el-descriptions :column="3" border>
+        <el-descriptions-item
+          v-for="item in planFields"
+          :key="item.prop"
+          :label="item.label"
+        >
+          {{ formatValue(categoryAps, item) }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="section">
+      <div class="section-title">备注信息</div>
+      <el-descriptions :column="1" border>
+        <el-descriptions-item label="附件">
+          <fileMain v-model="remarkform.remarkAttach" type="view"></fileMain>
+        </el-descriptions-item>
+        <el-descriptions-item label="备注">
+          {{ remarkform.remark || '-' }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <relatedInfo
+      :category-id="businessId"
+      :category-level-id="form.categoryLevelId"
+      :category-level-group-id="form.categoryLevelGroupId"
+    />
+  </div>
+</template>
+
+<script>
+  import fileMain from '@/components/addDoc/index.vue';
+  import relatedInfo from './relatedInfo.vue';
+  import {
+    fieldModel,
+    getCategoryDetails
+  } from '@/api/bpm/components/itemPublishApproval';
+  import { getByCode } from '@/api/system/dictionary-data';
+  import { parameterGetByCode } from '@/api/main';
+  import dictEnum from '@/enum/dict';
+
+  const DICT_CODE_MAP = {
+    存货类型: 'inventory_type',
+    包装单位: 'packing_unit',
+    周期单位: 'periodic_unit',
+    包装强度: 'packagingStrength',
+    包装密度: 'packagingDensity',
+    贮藏: 'layBy',
+    执行标准: 'enforceStandards'
+  };
+
+  export default {
+    name: 'ItemPublishApprovalDetail',
+    components: { fileMain, relatedInfo },
+    props: {
+      taskDefinitionKey: {
+        type: String,
+        default: ''
+      },
+      taskId: {
+        default: ''
+      },
+      businessId: {
+        default: ''
+      },
+      id: {
+        default: ''
+      },
+      businessCode: {
+        default: ''
+      },
+      processDefinitionId: {
+        default: ''
+      },
+      businessType: {
+        default: ''
+      }
+    },
+    data() {
+      return {
+        loading: false,
+        industryAttribute: '',
+        form: {
+          extField: {},
+          extTagField: {}
+        },
+        categoryAps: {},
+        categoryMes: {},
+        categoryPurchase: {},
+        categorySales: {},
+        categoryWms: {},
+        packageDispositionVOList: [],
+        remarkform: {
+          remarkAttach: [],
+          remark: ''
+        },
+        dictMaps: {},
+        dynamicFields: [],
+        basicFields: [
+          { label: '分类', prop: 'categoryLevelName' },
+          { label: '编码', prop: 'code' },
+          { label: '名称', prop: 'name' },
+          { label: '存货类型', prop: 'attributeType', dictName: '存货类型' },
+          { label: '属性类型', prop: 'componentAttribute', formatter: this.componentAttributeText },
+          { label: '生产类型', prop: 'produceType', formatter: this.produceTypeText },
+          { label: '加工类型', prop: 'isConsumable', formatter: this.processingTypeText },
+          { label: '牌号', prop: 'brandNum' },
+          { label: '型号', prop: 'modelType' },
+          { label: '规格', prop: 'specification' },
+          { label: '计量类型', prop: 'measureType', formatter: this.measureTypeText },
+          { label: '计量单位', prop: 'measuringUnit', dictName: '计量单位' },
+          { label: '重量单位', prop: 'weightUnit', dictName: '重量单位' },
+          { label: '包装单位', prop: 'packingUnit', dictName: '包装单位' },
+          { label: '面积', prop: 'area' },
+          { label: '面积单位', prop: 'areaUnit', dictName: '计量单位' },
+          { label: '图号/件号', prop: 'imgCode' },
+          { label: '毛重', prop: 'roughWeight' },
+          { label: '净重', prop: 'netWeight' },
+          { label: '体积', prop: 'volume', unitProp: 'volumeUnit', unitDictName: '体积单位' },
+          { label: '级别', prop: 'level' },
+          { label: '机型', prop: 'modelKey', dictName: '物品机型' },
+          { label: '颜色', prop: 'colorKey', dictName: '物品颜色' },
+          { label: '状态', prop: 'isEnabled', formatter: this.enabledText },
+          { label: '物联产品Key', prop: 'iotProductKey' }
+        ],
+        medicineFields: [
+          { label: '性味与归经', prop: 'propertiesChannelTropism' },
+          { label: '功能与主治', prop: 'actionsAndUses' },
+          { label: '用法与用量', prop: 'usageAndDosage' }
+        ],
+        warehouseFields: [
+          { label: '启用库存预警', prop: 'isWarn', formatter: this.yesNo },
+          { label: '盘点模式', prop: 'inventoryMode', formatter: this.inventoryModeText },
+          { label: '允许拆包', prop: 'isUnpack', formatter: this.yesNo },
+          {
+            label: '安全库存',
+            prop: 'secureInventory',
+            unitValue: () => this.form.measuringUnit,
+            unitDictName: '计量单位'
+          },
+          {
+            label: '最小库存',
+            prop: 'minInventory',
+            unitValue: () => this.form.measuringUnit,
+            unitDictName: '计量单位'
+          },
+          {
+            label: '最大库存',
+            prop: 'maxInventory',
+            unitValue: () => this.form.measuringUnit,
+            unitDictName: '计量单位'
+          },
+          { label: '质保预警参考', prop: 'warrantyWarnRefer', dictName: '质保预警参考' },
+          {
+            label: '保质期',
+            prop: 'warrantyPeriod',
+            unitProp: 'warrantyPeriodUnit',
+            unitDictName: '保质期单位'
+          },
+          { label: '默认仓库', prop: 'warehouseName' }
+        ],
+        purchaseFields: [
+          {
+            label: '周期',
+            prop: 'purchasingCycle',
+            unitProp: 'purchasingCycleUnit',
+            unitDictName: '周期单位'
+          },
+          { label: '倍数', prop: 'purchaseMultiplier' },
+          {
+            label: '最低订购量',
+            prop: 'minimumOrderQuantity',
+            unitProp: 'measuringUnit',
+            unitDictName: '计量单位'
+          },
+          { label: '产地', prop: 'purchaseOrigins', dictName: '产地' },
+          { label: '采购组织', prop: 'checkDepartName' },
+          { label: '采购员', prop: 'checkPersonName' }
+        ],
+        productionFields: [
+          { label: '是否齐套件', prop: 'isCompleteSet', formatter: this.yesNo },
+          { label: '消耗波动', prop: 'consumWave', suffix: '%' },
+          { label: '变动损耗率', prop: 'changeLossRate', suffix: '%' },
+          { label: '固定损耗数', prop: 'fixLossNum' },
+          { label: '排程类型', prop: 'apsType', dictName: '排程类型' },
+          { label: '生产周期', prop: 'productionDays', suffix: '天' },
+          { label: '允许改型', prop: 'isModify', formatter: this.yesNo },
+          { label: '允许返工返修', prop: 'isRework', formatter: this.yesNo },
+          { label: '是否返回料', prop: 'isRematerial', formatter: this.yesNo },
+          { label: '是否副产品', prop: 'isByProduct', formatter: this.yesNo },
+          { label: '是否废品', prop: 'isWaste', formatter: this.yesNo },
+          { label: '是否不良品', prop: 'isDefective', formatter: this.yesNo }
+        ],
+        planFields: [
+          { label: '固定提前期', prop: 'fixLeadTime' },
+          { label: '变动提前期', prop: 'changeLeadTime' },
+          { label: '检验提前期', prop: 'checkLeadTime' },
+          { label: '累计提前期', prop: 'cumLeadTime' },
+          { label: '提前期单位', prop: 'unit', dictName: '提前期单位' },
+          {
+            label: '订货间隔期',
+            prop: 'orderIntervalTime',
+            unitProp: 'orderIntervalUnit',
+            unitDictName: '提前期单位'
+          }
+        ]
+      };
+    },
+    computed: {
+      clientEnvironmentId() {
+        return this.$store.state.user.info.clientEnvironmentId;
+      },
+      imageList() {
+        return this.normalizeList(this.form.imgUrl);
+      },
+      previewList() {
+        return this.imageList.map((item) => this.getFileUrl(item)).filter(Boolean);
+      }
+    },
+    watch: {
+      businessId: {
+        immediate: true,
+        handler(val) {
+          if (val) {
+            this.getDetail();
+          }
+        }
+      }
+    },
+    created() {
+      this.loadStaticDictionaries();
+      this.loadIndustryAttribute();
+      this.getFieldModel();
+    },
+    methods: {
+      async getDetail() {
+        this.loading = true;
+        try {
+          const data = await getCategoryDetails(this.businessId);
+          const info = this.deepClone(data || {});
+          const category = info.category || {};
+          category.colorKey = this.normalizeJoinValue(category.colorKey);
+          category.modelKey = this.normalizeJoinValue(category.modelKey);
+          category.componentAttribute = this.normalizeList(category.componentAttribute);
+          category.produceType = this.normalizeList(category.produceType)[0] || category.produceType;
+          category.extField = category.extField || {};
+          category.extTagField = category.extTagField || {};
+
+          this.form = category;
+          const detailIndustryAttribute =
+            info.industryAttribute || category.industryAttribute;
+          if (
+            detailIndustryAttribute !== undefined &&
+            detailIndustryAttribute !== null &&
+            detailIndustryAttribute !== ''
+          ) {
+            this.industryAttribute = detailIndustryAttribute;
+          }
+          this.remarkform = {
+            remark: category.remark || '',
+            remarkAttach: this.normalizeList(category.remarkAttach)
+          };
+          this.categoryAps = this.normalizeObject(info.categoryAps);
+          this.categoryMes = this.normalizeObject(info.categoryMes);
+          this.categoryPurchase = this.normalizeObject(info.categoryPurchase);
+          this.categorySales = this.normalizeObject(info.categorySales);
+          this.categoryWms = this.normalizeObject(info.categoryWms);
+          this.packageDispositionVOList = this.buildPackageGroups(
+            this.normalizeList(info.packageDispositionVOList)
+          );
+        } finally {
+          this.loading = false;
+        }
+      },
+      async getFieldModel() {
+        try {
+          const list = await fieldModel({ relevance: 't_main_category' });
+          this.dynamicFields = (list || []).map((item) => ({
+            ...item,
+            dictName: item.modelType === 'dict' ? item.label : ''
+          }));
+          await Promise.all(
+            this.dynamicFields
+              .filter((item) => item.dictName)
+              .map((item) => this.loadDictionary(item.dictName))
+          );
+        } catch (error) {
+          this.dynamicFields = [];
+        }
+      },
+      deepClone(data) {
+        return JSON.parse(JSON.stringify(data));
+      },
+      normalizeObject(value) {
+        return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
+      },
+      normalizeList(value) {
+        if (!value) return [];
+        if (Array.isArray(value)) return value;
+        if (typeof value === 'string') {
+          try {
+            const data = JSON.parse(value);
+            return Array.isArray(data) ? data : value.split(',').filter(Boolean);
+          } catch (error) {
+            return value.split(',').filter(Boolean);
+          }
+        }
+        return [value];
+      },
+      normalizeJoinValue(value) {
+        return this.normalizeList(value).join(',');
+      },
+      async loadStaticDictionaries() {
+        const names = [
+          '存货类型',
+          '计量单位',
+          '重量单位',
+          '包装单位',
+          '体积单位',
+          '物品机型',
+          '物品颜色',
+          '质保预警参考',
+          '保质期单位',
+          '周期单位',
+          '产地',
+          '排程类型',
+          '提前期单位'
+        ];
+        await Promise.all(names.map((name) => this.loadDictionary(name)));
+      },
+      async loadIndustryAttribute() {
+        try {
+          const data = await parameterGetByCode({ code: 'industry_attribute' });
+          if (data && data.value !== undefined && data.value !== null) {
+            this.industryAttribute = data.value;
+          }
+        } catch (error) {}
+      },
+      async loadDictionary(dictName) {
+        if (!dictName || this.dictMaps[dictName]) return;
+        const code = DICT_CODE_MAP[dictName] || dictEnum[dictName];
+        if (!code) return;
+        try {
+          const res = await getByCode(code);
+          const map = (res.data || []).reduce((result, item) => {
+            const entry = Object.entries(item)[0];
+            if (entry) result[String(entry[0])] = entry[1];
+            return result;
+          }, {});
+          this.$set(this.dictMaps, dictName, map);
+        } catch (error) {
+          this.$set(this.dictMaps, dictName, {});
+        }
+      },
+      dictValue(dictName, value) {
+        if (value === undefined || value === null || value === '') return '-';
+        const map = this.dictMaps[dictName] || {};
+        const values = Array.isArray(value)
+          ? value
+          : typeof value === 'string' && value.includes(',')
+          ? value.split(',').filter(Boolean)
+          : [value];
+        return values.map((item) => map[String(item)] || item).join(',');
+      },
+      formatValue(source, item) {
+        if (!source) return '-';
+        let value = source[item.prop];
+        if (item.formatter) {
+          return item.formatter(value);
+        }
+        if (item.dictName) {
+          value = this.dictValue(item.dictName, value);
+        }
+        if (Array.isArray(value)) {
+          value = value.join(',');
+        }
+        if (value === undefined || value === null || value === '') {
+          return '-';
+        }
+        const unitValue = item.unitProp
+          ? source[item.unitProp]
+          : item.unitValue
+          ? item.unitValue()
+          : '';
+        const formattedUnit = item.unitDictName
+          ? this.dictValue(item.unitDictName, unitValue)
+          : unitValue;
+        const unit = formattedUnit === '-' ? '' : formattedUnit;
+        return `${value}${unit || item.suffix ? ' ' + (unit || item.suffix) : ''}`;
+      },
+      buildPackageGroups(rows) {
+        if (!rows.length) return [];
+        if (rows.some((item) => item.minPackageCell !== undefined)) return rows;
+        const groups = {};
+        rows.forEach((item) => {
+          const key = item.code || item.name || item.id;
+          if (!groups[key]) {
+            groups[key] = {
+              code: item.code,
+              name: item.name,
+              status: item.status
+            };
+          }
+          const group = groups[key];
+          if (item.name) group.name = item.name;
+          if (String(item.sort) === '0') {
+            group.status = item.status;
+          } else if (String(item.sort) === '1') {
+            group.minPackageCell = item.packageCell;
+            group.packageUnit = item.packageUnit;
+            group.minConversionUnit = item.conversionUnit;
+          } else if (String(item.sort) === '2') {
+            group.inPackageCell = item.packageCell;
+            group.inPackageUnit = item.packageUnit;
+            group.inConversionUnit = item.conversionUnit;
+          } else if (String(item.sort) === '3') {
+            group.outPackageCell = item.packageCell;
+            group.outPackageUnit = item.packageUnit;
+            group.outConversionUnit = item.conversionUnit;
+          }
+        });
+        return Object.values(groups);
+      },
+      formatPackageUnit(row, type) {
+        const config = {
+          min: ['minPackageCell', 'packageUnit', 'minConversionUnit'],
+          in: ['inPackageCell', 'inPackageUnit', 'inConversionUnit'],
+          out: ['outPackageCell', 'outPackageUnit', 'outConversionUnit']
+        }[type];
+        const value = row[config[0]];
+        if (value === undefined || value === null || value === '') return '-';
+        const fromUnit = this.dictValue('计量单位', row[config[1]]);
+        const toUnit = this.dictValue('计量单位', row[config[2]]);
+        return `${value}${fromUnit === '-' ? '' : fromUnit}${
+          toUnit === '-' ? '' : '/' + toUnit
+        }`;
+      },
+      yesNo(value) {
+        if (value === undefined || value === null || value === '') return '-';
+        return Number(value) === 1 ? '是' : '否';
+      },
+      enabledText(value) {
+        if (value === undefined || value === null || value === '') return '-';
+        return Number(value) === 1 ? '启用' : '停用';
+      },
+      processingTypeText(value) {
+        if (value === undefined || value === null || value === '') return '-';
+        return Number(value) === 1 ? '批量' : '单件';
+      },
+      inventoryModeText(value) {
+        if (value === undefined || value === null || value === '') return '-';
+        return Number(value) === 1 ? '批量盘点' : '逐个盘点';
+      },
+      componentAttributeText(value) {
+        const map = {
+          1: '自制件',
+          2: '采购件',
+          3: '外协件',
+          4: '受托件'
+        };
+        const list = this.normalizeList(value);
+        return list.length ? list.map((item) => map[item] || item).join(',') : '-';
+      },
+      produceTypeText(value) {
+        const map = {
+          1: '加工',
+          3: '装配'
+        };
+        return map[value] || value || '-';
+      },
+      measureTypeText(value) {
+        const map = {
+          1: '数量',
+          2: '重量',
+          3: '体积',
+          4: '容积',
+          5: '面积'
+        };
+        return map[value] || value || '-';
+      },
+      pricingWayText(value) {
+        const map = {
+          1: '按数量计价',
+          2: '按重量计价'
+        };
+        return map[value] || value || '-';
+      },
+      getFileUrl(file) {
+        if (!file) return '';
+        if (typeof file === 'string') return file;
+        return file.url || file.path || file.fileUrl || file.downloadUrl || '';
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .item-publish-approval {
+    padding: 10px 0;
+
+    .section {
+      margin-bottom: 16px;
+    }
+
+    .section-title {
+      display: flex;
+      align-items: center;
+      margin-bottom: 10px;
+      font-size: 16px;
+      font-weight: 600;
+
+      &::before {
+        display: inline-block;
+        width: 4px;
+        height: 16px;
+        margin-right: 8px;
+        background: #1890ff;
+        content: '';
+      }
+    }
+
+    .product-image {
+      width: 96px;
+      height: 96px;
+      margin-right: 10px;
+      margin-bottom: 10px;
+      border: 1px solid #ebeef5;
+      border-radius: 4px;
+    }
+  }
+</style>

+ 311 - 0
src/views/bpm/handleTask/components/itemPublishApproval/relatedInfo.vue

@@ -0,0 +1,311 @@
+<template>
+  <div class="related-info" v-loading="loading">
+    <div class="section-title">关联信息</div>
+    <el-tabs v-model="activeTab" type="border-card">
+      <el-tab-pane label="可用模具" name="mold">
+        <ele-pro-table
+          :columns="moldColumns"
+          :datasource="moldData"
+          :need-page="false"
+          :toolkit="[]"
+        />
+      </el-tab-pane>
+      <el-tab-pane label="BOM" name="bom">
+        <ele-pro-table
+          :columns="bomColumns"
+          :datasource="bomData"
+          :need-page="false"
+          :toolkit="[]"
+        />
+      </el-tab-pane>
+      <el-tab-pane label="工艺路线" name="routing">
+        <ele-pro-table
+          :columns="routingColumns"
+          :datasource="routingData"
+          :need-page="false"
+          :toolkit="[]"
+        />
+      </el-tab-pane>
+      <el-tab-pane label="关联设备" name="equipment">
+        <ele-pro-table
+          :columns="equipmentColumns"
+          :datasource="equipmentData"
+          :need-page="false"
+          :toolkit="[]"
+        />
+      </el-tab-pane>
+      <el-tab-pane label="供应商" name="supplier">
+        <ele-pro-table
+          :columns="contactColumns"
+          :datasource="supplierData"
+          :need-page="false"
+          :toolkit="[]"
+        />
+      </el-tab-pane>
+      <el-tab-pane label="生产厂家" name="manufacturer">
+        <ele-pro-table
+          :columns="contactColumns"
+          :datasource="manufacturerData"
+          :need-page="false"
+          :toolkit="[]"
+        />
+      </el-tab-pane>
+      <el-tab-pane label="客户" name="customer">
+        <ele-pro-table
+          :columns="contactColumns"
+          :datasource="customerData"
+          :need-page="false"
+          :toolkit="[]"
+        />
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+  import {
+    getBomAssociationList,
+    getBomRoutingList,
+    getContactsByCategoryIds,
+    getRelatesInformationList
+  } from '@/api/bpm/components/itemPublishApproval';
+  import { getByCode } from '@/api/system/dictionary-data';
+
+  export default {
+    name: 'ItemPublishApprovalRelatedInfo',
+    props: {
+      categoryId: {
+        type: [String, Number],
+        default: ''
+      },
+      categoryLevelId: {
+        type: [String, Number],
+        default: ''
+      },
+      categoryLevelGroupId: {
+        type: [String, Number],
+        default: ''
+      }
+    },
+    data() {
+      return {
+        activeTab: 'mold',
+        loading: false,
+        requestVersion: 0,
+        moldData: [],
+        bomData: [],
+        routingData: [],
+        equipmentData: [],
+        supplierData: [],
+        manufacturerData: [],
+        customerData: [],
+        weightUnitMap: {},
+        moldColumns: [
+          { type: 'index', label: '序号', width: 55, align: 'center' },
+          { label: '编码', prop: 'code' },
+          { label: '名称', prop: 'name' },
+          { label: '牌号', prop: 'brandNum' },
+          { label: '型号', prop: 'modelType' },
+          { label: '收缩系数', prop: 'extendInfo.shrinkEffictive' },
+          { label: '大模体型号', prop: 'extendInfo.maxMoldType' },
+          { label: '分类', prop: 'categoryLevelGroupName' }
+        ],
+        bomColumns: [
+          { label: 'BOM编码', prop: 'code' },
+          { label: 'BOM名称', prop: 'name' },
+          { label: '版本号', prop: 'versions' },
+          { label: '基本数量', prop: 'baseCount' },
+          { label: '单位', prop: 'unit' }
+        ],
+        routingColumns: [
+          { label: 'BOM编码', prop: 'bomCategoryCode' },
+          { label: 'BOM名称', prop: 'bomCategoryName' },
+          { label: 'BOM类型', prop: 'bomCategoryType', formatter: this.bomTypeText },
+          { label: 'BOM版本', prop: 'bomCategoryVersion' },
+          { label: '工艺路线编码', prop: 'code' },
+          { label: '工艺路线名称', prop: 'name' },
+          { label: '工艺类型', prop: 'produceVersionName' },
+          { label: '工艺路线版本', prop: 'version' }
+        ],
+        equipmentColumns: [
+          { label: '设备分类', prop: 'categoryLevelGroupName' },
+          { label: '设备编码', prop: 'code' },
+          { label: '设备名称', prop: 'name' },
+          { label: '产能', formatter: this.capacityText }
+        ],
+        contactColumns: [
+          { label: '编码', prop: 'code', minWidth: 140, showOverflowTooltip: true },
+          { label: '名称', prop: 'name', minWidth: 180, showOverflowTooltip: true },
+          { label: '代号', prop: 'serialNo', minWidth: 120, showOverflowTooltip: true },
+          { label: '电话', prop: 'phone', minWidth: 120, showOverflowTooltip: true },
+          { label: '注册地址', minWidth: 180, formatter: this.registeredAddressText },
+          { label: '联系地址', minWidth: 180, formatter: this.contactAddressText },
+          { label: '联系人', prop: 'linkName', minWidth: 110 },
+          { label: '联系人电话', prop: 'linkPhone', minWidth: 130 },
+          { label: '状态', prop: 'status', formatter: this.statusText },
+          { label: '创建人', prop: 'createUsername', minWidth: 100 },
+          {
+            label: '创建时间',
+            prop: 'createTime',
+            minWidth: 160,
+            formatter: this.createTimeText
+          }
+        ]
+      };
+    },
+    created() {
+      this.loadWeightUnitMap();
+    },
+    watch: {
+      categoryId: {
+        immediate: true,
+        handler() {
+          this.loadRelatedInfo();
+        }
+      },
+      categoryLevelId() {
+        this.loadRelatedInfo();
+      },
+      categoryLevelGroupId() {
+        this.loadRelatedInfo();
+      }
+    },
+    methods: {
+      async loadRelatedInfo() {
+        if (!this.categoryId) return;
+        const version = ++this.requestVersion;
+        this.loading = true;
+        const requests = [
+          this.loadBomData(),
+          this.loadRoutingData(),
+          this.loadContactData(1),
+          this.loadContactData(2),
+          this.loadContactData(3)
+        ];
+        if (this.categoryLevelId) {
+          requests.push(this.loadMoldData(), this.loadEquipmentData());
+        }
+        await Promise.all(requests.map((request) => request.catch(() => null)));
+        if (version === this.requestVersion) {
+          this.loading = false;
+        }
+      },
+      async loadWeightUnitMap() {
+        try {
+          const res = await getByCode('weight_unit');
+          this.weightUnitMap = (res.data || []).reduce((map, item) => {
+            const entry = Object.entries(item)[0];
+            if (entry) map[String(entry[0])] = entry[1];
+            return map;
+          }, {});
+        } catch (error) {
+          this.weightUnitMap = {};
+        }
+      },
+      async loadBomData() {
+        const data = await getBomAssociationList({
+          categoryId: this.categoryId,
+          versions: '',
+          bomType: 1,
+          isTemp: 0
+        });
+        this.bomData = this.getTableData(data);
+      },
+      async loadRoutingData() {
+        const data = await getBomRoutingList(this.categoryId);
+        this.routingData = this.getTableData(data);
+      },
+      async loadMoldData() {
+        const data = await getRelatesInformationList({
+          mainCategoryId: this.categoryLevelId,
+          categoryLevelRootId: 5,
+          categoryLevelGroupId: this.categoryLevelGroupId
+        });
+        this.moldData = this.getTableData(data);
+      },
+      async loadEquipmentData() {
+        const data = await getRelatesInformationList({
+          mainCategoryId: this.categoryLevelId,
+          categoryLevelRootId: 4,
+          categoryLevelGroupId: this.categoryLevelGroupId
+        });
+        this.equipmentData = this.getTableData(data);
+      },
+      async loadContactData(type) {
+        const data = await getContactsByCategoryIds({
+          categoryIds: [this.categoryId],
+          type
+        });
+        const list = data[this.categoryId] || data[String(this.categoryId)] || [];
+        if (type === 1) this.customerData = list;
+        if (type === 2) this.supplierData = list;
+        if (type === 3) this.manufacturerData = list;
+      },
+      getTableData(data) {
+        if (Array.isArray(data)) return data;
+        if (Array.isArray(data && data.list)) return data.list;
+        if (Array.isArray(data && data.records)) return data.records;
+        if (data && typeof data === 'object') {
+          return Object.values(data).reduce(
+            (list, item) => list.concat(Array.isArray(item) ? item : []),
+            []
+          );
+        }
+        return [];
+      },
+      bomTypeText(row) {
+        const value = row && row.bomCategoryType;
+        return { 1: 'PBOM', 2: 'MBOM', 3: 'ABom' }[value] || '-';
+      },
+      capacityText(row) {
+        if (!row || row.quantity === undefined || row.quantity === null) return '-';
+        const unit =
+          this.weightUnitMap[String(row.quantityUnitId)] || row.quantityUnitId || '';
+        return `${row.quantity}${unit ? '/' + unit : ''}`;
+      },
+      registeredAddressText(row) {
+        return `${(row.addressName || '').replace(/,/g, '')}${row.address || ''}` || '-';
+      },
+      contactAddressText(row) {
+        return `${(row.otherAddressName || '').replace(/,/g, '')}${
+          row.otherAddress || ''
+        }` || '-';
+      },
+      statusText(row) {
+        if (!row || row.status === undefined || row.status === null) return '-';
+        return Number(row.status) === 1 ? '启用' : '禁用';
+      },
+      createTimeText(row) {
+        if (!row || !row.createTime) return '-';
+        return this.$util.toDateString(row.createTime);
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .related-info {
+    margin-bottom: 16px;
+
+    .section-title {
+      display: flex;
+      align-items: center;
+      margin-bottom: 10px;
+      font-size: 16px;
+      font-weight: 600;
+
+      &::before {
+        display: inline-block;
+        width: 4px;
+        height: 16px;
+        margin-right: 8px;
+        background: #1890ff;
+        content: '';
+      }
+    }
+
+    ::v-deep .el-tabs__content {
+      padding: 12px;
+    }
+  }
+</style>

+ 173 - 0
src/views/bpm/handleTask/components/itemPublishApproval/submit.vue

@@ -0,0 +1,173 @@
+<template>
+  <el-col :span="16" :offset="6" v-loading="isSaveLoading">
+    <el-form label-width="100px" ref="formRef" :model="form" :rules="rules">
+      <el-form-item label="审批建议" prop="reason" style="margin-bottom: 20px">
+        <el-input
+          type="textarea"
+          v-model="form.reason"
+          placeholder="请输入审批建议"
+        />
+      </el-form-item>
+    </el-form>
+    <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
+      <el-button
+        icon="el-icon-edit-outline"
+        type="success"
+        size="mini"
+        v-click-once
+        :loading="isSaveLoading"
+        @click="handleAudit(1)"
+        >通过
+      </el-button>
+
+      <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        v-click-once
+        :loading="isSaveLoading"
+        @click="handleAudit(0)"
+        >驳回
+      </el-button>
+
+      <el-dropdown
+        @command="(command) => handleCommand(command)"
+        style="margin-left: 30px"
+      >
+        <span class="el-dropdown-link">
+          更多<i class="el-icon-arrow-down el-icon--right"></i>
+        </span>
+        <el-dropdown-menu slot="dropdown">
+          <el-dropdown-item command="transfer">转办</el-dropdown-item>
+          <el-dropdown-item command="back">退回</el-dropdown-item>
+          <el-dropdown-item command="cancel">作废</el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+    </div>
+  </el-col>
+</template>
+
+<script>
+  import {
+    approveTaskWithVariables,
+    rejectTask,
+    cancelTask
+  } from '@/api/bpm/task';
+
+  export default {
+    name: 'ItemPublishApprovalSubmit',
+    props: {
+      businessId: {
+        default: ''
+      },
+      businessCode: {
+        default: ''
+      },
+      taskId: {
+        default: ''
+      },
+      id: {
+        default: ''
+      },
+      taskDefinitionKey: {
+        default: ''
+      },
+      fromUser: {
+        default: ''
+      },
+      businessType: {
+        default: ''
+      }
+    },
+    data() {
+      return {
+        form: {
+          reason: '同意'
+        },
+        rules: {
+          reason: [
+            {
+              required: true,
+              message: '请输入审批建议',
+              trigger: 'blur'
+            }
+          ]
+        },
+        isSaveLoading: false
+      };
+    },
+    methods: {
+      handleAudit(status) {
+        this.$refs.formRef.validate((valid) => {
+          if (!valid) {
+            return;
+          }
+          this.approveTask(status);
+        });
+      },
+      async approveTask(status) {
+        const API = status ? approveTaskWithVariables : rejectTask;
+        this.isSaveLoading = true;
+        try {
+          const res = await API({
+            id: this.taskId,
+            reason: this.form.reason,
+            variables: {
+              pass: !!status
+            }
+          });
+          if (res.data.code != '-1') {
+            this.$emit('handleAudit', {
+              status,
+              title: status === 0 ? '驳回' : ''
+            });
+          }
+        } finally {
+          this.isSaveLoading = false;
+        }
+      },
+      handleCommand(command) {
+        if (command === 'transfer') {
+          this.$emit('handleUpdateAssignee');
+          return;
+        }
+        if (command === 'back') {
+          this.$emit('handleBackList');
+          return;
+        }
+        if (command === 'cancel') {
+          this.cancelTask();
+        }
+      },
+      cancelTask() {
+        this.$confirm('是否确认作废?', {
+          type: 'warning',
+          cancelButtonText: '取消',
+          confirmButtonText: '确定'
+        })
+          .then(() => {
+            this.isSaveLoading = true;
+            return cancelTask({
+              id: this.id,
+              taskId: this.taskId,
+              reason: this.form.reason,
+              businessId: this.businessId
+            });
+          })
+          .then(() => {
+            this.$emit('handleClose');
+          })
+          .catch((error) => {
+            if (error !== 'cancel') {
+              this.$message.error('流程作废失败');
+            }
+          })
+          .finally(() => {
+            this.isSaveLoading = false;
+          });
+      }
+    }
+  };
+</script>
+
+<style lang="scss"></style>

+ 2 - 1
vue.config.js

@@ -37,8 +37,9 @@ module.exports = {
       '/api': {
         // target: 'http://124.71.68.31:50001', // 测试环境
         // target: 'http://124.71.68.31:50001',
+        target: 'http://192.168.1.125:18086',
         // target: 'http://192.168.1.105:18086',
-        target: 'http://192.168.1.102:18086',
+        // target: 'http://192.168.1.102:18086',
         // target: 'http://192.168.1.116:18086',
         // target: 'http://192.168.1.3:18086',
         // target: 'http://192.168.1.251:18186',