فهرست منبع

增加oneToOne页签

liuyanpeng 1 سال پیش
والد
کامیت
64e5001929

+ 31 - 0
src/resource/dictionary/WindowClientUtil.js

@@ -64,6 +64,37 @@ export default {
       });
     }
   },
+  // 获取oneToOne页签
+  getOneToOneSubTabs: function (window, oneToOneParentTabName) {
+    var subOneToOneTabs = [];
+    window.tabs.forEach(function (tab) {
+      if (tab.oneToOneParentTabName == oneToOneParentTabName) {
+        subOneToOneTabs.push(tab);
+      }
+    });
+    return subOneToOneTabs;
+  },
+
+  // 转换oneToOne页签
+  parseOneToOneTab: function (window, tab) {
+    var _self = this;
+    var subOneToOneTabs = this.getOneToOneSubTabs(window, tab.name);
+    if (tab.subOneToOneTabs == undefined) {
+      tab.subOneToOneTabs = [];
+    }
+
+    if (subOneToOneTabs.length > 0) {
+      subOneToOneTabs.forEach(function (subTab) {
+        tab.subOneToOneTabs.push(subTab);
+      });
+    }
+
+    if (tab.subOneToOneTabs.length > 0) {
+      tab.subOneToOneTabs.forEach(function (oneToOneTab) {
+        _self.parseOneToOneTab(window, oneToOneTab);
+      });
+    }
+  },
 
   // 获取表格字段
   getGridField: function (tab) {

+ 777 - 0
src/window1/tabFormEdit/SubOneToOneFormFieldEdit.vue

@@ -0,0 +1,777 @@
+<template>
+  <div
+    v-if="modelData && modelData.data != undefined && field.visible"
+    v-show="showField"
+    class="form-group"
+    :class="formGroupClass"
+  >
+    <label
+      v-tooltip.right="field.help"
+      class="control-label"
+      :class="formGroupLabelClass"
+      :style="field.cssStyle"
+    >
+      <span v-if="field.mandatory" class="required-mark">*</span>
+      <font size="2">{{ Language.getDisplayNameTrl($i18n.locale, field) }}</font><br />
+      <font v-if="isChineseEnglish && $i18n.locale == 'zh-CN'" size="0.5">{{ field.displayNameEng }}</font>
+    </label>
+    <div class="input-group" :class="inputGroupClass">
+      <input
+        v-if="fieldUtil.isTextType(field)"
+        v-model="displayValue"
+        autocomplete="off"
+        type="text"
+        class="form-control"
+        :name="field.displayName"
+        :readonly="readOnly"
+        :disabled="readOnly"
+      />
+
+      <input
+        v-if="fieldUtil.isNumberType(field)"
+        v-model="displayValue"
+        autocomplete="off"
+        type="number"
+        class="form-control"
+        :name="field.displayName"
+        :readonly="readOnly"
+        :disabled="readOnly"
+        onmousewheel="return false;"
+        @mousewheel="mouseWheelEvent"
+        @change="numberFormat"
+      />
+
+      <switches
+        v-if="fieldUtil.isCheckBoxType(field)"
+        v-model="displayValue"
+        :selected="displayValue"
+        color="green"
+        :type-bold="true"
+        :disabled="readOnly"
+      />
+
+      <RedGreenSelect
+        v-if="fieldUtil.isRedGreenEditorType(field)"
+        v-model="displayValue"
+        type="string"
+        :disabled="readOnly"
+      />
+
+      <input
+        v-if="fieldUtil.isPasswordType(field)"
+        v-model="displayValue"
+        autocomplete="off"
+        type="password"
+        class="form-control"
+        :name="field.displayName"
+        :readonly="readOnly"
+        :disabled="readOnly"
+      />
+
+      <textarea
+        v-if="fieldUtil.isTextAreaType(field)"
+        v-model="displayValue"
+        class="form-control"
+        rows="3"
+        :name="field.displayName"
+        :style="field.cssStyle"
+        :readonly="readOnly"
+        :disabled="readOnly"
+      />
+
+      <Date
+        v-if="fieldUtil.isDateType(field)"
+        :model-value="displayValue"
+        :data-vv-name="field.displayName"
+        :name="field.displayName"
+        :data-vv-value-path="displayValue"
+        :readonly="readOnly"
+        style="width:100%"
+        @update:model-value="textChanged"
+      />
+
+      <Time
+        v-if="fieldUtil.isTimeType(field)"
+        v-model="displayValue"
+        class="form-control"
+        :data-vv-name="field.displayName"
+        :name="field.displayName"
+        :data-vv-value-path="displayValue"
+        :readonly="readOnly"
+      />
+
+      <DateTime
+        v-if="fieldUtil.isDateTimeType(field)"
+        :model-value="displayValue"
+        :readonly="readOnly"
+        style="width:100%"
+        @update:model-value="textChanged"
+      />
+
+      <YearPicker
+        v-if="fieldUtil.isYearType(field)"
+        :value="displayValue"
+        :readonly="readOnly"
+        @selected="textChanged"
+      />
+
+      <VueMonthlyPicker
+        v-if="fieldUtil.isYearMonthType(field)"
+        id="month-picker"
+        v-model="month"
+        date-format="YYYY-MM"
+        class="vue-monthly-picker m-date-fieldEditView"
+        @selected="refresh"
+      />
+
+      <ImageWidget
+        v-if="fieldUtil.isImageType(field)"
+        :uuid="uuid1"
+        :pattern-json="field.pattern"
+        :field="field"
+        :field-value="fieldValue"
+        :class-name="className"
+        :readonly="readOnly"
+        @value-changed="valueChanged"
+      />
+
+      <ImageListWidget
+        v-if="fieldUtil.isImageListType(field)"
+        :field="field"
+        :field-value="fieldValue"
+        :class-name="className"
+        :readonly="readOnly"
+        @value-changed="valueChanged"
+      />
+
+      <VideoListWidget
+        v-if="fieldUtil.isVideoType(field)"
+        :field="field"
+        :field-value="fieldValue"
+        :class-name="className"
+        :readonly="readOnly"
+        @value-changed="valueChanged"
+      />
+
+      <FileListWidget
+        v-if="fieldUtil.isFileType(field)"
+        :field="field"
+        :field-value="fieldValue"
+        :class-name="className"
+        :readonly="readOnly"
+        @value-changed="valueChanged"
+      />
+
+      <EnumRadioGroupWidget
+        v-if="fieldUtil.isRadioButtonGroupType(field)"
+        :field="field"
+        :field-value="fieldValue"
+        :data-vv-name="field.displayName"
+        :name="field.displayName"
+        :data-vv-value-path="displayValue"
+        :readonly="readOnly"
+        @value-changed="valueChanged"
+      />
+
+      <ManyToManySetSearchWidget
+        v-if="fieldUtil.isManyToManySetType(field)"
+        :info-window-no="field.infoWindowNo"
+        :field="field"
+        :field-value="fieldValue"
+        :readonly="readOnly"
+        :model-data="modelData"
+        :window-no="windowNo"
+        :tab-index="tabIndex"
+        @value-changed="valueChanged"
+      />
+
+      <SearchWidget
+        v-if="fieldUtil.isSearchType(field)"
+        :info-window-no="field.infoWindowNo"
+        :field="field"
+        :field-value="fieldValue"
+        :readonly="readOnly"
+        :model-data="modelData"
+        :window-no="windowNo"
+        :tab-index="tabIndex"
+        @value-changed="valueChanged"
+      />
+
+      <EnumSelectWidget
+        v-if="fieldUtil.isEnumListType(field)"
+        :field="field"
+        :field-value="fieldValue"
+        :name="field.displayName"
+        :readonly="readOnly"
+        @value-changed="valueChanged"
+      />
+
+      <EnumMultiSelectWidget
+        v-if="fieldUtil.isEnumMultiType(field)"
+        :field="field"
+        :field-value="fieldValue"
+        :name="field.displayName"
+        :readonly="readOnly"
+        @value-changed="valueChanged"
+      />
+
+      <SelectWidget
+        v-if="fieldUtil.isSelectType(field)"
+        ref="selectWidget"
+        :field="field"
+        :field-value="fieldValue"
+        :window-no="windowNo"
+        :tab-index="tabIndex"
+        :readonly="readOnly"
+        :model-data="modelData"
+        @value-changed="valueChanged"
+      />
+
+      <ButtonWidget
+        v-if="fieldUtil.isButtonType(field)"
+        :field="field"
+        :field-value="fieldValue"
+        :readonly="readOnly"
+        @value-changed="valueChanged"
+        @execute-callout="executeCallout"
+      />
+
+      <RichTextAreaEditorWidget
+        v-if="fieldUtil.isRichTextAreaEditor(field)"
+        :display-value="displayValue"
+        :class-name="className"
+        :field-value="fieldValue"
+        :readonly="isReadOnly"
+        @value-changed="valueChanged"
+      />
+
+      <p v-show="!validateMandatory" class="required-mark required-error">
+        该字段为必填字段
+      </p>
+    </div>
+  </div>
+</template>
+
+<script>
+import fieldUtil from '../../resource/dictionary/FieldUtil.js';
+
+import WindowClientUtil from '../../resource/dictionary/WindowClientUtil.js';
+import Language from '../../common/Language.js';
+import Context from '../common/Context.js';
+
+import SearchWidget from '../tabFormWidget/SearchWidget.vue';
+import ImageListWidget from '../tabFormWidget/ImageListWidget.vue';
+import EnumSelectWidget from '../tabFormWidget/EnumSelectWidget.vue';
+import SelectWidget from '../tabFormWidget/SelectWidget.vue';
+import EnumRadioGroupWidget from '../tabFormWidget/EnumRadioGroupWidget.vue';
+import VideoListWidget from '../tabFormWidget/VideoListWidget.vue';
+import FileListWidget from '../tabFormWidget/FileListWidget.vue';
+import ButtonWidget from '../tabFormWidget/ButtonWidget.vue';
+
+import EnumMultiSelectWidget from '../tabFormWidget/EnumMultiSelectWidget.vue';
+import ManyToManySetSearchWidget from '../tabFormWidget/ManyToManySetSearchWidget.vue';
+import RedGreenSelect from '../../widget/RedGreenSelect.vue';
+import ImageWidget from '../tabFormWidget/ImageWidget.vue';
+import RichTextAreaEditorWidget from '../tabFormWidget/RichTextAreaEditorWidget.vue';
+import JsUtil from '../../common/JsUtil.js';
+import { Notify, Uuid } from 'pc-component-v3';
+
+
+
+
+
+
+
+
+
+export default {
+
+  components: {
+    SearchWidget,
+    ImageListWidget,
+    EnumSelectWidget,
+    SelectWidget,
+    EnumRadioGroupWidget,
+    VideoListWidget,
+    FileListWidget,
+    EnumMultiSelectWidget,
+    ManyToManySetSearchWidget,
+    ImageWidget,
+    RedGreenSelect,
+    ButtonWidget,
+    RichTextAreaEditorWidget,
+  },
+  
+  props: {
+   
+    windowNo: {
+      type: String,
+      default: null,
+    },
+    tabIndex: {
+      type: Number,
+      default: null,
+    },
+    className: {
+      type: String,
+      default: null,
+    },
+    multipleColumn: {
+      type: Boolean,
+      default: null,
+    },
+    jsUrl: {
+      type: String,
+      default: null,
+    },
+    field: {
+      type: Object,
+      default : function(){
+        return null;
+      },
+    },
+    modelData: {
+      type: Object,
+      default : function(){
+        return null;
+      },
+    },
+    isChineseEnglish: {
+      type: Boolean,
+      default: null,
+    },
+  },
+
+  
+  emits: ['executeCallout', 'valueChanged'],
+
+  data: function () {
+    this.Language = Language;
+    // eslint-disable-next-line
+    var value =
+      this.modelData == undefined ||
+      this.modelData.data == undefined ||
+      this.modelData.data[this.field.fieldName] == undefined
+        ? ''
+        : this.modelData.data[this.field.fieldName].displayValue[
+          this.field.entityFieldIndex
+        ];
+
+    var fieldValue =
+      this.modelData == undefined || this.modelData.data == undefined
+        ? []
+        : this.modelData.data[this.field.fieldName];
+
+    return {
+      displayValue: value,
+      fieldValue: fieldValue,
+      fieldUtil: fieldUtil,
+      validateMandatory: true, // 验证强制字段是否通过
+      month: undefined,
+      uuid1: Uuid.createUUID(),
+      showField: true, // 显示字段
+    };
+  },
+
+  computed: {
+    readOnly: function () {
+      if (this.field == undefined) {
+        return true;
+      }
+
+      if (this.field != undefined && this.field.readOnly == true) {
+        return true;
+      }
+
+      if (this.field != undefined && this.field.entityFieldIndex > 0) {
+        return true;
+      }
+
+      if (
+        this.modelData != undefined &&
+        this.modelData.id > 0 &&
+        (this.field.updatable == undefined || this.field.updatable == false)
+      ) {
+        return true;
+      }
+
+      return this.field.readOnly;
+    },
+
+    /**
+     * 表单组的CSS样式
+     * 对应 bootstrap 表单 form-group
+     */
+    formGroupClass: function () {
+      return {
+        'form-group-single-row':
+          this.field != undefined && this.field.singleRow,
+        'form-group-multiple-row':
+          this.field != undefined && !this.field.singleRow,
+        'form-group-multiple-column':
+          this.field != undefined && this.multipleColumn == true, // 多列
+      };
+    },
+
+    /**
+     * 标题的CSS样式
+     * 对应 bootstrap 表单 label 样式
+     */
+    formGroupLabelClass: function () {
+      return {
+        'm-sr-only':
+          this.field == null || !this.field.visible || !this.field.showLabel, // 显示/隐藏
+        'col-xs-5 col-sm-4 col-md-3 col-lg-2':
+          this.field != null && this.multipleColumn == false, // 单列
+        'label-multiple-column':
+          this.field != null && this.multipleColumn == true, // 多列
+      };
+    },
+
+    /**
+     * 控件的CSS样式
+     * 对应 bootstrap 表单 input-group
+     */
+    inputGroupClass: function () {
+      return {
+        'col-xs-7 col-sm-8 col-md-9 col-lg-10':
+          this.field != null && this.multipleColumn == false, // 单列
+        'input-group-multiple-column': this.multipleColumn, // 多列
+        'input-group-single-row':
+          this.field != undefined &&
+          this.field.singleRow == true &&
+          this.multipleColumn, // 单独一行
+      };
+    },
+
+    /**
+     * 控件的CSS样式
+     * 对应 bootstrap 表单 input-group 内的元素
+     */
+    inputGroupItemClass: function () {
+      return {};
+    },
+  },
+
+  watch: {
+    displayValue: function (currentValue, oldValue) {
+      if (
+        fieldUtil.isSearchType(this.field) == false &&
+        this.field.entityFieldIndex == 0 &&
+        fieldUtil.isSelectType(this.field) == false &&
+        fieldUtil.isRadioButtonGroupType(this.field) == false &&
+        fieldUtil.isEnumListType(this.field) == false &&
+        fieldUtil.isManyToManySetType(this.field) == false
+      ) {
+        // add by jack 20240220
+        // 修复bug:当modelData.data的值从外部修改以后,修改执行 watch->'modelData.data'  中的 this.displayValue = ? 方法
+        // 执行完成以后,watch->displayValue 又会执行,会触发valueChanged事件,这会导致 callout/calloutjs 会被多次执行。
+        // 所以在触发 valueChanged 的之前,先判断currentValue与modelData中的值是否相等,
+        // 如果相等,说明是外部修改了modelData,然后执行了 watch->'modelData.data' 方法修改了 displayValue,此时不触发valueChanged事件。
+        // 如果不相等,说明是DOM修改了,触发修改了displayValue,此时触发valueChanged事件。
+
+        const modelDataFieldDisplayValue = this.getModelDataFieldDisplayValue();
+
+        if (currentValue !== oldValue && currentValue !== modelDataFieldDisplayValue) {
+          var newFieldValue = {
+            displayValue: [currentValue],
+            fieldType: 'String',
+          };
+          this.$emit('valueChanged', newFieldValue);
+        }
+      }
+    },
+
+    'modelData.data': {
+      deep: true,
+      handler(curVal, oldVal) {
+        if(curVal == undefined ||
+            curVal[this.field.fieldName] == undefined ||
+            curVal[this.field.fieldName].displayValue == undefined ||
+            curVal[this.field.fieldName].displayValue.length < this.field.entityFieldIndex + 1){
+          this.displayValue = '';
+        }else{
+          let tempValue = curVal[this.field.fieldName].displayValue[this.field.entityFieldIndex];
+          if(fieldUtil.isCheckBoxType(this.field)){
+            if(typeof(tempValue) =='string'){
+              this.displayValue = (tempValue === 'true') ? true : false;
+            }
+            if(typeof(tempValue) =='boolean'){
+              this.displayValue = (tempValue === true) ? true : false;
+            }
+          }else{
+            this.displayValue = tempValue;
+          }          
+        }
+        this.fieldValue = curVal == undefined ? [] : curVal[this.field.fieldName];
+        this.showLogical();
+      },
+    },
+
+    month: function () {
+      this.textChanged(this.month);
+    },
+  },
+
+  mounted: function () {},
+
+  methods: {
+
+    
+    /**
+     * 获取Context
+     */
+    getContext: Context,
+
+
+    /**
+     * 获取modelData中字段的显示值
+     */
+    getModelDataFieldDisplayValue : function(){
+      if(this.modelData == null 
+          || this.field == null
+          || this.modelData.data[this.field.fieldName] == undefined
+          || this.modelData.data[this.field.fieldName].displayValue == undefined
+          || this.modelData.data[this.field.fieldName].displayValue.length < this.field.entityFieldIndex + 1){
+        return null;
+      }
+      return this.modelData.data[this.field.fieldName].displayValue[this.field.entityFieldIndex];
+    },
+
+
+    // 值改变事件
+    valueChanged: function (newFieldValue) {
+      // this.fieldValue = newFieldValue;
+      this.$emit('valueChanged', newFieldValue);
+    },
+
+    /**
+     * 执行Callout
+     */
+    executeCallout: function (field) {
+      this.$emit('executeCallout', field);
+    },
+
+    //年份选择器值改变
+    textChanged: function (value) {
+      var newFieldValue = {
+        displayValue: [value],
+        fieldType: 'String',
+      };
+      this.$emit('valueChanged', newFieldValue);
+    },
+
+    validateRule: function (field) {
+      return field.mandatory ? 'required' : '';
+    },
+
+    performValide: function () {
+      var _self = this;
+      _self.validateMandatory = true;
+
+      if (this.field.mandatory == true) {
+        if (
+          fieldUtil.isRadioButtonGroupType(this.field) ||
+          fieldUtil.isSelectType(this.field) ||
+          fieldUtil.isSearchType(this.field)
+        ) {
+          var idValue =
+            this.modelData == undefined ||
+            this.modelData.data == undefined ||
+            this.modelData.data[this.field.fieldName] == undefined
+              ? undefined
+              : this.modelData.data[this.field.fieldName].id;
+          if (idValue == null || idValue == '' || idValue < 1) {
+            _self.validateMandatory = false;
+          }
+        } else if (fieldUtil.isCheckBoxType(this.field)) {
+          return true;
+        } else if (fieldUtil.isRedGreenEditorType(this.field)) {
+          return true;
+        } else {
+          var stringValue =
+            this.modelData == undefined ||
+            this.modelData.data == undefined ||
+            this.modelData.data[this.field.fieldName] == undefined
+              ? ''
+              : this.modelData.data[this.field.fieldName].displayValue[0];
+          if (stringValue == undefined || stringValue === '') {
+            _self.validateMandatory = false;
+          }
+        }
+      }
+      return _self.validateMandatory;
+    },
+
+    /**
+     * 年份变化
+     */
+    yearChange: function () {},
+
+    /**
+     * 年月选择完成
+     */
+    refresh: function () {},
+
+    numberFormat: function () {
+      if (
+        this.fieldItem &&
+        this.fieldItem.pattern &&
+        this.fieldItem.pattern != ''
+      ) {
+        var _self = this;
+        fieldUtil.formatNumber(
+          this.displayValue,
+          this.fieldItem.pattern,
+          function (formatValue) {
+            _self.displayValue = formatValue;
+          },
+        );
+      }
+    },
+
+    /**
+     * 禁止滚轮滚动影响数字
+     */
+    mouseWheelEvent: function (event) {
+      if (event || Window.event) {
+        event.preventDefault();
+      }
+    },
+
+    /**
+     * 响应显示逻辑
+     */
+    showLogical: function () {
+      let _self = this;
+      var logic = this.field.showLogical;
+      if (logic == null || logic == '') {
+        this.showField = true;
+        return;
+      }
+
+      const functionName =
+        this.field.fieldName.replace('.', '_') + '_showLogical';
+
+
+      let executeFunction = function(){
+        try{
+          let context = new _self.getContext(_self.modelData);
+          _self.showField = _self[functionName](context);
+        }catch(error){
+          console.error(error);
+          Notify.error('数据字典定义异常', '【' + logic + '】前端列显示逻辑定义异常,请联系管理员检查数据字典的定义。', false);
+        }
+      };
+
+      if (_self[functionName] == null) {
+        const jsUrl = _self.jsUrl;
+        if(jsUrl == null || jsUrl == undefined){
+          Notify.error('数据字典定义异常', '【' + logic + '】前端列显示逻辑的JS文件不存在,请联系管理员检查数据字典是否JS文件。', false);
+          return;
+        }
+
+        let promise = JsUtil.dynamicLoadJsFunction(jsUrl, logic);
+
+        promise.then(targetFunction => {
+          _self[functionName] = targetFunction;
+          if(_self[functionName] == null){
+            Notify.error('数据字典定义异常', '【' + logic + '】前端列显示逻辑方法不存在,请联系管理员检查数据字典。', false);
+            return;
+          }
+          executeFunction();
+        }, errorData => {
+          console.error(errorData);
+        });
+      }else{
+        executeFunction();
+      }
+    },
+  },
+};
+</script>
+<style scoped>
+.required-mark {
+  color: red;
+  margin-right: 10px;
+}
+
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+  -webkit-appearance: none !important;
+  margin: 0;
+}
+
+input[type="number"] {
+  -moz-appearance: textfield;
+}
+textarea {
+  overflow-y: auto !important;
+  overflow-x: hidden !important;
+}
+
+.form-group-single-row {
+  display: block;
+  width: 100%;
+}
+
+.input-group-item {
+  word-break: break-all;
+}
+
+.input-group-item-manytomany {
+  color: #333;
+  background-color: #f0f0f0;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  height: 26px;
+  margin: 4px 4px 0px 0px;
+  padding: 6px 0.25em;
+}
+
+.label-multiple-column {
+  width: 120px;
+}
+
+.form-group-multiple-column {
+  margin-top: 5px;
+}
+
+@media (max-width: 768px) {
+  .input-group-single-row {
+    width: 100%;
+  }
+
+  .input-group-item {
+    padding-top: 0px;
+  }
+}
+
+@media (min-width: 768px) {
+  .label-multiple-column {
+    text-overflow: ellipsis;
+    text-align: right;
+    padding-right: 5px;
+  }
+
+  .input-group-multiple-column {
+    width: 230px;
+  }
+
+  .input-group-single-row {
+    width: calc(100% - 200px) !important;
+    word-wrap: break-word;
+  }
+}
+
+.m-sr-only {
+  visibility: hidden;
+}
+</style>
+
+
+<style>
+.m-date-fieldEditView > div:first-child {
+  width: 100%;
+}
+</style>

