<template>
  <div class="app-container">
    <el-card shadow="never">
      <el-form ref="queryForm" :model="queryParams" size="small" :inline="true">
        <el-form-item>
          <template slot="label">{{ tmT("queryPatrolDate") }}</template>
          <el-date-picker
            v-model="queryDateRange"
            type="daterange"
            value-format="yyyy-MM-dd"
            range-separator="-"
            :start-placeholder="tmT('queryDateRangePh')"
            :end-placeholder="tmT('queryDateRangeEndPh')"
            clearable
            style="width: 240px"
          />
        </el-form-item>
        <el-form-item prop="penCode">
          <template slot="label">{{ tmT("queryPenCode") }}</template>
          <el-input
            v-model="queryParams.penCode"
            :placeholder="tmT('queryPenCodePh')"
            clearable
            style="width: 160px"
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ tmCommon("search") }}</el-button>
          <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ tmCommon("reset") }}</el-button>
          <el-button
            v-hasPermi="['tradeMarket:isolationPatrol:add']"
            type="primary"
            plain
            icon="el-icon-plus"
            size="mini"
            @click="handleAdd"
          >{{ tmCommon("add") }}</el-button>
        </el-form-item>
      </el-form>
    </el-card>

    <br />

    <el-card shadow="never">
      <el-table v-loading="loading" :data="tableList" border>
        <el-table-column :label="tmT('colPatrolTime')" prop="patrolTime" align="center" width="160" />
        <el-table-column :label="tmT('colPenCode')" prop="penCode" align="center" width="100" />
        <el-table-column :label="tmT('colPhoto')" align="center" width="90">
          <template slot-scope="scope">
            <el-image
              v-if="scope.row.photoUrl"
              :src="mediaUrl(scope.row.photoUrl)"
              :preview-src-list="[mediaUrl(scope.row.photoUrl)]"
              fit="cover"
              class="patrol-list-photo"
            />
            <span v-else>{{ dash }}</span>
          </template>
        </el-table-column>
        <el-table-column :label="tmT('colContentDesc')" prop="contentDesc" align="center" min-width="140" :show-overflow-tooltip="true" />
        <el-table-column :label="tmT('colHandleDesc')" prop="handleDesc" align="center" min-width="120" :show-overflow-tooltip="true" />
        <el-table-column :label="tmT('colSickDeadQty')" align="center" width="130">
          <template slot-scope="scope">
            <span>{{ scope.row.sickDeadQty }}{{ tmT("unitHead") }}</span>
          </template>
        </el-table-column>
        <el-table-column :label="tmT('colEarTags')" align="center" min-width="120" :show-overflow-tooltip="true">
          <template slot-scope="scope">
            <span>{{ formatEarTagsDisplay(scope.row) }}</span>
          </template>
        </el-table-column>
        <el-table-column :label="tmCommon('colOp')" align="center" class-name="small-padding fixed-width" width="200" fixed="right">
          <template slot-scope="scope">
            <el-button v-hasPermi="['tradeMarket:isolationPatrol:query']" type="text" size="mini" @click="handleView(scope.row)">{{ tmCommon("view") }}</el-button>
            <el-button v-hasPermi="['tradeMarket:isolationPatrol:edit']" type="text" size="mini" @click="handleEdit(scope.row)">{{ tmCommon("edit") }}</el-button>
            <el-button v-hasPermi="['tradeMarket:isolationPatrol:remove']" type="text" size="mini" @click="handleDelete(scope.row)">{{ tmCommon("delete") }}</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="queryParams.pageNum"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />
    </el-card>

    <el-dialog :title="dialogTitle" :visible.sync="open" width="640px" append-to-body @close="cancel">
      <el-form :key="formKey" ref="form" :model="form" label-width="130px">
        <el-form-item prop="patrolTime" :rules="formRules.patrolTime">
          <template slot="label">{{ tmT("formPatrolTime") }}</template>
          <el-date-picker
            v-model="form.patrolTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            :placeholder="tmCommon('pleaseSelect')"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item prop="penId" :rules="formRules.penId">
          <template slot="label">{{ tmT("formPenCode") }}</template>
          <el-select
            v-model="form.penId"
            :placeholder="tmCommon('pleaseSelect')"
            style="width: 100%"
            @change="onPenChange"
          >
            <el-option v-for="item in penOptions" :key="item.penId" :label="item.penCode" :value="item.penId" />
          </el-select>
          <div v-if="!penOptions.length" class="el-form-item__error" style="position: static">{{ tmT("tipNoPenInStock") }}</div>
        </el-form-item>
        <el-form-item prop="sickDeadQty" :rules="formRules.sickDeadQty">
          <template slot="label">{{ tmT("formSickDeadQty") }}</template>
          <el-input-number
            v-model="form.sickDeadQty"
            :min="0"
            :precision="0"
            :step="1"
            controls-position="right"
            style="width: 200px"
            @change="onSickDeadQtyChange"
          />
          <span class="form-unit">{{ tmT("unitHead") }}</span>
        </el-form-item>
        <el-form-item prop="contentDesc" :rules="formRules.contentDesc">
          <template slot="label">{{ tmT("formContentDesc") }}</template>
          <el-input
            v-model="form.contentDesc"
            type="textarea"
            :rows="2"
            :placeholder="tmT('phContentDesc')"
            maxlength="200"
            show-word-limit
          />
        </el-form-item>
        <el-form-item prop="handleDesc" :rules="formRules.handleDesc">
          <template slot="label">{{ tmT("formHandleDesc") }}</template>
          <el-input
            v-model="form.handleDesc"
            type="textarea"
            :rows="2"
            :placeholder="tmT('phHandleDesc')"
            maxlength="200"
            show-word-limit
          />
        </el-form-item>
        <el-form-item v-if="showEarTagField" prop="earTagList" :rules="formRules.earTagList">
          <template slot="label">{{ tmT("formEarTags") }}</template>
          <el-select
            v-model="form.earTagList"
            multiple
            :placeholder="tmCommon('pleaseSelect')"
            style="width: 100%"
            :disabled="!form.penId"
          >
            <el-option v-for="tag in penEarTagOptions" :key="tag" :label="tag" :value="tag" />
          </el-select>
        </el-form-item>
        <el-form-item prop="photoPath">
          <template slot="label">{{ tmT("formPhoto") }}</template>
          <el-upload
            class="patrol-photo-uploader"
            :action="uploadAction"
            :headers="uploadHeaders"
            :show-file-list="false"
            accept=".jpg,.jpeg,.png"
            :before-upload="beforePhotoUpload"
            :on-success="handlePhotoUploadSuccess"
            :on-error="handleUploadHttpError"
          >
            <img v-if="form.photoUrl" :src="mediaUrl(form.photoUrl)" class="patrol-photo-preview">
            <i v-else class="el-icon-plus patrol-photo-icon" />
          </el-upload>
          <div class="el-upload__tip">{{ tmT("tipPhoto") }}</div>
          <el-button v-if="form.photoPath" type="text" size="mini" @click="clearPhoto">{{ tmCommon("delete") }}</el-button>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">{{ tmCommon("ok") }}</el-button>
        <el-button @click="cancel">{{ tmCommon("cancel") }}</el-button>
      </div>
    </el-dialog>

    <el-dialog :title="tmCommon('viewTitle')" :visible.sync="viewOpen" width="640px" append-to-body>
      <el-form label-width="130px" size="small">
        <el-form-item :label="tmT('formPatrolTime')"><span>{{ viewRow.patrolTime }}</span></el-form-item>
        <el-form-item :label="tmT('formPenCode')"><span>{{ viewRow.penCode }}</span></el-form-item>
        <el-form-item :label="tmT('formSickDeadQty')"><span>{{ viewRow.sickDeadQty }}{{ tmT("unitHead") }}</span></el-form-item>
        <el-form-item :label="tmT('formContentDesc')"><span>{{ viewRow.contentDesc }}</span></el-form-item>
        <el-form-item :label="tmT('formHandleDesc')"><span>{{ viewRow.handleDesc }}</span></el-form-item>
        <el-form-item :label="tmT('formEarTags')"><span>{{ viewEarTagsText }}</span></el-form-item>
        <el-form-item :label="tmT('formPhoto')">
          <el-image
            v-if="viewRow.photoUrl"
            :src="mediaUrl(viewRow.photoUrl)"
            :preview-src-list="[mediaUrl(viewRow.photoUrl)]"
            fit="cover"
            class="patrol-view-photo"
          />
          <span v-else>{{ dash }}</span>
        </el-form-item>
        <el-form-item :label="tmT('viewCreateBy')"><span>{{ viewRow.createBy || dash }}</span></el-form-item>
        <el-form-item :label="tmT('viewCreateTime')"><span>{{ viewRow.createTime ? parseTime(viewRow.createTime) : dash }}</span></el-form-item>
        <el-form-item :label="tmT('viewUpdateBy')"><span>{{ viewRow.updateBy || dash }}</span></el-form-item>
        <el-form-item :label="tmT('viewUpdateTime')"><span>{{ viewRow.updateTime ? parseTime(viewRow.updateTime) : dash }}</span></el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button v-hasPermi="['tradeMarket:isolationPatrol:edit']" type="primary" plain @click="openEditFromView">{{ tmCommon("edit") }}</el-button>
        <el-button v-hasPermi="['tradeMarket:isolationPatrol:remove']" type="danger" plain @click="handleDelete(viewRow)">{{ tmCommon("delete") }}</el-button>
        <el-button @click="viewOpen = false">{{ tmCommon("close") }}</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { getToken } from "@/utils/auth"
