动态表单组件渲染并采集 展示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>
相关推荐
江公望2 分钟前
VUE3中,reactive()和ref()的区别10分钟讲清楚
前端·javascript·vue.js
徐同保34 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫35 分钟前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
博主花神36 分钟前
【React】扩展知识点
javascript·react.js·ecmascript
内存不泄露41 分钟前
基于Spring Boot和Vue 3的智能心理健康咨询平台设计与实现
vue.js·spring boot·后端
欧阳天风43 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder1 小时前
箭头函数和 this 绑定
linux·前端·javascript·typescript
xkxnq1 小时前
第一阶段:Vue 基础入门(第 11 天)
前端·javascript·vue.js
小oo呆1 小时前
【自然语言处理与大模型】LangGraphV1.0入门指南:核心组件Nodes
前端·javascript·easyui
行走的陀螺仪1 小时前
在UniApp H5中,实现路由栈的持久化
前端·javascript·uni-app·路由持久化·路由缓存策略