+ 189 - 0
src/window1/tabFormEdit/SubOneToOneTabEdit.vue

@@ -0,0 +1,189 @@
+<template>
+  <div class="m-row clearfix">
+    <div>
+      <div
+        :class="{
+          'form-inline': tab.tabFormView.multipleColumn,
+          'form-horizontal': !tab.tabFormView.multipleColumn,
+        }"
+      >
+        <template
+          v-for="fieldItem in tabFormFields"
+          :key="
+            'FieldEditView_' +
+              windowNo +
+              '_' +
+              tabIndex +
+              '_' +
+              fieldItem.fieldName +
+              '_' +
+              fieldItem.entityFieldIndex
+          "
+        >
+          <FieldEditView
+            ref="fieldItem1"
+            :class-name="tab.tabDataSource.className"
+            :field="fieldItem"
+            :model-data="oneToOneModelData"
+            :window-no="windowNo"
+            :tab-index="tabIndex"
+            :js-url="jsUrl"
+            :is-chinese-english="curdWindow.isChineseEnglish"
+            :multiple-column="tab.tabFormView.multipleColumn"
+            @value-changed="valueChanged($event, fieldItem)"
+            @execute-callout="executeCallout(fieldItem)"
+          />
+        </template>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Common from '../../common/Common';
+import { Notify } from 'pc-component-v3';
+import FieldEditView from './SubOneToOneFormFieldEdit.vue';
+import WindowClientUtil from '../../resource/dictionary/WindowClientUtil.js';
+import WindowServerUtil from '../../resource/dictionary/WindowServerUtil.js';
+
+export default {
+  components: {
+    FieldEditView,
+  },
+
+  props: {
+    subOneToOneTab: {
+      type: Object,
+      default: null,
+    },
+    tab: {
+      type: Object,
+      default: null,
+    },
+    modelData: {
+      type: Object,
+      default: null,
+    },
+    modelDataCreate: {
+      type: Object,
+      default: null,
+    },
+    curdWindow: {
+      type: Object,
+      default: null,
+    },
+
+    windowNo: {
+      type: String,
+      default: null,
+    },
+    tabIndex: {
+      type: Number,
+      default: null,
+    },
+
+    type: {
+      type: String,
+      default: null,
+    },
+    jsUrl: {
+      type: String,
+      default: null,
+    },
+    recordId: {
+      type: String,
+      default: null,
+    },
+    isChineseEnglish: {
+      type: Boolean,
+      default: null,
+    },
+  },
+
+  emits: ['executeCallout', 'valueChanged'],
+
+  data() {
+    return {
+      tabFormFields: [],
+      oneToOneModelData: null,
+    };
+  },
+
+  watch: {
+    subOneToOneTab: {
+      handler(newVal) {
+        if (newVal) {
+          this.tabFormFields = WindowClientUtil.getDetailField(newVal);
+          if (this.type === 'create') {
+            this.getOneToOneModelData(newVal.tabIndex);
+          } else {
+            this.getOneToOneTabData(newVal.tabIndex);
+          }
+        }
+      },
+      immediate: true,
+    },
+  },
+
+  methods: {
+    // 获取一对一页签modelData(新建)
+    getOneToOneModelData: function (tabIndex) {
+      const _self = this;
+      WindowServerUtil.newModelData(
+        _self.windowNo,
+        0,
+        _self.modelDataCreate,
+        function (response) {
+          if (response.errorCode != 0) {
+            Notify.error('数据新建异常', response.errorMessage, false);
+            return;
+          }
+          let modelData = response.data;
+
+          modelData.editMode = true;
+
+          _self.oneToOneModelData = modelData;
+
+        },
+      );
+    },
+
+    // 查询一对一页签数据(编辑)
+    getOneToOneTabData: function (tabIndex) {
+      const _self = this;
+      const params = {
+        tabIndex,
+        windowNo: this.windowNo,
+        parentId: _self.modelData == undefined ? undefined : _self.modelData.id,
+      };
+      $.ajax({
+        url: Common.getApiURL('CurdWindowResource/oneToOneTabData'),
+        async: false,
+        dataType: 'json',
+        type: 'post',
+        data: JSON.stringify(params),
+        contentType: 'application/json',
+        beforeSend: function (request) {
+          Common.addTokenToRequest(request);
+        },
+        success: function (data) {
+          _self.oneToOneModelData = data;
+        },
+        error: function (XMLHttpRequest, textStatus, errorThrown) {
+          Common.processException(XMLHttpRequest, textStatus, errorThrown);
+        },
+      });
+    },
+    valueChanged(newFieldValue, fieldItem) {
+      console.log(newFieldValue, fieldItem, 'oneToOne数据---');
+      this.$emit('valueChanged', newFieldValue, fieldItem);
+    },
+    executeCallout(field) {
+      this.$emit('executeCallout', field);
+    },
+  },
+};
+</script>
+
+<style scoped>
+</style>