import { parseTime } from "@/utils/ruoyi"
import tradeMarketLocaleMixin from "@/mixins/tradeMarketLocaleMixin"
import {
  listIsolationPatrol,
  getIsolationPatrol,
  addIsolationPatrol,
  updateIsolationPatrol,
  delIsolationPatrol,
  listPatrolPenOptions,
  listPatrolPenEarTags
} from "@/api/tradeMarket/isolationPatrol"

const PHOTO_EXTS = ["jpg", "jpeg", "png"]
const MAX_PHOTO_MB = 10
const TEXT_MAX_LEN = 200

function createEmptyForm() {
  return {
    id: undefined,
    patrolTime: undefined,
    penId: undefined,
    sickDeadQty: 0,
    contentDesc: "",
    handleDesc: "",
    earTagList: [],
    photoUrl: undefined,
    photoPath: undefined
  }
}

export default {
  name: "TradeMarketIsolationPatrol",
  mixins: [tradeMarketLocaleMixin],
  data() {
    return {
      tmNs: "isolationPatrol",
      loading: false,
      open: false,
      viewOpen: false,
      viewRow: {},
      dialogEdit: false,
      formKey: 0,
      total: 0,
      tableList: [],
      queryDateRange: [],
      queryParams: {
        pageNum: 1,
        pageSize: 20,
        penCode: undefined
      },
      penOptions: [],
      penEarTagOptions: [],
      form: createEmptyForm()
    }
  },
  computed: {
    dash() {
      return this.$t("tradeMarket.status.dash")
    },
    dialogTitle() {
      return this.dialogEdit ? this.tmT("dialogEdit") : this.tmT("dialogAdd")
    },
    showEarTagField() {
      return Number(this.form.sickDeadQty) > 0
    },
    uploadAction() {
      return process.env.VUE_APP_BASE_API + "/common/upload"
    },
    uploadHeaders() {
      return { Authorization: "Bearer " + getToken() }
    },
    viewEarTagsText() {
      const qty = Number(this.viewRow.sickDeadQty) || 0
      if (qty === 0) {
        return this.dash
      }
      const list = this.viewRow.earTagList
      if (list && list.length) {
        return list.join("、")
      }
      return this.viewRow.earTags || this.dash
    },
    formRules() {
      return {
        patrolTime: [{ validator: this.validatePatrolTime, trigger: "change" }],
        penId: [{ required: true, message: this.tmT("rulePenCode"), trigger: "change" }],
        sickDeadQty: [{ required: true, message: this.tmT("ruleSickDeadQty"), trigger: "change" }],
        contentDesc: [
          { required: true, message: this.tmT("ruleContentDesc"), trigger: "blur" },
          { min: 1, max: TEXT_MAX_LEN, message: this.tmT("ruleContentLen"), trigger: "blur" }
        ],
        handleDesc: [
          { required: true, message: this.tmT("ruleHandleDesc"), trigger: "blur" },
          { min: 1, max: TEXT_MAX_LEN, message: this.tmT("ruleHandleLen"), trigger: "blur" }
        ],
        earTagList: [{ validator: this.validateEarTagList, trigger: "change" }]
      }
    }
  },
  created() {
    this.getList()
  },
  methods: {
    parseTime,
    mediaUrl(url) {
      if (!url) {
        return ""
      }
      if (/^https?:\/\//i.test(url)) {
        return url
      }
      return process.env.VUE_APP_BASE_API + url
    },
    formatNow() {
      return parseTime(new Date(), "{y}-{m}-{d} {h}:{i}:{s}")
    },
    formatEarTagsDisplay(row) {
      const qty = Number(row.sickDeadQty) || 0
      if (qty === 0) {
        return this.dash
      }
      const list = row.earTagList
      if (list && list.length) {
        return list.join("、")
      }
      return row.earTags || this.dash
    },
    applyDefaultDescByQty(qty) {
      const n = Number(qty) || 0
      if (n === 0) {
        this.form.contentDesc = this.tmT("defaultContentNormal")
        this.form.handleDesc = this.tmT("defaultHandleNormal")
        this.form.earTagList = []
      } else {
        this.form.contentDesc = this.tmT("defaultContentAbnormal")
        this.form.handleDesc = this.tmT("defaultHandleNeed")
      }
    },
    onSickDeadQtyChange(val) {
      this.applyDefaultDescByQty(val)
      if (Number(val) <= 0) {
        this.form.earTagList = []
      }
      this.$nextTick(() => {
        if (this.$refs.form) {
          this.$refs.form.validateField("earTagList")
        }
      })
    },
    loadPenOptions() {
      return listPatrolPenOptions().then((res) => {
        this.penOptions = res.data || []
        if (!this.penOptions.length && this.open) {
          this.$modal.msgWarning(this.tmT("tipNoPenInStock"))
        }
      })
    },
    loadPenEarTags(penId) {
      if (!penId) {
        this.penEarTagOptions = []
        return Promise.resolve()
      }
      return listPatrolPenEarTags(penId).then((res) => {
        this.penEarTagOptions = res.data || []
      })
    },
    onPenChange(penId) {
      this.form.earTagList = []
      this.loadPenEarTags(penId)
    },
    validatePatrolTime(rule, value, callback) {
      if (!value) {
        callback(new Error(this.tmT("rulePatrolTime")))
        return
      }
      const t = new Date(String(value).replace(/-/g, "/")).getTime()
      if (isNaN(t)) {
        callback(new Error(this.tmT("rulePatrolTime")))
        return
      }
      if (t > Date.now()) {
        callback(new Error(this.tmT("rulePatrolTimeFuture")))
        return
      }
      callback()
    },
    validateEarTagList(rule, value, callback) {
      const qty = Number(this.form.sickDeadQty) || 0
      const list = value || []
      if (qty === 0) {
        if (list.length) {
          callback(new Error(this.tmT("ruleQtyEarMismatch")))
          return
        }
        callback()
        return
      }
      if (!list.length) {
        callback(new Error(this.tmT("ruleEarTags")))
        return
      }
      if (list.length !== qty) {
        callback(new Error(this.tmT("ruleQtyEarMismatch")))
        return
      }
      const allowed = new Set(this.penEarTagOptions)
      for (const tag of list) {
        if (!allowed.has(tag)) {
          callback(new Error(this.tmT("ruleQtyEarMismatch")))
          return
        }
      }
      callback()
    },
    extOf(fileName) {
      if (!fileName || fileName.lastIndexOf(".") < 0) {
        return ""
      }
      return fileName.slice(fileName.lastIndexOf(".") + 1).toLowerCase()
    },
    beforePhotoUpload(file) {
      const ext = this.extOf(file.name)
      if (!PHOTO_EXTS.includes(ext)) {
        this.$modal.msgError(this.tmT("errPhotoFmt"))
        return false
      }
      if (file.size / 1024 / 1024 > MAX_PHOTO_MB) {
        this.$modal.msgError(this.tmT("errPhotoMb"))
        return false
      }
      return true
    },
    handlePhotoUploadSuccess(res) {
      if (res.code === 200) {
        this.form.photoUrl = res.url
        this.form.photoPath = res.fileName
      } else {
        this.$modal.msgError(res.msg || this.tmT("errPhotoFmt"))
      }
    },
    clearPhoto() {
      this.form.photoUrl = undefined
      this.form.photoPath = undefined
    },
    handleUploadHttpError() {
      this.$modal.msgError(this.tmT("errPhotoFmt"))
    },
    buildListParams() {
      const params = { ...this.queryParams }
      if (this.queryDateRange && this.queryDateRange.length === 2) {
        params.patrolDateStart = this.queryDateRange[0]
        params.patrolDateEnd = this.queryDateRange[1]
      } else {
        params.patrolDateStart = undefined
        params.patrolDateEnd = undefined
      }
      return params
    },
    buildSubmitPayload() {
      const qty = Number(this.form.sickDeadQty) || 0
      const payload = {
        id: this.form.id,
        patrolTime: this.form.patrolTime,
        penId: this.form.penId,
        sickDeadQty: qty,
        contentDesc: (this.form.contentDesc || "").trim(),
        handleDesc: (this.form.handleDesc || "").trim(),
        photoUrl: this.form.photoUrl,
        photoPath: this.form.photoPath
      }
      if (qty > 0) {
        payload.earTagList = [...(this.form.earTagList || [])]
      } else {
        payload.earTagList = []
      }
      return payload
    },
    validateBeforeSubmit() {
      if (!this.penOptions.length) {
        this.$modal.msgWarning(this.tmT("tipNoPenInStock"))
        return false
      }
      return true
    },
    getList() {
      this.loading = true
      listIsolationPatrol(this.buildListParams())
        .then((res) => {
          this.tableList = res.rows || []
          this.total = res.total != null ? res.total : 0
        })
        .finally(() => {
          this.loading = false
        })
    },
    handleQuery() {
      this.queryParams.pageNum = 1
      this.getList()
    },
    resetQuery() {
      this.queryDateRange = []
      this.queryParams.penCode = undefined
      this.resetForm("queryForm")
      this.handleQuery()
    },
    resetFormModel() {
      this.form = createEmptyForm()
      this.form.patrolTime = this.formatNow()
      this.applyDefaultDescByQty(0)
      this.penEarTagOptions = []
    },
    fillFormFromApi(data) {
      const tags = data.earTagList || []
      this.form = {
        id: data.id,
        patrolTime: data.patrolTime,
        penId: data.penId,
        sickDeadQty: data.sickDeadQty != null ? data.sickDeadQty : 0,
        contentDesc: data.contentDesc || "",
        handleDesc: data.handleDesc || "",
        earTagList: tags.length ? [...tags] : [],
        photoUrl: data.photoUrl,
        photoPath: data.photoPath
      }
      this.loadPenEarTags(this.form.penId)
    },
    handleAdd() {
      this.formKey += 1
      this.resetFormModel()
      this.dialogEdit = false
      this.loadPenOptions().then(() => {
        this.open = true
        this.$nextTick(() => this.resetForm("form"))
      })
    },
    handleEdit(row) {
      getIsolationPatrol(row.id).then((res) => {
        this.formKey += 1
        this.dialogEdit = true
        this.loadPenOptions().then(() => {
          this.fillFormFromApi(res.data || {})
          this.open = true
          this.$nextTick(() => {
            if (this.$refs.form) {
              this.$refs.form.clearValidate()
            }
          })
        })
      })
    },
    openEditFromView() {
      this.viewOpen = false
      this.handleEdit(this.viewRow)
    },
    handleView(row) {
      getIsolationPatrol(row.id).then((res) => {
        this.viewRow = res.data || {}
        this.viewOpen = true
      })
    },
    handleDelete(row) {
      this.$modal
        .confirm(this.tmT("confirmDelete", { penCode: row.penCode, patrolTime: row.patrolTime }))
        .then(() => delIsolationPatrol(row.id))
        .then(() => {
          this.$modal.msgSuccess(this.tmCommon("msgDelOk"))
          this.viewOpen = false
          this.getList()
        })
        .catch(() => {})
    },
    submitForm() {
      if (!this.validateBeforeSubmit()) {
        return
      }
      this.$refs.form.validate((valid) => {
        if (!valid) {
          return
        }
        const payload = this.buildSubmitPayload()
        const req = this.dialogEdit ? updateIsolationPatrol(payload) : addIsolationPatrol(payload)
        req.then(() => {
          this.$modal.msgSuccess(this.dialogEdit ? this.tmCommon("msgModOk") : this.tmCommon("msgAddOk"))
          this.open = false
          this.getList()
        })
      })
    },
    cancel() {
      this.open = false
      this.resetFormModel()
    }
  }
}
</script>

<style scoped lang="scss">
.form-unit {
  margin-left: 8px;
  color: #606266;
}
.patrol-list-photo {
  width: 48px;
  height: 48px;
  border-radius: 4px;
}
.patrol-view-photo {
  width: 120px;
  height: 120px;
  border-radius: 4px;
}
.patrol-photo-uploader ::v-deep .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  width: 120px;
  height: 120px;
  line-height: 120px;
  text-align: center;
  overflow: hidden;
}
.patrol-photo-uploader ::v-deep .el-upload:hover {
  border-color: #409eff;
}
.patrol-photo-preview {
  width: 120px;
  height: 120px;
  object-fit: cover;
  display: block;
}
.patrol-photo-icon {
  font-size: 28px;
  color: #8c939d;
}
</style>
