zhou zhou
2026-04-17 d9ff374c80f3ed9077eae5136a7edea37668afbf
#条码自定义增加图片
9个文件已修改
517 ■■■■ 已修改文件
rsf-design/src/locales/langs/en.json 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/locales/langs/zh.json 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/basic-info/wh-mat/matnrPrintTemplate.helpers.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-canvas.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-property-panel.vue 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-toolbar.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/basic-info/wh-mat/modules/wh-mat-print-dialog.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/MatnrPrintTemplateServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/controller/DictDataController.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/locales/langs/en.json
@@ -2882,14 +2882,15 @@
            "zoomOut": "Zoom Out",
            "zoomIn": "Zoom In",
            "zoomFit": "Auto Fit",
            "elements": {
              "text": "Text",
              "barcode": "Barcode",
              "qrcode": "QR Code",
              "line": "Line",
              "rect": "Rectangle",
              "table": "Field Table"
            }
              "elements": {
                "text": "Text",
                "barcode": "Barcode",
                "qrcode": "QR Code",
                "image": "Image",
                "line": "Line",
                "rect": "Rectangle",
                "table": "Field Table"
              }
          },
          "fieldPanel": {
            "title": "Available Fields",
@@ -2919,11 +2920,14 @@
              "fontSizeMm": "Font Size (mm)",
              "fontWeight": "Font Weight",
              "textAlign": "Alignment",
              "textColor": "Text Color",
              "valueTemplate": "Value Template",
              "symbology": "Symbology",
              "showText": "Show Text",
              "direction": "Direction",
                "textColor": "Text Color",
                "valueTemplate": "Value Template",
                "imageSource": "Image Source",
                "fitMode": "Fit Mode",
                "previewImage": "Image Preview",
                "symbology": "Symbology",
                "showText": "Show Text",
                "direction": "Direction",
              "lineWidthMm": "Line Width (mm)",
              "lineColor": "Line Color",
              "borderWidthMm": "Border Width (mm)",
@@ -2937,22 +2941,31 @@
              "rightContent": "Right Content"
            },
            "options": {
              "staticText": "Static Text",
              "templateText": "Template Placeholder",
              "template": "Template",
              "alignLeft": "Left",
              "alignCenter": "Center",
              "alignRight": "Right",
                "staticText": "Static Text",
                "templateText": "Template Placeholder",
                "template": "Template",
                "contain": "Contain",
                "cover": "Cover",
                "fill": "Fill",
                "alignLeft": "Left",
                "alignCenter": "Center",
                "alignRight": "Right",
              "horizontal": "Horizontal",
              "vertical": "Vertical"
            },
            "actions": {
              "removeElement": "Delete Element",
              "addRow": "Add Row"
            },
            "empty": "Select an element on the canvas to edit its position, size, and content here.",
            "rowTitle": "Row {index}",
            "defaultFieldLabel": "Field Label"
              "actions": {
                "removeElement": "Delete Element",
                "addRow": "Add Row",
                "uploadImage": "Upload Image"
              },
              "messages": {
                "uploadOnlyImage": "Only image files are allowed",
                "uploadSizeLimit": "Image size must be smaller than 5MB",
                "readImageFailed": "Failed to read the image"
              },
              "empty": "Select an element on the canvas to edit its position, size, and content here.",
              "rowTitle": "Row {index}",
              "defaultFieldLabel": "Field Label"
          },
          "dialog": {
            "title": "Print",
rsf-design/src/locales/langs/zh.json
@@ -2890,14 +2890,15 @@
            "zoomOut": "缩小",
            "zoomIn": "放大",
            "zoomFit": "自适应",
            "elements": {
              "text": "文本",
              "barcode": "一维码",
              "qrcode": "二维码",
              "line": "直线",
              "rect": "矩形",
              "table": "字段表格"
            }
              "elements": {
                "text": "文本",
                "barcode": "一维码",
                "qrcode": "二维码",
                "image": "图片",
                "line": "直线",
                "rect": "矩形",
                "table": "字段表格"
              }
          },
          "fieldPanel": {
            "title": "可用字段",
@@ -2927,11 +2928,14 @@
              "fontSizeMm": "字号(mm)",
              "fontWeight": "字重",
              "textAlign": "对齐",
              "textColor": "文字颜色",
              "valueTemplate": "码值模板",
              "symbology": "编码制式",
              "showText": "显示文字",
              "direction": "方向",
                "textColor": "文字颜色",
                "valueTemplate": "码值模板",
                "imageSource": "图片地址",
                "fitMode": "填充方式",
                "previewImage": "图片预览",
                "symbology": "编码制式",
                "showText": "显示文字",
                "direction": "方向",
              "lineWidthMm": "粗细(mm)",
              "lineColor": "线条颜色",
              "borderWidthMm": "边框宽度(mm)",
@@ -2945,22 +2949,31 @@
              "rightContent": "右列内容"
            },
            "options": {
              "staticText": "静态文本",
              "templateText": "占位符模板",
              "template": "模板",
              "alignLeft": "左对齐",
              "alignCenter": "居中",
              "alignRight": "右对齐",
                "staticText": "静态文本",
                "templateText": "占位符模板",
                "template": "模板",
                "contain": "完整显示",
                "cover": "铺满裁切",
                "fill": "拉伸铺满",
                "alignLeft": "左对齐",
                "alignCenter": "居中",
                "alignRight": "右对齐",
              "horizontal": "横线",
              "vertical": "竖线"
            },
            "actions": {
              "removeElement": "删除元素",
              "addRow": "新增一行"
            },
            "empty": "选择画布中的元素后,在这里编辑位置、尺寸和字段内容。",
            "rowTitle": "第 {index} 行",
            "defaultFieldLabel": "字段名"
              "actions": {
                "removeElement": "删除元素",
                "addRow": "新增一行",
                "uploadImage": "上传图片"
              },
              "messages": {
                "uploadOnlyImage": "只能上传图片文件",
                "uploadSizeLimit": "图片大小不能超过 5MB",
                "readImageFailed": "读取图片失败"
              },
              "empty": "选择画布中的元素后,在这里编辑位置、尺寸和字段内容。",
              "rowTitle": "第 {index} 行",
              "defaultFieldLabel": "字段名"
          },
          "dialog": {
            "title": "打印",
rsf-design/src/views/basic-info/wh-mat/matnrPrintTemplate.helpers.js
@@ -75,6 +75,25 @@
  return color || fallback
}
function getImageNaturalSize(element = {}) {
  const naturalWidth = normalizeNumber(element?.naturalWidth, 0)
  const naturalHeight = normalizeNumber(element?.naturalHeight, 0)
  return {
    naturalWidth,
    naturalHeight
  }
}
export function getImageMinHeight(width, element = {}, fallback = 8) {
  const { naturalWidth, naturalHeight } = getImageNaturalSize(element)
  if (naturalWidth > 0 && naturalHeight > 0) {
    return Number(
      ((Math.max(1, normalizeNumber(width, 1)) * naturalHeight) / naturalWidth).toFixed(2)
    )
  }
  return Math.max(8, normalizeNumber(fallback, 8))
}
function createElementId(prefix = 'el') {
  return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`
}
@@ -197,6 +216,14 @@
  if (base.type === 'qrcode') {
    base.resolvedValue = resolveTemplatePlaceholders(base.valueTemplate, record)
    return base
  }
  if (base.type === 'image') {
    base.resolvedSrc = normalizeText(base.src)
    base.objectFit = ['contain', 'cover', 'fill'].includes(base.objectFit)
      ? base.objectFit
      : 'contain'
    return base
  }
@@ -380,6 +407,21 @@
    normalized.valueTemplate = normalizeText(element?.valueTemplate)
    normalized.w = Math.max(8, normalizeNumber(element?.w, 18))
    normalized.h = Math.max(8, normalizeNumber(element?.h, 18))
    return normalized
  }
  if (normalized.type === 'image') {
    normalized.src = normalizeText(element?.src)
    normalized.objectFit = ['contain', 'cover', 'fill'].includes(element?.objectFit)
      ? element.objectFit
      : 'contain'
    normalized.naturalWidth = Math.max(0, normalizeNumber(element?.naturalWidth, 0))
    normalized.naturalHeight = Math.max(0, normalizeNumber(element?.naturalHeight, 0))
    normalized.w = Math.max(8, normalizeNumber(element?.w, 20))
    normalized.h = Math.max(
      getImageMinHeight(normalized.w, element, normalizeNumber(element?.h, 20)),
      normalizeNumber(element?.h, 20)
    )
    return normalized
  }
@@ -645,6 +687,18 @@
    })
  }
  if (type === 'image') {
    return normalizeElement({
      ...common,
      w: 20,
      h: 20,
      src: '',
      naturalWidth: 0,
      naturalHeight: 0,
      objectFit: 'contain'
    })
  }
  if (type === 'line') {
    return normalizeElement({
      ...common,
rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-canvas.vue
@@ -46,6 +46,17 @@
          />
        </div>
        <div v-else-if="element.type === 'image'" class="matnr-print-element__image">
          <img
            v-if="element.resolvedSrc"
            :src="element.resolvedSrc"
            :style="getImageStyle(element)"
            alt=""
            draggable="false"
          />
          <div v-else class="matnr-print-element__image-placeholder"></div>
        </div>
        <div
          v-else-if="element.type === 'line'"
          class="matnr-print-element__line"
@@ -234,6 +245,12 @@
      borderRadius: editorMode.value
        ? mmToPx(element.radius || 0, props.scale)
        : `${element.radius || 0}mm`
    }
  }
  function getImageStyle(element) {
    return {
      objectFit: element.objectFit || 'contain'
    }
  }
@@ -459,6 +476,7 @@
  .matnr-print-element__text,
  .matnr-print-element__barcode,
  .matnr-print-element__qrcode,
  .matnr-print-element__image,
  .matnr-print-element__rect,
  .matnr-print-element__table {
    width: 100%;
@@ -489,6 +507,30 @@
    height: 100%;
  }
  .matnr-print-element__image {
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(248, 250, 252, 0.6);
  }
  .matnr-print-element__image img {
    width: 100%;
    height: 100%;
    display: block;
    user-select: none;
    pointer-events: none;
  }
  .matnr-print-element__image-placeholder {
    width: 100%;
    height: 100%;
    border: 1px dashed rgba(148, 163, 184, 0.65);
    background:
      linear-gradient(135deg, rgba(226, 232, 240, 0.75), rgba(241, 245, 249, 0.35)),
      linear-gradient(45deg, rgba(203, 213, 225, 0.35), transparent);
  }
  .matnr-print-element__line {
    position: absolute;
    inset: 0;
rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-property-panel.vue
@@ -180,7 +180,7 @@
                }}</span>
                <ElInputNumber
                  :model-value="selectedElement.h"
                  :min="0.4"
                  :min="selectedElement.type === 'image' ? imageMinHeight : 0.4"
                  :step="0.2"
                  controls-position="right"
                  @update:model-value="updateElement('h', $event)"
@@ -341,6 +341,77 @@
                    @focus="setPlaceholderTarget('valueTemplate')"
                    @update:model-value="updateElement('valueTemplate', $event)"
                  />
                </label>
              </div>
            </template>
            <template v-else-if="selectedElement.type === 'image'">
              <div class="matnr-print-property-panel__grid">
                <label class="matnr-print-property-panel__span-2">
                  <span>{{
                    t('pages.basicInfo.whMat.printTemplate.propertyPanel.fields.imageSource')
                  }}</span>
                  <ElInput
                    :model-value="selectedElement.src"
                    @update:model-value="updateElement('src', $event)"
                    @change="handleImageSourceChange"
                  />
                </label>
                <label>
                  <span>{{
                    t('pages.basicInfo.whMat.printTemplate.propertyPanel.fields.fitMode')
                  }}</span>
                  <ElSelect
                    :model-value="selectedElement.objectFit"
                    @update:model-value="updateElement('objectFit', $event)"
                  >
                    <ElOption
                      value="contain"
                      :label="
                        t('pages.basicInfo.whMat.printTemplate.propertyPanel.options.contain')
                      "
                    />
                    <ElOption
                      value="cover"
                      :label="t('pages.basicInfo.whMat.printTemplate.propertyPanel.options.cover')"
                    />
                    <ElOption
                      value="fill"
                      :label="t('pages.basicInfo.whMat.printTemplate.propertyPanel.options.fill')"
                    />
                  </ElSelect>
                </label>
                <label class="matnr-print-property-panel__upload">
                  <span>{{
                    t('pages.basicInfo.whMat.printTemplate.propertyPanel.actions.uploadImage')
                  }}</span>
                  <ElUpload
                    accept="image/*"
                    :auto-upload="false"
                    :show-file-list="false"
                    @change="handleImageFileChange"
                  >
                    <ElButton>
                      {{
                        t('pages.basicInfo.whMat.printTemplate.propertyPanel.actions.uploadImage')
                      }}
                    </ElButton>
                  </ElUpload>
                </label>
                <label class="matnr-print-property-panel__span-2">
                  <span>{{
                    t('pages.basicInfo.whMat.printTemplate.propertyPanel.fields.previewImage')
                  }}</span>
                  <div class="matnr-print-property-panel__image-preview">
                    <ElImage
                      v-if="selectedElement.src"
                      :src="selectedElement.src"
                      fit="contain"
                      preview-teleported
                      class="matnr-print-property-panel__image-preview-inner"
                    />
                    <div v-else class="matnr-print-property-panel__image-preview-empty"></div>
                  </div>
                </label>
              </div>
            </template>
@@ -623,8 +694,13 @@
<script setup>
  import { computed } from 'vue'
  import { ElMessage } from 'element-plus'
  import { useI18n } from 'vue-i18n'
  import { getFieldListTableRows, updateFieldListTableRows } from '../matnrPrintTemplate.helpers'
  import {
    getFieldListTableRows,
    getImageMinHeight,
    updateFieldListTableRows
  } from '../matnrPrintTemplate.helpers'
  defineOptions({ name: 'MatnrPrintPropertyPanel' })
@@ -650,6 +726,11 @@
  ])
  const tableRows = computed(() => getFieldListTableRows(props.selectedElement))
  const imageMinHeight = computed(() =>
    props.selectedElement?.type === 'image'
      ? getImageMinHeight(props.selectedElement?.w, props.selectedElement, props.selectedElement?.h)
      : 0.4
  )
  function updateTemplateMeta(field, value) {
    emit('update-template-meta', { field, value })
@@ -742,6 +823,116 @@
      id: props.selectedElement?.id,
      patch: updateFieldListTableRows(props.selectedElement, rows)
    })
  }
  function validateImageFile(rawFile) {
    const isImageFile =
      String(rawFile?.type || '').startsWith('image/') ||
      /\.(png|jpe?g|gif|bmp|webp|svg)$/i.test(rawFile?.name || '')
    if (!isImageFile) {
      ElMessage.error(
        t('pages.basicInfo.whMat.printTemplate.propertyPanel.messages.uploadOnlyImage')
      )
      return false
    }
    const isLt5MB = Number(rawFile?.size || 0) / 1024 / 1024 < 5
    if (!isLt5MB) {
      ElMessage.error(
        t('pages.basicInfo.whMat.printTemplate.propertyPanel.messages.uploadSizeLimit')
      )
      return false
    }
    return true
  }
  function readFileAsDataUrl(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader()
      reader.onload = () => resolve(String(reader.result || ''))
      reader.onerror = reject
      reader.readAsDataURL(file)
    })
  }
  function loadImageNaturalSize(src) {
    return new Promise((resolve, reject) => {
      const image = new Image()
      image.onload = () => {
        resolve({
          naturalWidth: Number(image.naturalWidth) || 0,
          naturalHeight: Number(image.naturalHeight) || 0
        })
      }
      image.onerror = reject
      image.src = src
    })
  }
  async function applyImageSource(src) {
    const nextSrc = String(src || '').trim()
    if (!props.selectedElement?.id) {
      return
    }
    if (!nextSrc) {
      emit('update-element', {
        id: props.selectedElement.id,
        patch: {
          src: '',
          naturalWidth: 0,
          naturalHeight: 0
        }
      })
      return
    }
    const size = await loadImageNaturalSize(nextSrc)
    const nextHeight = getImageMinHeight(props.selectedElement?.w, size, props.selectedElement?.h)
    emit('update-element', {
      id: props.selectedElement.id,
      patch: {
        src: nextSrc,
        naturalWidth: size.naturalWidth,
        naturalHeight: size.naturalHeight,
        h: Math.max(Number(props.selectedElement?.h) || 0, nextHeight)
      }
    })
  }
  async function handleImageSourceChange(value) {
    const nextSrc = String(value || '').trim()
    if (!nextSrc) {
      await applyImageSource('')
      return
    }
    try {
      await applyImageSource(nextSrc)
    } catch (error) {
      ElMessage.error(
        error?.message ||
          t('pages.basicInfo.whMat.printTemplate.propertyPanel.messages.readImageFailed')
      )
    }
  }
  async function handleImageFileChange(uploadFile) {
    const rawFile = uploadFile?.raw
    if (!rawFile || !props.selectedElement?.id) {
      return
    }
    if (!validateImageFile(rawFile)) {
      return
    }
    try {
      const dataUrl = await readFileAsDataUrl(rawFile)
      await applyImageSource(dataUrl)
    } catch (error) {
      ElMessage.error(
        error?.message ||
          t('pages.basicInfo.whMat.printTemplate.propertyPanel.messages.readImageFailed')
      )
    }
  }
</script>
@@ -847,6 +1038,39 @@
    color: var(--art-text-primary);
  }
  .matnr-print-property-panel__upload {
    justify-content: flex-end;
  }
  .matnr-print-property-panel__image-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 120px;
    padding: 12px;
    border: 1px dashed rgba(148, 163, 184, 0.35);
    border-radius: 12px;
    background: rgba(248, 250, 252, 0.75);
  }
  .matnr-print-property-panel__image-preview-inner {
    width: 100%;
    height: 140px;
    border-radius: 8px;
    overflow: hidden;
    background: #ffffff;
  }
  .matnr-print-property-panel__image-preview-empty {
    width: 100%;
    height: 96px;
    border-radius: 10px;
    border: 1px dashed rgba(148, 163, 184, 0.5);
    background:
      linear-gradient(135deg, rgba(226, 232, 240, 0.75), rgba(241, 245, 249, 0.35)),
      linear-gradient(45deg, rgba(203, 213, 225, 0.35), transparent);
  }
  @media (max-width: 1280px) {
    .matnr-print-property-panel__grid {
      grid-template-columns: minmax(0, 1fr);
rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-toolbar.vue
@@ -65,6 +65,7 @@
      { type: 'text', label: t('pages.basicInfo.whMat.printTemplate.toolbar.elements.text') },
      { type: 'barcode', label: t('pages.basicInfo.whMat.printTemplate.toolbar.elements.barcode') },
      { type: 'qrcode', label: t('pages.basicInfo.whMat.printTemplate.toolbar.elements.qrcode') },
      { type: 'image', label: t('pages.basicInfo.whMat.printTemplate.toolbar.elements.image') },
      { type: 'line', label: t('pages.basicInfo.whMat.printTemplate.toolbar.elements.line') },
      { type: 'rect', label: t('pages.basicInfo.whMat.printTemplate.toolbar.elements.rect') },
      { type: 'table', label: t('pages.basicInfo.whMat.printTemplate.toolbar.elements.table') }
rsf-design/src/views/basic-info/wh-mat/modules/wh-mat-print-dialog.vue
@@ -424,6 +424,7 @@
      .matnr-print-element__text,
      .matnr-print-element__barcode,
      .matnr-print-element__qrcode,
      .matnr-print-element__image,
      .matnr-print-element__rect,
      .matnr-print-element__table {
        width: 100%;
@@ -446,6 +447,16 @@
        align-items: center;
        justify-content: center;
      }
      .matnr-print-element__image {
        display: flex;
        align-items: center;
        justify-content: center;
      }
      .matnr-print-element__image img {
        width: 100%;
        height: 100%;
        display: block;
      }
      .matnr-print-element__table {
        border-collapse: collapse;
        table-layout: fixed;
@@ -458,9 +469,27 @@
      </style>
      <scr` +
      `ipt>
        window.addEventListener('load', () => {
        function waitForImages() {
          const images = Array.from(document.images || []);
          const pending = images.filter((image) => !image.complete);
          if (!pending.length) {
            return Promise.resolve();
          }
          return Promise.all(
            pending.map(
              (image) =>
                new Promise((resolve) => {
                  const done = () => resolve();
                  image.addEventListener('load', done, { once: true });
                  image.addEventListener('error', done, { once: true });
                })
            )
          );
        }
        window.addEventListener('load', async () => {
          await waitForImages();
          window.focus();
          setTimeout(() => window.print(), 60);
          setTimeout(() => window.print(), 120);
        });
        window.addEventListener('afterprint', () => {
          window.close();
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/MatnrPrintTemplateServiceImpl.java
@@ -32,7 +32,7 @@
        implements MatnrPrintTemplateService {
    private static final Set<String> SUPPORTED_ELEMENT_TYPES = Collections.unmodifiableSet(
            new LinkedHashSet<>(Arrays.asList("text", "barcode", "qrcode", "line", "rect", "table"))
            new LinkedHashSet<>(Arrays.asList("text", "barcode", "qrcode", "image", "line", "rect", "table"))
    );
    @Override
@@ -316,6 +316,15 @@
                    throw new CoolException("二维码元素值模板不能为空");
                }
                break;
            case "image":
                if (normalizeText(element.getString("src")).isEmpty()) {
                    throw new CoolException("图片元素地址不能为空");
                }
                String objectFit = normalizeText(element.getString("objectFit"));
                if (!objectFit.isEmpty() && !Arrays.asList("contain", "cover", "fill").contains(objectFit)) {
                    throw new CoolException("图片元素填充方式仅支持 contain、cover 或 fill");
                }
                break;
            case "table":
                if (element.getJSONArray("columns") == null) {
                    throw new CoolException("表格元素 columns 不能为空");
rsf-server/src/main/java/com/vincent/rsf/server/system/controller/DictDataController.java
@@ -27,7 +27,7 @@
    @Autowired
    private DictDataService dictDataService;
    @PreAuthorize("hasAuthority('system:dictData:list')")
    @PreAuthorize("hasAuthority('system:dictType:list')")
    @PostMapping("/dictData/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
@@ -35,25 +35,25 @@
        return R.ok().add(buildPageRowsUtils.rowsMap(dictDataService.page(pageParam, pageParam.buildWrapper(true))));
    }
    @PreAuthorize("hasAuthority('system:dictData:list')")
    @PreAuthorize("hasAuthority('system:dictType:list')")
    @PostMapping("/dictData/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(buildPageRowsUtils.rowsMap(dictDataService.list()));
    }
    @PreAuthorize("hasAuthority('system:dictData:list')")
    @PreAuthorize("hasAuthority('system:dictType:list')")
    @PostMapping({"/dictData/many/{ids}", "/dictDatas/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(buildPageRowsUtils.rowsMap(dictDataService.listByIds(Arrays.asList(ids))));
    }
    @PreAuthorize("hasAuthority('system:dictData:list')")
    @PreAuthorize("hasAuthority('system:dictType:list')")
    @GetMapping("/dictData/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(buildPageRowsUtils.rowsMap(dictDataService.getById(id)));
    }
    @PreAuthorize("hasAuthority('system:dictData:save')")
    @PreAuthorize("hasAuthority('system:dictType:save')")
    @OperationLog("Create 字典数据集")
    @PostMapping("/dictData/save")
    public R save(@RequestBody DictData dictData) {
@@ -77,7 +77,7 @@
        return R.ok("Save Success").add(buildPageRowsUtils.rowsMap(dictData));
    }
    @PreAuthorize("hasAuthority('system:dictData:update')")
    @PreAuthorize("hasAuthority('system:dictType:update')")
    @OperationLog("Update 字典数据集")
    @PostMapping("/dictData/update")
    public R update(@RequestBody DictData dictData) {
@@ -89,7 +89,7 @@
        return R.ok("Update Success").add(buildPageRowsUtils.rowsMap(dictData));
    }
    @PreAuthorize("hasAuthority('system:dictData:remove')")
    @PreAuthorize("hasAuthority('system:dictType:remove')")
    @OperationLog("Delete 字典数据集")
    @PostMapping("/dictData/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
@@ -99,7 +99,7 @@
        return R.ok("Delete Success").add(buildPageRowsUtils.rowsMap(ids));
    }
    @PreAuthorize("hasAuthority('system:dictData:list')")
    @PreAuthorize("hasAuthority('system:dictType:list')")
    @PostMapping("/dictData/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
@@ -113,7 +113,7 @@
        return R.ok().add(buildPageRowsUtils.rowsMap(vos));
    }
    @PreAuthorize("hasAuthority('system:dictData:list')")
    @PreAuthorize("hasAuthority('system:dictType:list')")
    @PostMapping("/dictData/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(buildPageRowsUtils.rowsMap(dictDataService.list()), DictData.class), response);