+ 24 - 1
src/window1/tabFormEdit/TabFormEditModal.vue

@@ -210,6 +210,21 @@
             </div>
           </div>
         </div>
+        <template v-if="modelData && tab.subOneToOneTabs && tab.subOneToOneTabs.length">
+          <div v-for="oneToOneTab in tab.subOneToOneTabs " :key="'SubOneToOneTabGridEdit-' + windowNo + '-' + tabIndex + '-' + modelData.id + '-oneToOneTab-' + oneToOneTab.tabIndex">
+            <SubOneToOneTabEdit
+              :sub-one-to-one-tab="oneToOneTab" :tab="tab" :model-data="modelData"
+              :window-no="windowNo" :model-data-create="modelDataCreate"
+              :tab-index="tabIndex"
+              :type="type"
+              :js-url="jsUrl"
+              :record-id="recordId"
+              :curd-window="curdWindow"
+              :is-chinese-english="curdWindow.isChineseEnglish"
+              @value-changed="valueChanged" @execute-callout="executeCallout"
+            />
+          </div>
+        </template>
 
         <template v-if="modelData != null">
           <div
@@ -326,7 +341,9 @@ import ApproveComment from '../../workflow/ApproveComment.vue';
 import DataRecoveryResource from '../../api/base/DataRecoveryResource.js';
 import JsUtil from '../../common/JsUtil.js';
 import { Notify } from 'pc-component-v3';
