动态表单组件渲染并采集 展示vue component

复制代码
<!-- src/components/DynamicField/index.vue -->
<template>
  <el-form-item
      :label="fieldLabel"
      :prop="prop"
      :required="isRequired"
      :rules="fieldRules"
  >
    <component
        :is="currentComponent"
        v-model="internalValue"
        v-bind="dynamicProps"
        v-on="dynamicEvents"
        :class="noplus?'noplus':''"
    >
      <i slot="default" v-show="currentComponent=='ElUpload'&&!disabled" class="el-icon-plus"></i>
    </component>
  </el-form-item>

</template>

<script>
// 导入实际的 Element UI 组件
import { Input, InputNumber, DatePicker, Upload } from 'element-ui';
import { getToken } from '@/utils/auth';
// 组件映射表
const COMPONENT_MAP = {
  TEXT: 'ElInput',
  NUMBER: 'ElInputNumber',
  DATE: 'ElDatePicker',
  IMAGE: 'ElUpload'
};

export default {
  name: 'DynamicField',
  components: {
    ElInput: Input,
    ElInputNumber: InputNumber,
    ElDatePicker: DatePicker,
    ElUpload: Upload
  },
  props: {
    // 数据类型:TEXT/NUMBER/DATE/IMAGE
    lx: {
      type: String,
      required: true
    },
    // 字段值
    value: {
      type: [String, Number, Date, Array],
      default: ''
    },
    // 字段配置
    config: {
      type: Object,
      default: () => ({})
    },
    // 是否显示标签
    showLabel: {
      type: Boolean,
      default: true
    },
    // 标签名称(覆盖config中的sjxmc)
    label: {
      type: String,
      default: ''
    },
    // 是否必填
    required: {
      type: Boolean,
      default: false
    },
    // 表单验证规则路径
    prop: {
      type: String,
      default: ''
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false
    },
    // 占位符
    placeholder: {
      type: String,
      default: ''
    },
    // 其他属性透传
    otherProps: {
      type: Object,
      default: () => ({})
    },
    // 是否显示错误信息
    showMessage: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      noplus:false,
      internalValue: this.formatValue(this.value),
      // 上传相关状态
      uploadLoading: false,
      uploadHeaders: {
        Authorization: 'Bearer ' + getToken()
      }
    };
  },
  computed: {
    // 根据类型选择组件
    currentComponent() {
      const type = this.lx.toUpperCase();
      return COMPONENT_MAP[type] || 'ElInput';
    },

    // 字段标签(包含必填星号)
    fieldLabel() {
      const label = this.label || this.config.sjxmc || '';
      return label;
    },

    isRequired() {
      if (this.required !== undefined) {
        return this.required;
      }
      return this.config.sfbt === '1';
    },

    // 字段验证规则
    fieldRules() {
      const rules = [];

      if (this.isRequired) {
        const message = (this.label || this.config.sjxmc || '该字段') + '不能为空';

        if (this.lx.toUpperCase() === 'IMAGE') {
          // 图片类型特殊验证
          rules.push({
            required: true,
            message: message,
            trigger: 'change',
            validator: (rule, value, callback) => {
              if (!value || (typeof value === 'string' && !value.trim())) {
                callback(new Error(message));
              } else {
                callback();
              }
            }
          });
        } else {
          rules.push({
            required: true,
            message: message,
            trigger: 'blur'
          });
        }
      }

      return rules;
    },

    // 动态属性
    dynamicProps() {
      const baseProps = {
        disabled: this.disabled,
        placeholder: this.getPlaceholder(),
        ...this.otherProps
      };

      const type = this.lx.toUpperCase();

      // 根据不同的数据类型添加特定的属性
      switch (type) {
        case 'TEXT':
          const isTextarea = this.config.zdkdxs === '2';
          return {
            ...baseProps,
            type:  'textarea',
            rows: isTextarea ? (this.config.hs || 3) : undefined,
            maxlength: this.config.zdcd,
            'show-word-limit': !!this.config.zdcd,
            clearable: this.config.clearable !== false
          };

        case 'NUMBER':
          return {
            ...baseProps,
            step: this.config.bdz || 1,
            precision: this.config.decimal || 2,
            controls: this.config.controls !== false,
            'controls-position': this.config.controlsPosition || 'right',
            style: { width: this.config.width || '100%' }
          };

        case 'DATE':
          // 根据配置确定日期类型
          let dateType = 'datetime';
          let format = 'yyyy-MM-dd HH:mm:ss';
          let valueFormat = 'yyyy-MM-dd HH:mm:ss';

          switch (String(this.config.zdlx)) {
            case '1': // 日期
              dateType = 'date';
              format = 'yyyy-MM-dd';
              valueFormat = 'yyyy-MM-dd';
              break;
            case '2': // 日期时间
              dateType = 'datetime';
              format = 'yyyy-MM-dd HH:mm:ss';
              valueFormat = 'yyyy-MM-dd HH:mm:ss';
              break;
            case '3': // 年
              dateType = 'year';
              format = 'yyyy';
              valueFormat = 'yyyy';
              break;
            case '4': // 月
              dateType = 'month';
              format = 'yyyy-MM';
              valueFormat = 'yyyy-MM';
              break;
          }

          return {
            ...baseProps,
            type: dateType,
            format: format,
            'value-format': valueFormat,
            clearable: this.config.clearable !== false,
            editable: this.config.editable !== false,
            style: { width: this.config.width || '100%' }
          };

        case 'IMAGE':
          const fileList = this.getFileList();
          const hasFile = fileList && fileList.length > 0;

          return {
            ...baseProps,
            action: this.config.action || process.env.VUE_APP_BASE_API + '/common/upload',
            'list-type': 'picture-card',
            headers: { Authorization: 'Bearer ' + getToken() },
            multiple: false,  // 单文件上传
            limit: 1,  // 限制1个文件
            accept: this.config.accept || 'image/jpeg,image/png,image/gif',
            'file-list': this.getFileList(),
            'on-success': this.handleUploadSuccess,
            'on-remove': this.handleRemove,
            'on-exceed': this.handleExceed,
            'on-preview': this.handlePreview,
            'before-upload': this.beforeUpload,
            'on-error': this.handleUploadError,

          };

        default:
          return baseProps;
      }
    },

    // 动态事件
    dynamicEvents() {
      const events = {
        input: (value) => {
          this.internalValue = value;
          this.$emit('input', value);
        }
      };

      // 添加其他事件
      ['focus', 'blur', 'clear','change'].forEach(event => {
        events[event] = (e) => {

          if(event==='change'||event==='blur'){
            this.$emit('my'+event, {
              field: this.config.sjxdm,
              value: this.internalValue,
              config: this.config
            });

          }else {
            this.$emit(event, {
              field: this.config.sjxdm,
              value: this.internalValue,
              config: this.config
            });
          }





        };
      });

      return events;
    }
  },
  watch: {
    // 监听外部 value 变化,实现回显
    value: {
      immediate: true,
      handler(newVal) {
        this.internalValue = this.formatValue(newVal);
        this.noplus=newVal?true:false
      }
    },
    // 监听内部值变化
    internalValue(newVal) {

      console.log('newVal',newVal)
      this.$emit('input', newVal);
      this.noplus=newVal?true:false
    }
  },
  methods: {
    // 格式化值
    formatValue(value) {
      if (value === null || value === undefined) {
        // 如果有默认值,使用默认值
        return this.config.mrz || '';
      }
      return value;
    },

    // 获取占位符
    getPlaceholder() {
      if (this.placeholder) {
        return this.placeholder;
      }


      if (this.config.cjts) {
        return this.config.cjts;
      }

      const placeholderMap = {
        TEXT: '请输入',
        NUMBER: '请输入',
        DATE: '请选择',
        IMAGE: '请上传'
      };

      return placeholderMap[this.lx.toUpperCase()] || '';
    },

    // 获取文件列表(图片上传用)
    getFileList() {
      if (this.lx.toUpperCase() !== 'IMAGE') return [];

      if (!this.internalValue) return [];

      // 单文件上传,直接返回文件信息
      return [{
        name: this.getFileNameFromUrl(this.internalValue),
        url: this.internalValue
      }];
    },

    // 从URL中提取文件名
    getFileNameFromUrl(url) {
      if (!url) return '图片';
      const parts = url.split('/');
      return parts[parts.length - 1] || '图片';
    },

    // 上传前的校验
    beforeUpload(file) {
      // 检查文件类型
      const isImage = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'].includes(file.type.toLowerCase());
      if (!isImage) {
        this.$message.error('只能上传图片格式!');
        return false;
      }

      // 检查文件大小(限制5MB)
      const isLt5M = file.size / 1024 / 1024 < 5;
      if (!isLt5M) {
        this.$message.error('图片大小不能超过5MB!');
        return false;
      }

      this.uploadLoading = true;
      return true;
    },

    // 上传成功处理
    handleUploadSuccess(response, file) {
      this.uploadLoading = false;

      console.log('handleUploadSuccess',response)

      if (response.code === 200) {
        const fileUrl = response.fileName;
        if (fileUrl) {
          // 单文件上传,直接赋值
          this.internalValue = fileUrl;
          this.$emit('input', fileUrl);
          this.$emit('mychange', {
            field: this.config.sjxdm,
            value: fileUrl,
            config: this.config
          });
          console.log('更新url',fileUrl)
          this.$message.success('上传成功');
        } else {
          this.$message.error('上传失败:未获取到文件地址');
        }
      } else {
        this.$message.error(response.msg || '上传失败');
      }
    },

    // 上传错误处理
    handleUploadError(err, file) {
      this.uploadLoading = false;
      this.$message.error('上传失败');
      console.error('上传错误:', err, file);
    },

    // 文件移除处理
    handleRemove(file, fileList) {
      // 清空值
      this.internalValue = '';
      this.$emit('mychange', {
        field: this.config.sjxdm,
        value: null,
        config: this.config
      });

      this.$message.success('已移除图片');
    },

    // 图片预览
    handlePreview(file) {
      if (file.url) {
        window.open(file.url);
      }
    },

    // 超出限制处理
    handleExceed(files, fileList) {
      this.$message.warning('只能上传一个文件,如需更换请先删除当前文件');
    },

    // 清空字段
    clear() {
      this.internalValue = '';
      this.$emit('clear', {
        field: this.config.sjxdm,
        config: this.config
      });
    },

    // 获取当前值
    getValue() {
      return this.internalValue;
    },

    // 设置值
    setValue(value) {
      this.internalValue = this.formatValue(value);
    },

    // 重置为默认值
    reset() {
      this.internalValue = this.config.mrz || '';
    },

    // 手动触发上传
    submitUpload() {
      this.$refs.upload && this.$refs.upload.submit();
    },

    // 清空已上传文件
    clearFiles() {
      this.internalValue = '';
    }
  },
};
</script>




<style scoped>
/* 图片上传样式优化 */
:deep(.el-upload--picture-card) {
  width: 100px;
  height: 100px;
  line-height: 100px;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  background-color: #fafafa;
}

:deep(.el-upload--picture-card:hover) {
  border-color: #409eff;
}

:deep(.el-upload-list--picture-card .el-upload-list__item) {
  width: 100px;
  height: 100px;
  border: 1px solid #d9d9d9;
  border-radius: 6px;
}

/* 上传按钮中的图标样式 */
:deep(.el-upload--picture-card .el-icon-plus) {
  font-size: 28px;
  color: #8c939d;
  margin-top: 20px;
}

/* 上传组件容器 */
:deep(.el-upload-list) {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 文件列表项样式 */
:deep(.el-upload-list__item) {
  transition: all 0.3s ease;
}

:deep(.el-upload-list__item:hover) {
  background-color: #f5f7fa;
}

/* 上传提示信息 */
:deep(.el-upload__tip) {
  font-size: 12px;
  color: #909399;
  margin-top: 5px;
  line-height: 1.2;
}

body > div.el-dialog__wrapper > div > div.el-dialog__body > form > div:nth-child(4) > div.el-col.el-col-12 > div > div > div{

  display: flex;

}


 ::v-deep .noplus .el-upload--picture-card{
  display: none;
}

</style>
复制代码
<template>
  <div class="app-container">
    <!-- 如果没有选择应急信息,显示提示 -->
    <div v-if="!zbid" style="text-align: center; padding: 50px; color: #909399;">
      <i class="el-icon-warning" style="font-size: 48px; margin-bottom: 20px;"></i>
      <p>请先在应急事件列表中选择一条记录,然后切换到"应急处置"步骤</p>
    </div>

    <!-- 已选择应急信息时显示内容 -->
    <div v-else>
      <el-table :data="CZZBList" border style="margin-bottom: 16px">
      <el-table-column type="index" width="50" align="center" label="序号" />
      <el-table-column label="出发时间" align="center" prop="czsj" width="150">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.cfsj, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="到达时间" align="center" prop="czsj" width="150">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.ddsj, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="现场情况" align="center" prop="xcqk"/>
      <el-table-column label="汇报时间" align="center" prop="czsj" width="150">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.hbsj, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="汇报人员" align="center" prop="hbry" width="180"/>

      <el-table-column label="处置内容" align="center" prop="cznr"/>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon=""
            @click="fjck(scope.row)"
            v-hasPermi="['yjzh:yjcz:fjck']"
          >附件查看
          </el-button>
          <el-button
            size="mini"
            type="text"
            icon=""
            @click="ryqkck(scope.row)"
          >人员情况
          </el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            v-hasPermi="['yjzh:yjcz:ryqkbj']"
            @click="ryqkEdit(scope.row)"
          >人员编辑
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <el-table v-loading="loading" :data="CZMXList" border>
      <el-table-column type="index" width="50" align="center" label="序号">
      </el-table-column>
      <el-table-column label="处置项目名称" align="center" prop="czmxmc" width="100"/>
      <el-table-column label="处置项目内容" align="left" prop="czmxnr"/>
      <el-table-column label="是否处置" align="center" prop="czzt">
        <template slot-scope="scope">
          <el-radio-group v-model="scope.row.czzt" @change="handleChange(scope.row)">
            <el-radio v-for="dict in zdsfcz" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
          </el-radio-group>
        </template>
      </el-table-column>
      <el-table-column label="处置时间" align="center" prop="czsj" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.czsj, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="bz" label="备注" show-overflow-tooltip>
        <template slot-scope="scope">
          <el-input v-model="scope.row.bz"  placeholder="请输入备注"></el-input>
        </template>
      </el-table-column>

      <el-table-column  label="操作" >
        <template slot-scope="scope">
          <el-button
            icon="el-icon-edit"
            size="mini"
            type="primary"
            @click="sjxopen_m(scope.row,'pc')"
            v-hasPermi="['yjzh:yjcz:sjxcj']"
          >采集
          </el-button>

          <el-button
            icon="el-icon-edit"
            size="mini"
            type="primary"
            @click="sjxopen_m(scope.row,'ck')"
            v-hasPermi="['yjzh:yjcz:sjxck']"
          >查看
          </el-button>
        </template>
      </el-table-column>


    </el-table>

    <!-- 添加或修改问题明细对话框 -->
    <el-dialog title="附件列表" :visible.sync="fjopen" width="1200px" append-to-body>
      <el-table :data="fjlist" @row-click="click">
        <el-table-column type="index" width="55" align="center" label="序号"/>
        <el-table-column label="图片类型" align="center" prop="fjlx"/>
        <el-table-column label="附件预览" align="center">
          <template slot-scope="scope">
            <!-- 如果是图片,则显示图片;否则显示默认文本 -->
            <span v-if="!isImage(scope.row.fjlj)"></span>
            <img v-else :src="scope.row.fjlj" style="max-width: 50px; max-height: 50px;">
          </template>

        </el-table-column>
        <el-table-column label="附件类型" align="center" prop="fjlx"/>
      </el-table>
      <div slot="footer" class="dialog-footer">
        <el-button @click="fjcancel">关 闭</el-button>
      </div>
    </el-dialog>

    <!-- 人员情况弹窗 -->
    <el-dialog :title="ryqkDialogTitle" :visible.sync="ryqkOpen" width="1600px" append-to-body>
      <div class="ryqk-container">
        <!-- 基本信息 -->
        <el-card class="info-card">
          <div slot="header" class="card-header">
            <span>基本信息</span>
            <el-button
              type="text"
              icon="el-icon-edit"
              @click="toggleEditMode"
              v-if="!ryqkEditMode"
            >编辑</el-button>
            <el-button
              type="text"
              icon="el-icon-check"
              @click="saveRyqkData"
              v-if="ryqkEditMode"
            >保存</el-button>
            <el-button
              type="text"
              icon="el-icon-close"
              @click="toggleEditMode"
              v-if="ryqkEditMode"
            >取消</el-button>
          </div>
          <div class="info-content">
            <div class="info-row">
              <div class="info-item">
                <label>交通方式:</label>
                <el-select
                  v-model="ryqkData.jtfs"
                  placeholder="请选择交通方式"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode">
                  <el-option
                    v-for="item in jtfsOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value">
                  </el-option>
                </el-select>
              </div>
              <div class="info-item">
                <label>车号:</label>
                <el-input
                  v-model="ryqkData.czch"
                  placeholder="请输入车号"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode"></el-input>
              </div>
            </div>
            <div class="info-row">
              <div class="info-item">
                <label>实际开始动员时间:</label>
                <el-date-picker
                  v-model="ryqkData.sjksdysj"
                  type="datetime"
                  placeholder="选择日期时间"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode">
                </el-date-picker>
              </div>
              <div class="info-item">
                <label>到达时间:</label>
                <el-date-picker
                  v-model="ryqkData.ddsj"
                  type="datetime"
                  placeholder="选择日期时间"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode">
                </el-date-picker>
              </div>
            </div>
            <div class="info-row">
              <div class="info-item">
                <label>开始工作:</label>
                <el-date-picker
                  v-model="ryqkData.ksgzsj"
                  type="datetime"
                  placeholder="选择日期时间"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode">
                </el-date-picker>
              </div>
              <div class="info-item">
                <label>结束时间:</label>
                <el-date-picker
                  v-model="ryqkData.qyjssj"
                  type="datetime"
                  placeholder="选择日期时间"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode">
                </el-date-picker>
              </div>
            </div>
            <div class="info-row">
              <div class="info-item">
                <label>填报人:</label>
                <el-input
                  v-model="ryqkData.ddldXm"
                  placeholder="请输入填报人"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode"></el-input>
              </div>
              <div class="info-item">
                <label>手机号:</label>
                <el-input
                  v-model="ryqkData.ddldSj"
                  placeholder="请输入手机号"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode"></el-input>
              </div>
            </div>
            <div class="info-row">
              <div class="info-item">
                <label>整体状态:</label>
                <el-select
                  v-model="ryqkData.dqzt"
                  placeholder="请选择状态"
                  style="width: 200px;"
                  :disabled="!ryqkEditMode">
                  <el-option
                    v-for="item in dqztOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value">
                  </el-option>
                </el-select>
              </div>
            </div>
            <div class="info-row">
              <div class="info-item">
                <label>备注:</label>
                <el-input
                  v-model="ryqkData.bz"
                  type="textarea"
                  :rows="2"
                  placeholder="请输入备注信息"
                  style="width: 500px;"
                  :disabled="!ryqkEditMode"></el-input>
              </div>
            </div>
          </div>
        </el-card>

        <!-- 队伍人员信息 -->
        <el-card class="list-card">
          <div slot="header" class="card-header">
            <span>队伍人员信息</span>
            <el-button
              style="float: right;"
              type="primary"
              size="small"
              @click="addPerson"
              :disabled="!ryqkEditMode">新增人员</el-button>
          </div>
          <el-table :data="ryqkList" border>
            <el-table-column type="index" width="50" align="center" label="序号" />
            <el-table-column label="姓名" align="center" prop="ryxm" width="120">
              <template slot-scope="scope">
                <el-input
                  v-model="scope.row.ryxm"
                  placeholder="请输入姓名"
                  :disabled="!ryqkEditMode"></el-input>
              </template>
            </el-table-column>
            <el-table-column label="联系电话" align="center" prop="lxdh" width="150">
              <template slot-scope="scope">
                <el-input
                  v-model="scope.row.lxdh"
                  placeholder="请输入联系电话"
                  :disabled="!ryqkEditMode"></el-input>
              </template>
            </el-table-column>
            <el-table-column label="状态" align="center" prop="ryzt" width="100">
              <template slot-scope="scope">
                <el-select
                  v-model="scope.row.ryzt"
                  placeholder="请选择状态"
                  style="width: 100%;"
                  :disabled="!ryqkEditMode">
                  <el-option
                    v-for="item in ryztOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value">
                  </el-option>
                </el-select>
              </template>
            </el-table-column>
            <el-table-column label="备注" align="center" prop="bz">
              <template slot-scope="scope">
                <el-input
                  v-model="scope.row.bz"
                  placeholder="请输入备注"
                  :disabled="!ryqkEditMode"></el-input>
              </template>
            </el-table-column>
          </el-table>
        </el-card>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="ryqkcancel">关 闭</el-button>
      </div>
    </el-dialog>

    <!-- 添加或修改问题明细对话框 -->
    <el-dialog title="图片" :visible.sync="tpopen" width="1600px" append-to-body>
      <img :src="fjform.fjlj" width="1500px">
      <div slot="footer" class="dialog-footer">
        <el-button @click="tpcancel">关 闭</el-button>
      </div>
    </el-dialog>
    <!-- 添加或修改问题明细对话框 -->
    <el-dialog title="视频" :visible.sync="spopen" width="1200px" append-to-body>
      <video-player
        ref="videoPlayer"
        class="video-player vjs-custom-skin"
        :playsinline="true"
        :options="playerOptions"/>
      <div slot="footer" class="dialog-footer">
        <el-button @click="spcancel">关 闭</el-button>
      </div>
    </el-dialog>

    <el-dialog title="数据项采集" :visible.sync="sjxopen" width="900px" append-to-body>


      <el-form
        ref="dynamicForm"
        label-width="80px"
        :model="formData"
      >
        <el-row>
          <el-divider>文本类型采集</el-divider>
          <el-col :span="8"   v-for="item in sjxlist.filter(item => item.sjlx === 'text')">
            <dynamic-field
             :key="item.id"
             lx="TEXT"
             :config="item"
             :placeholder="item.cjts"
             :label="item.sjxmc"
             v-model="item.zdz"
             :prop="item.sjxdm"
             :required="item.sfbt === '1'"
             @myblur="blurInput"
             :disabled="item.disabled"
            />
          </el-col>
        </el-row>
        <el-row>
        <el-divider>数字类型采集</el-divider>
        <el-col  :span="6" v-for="item in sjxlist.filter(item => item.sjlx === 'number')">
          <dynamic-field
            :prop="item.sjxdm"
            :key="item.id"
             lx="NUMBER"
            :config="item"
            v-model="item.zdz"
            :placeholder="item.cjts"
            :label="item.sjxmc"
            :required="item.sfbt === '1'"
            @myblur="blurInput"
            :disabled="item.disabled"

          />

        </el-col>
        </el-row>
        <el-row>
        <el-divider>日期类型采集</el-divider>
        <el-col :span="8"  v-for="item in sjxlist.filter(item => item.sjlx === 'date')">
          <dynamic-field
            :prop="item.sjxdm"
            :key="item.id"
             lx="DATE"
            :config="item"
            v-model="item.zdz"
            :placeholder="item.cjts"
            :label="item.sjxmc"
            :required="item.sfbt === '1'"
            @myblur="blurInput"
            :disabled="item.disabled"
          />
        </el-col>
        </el-row>
        <el-row>
        <el-divider>图片类型采集</el-divider>
        <el-col  :span="12" v-for="item in sjxlist.filter(item => item.sjlx === 'image')">
          <dynamic-field
            :key="item.id"
            lx="IMAGE"
            :prop="item.sjxdm"
            :config="item"
            :placeholder="item.cjts"
            :label="item.sjxmc"
            v-model="item.zdz"
            :required="item.sfbt === '1'"
            @mychange="blurInput"
            :disabled="item.disabled"
          />
        </el-col>
        </el-row>

      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="updateSjx">保存</el-button>
        <el-button @click="sjxopen=false">关 闭</el-button>
      </div>
    </el-dialog>

    <div style="float: right;margin-top: 15px;margin-right: 5px;">
      <el-button type="primary" plain size="small"  v-hasPermi="['yjzh:yjcz:czbc']" @click="submitForm">处置保存</el-button>
    </div>
    </div>
  </div>
</template>

<script>
import {listCZMX, updataAll, listCzmxzd, updataYjzhYjxxZt, listCZZB, listFj, getRyqkZb, listRyqkMx, saveRyqkZb, saveRyqkMx,getsjxlist,updataAllSjx} from "@/api/yjcz/czmx";
import DynamicField from "@/views/yjzh/yjcz/czmx/DynamicField.vue";
export default {
  name: "czmx",
  props: {
    zbid: '',
  },
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      CZZBList:[],
      // 处置明细表格数据
      CZMXList: [],
      zdCzmxList: {},
      zdsfcz: [{
        label: "未处置",
        value: "0",
        raw: {},
      }, {
        label: "已处置",
        value: "1",
        raw: {},
      },
      ],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 1000,
        zbid: null,
      },
      sjxopen:false,
      fjopen: false,
      tpopen:false,
      spopen:false,
      playerOptions: {
        playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
        autoplay: false, //如果true,浏览器准备好时开始回放。
        muted: false, // 默认情况下将会消除任何音频。
        loop: false, // 导致视频一结束就重新开始。
        preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
        language: "zh-CN",
        aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
        fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
        sources: [],
        //poster: require("/profile/upload/2023/11/21/1700552582344_20231121154319A002.mp4"), //你的封面地址
        notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
        controlBar: {
          timeDivider: true,
          durationDisplay: true,
          remainingTimeDisplay: false,
          fullscreenToggle: true, //全屏按钮
        },
      },
      fjform: {},
      fjlist:[],
      sjxlist:[],
      formData: {},
      formRules: {
      },
      saveable: true,
      currentZbid: '', // 当前选择的应急信息ID
      // 人员情况弹窗相关
      ryqkOpen: false,
      ryqkEditMode: false, // 编辑模式状态
      ryqkDialogTitle: '人员状态实时情况', // 弹窗标题
      ryqkData: {},
      ryqkList: [],
      jtfsOptions: [
        { label: '自驾', value: '自驾' },
        { label: '公司车辆', value: '公司车辆' },
        { label: '公共交通', value: '公共交通' }
      ],
      dqztOptions: [
        { label: '已出发', value: '0' },
        { label: '已到达', value: '1' },
        { label: '工作中', value: '2' },
        { label: '已结束', value: '3' }
      ],
      ryztOptions: [
        { label: '正常', value: '0' },
        { label: '异常', value: '1' }
      ]
    };
  },
  components: {
    DynamicField: DynamicField
  },
  created() {
    listCzmxzd(null).then(response => {
      var rows = response;
      for (var key in rows) {
        var data = rows[key];
        this.zdCzmxList[data.czmxdm + "czmxmc"] = data.czmxmc;
        this.zdCzmxList[data.czmxdm + "czmxnr"] = data.czmxnr;
      }
      this.getData();
      this.getzbData()
    });
  },
  methods: {
    blurInput(e){
      console.log("e触发",e)
      if(e.value==null||e.value==""){
        this.formData[e.field]= null;
      }else {
        this.formData[e.field]=e.value.toString()
        this.$nextTick(() => {
          this.$refs.dynamicForm.clearValidate(e.field);
        });
      }
    },
    updateSjx(){
      let find1=this.sjxlist.find(item=> {
        return  (item.zdz==''|| item.zdz==null)&&item.sfbt=="1";
      });
      if(find1==null){
        this.saveable = true;
      }else {
        this.saveable = false;
      }

        if (!this.saveable) {
          this.$message.error('请填写必填项');
          return;
        }else {
          console.log("this.sjxlist",this.sjxlist)
          updataAllSjx(this.sjxlist).then(response=>{
            this.sjxopen = false;
            this.$message.success("保存成功");
          })
        }
    },

    sjxopen_m(row,type="pc"){
      console.log("row",row)



      getsjxlist(row.zbid,row.czmxdm,type).then(response=>{
        this.sjxlist = response.rows;
        this.sjxlist.forEach(item=>{
        item.disabled=(type=="pc"?false:true);
        })
        this.sjxopen = true;

      })

    },
    getzbData(){
      listCZZB({zbid:this.zbid}).then(response=>{
        this.CZZBList = response.rows;
      })
    },
    /** 查询处置明细列表 */
    getData() {
      if (this.zbid == null) {
        this.loading = false;
      } else {
        this.loading = true;
        this.queryParams.zbid = this.zbid;
        listCZMX(this.queryParams).then(response => {
          var rows = response.rows;
          for (var key in rows) {
            var data = rows[key];
            data.czmxmc = this.zdCzmxList[data.czmxdm + "czmxmc"];
            data.czmxnr = this.zdCzmxList[data.czmxdm + "czmxnr"];
            if (data.czzt == null || data.czzt != "1") {
              data.czzt = "0";
              data.czsj = null;
            }
          }
          this.CZMXList = rows;
          this.total = response.total;
          this.loading = false;
        });
      }
    },
    handleChange(val) {
      if (val.czzt == "1") {
        val.czsj = new Date();
      } else {
        val.czsj = null;
      }
    },

    /** 提交按钮 */
    submitForm() {
      var rows = this.CZMXList;
      for (const key in rows) {
        var row = rows[key];
        console.log(row);
        if (row.czsj != null && Object.prototype.toString.call(row.czsj) === '[object Date]') {
          row.czsj = this.formatDate(row.czsj, "yyyy-MM-dd hh:mm:ss");
        }
      }
      updataAll(rows).then(response => {
        this.$modal.msgSuccess("修改成功");
        this.open = false;
        this.getList();
      });
    },
    /**
     * 格式化函数 , 给日期格式化
     * date为 new Date()对象, fmt为 'yyyy-MM-dd hh:mm:ss'的格式
     */
    formatDate(date, fmt) {
      //获取年份
      if (/(y+)/.test(fmt)) {
        // 把数字变成字符串
        let dateY = date.getFullYear() + "";
        //RegExp.$1 在判断中出现过,且是括号括起来的,所以 RegExp.$1 就是 "yyyy"
        fmt = fmt.replace(RegExp.$1, dateY.substr(4 - RegExp.$1.length));
      }

      //获取其他
      let o = {
        "M+": date.getMonth() + 1,
        "d+": date.getDate(),
        "h+": date.getHours(),
        "m+": date.getMinutes(),
        "s+": date.getSeconds(),
      };
      for (const k in o) {
        if (new RegExp(`(${k})`).test(fmt)) {
          let str = o[k] + "";
          fmt = fmt.replace(
            RegExp.$1,
            RegExp.$1.length == 1 ? str : this.padLeftZero(str)
          );
        }
      }
      return fmt;
    },

    padLeftZero(str) {
      return ("00" + str).substr(str.length);
    },
    isImage(url) {
      // 检查文件扩展名是否为常见的图片格式
      return /\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(url);
    },
    fjck(row){
      console.log(row)
      listFj(this.queryParams).then(response=>{
        this.fjlist = response.rows;
      })
      this.fjopen=true;
    },
    fjcancel(){
      this.fjopen=false;
    },
    // 取消按钮
    tpcancel() {
      this.tpopen = false;
    },
    // 取消按钮
    spcancel() {
      this.spopen = false;
    },
    click(row) {
      this.fjform = row;
        if(row.fjlx == 'SP'){
        this.playerOptions.sources = [
          {
            type: "video/mp4",
            src: row.fjlj, //视频url地址
          },
        ];
        this.spopen = true;
      }else if(row.fjlx == 'TP') {
        this.tpopen = true;
      }
    },

    // 人员情况查看
    ryqkck(row) {
      console.log('=== 人员情况查看 ===');
      console.log('传入的row:', row);

      // 如果传入的是行对象,说明是从按钮点击触发的
      if (row && row.zbid) {
        this.currentZbid = row.zbid;
      } else {
        // 如果没有传入行对象,使用当前zbid
        this.currentZbid = this.zbid;
      }

      console.log('当前应急信息ID (currentZbid):', this.currentZbid);

      if (!this.currentZbid) {
        this.$modal.msgError("请先选择应急信息");
        return;
      }

      // 重置编辑模式
      this.ryqkEditMode = false;
      this.ryqkDialogTitle = '人员状态实时情况';

      // 获取人员情况主表数据
      getRyqkZb(this.currentZbid).then(response => {
        console.log('主表查询响应:', response);
        this.ryqkData = response.data && response.data.length > 0 ? response.data[0] : {};
        console.log('主表数据 (ryqkData):', this.ryqkData);

        // 如果没有数据,设置默认的填报人信息
        if (!this.ryqkData.ddldXm) {
          // 获取当前登录用户信息
          const username = this.$store.state.user.name || '当前用户';
          const phonenumber = this.$store.state.user.phonenumber || '';
          this.ryqkData.ddldXm = username;
          this.ryqkData.ddldSj = phonenumber;
          console.log('设置默认填报人信息:', { username, phonenumber });
        }

        // 如果有主表数据且有ID,则用主表ID查询明细
        if (this.ryqkData && this.ryqkData.id) {
          console.log('使用主表ID查询明细,主表ID:', this.ryqkData.id);
          listRyqkMx(this.ryqkData.id).then(response => {
            console.log('明细查询响应:', response);
            this.ryqkList = response.data || [];
            console.log('明细数据 (ryqkList):', this.ryqkList);
            this.ryqkOpen = true;
          });
        } else {
          // 如果没有主表数据,则明细为空
          console.log('没有主表数据,明细为空');
          this.ryqkList = [];
          this.ryqkOpen = true;
        }
      });
    },

    // 重新获取人员数据(用于取消编辑时恢复)
    getPersonnelData() {
      if (!this.currentZbid) return;

      getRyqkZb(this.currentZbid).then(response => {
        this.ryqkData = response.data && response.data.length > 0 ? response.data[0] : {};

        // 如果有主表数据且有ID,则用主表ID查询明细
        if (this.ryqkData && this.ryqkData.id) {
          listRyqkMx(this.ryqkData.id).then(response => {
            this.ryqkList = response.data || [];
          });
        } else {
          this.ryqkList = [];
        }
      });
    },

    // 关闭人员情况弹窗
    ryqkcancel() {
      this.ryqkOpen = false;
      this.ryqkData = {};
      this.ryqkList = [];
      this.currentZbid = '';
      this.ryqkEditMode = false;
      this.ryqkDialogTitle = '人员状态实时情况';
    },

    // 切换编辑模式
    toggleEditMode() {
      if (this.ryqkEditMode) {
        // 取消编辑,恢复原始数据
        this.getPersonnelData();
      }
      this.ryqkEditMode = !this.ryqkEditMode;
      this.ryqkDialogTitle = this.ryqkEditMode ? '编辑人员状态实时情况' : '人员状态实时情况';
    },

    // 保存人员情况数据
    saveRyqkData() {
      console.log('=== 开始保存人员情况数据 ===');
      console.log('当前应急信息ID (currentZbid):', this.currentZbid);
      console.log('主表数据 (ryqkData):', this.ryqkData);
      console.log('明细数据 (ryqkList):', this.ryqkList);

      // 验证必填字段
      if (!this.ryqkData.jtfs) {
        this.$modal.msgError('请选择交通方式');
        return;
      }
      if (!this.ryqkData.ddldXm) {
        this.$modal.msgError('填报人不能为空');
        return;
      }

      // 验证人员明细必填字段
      for (let i = 0; i < this.ryqkList.length; i++) {
        const person = this.ryqkList[i];
        if (!person.ryxm || person.ryxm.trim() === '') {
          this.$modal.msgError(`第${i + 1}行人员姓名不能为空`);
          return;
        }
        if (!person.lxdh || person.lxdh.trim() === '') {
          this.$modal.msgError(`第${i + 1}行联系电话不能为空`);
          return;
        }
        if (!person.ryzt) {
          this.$modal.msgError(`第${i + 1}行人员状态不能为空`);
          return;
        }
      }

      // 格式化日期字段
      const formatDateTime = (date) => {
        if (!date) return null;
        if (typeof date === 'string') {
          // 如果已经是字符串格式,尝试转换
          date = new Date(date);
        }
        if (date instanceof Date && !isNaN(date)) {
          return this.formatDate(date, 'yyyy-MM-dd hh:mm:ss');
        }
        return null;
      };

      // 保存主表数据
      const mainData = {
        ...this.ryqkData,
        yjzbid: this.currentZbid,
        ddldSj: this.ryqkData.ddldSj || '', // 联系电话(驼峰命名)
        ddldXm: this.ryqkData.ddldXm || '', // 填报人(驼峰命名)
        dqzt: this.ryqkData.dqzt || '0', // 默认状态
        // 格式化所有日期字段
        sjksdysj: formatDateTime(this.ryqkData.sjksdysj),
        ddsj: formatDateTime(this.ryqkData.ddsj),
        ksgzsj: formatDateTime(this.ryqkData.ksgzsj),
        qyjssj: formatDateTime(this.ryqkData.qyjssj)
      };

      console.log('准备保存的主表数据:', mainData);

      saveRyqkZb(mainData).then(response => {
        // 获取主表保存后返回的ID(这是YJCZ_RYQK_ZB的主键ID)
        const ryqkZbId = response.data;

        console.log('主表保存成功,完整响应:', response);
        console.log('主表保存成功,返回ID:', ryqkZbId);

        // 验证返回的ID是否有效
        if (!ryqkZbId) {
          console.error('主表保存成功但返回的ID为空!');
          this.$modal.msgError('保存失败:未获取到主表ID');
          return;
        }

        // 只有当有人员数据时才保存明细表
        if (this.ryqkList && this.ryqkList.length > 0) {
          // 保存明细表数据,使用主表返回的ID作为zbid
          const detailData = this.ryqkList.map((item, index) => ({
            zbid: ryqkZbId,  // 使用人员情况主表的ID,而不是应急信息ID
            pxh: index + 1, // 设置排序号
            ryxm: item.ryxm || '', // 确保姓名不为空
            lxdh: item.lxdh || '', // 确保电话不为空
            ryzt: item.ryzt || '0', // 确保状态不为空
            bz: item.bz || '' // 备注可以为空
          }));

          console.log('准备保存的人员明细数据:', detailData);

          saveRyqkMx(detailData).then(response => {
            console.log('明细保存成功,响应:', response);
            this.$modal.msgSuccess('保存成功');
            // 退出编辑模式
            this.toggleEditMode();
          }).catch(error => {
            console.error('保存明细数据失败,完整错误:', error);
            console.error('错误响应:', error.response);
            const errorMsg = error.response && error.response.data && error.response.data.msg
              ? error.response.data.msg
              : '保存人员信息失败';
            this.$modal.msgError(errorMsg);
          });
        } else {
          // 没有人员数据时,只保存主表,直接显示成功
          console.log('无人员明细数据,仅保存主表');
          this.$modal.msgSuccess('保存成功');
          // 退出编辑模式
          this.toggleEditMode();
        }
      }).catch(error => {
        console.error('保存主表数据失败:', error);
        this.$modal.msgError('保存基本信息失败');
      });
    },

    // 新增人员
    addPerson() {
      this.ryqkList.push({
        ryxm: '',
        lxdh: '',
        ryzt: '0',
        bz: ''
      });
    },

    // 人员编辑(直接进入编辑模式)
    ryqkEdit(row) {
      // 如果传入的是行对象,说明是从按钮点击触发的
      if (row && row.zbid) {
        this.currentZbid = row.zbid;
      } else {
        // 如果没有传入行对象,使用当前zbid
        this.currentZbid = this.zbid;
      }

      if (!this.currentZbid) {
        this.$modal.msgError("请先选择应急信息");
        return;
      }

      // 设置为编辑模式
      this.ryqkEditMode = true;
      this.ryqkDialogTitle = '编辑人员状态实时情况';

      // 获取人员情况主表数据
      getRyqkZb(this.currentZbid).then(response => {
        this.ryqkData = response.data && response.data.length > 0 ? response.data[0] : {};

        // 如果没有数据,设置默认的填报人信息
        if (!this.ryqkData.ddldXm) {
          // 获取当前登录用户信息
          const username = this.$store.state.user.name || '当前用户';
          const phonenumber = this.$store.state.user.phonenumber || '';
          this.ryqkData.ddldXm = username;
          this.ryqkData.ddldSj = phonenumber;
        }

        // 如果有主表数据且有ID,则用主表ID查询明细
        if (this.ryqkData && this.ryqkData.id) {
          listRyqkMx(this.ryqkData.id).then(response => {
            this.ryqkList = response.data || [];
            this.ryqkOpen = true;
          });
        } else {
          // 如果没有主表数据,则明细为空
          this.ryqkList = [];
          this.ryqkOpen = true;
        }
      });
    }

  }
};
</script>