-import { SaveOutlined, RotateLeftOutlined, ReloadOutlined, SyncOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue';
+import { SaveOutlined, RotateLeftOutlined, ReloadOutlined, SyncOutlined, QuestionCircleOutlined,CaretLeftOutlined,CaretRightOutlined } from '@ant-design/icons-vue';
+import SubOneToOneTabEdit from './SubOneToOneTabEdit.vue';
+
 export default {
   components: {
     SubTabGridEdit,
@@ -342,6 +359,9 @@ export default {
     ReloadOutlined,
     SyncOutlined,
     QuestionCircleOutlined,
+    SubOneToOneTabEdit,
+    CaretLeftOutlined,
+    CaretRightOutlined,
   },
 
   /**
@@ -794,6 +814,7 @@ export default {
           WindowClientUtil.parseFilterSchema(curdWindow);
           var tab = WindowClientUtil.getTab(curdWindow, _self.tabIndex);
           WindowClientUtil.parseSubTab(curdWindow, tab);
+          WindowClientUtil.parseOneToOneTab(curdWindow, tab);
           WindowClientUtil.parseFieldGroup(tab);
           WindowClientUtil.restoreWindowTabFieldGroupsVisible(curdWindow);
 
@@ -882,6 +903,7 @@ export default {
         WindowClientUtil.parseFilterSchema(curdWindow);
         var tab = WindowClientUtil.getTab(curdWindow, _self.tabIndex);
         WindowClientUtil.parseSubTab(curdWindow, tab);
+        WindowClientUtil.parseOneToOneTab(curdWindow, tab);
         WindowClientUtil.parseFieldGroup(tab);
         WindowClientUtil.restoreWindowTabFieldGroupsVisible(curdWindow);
 
@@ -957,6 +979,7 @@ export default {
           WindowClientUtil.parseFilterSchema(curdWindow);
           var tab = WindowClientUtil.getTab(curdWindow, _self.tabIndex);
           WindowClientUtil.parseSubTab(curdWindow, tab);
+          WindowClientUtil.parseOneToOneTab(curdWindow, tab);
           WindowClientUtil.parseFieldGroup(tab);
           WindowClientUtil.restoreWindowTabFieldGroupsVisible(curdWindow);
 

+ 143 - 0
src/window1/tabFormView/SubOneToOneTabView.vue

@@ -0,0 +1,143 @@
+<template>
+  <div class="m-row clearfix">
+    <div>
+      <div
+        :class="{
+          'form-inline': tab.tabFormView.multipleColumn,
+          'form-horizontal': !tab.tabFormView.multipleColumn,
+        }"
+      >
+        <template
+          v-for="fieldItem in tabFormFields"
+          :key="
+            'FieldEditView_' +
+              windowNo +
+              '_' +
+              tabIndex +
+              '_' +
+              fieldItem.fieldName +
+              '_' +
+              fieldItem.entityFieldIndex
+          "
+        >
+          <FieldEditView
+            ref="fieldItem1"
+            :class-name="tab.tabDataSource.className"
+            :field="fieldItem"
+            :model-data="modelData"
+            :window-no="windowNo"
+            :tab-index="tabIndex"
+            :js-url="jsUrl"
+            :is-chinese-english="curdWindow.isChineseEnglish"
+            :multiple-column="tab.tabFormView.multipleColumn"
+            @execute-callout="executeCallout(fieldItem)"
+          />
+        </template>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Common from '../../common/Common';
+import FieldEditView from './TabFormFieldView.vue';
+import WindowClientUtil from '../../resource/dictionary/WindowClientUtil.js';
+
+export default {
+  components: {
+    FieldEditView,
+  },
+
+  props: {
+    subOneToOneTab: {
+      type: Object,
+      default: null,
+    },
+    tab: {
+      type: Object,
+      default: null,
+    },
+    modelData: {
+      type: Object,
+      default: null,
+    },
+    curdWindow: {
+      type: Object,
+      default: null,
+    },
+
+    windowNo: {
+      type: String,
+      default: null,
+    },
+    tabIndex: {
+      type: Number,
+      default: null,
+    },
+
+    jsUrl: {
+      type: String,
+      default: null,
+    },
+    isChineseEnglish: {
+      type: Boolean,
+      default: null,
+    },
+  },
+
+  emits: ['executeCallout'],
+
+  data() {
+    return {
+      oneToOneModelData: [],
+    };
+  },
+  watch: {
+    subOneToOneTab: {
+      handler(newVal) {
+        if (newVal) {
+          this.tabFormFields = WindowClientUtil.getDetailField(newVal);
+          this.getOneToOneTabData(newVal.tabIndex);
+        }
+      },
+      immediate: true,
+    },
+  },
+
+  methods: {
+    // 查询一对一页签数据
+    getOneToOneTabData: function (tabIndex) {
+      const _self = this;
+      const params = {
+        tabIndex,
+        windowNo: this.windowNo,
+        parentId: _self.modelData == undefined ? undefined : _self.modelData.id,
+      };
+      $.ajax({
+        url: Common.getApiURL('CurdWindowResource/oneToOneTabData'),
+        async: false,
+        dataType: 'json',
+        type: 'post',
+        data: JSON.stringify(params),
+        contentType: 'application/json',
+        beforeSend: function (request) {
+          Common.addTokenToRequest(request);
+        },
+        success: function (data) {
+          _self.oneToOneModelData = data;
+        },
+        error: function (XMLHttpRequest, textStatus, errorThrown) {
+          Common.processException(XMLHttpRequest, textStatus, errorThrown);
+        },
+      });
+    },
+
+    executeCallout(field) {
+      this.$emit('executeCallout', field);
+    },
+  },
+};
+</script>
+
+<style scoped>
+</style>

+ 18 - 1
src/window1/tabFormView/TabFormViewModal.vue

@@ -210,6 +210,20 @@
         <!-- eslint-disable-next-line -->
         <div v-html="footerHtml" />
 
+        <template v-if="modelData && tab.subOneToOneTabs && tab.subOneToOneTabs.length">
+          <div v-for="oneToOneTab in tab.subOneToOneTabs " :key="'SubOneToOneTabGridEdit-' + windowNo + '-' + tabIndex + '-' + modelData.id + '-oneToOneTab-' + oneToOneTab.tabIndex">
+            <SubOneToOneTabView
+              :sub-one-to-one-tab="oneToOneTab" :tab="tab" :model-data="modelData"
+              :window-no="windowNo"
+              :tab-index="tabIndex"
+              :js-url="jsUrl"
+              :curd-window="curdWindow"
+              :is-chinese-english="curdWindow.isChineseEnglish"
+              @execute-callout="executeCallout(fieldItem)"
+            />
+          </div>
+        </template>
+
         <template v-if="modelData != null">
           <div
             v-for="subTab in tab.subTabs"
@@ -388,7 +402,7 @@ import HistoryApproveComment from '../../workflow/HistoryApproveComment.vue';
 import { CaretRightOutlined, CaretLeftOutlined, SmallDashOutlined, ExclamationCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue';
 import { createVNode } from 'vue';
 import { Modal } from 'ant-design-vue';
-
+import SubOneToOneTabView from './SubOneToOneTabView.vue';
 export default {
 
   components: {
@@ -413,6 +427,7 @@ export default {
     ExclamationCircleOutlined,
     Modal,
     QuestionCircleOutlined,
+    SubOneToOneTabView,
   },
 
   props: {
@@ -1162,6 +1177,7 @@ export default {
         WindowClientUtil.parseFilterSchema(curdWindow);
         var tab = WindowClientUtil.getTab(curdWindow, _self.tabIndex);
         WindowClientUtil.parseSubTab(curdWindow, tab);
+        WindowClientUtil.parseOneToOneTab(curdWindow, tab);
         WindowClientUtil.parseFieldGroup(tab);
         WindowClientUtil.restoreWindowTabFieldGroupsVisible(curdWindow);
         _self.tabFormFields = WindowClientUtil.getDetailField(tab);
@@ -1216,6 +1232,7 @@ export default {
           WindowClientUtil.parseFilterSchema(curdWindow);
           var tab = WindowClientUtil.getTab(curdWindow, _self.tabIndex);
           WindowClientUtil.parseSubTab(curdWindow, tab);
+          WindowClientUtil.parseOneToOneTab(curdWindow, tab);
           WindowClientUtil.parseFieldGroup(tab);
           WindowClientUtil.restoreWindowTabFieldGroupsVisible(curdWindow);
           _self.tabFormFields = WindowClientUtil.getDetailField(tab);