<style scoped>
.ryqk-container {
  padding: 20px;
}

.info-card, .list-card {
  margin-bottom: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.card-header span {
  font-size: 16px;
  font-weight: bold;
}

.info-content {
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.info-row {
  display: flex;
  gap: 20px;
}

.info-item {
  display: flex;
  align-items: center;
  min-width: 300px;
}

.info-item label {
  width: 120px;
  text-align: right;
  margin-right: 10px;
  font-weight: normal;
  color: #606266;
}

.info-item .el-input,
.info-item .el-select,
.info-item .el-date-picker {
  flex: 1;
}

@media (max-width: 1600px) {
  .info-row {
    flex-direction: column;
  }

  .info-item {
    min-width: 100%;
  }

  .info-item label {
    text-align: left;
    margin-right: 0;
    margin-bottom: 5px;
  }
}
</style>
相关推荐
VX:Fegn08955 小时前
计算机毕业设计|基于ssm + vue超市管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
2401_892000526 小时前
Flutter for OpenHarmony 猫咪管家App实战 - 添加提醒实现
前端·javascript·flutter
Yolanda946 小时前
【项目经验】vue h5移动端禁止缩放
前端·javascript·vue.js
VX:Fegn08957 小时前
计算机毕业设计|基于springboot + vue酒店管理系统(源码+数据库+文档)
vue.js·spring boot·课程设计
EndingCoder7 小时前
案例研究:从 JavaScript 迁移到 TypeScript
开发语言·前端·javascript·性能优化·typescript
Irene19917 小时前
Vue3 中使用的命名规则 和 实际开发命名规范总结
vue.js·命名规范
Amumu121389 小时前
Vue脚手架(二)
前端·javascript·vue.js
lichenyang45310 小时前
从零开始构建 React 文档系统 - 完整实现指南
前端·javascript·react.js
比特森林探险记10 小时前
Hooks、状态管理
前端·javascript·react.js
比特森林探险记10 小时前
组件通信 与 ⏳ 生命周期
前端·javascript·vue.js