|
@@ -1,103 +1,107 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <el-dialog
|
|
|
|
|
- :model-value="modelValue"
|
|
|
|
|
- :title="isEdit ? '编辑设备' : '新增设备'"
|
|
|
|
|
- width="520px"
|
|
|
|
|
- align-center
|
|
|
|
|
- destroy-on-close
|
|
|
|
|
- class="device-form-dialog"
|
|
|
|
|
- @update:model-value="emit('update:modelValue', $event)"
|
|
|
|
|
- @closed="handleClosed"
|
|
|
|
|
- >
|
|
|
|
|
- <el-form
|
|
|
|
|
- ref="formRef"
|
|
|
|
|
- :model="form"
|
|
|
|
|
- :rules="rules"
|
|
|
|
|
- label-width="96px"
|
|
|
|
|
- label-position="right"
|
|
|
|
|
- class="device-form"
|
|
|
|
|
- >
|
|
|
|
|
- <el-form-item label="农机名称" prop="name">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="form.name"
|
|
|
|
|
- :placeholder="isEdit ? '' : '请输入农机'"
|
|
|
|
|
- maxlength="50"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <el-dialog :model-value="modelValue" :title="isEdit ? '编辑' : '新增'" width="780px" align-center destroy-on-close
|
|
|
|
|
+ class="device-form-dialog" @update:model-value="emit('update:modelValue', $event)" @closed="handleClosed">
|
|
|
|
|
+ <el-form ref="formRef" :model="form" :rules="rules" label-position="left" class="device-form">
|
|
|
|
|
+ <el-form-item label="姓名/手机号:" prop="ownerName" class="form-item--inline">
|
|
|
|
|
+ <div class="name-phone-row">
|
|
|
|
|
+ <el-input v-model="form.ownerName" placeholder="请输入姓名" maxlength="20" class="name-phone-row__name" />
|
|
|
|
|
+ <el-form-item prop="ownerPhone" class="name-phone-row__phone-item">
|
|
|
|
|
+ <el-input v-model="form.ownerPhone" placeholder="请输入手机号" maxlength="11" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
|
|
- <el-form-item label="农机类型" prop="type">
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="form.type"
|
|
|
|
|
- placeholder="请选择农机类型"
|
|
|
|
|
- style="width: 100%"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="item in typeOptions"
|
|
|
|
|
- :key="item"
|
|
|
|
|
- :label="item"
|
|
|
|
|
- :value="item"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
|
|
+ <div class="form-section">
|
|
|
|
|
|
|
|
- <el-form-item label="农事类别" prop="categories">
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="form.categories"
|
|
|
|
|
- multiple
|
|
|
|
|
- collapse-tags
|
|
|
|
|
- collapse-tags-tooltip
|
|
|
|
|
- :max-collapse-tags="3"
|
|
|
|
|
- placeholder="请选择农事类别"
|
|
|
|
|
- style="width: 100%"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="item in categoryOptions"
|
|
|
|
|
- :key="item"
|
|
|
|
|
- :label="item"
|
|
|
|
|
- :value="item"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
-
|
|
|
|
|
- <el-form-item label="存放点位" prop="location" required>
|
|
|
|
|
- <div class="field-block">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-if="isEdit || form.location"
|
|
|
|
|
- v-model="form.location"
|
|
|
|
|
- placeholder="请选择存放点位"
|
|
|
|
|
- readonly
|
|
|
|
|
- />
|
|
|
|
|
- <el-button class="btn-outline" @click="handleSelectLocation">
|
|
|
|
|
- {{ isEdit || form.location ? "修改点位" : "选择点位" }}
|
|
|
|
|
|
|
+ <div class="form-section__head">
|
|
|
|
|
+ <div class="form-section__label">
|
|
|
|
|
+ 种植品种:
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-button type="warning" @click="addCategory">
|
|
|
|
|
+ + 添加品类
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- </el-form-item>
|
|
|
|
|
|
|
|
|
|
- <el-form-item label="执行区域" prop="areaLabel" required>
|
|
|
|
|
- <div class="field-block">
|
|
|
|
|
- <div v-if="isEdit && form.areaLabel" class="area-preview">
|
|
|
|
|
- {{ form.areaLabel }}
|
|
|
|
|
|
|
+ <div class="category-list">
|
|
|
|
|
+ <div v-for="(group, gIdx) in form.categories" :key="group._key" class="category-block">
|
|
|
|
|
+ <div class="category-block__head">
|
|
|
|
|
+ <el-select v-model="group.category" placeholder="请选择品类" class="category-select">
|
|
|
|
|
+ <el-option v-for="item in categoryOptions" :key="item" :label="item" :value="item" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ <div class="link-btn secondary-link" @click="addPlanting(gIdx)">
|
|
|
|
|
+ + 添加品种
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-button v-if="form.categories.length > 1" link type="danger" class="link-btn"
|
|
|
|
|
+ @click="removeCategory(gIdx)">
|
|
|
|
|
+ 删除品类
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="planting-grid">
|
|
|
|
|
+ <div v-for="(item, pIdx) in group.plantings" :key="item._key" class="planting-card">
|
|
|
|
|
+ <div class="planting-card__top">
|
|
|
|
|
+ <el-select v-model="item.variety" placeholder="请选择品种" class="variety-select">
|
|
|
|
|
+ <el-option v-for="v in varietyOptions" :key="v" :label="v" :value="v" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ <el-button link type="danger" class="link-btn"
|
|
|
|
|
+ :disabled="group.plantings.length <= 1 && form.categories.length <= 1"
|
|
|
|
|
+ @click="removePlanting(gIdx, pIdx)">
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="planting-card__row">
|
|
|
|
|
+ <span class="planting-card__label">物候期:</span>
|
|
|
|
|
+ <el-select v-model="item.phenology" placeholder="请选择物候期" class="planting-card__input">
|
|
|
|
|
+ <el-option v-for="ph in phenologyOptions" :key="ph" :label="ph" :value="ph" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="planting-card__row">
|
|
|
|
|
+ <span class="planting-card__label">种植面积:</span>
|
|
|
|
|
+ <el-input v-model="item.area" placeholder="请输入种植面积" class="planting-card__input" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="planting-card__row">
|
|
|
|
|
+ <span class="planting-card__label">起种点:</span>
|
|
|
|
|
+ <el-select v-model="item.startPoint" placeholder="请选择" class="start-point-select">
|
|
|
|
|
+ <el-option v-for="sp in startPointOptions" :key="sp" :label="sp" :value="sp" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ <el-date-picker v-model="item.startDate" type="date" placeholder="选择日期" value-format="YYYY.MM.DD"
|
|
|
|
|
+ format="YYYY.MM.DD" class="start-date-picker" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="planting-card__row">
|
|
|
|
|
+ <span class="planting-card__label">种植点位:</span>
|
|
|
|
|
+ <div class="location-box">
|
|
|
|
|
+ <span class="location-box__text" :title="item.plantLocation">
|
|
|
|
|
+ {{ item.plantLocation || "请选择点位" }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <el-button link type="warning" class="link-btn" @click="openPlantLocation(gIdx, pIdx)">
|
|
|
|
|
+ 修改点位
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
</div>
|
|
</div>
|
|
|
- <el-button class="btn-outline" @click="handleSelectArea">
|
|
|
|
|
- {{ isEdit ? "修改区域" : "选择区域" }}
|
|
|
|
|
- </el-button>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
- </el-form-item>
|
|
|
|
|
|
|
|
|
|
- <el-form-item label="负责人" prop="ownerName">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="form.ownerName"
|
|
|
|
|
- :placeholder="isEdit ? '' : '请输入姓名'"
|
|
|
|
|
- maxlength="20"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <el-form-item label="手机号" prop="ownerPhone">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="form.ownerPhone"
|
|
|
|
|
- :placeholder="isEdit ? '' : '请输入手机号'"
|
|
|
|
|
- maxlength="11"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <el-form-item label="常驻点位:" prop="residentLocation" class="form-item--resident">
|
|
|
|
|
+ <div class="location-box location-box--full">
|
|
|
|
|
+ <span class="location-box__text" :title="form.residentLocation">
|
|
|
|
|
+ {{ form.residentLocation || "请选择点位" }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <el-button link type="warning" class="link-btn" @click="openResidentLocation">
|
|
|
|
|
+ 修改点位
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-form>
|
|
</el-form>
|
|
|
|
|
|
|
@@ -111,25 +115,13 @@
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
|
|
|
|
- <SelectAreaDialog
|
|
|
|
|
- v-model="areaDialogVisible"
|
|
|
|
|
- :farm-id="101532"
|
|
|
|
|
- :selected-ids="form.selectedAreaIds"
|
|
|
|
|
- @confirm="handleAreaConfirm"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <SelectLocationDialog
|
|
|
|
|
- v-model="locationDialogVisible"
|
|
|
|
|
- :farm-id="101532"
|
|
|
|
|
- :location-wkt="form.locationWkt"
|
|
|
|
|
- @confirm="handleLocationConfirm"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <SelectLocationDialog v-model="locationDialogVisible" :farm-id="101532" :location-wkt="activeLocationWkt"
|
|
|
|
|
+ @confirm="handleLocationConfirm" />
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
import { computed, reactive, ref, watch } from "vue";
|
|
import { computed, reactive, ref, watch } from "vue";
|
|
|
import { ElMessage } from "element-plus";
|
|
import { ElMessage } from "element-plus";
|
|
|
-import SelectAreaDialog from "./SelectAreaDialog.vue";
|
|
|
|
|
import SelectLocationDialog from "./SelectLocationDialog.vue";
|
|
import SelectLocationDialog from "./SelectLocationDialog.vue";
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
const props = defineProps({
|
|
@@ -152,82 +144,110 @@ const emit = defineEmits(["update:modelValue", "submit"]);
|
|
|
const isEdit = computed(() => props.mode === "edit");
|
|
const isEdit = computed(() => props.mode === "edit");
|
|
|
const formRef = ref();
|
|
const formRef = ref();
|
|
|
const submitting = ref(false);
|
|
const submitting = ref(false);
|
|
|
-const areaDialogVisible = ref(false);
|
|
|
|
|
const locationDialogVisible = ref(false);
|
|
const locationDialogVisible = ref(false);
|
|
|
|
|
+/** @type {import('vue').Ref<null | { type: 'resident' } | { type: 'plant', categoryIndex: number, plantingIndex: number }>} */
|
|
|
|
|
+const locationTarget = ref(null);
|
|
|
|
|
+
|
|
|
|
|
+const categoryOptions = ["品类一", "品类二", "荔枝", "龙眼"];
|
|
|
|
|
+const varietyOptions = ["品种一", "妃子笑", "桂味", "糯米糍", "白糖罂", "仙进奉"];
|
|
|
|
|
+const phenologyOptions = ["生育期一", "生育期二", "生育期三", "生育期四"];
|
|
|
|
|
+const startPointOptions = ["来白点", "来白点2"];
|
|
|
|
|
+
|
|
|
|
|
+let keySeed = 0;
|
|
|
|
|
+const nextKey = () => `k-${++keySeed}`;
|
|
|
|
|
+
|
|
|
|
|
+const createPlanting = (data = {}) => ({
|
|
|
|
|
+ _key: nextKey(),
|
|
|
|
|
+ variety: data.variety || "",
|
|
|
|
|
+ phenology: data.phenology || "生育期一",
|
|
|
|
|
+ area: data.area || "",
|
|
|
|
|
+ startPoint: data.startPoint || "",
|
|
|
|
|
+ startDate: data.startDate || "",
|
|
|
|
|
+ plantLocation: data.plantLocation || "",
|
|
|
|
|
+ plantLocationWkt: data.plantLocationWkt || "",
|
|
|
|
|
+});
|
|
|
|
|
|
|
|
-const typeOptions = ["肥水一体", "水肥一体", "植保无人机", "收割机", "播种机"];
|
|
|
|
|
-const categoryOptions = ["类别一", "类别二", "类别三", "类别四", "类别五", "类别六", "类别七"];
|
|
|
|
|
|
|
+const createCategory = (data = {}) => ({
|
|
|
|
|
+ _key: nextKey(),
|
|
|
|
|
+ category: data.category || data.cropCategory || "",
|
|
|
|
|
+ plantings:
|
|
|
|
|
+ Array.isArray(data.plantings) && data.plantings.length
|
|
|
|
|
+ ? data.plantings.map((p) => createPlanting(p))
|
|
|
|
|
+ : [createPlanting()],
|
|
|
|
|
+});
|
|
|
|
|
|
|
|
const createEmptyForm = () => ({
|
|
const createEmptyForm = () => ({
|
|
|
id: null,
|
|
id: null,
|
|
|
- name: "",
|
|
|
|
|
- type: "",
|
|
|
|
|
- categories: [],
|
|
|
|
|
- location: "",
|
|
|
|
|
- locationWkt: "",
|
|
|
|
|
- areaLabel: "",
|
|
|
|
|
- selectedAreaIds: [],
|
|
|
|
|
- selectedAreas: [],
|
|
|
|
|
ownerName: "",
|
|
ownerName: "",
|
|
|
ownerPhone: "",
|
|
ownerPhone: "",
|
|
|
|
|
+ residentLocation: "",
|
|
|
|
|
+ locationWkt: "",
|
|
|
|
|
+ categories: [createCategory({ category: "品类一" })],
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const form = reactive(createEmptyForm());
|
|
const form = reactive(createEmptyForm());
|
|
|
|
|
|
|
|
const rules = {
|
|
const rules = {
|
|
|
- name: [{ required: true, message: "请输入农机名称", trigger: "blur" }],
|
|
|
|
|
- type: [{ required: true, message: "请选择农机类型", trigger: "change" }],
|
|
|
|
|
- categories: [
|
|
|
|
|
- {
|
|
|
|
|
- type: "array",
|
|
|
|
|
- required: true,
|
|
|
|
|
- min: 1,
|
|
|
|
|
- message: "请选择农事类别",
|
|
|
|
|
- trigger: "change",
|
|
|
|
|
- },
|
|
|
|
|
- ],
|
|
|
|
|
- location: [
|
|
|
|
|
|
|
+ ownerName: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
|
|
|
|
+ ownerPhone: [
|
|
|
|
|
+ { required: true, message: "请输入手机号", trigger: "blur" },
|
|
|
{
|
|
{
|
|
|
- validator: (_rule, _value, callback) => {
|
|
|
|
|
- if (!form.location) callback(new Error("请选择存放点位"));
|
|
|
|
|
- else callback();
|
|
|
|
|
- },
|
|
|
|
|
- trigger: "change",
|
|
|
|
|
|
|
+ pattern: /^1\d{10}$/,
|
|
|
|
|
+ message: "请输入正确的手机号",
|
|
|
|
|
+ trigger: "blur",
|
|
|
},
|
|
},
|
|
|
],
|
|
],
|
|
|
- areaLabel: [
|
|
|
|
|
|
|
+ residentLocation: [
|
|
|
{
|
|
{
|
|
|
validator: (_rule, _value, callback) => {
|
|
validator: (_rule, _value, callback) => {
|
|
|
- if (!form.selectedAreaIds?.length) callback(new Error("请选择执行区域"));
|
|
|
|
|
|
|
+ if (!form.residentLocation) callback(new Error("请选择常驻点位"));
|
|
|
else callback();
|
|
else callback();
|
|
|
},
|
|
},
|
|
|
trigger: "change",
|
|
trigger: "change",
|
|
|
},
|
|
},
|
|
|
],
|
|
],
|
|
|
- ownerName: [{ required: true, message: "请输入负责人", trigger: "blur" }],
|
|
|
|
|
- ownerPhone: [
|
|
|
|
|
- { required: true, message: "请输入手机号", trigger: "blur" },
|
|
|
|
|
- {
|
|
|
|
|
- pattern: /^1\d{10}$/,
|
|
|
|
|
- message: "请输入正确的手机号",
|
|
|
|
|
- trigger: "blur",
|
|
|
|
|
- },
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const activeLocationWkt = computed(() => {
|
|
|
|
|
+ const target = locationTarget.value;
|
|
|
|
|
+ if (!target || target.type === "resident") return form.locationWkt || "";
|
|
|
|
|
+ const planting =
|
|
|
|
|
+ form.categories[target.categoryIndex]?.plantings?.[target.plantingIndex];
|
|
|
|
|
+ return planting?.plantLocationWkt || "";
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const normalizeCategoriesFromData = (data) => {
|
|
|
|
|
+ if (Array.isArray(data?.categories) && data.categories.length) {
|
|
|
|
|
+ return data.categories.map((c) => {
|
|
|
|
|
+ if (typeof c === "string") {
|
|
|
|
|
+ return createCategory({
|
|
|
|
|
+ category: c,
|
|
|
|
|
+ plantings: data.plantings || [],
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ return createCategory({
|
|
|
|
|
+ category: c.category || c.name || c.cropCategory || "",
|
|
|
|
|
+ plantings: c.plantings || [],
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ createCategory({
|
|
|
|
|
+ category: data?.cropCategory || data?.type || "荔枝",
|
|
|
|
|
+ plantings: data?.plantings || [],
|
|
|
|
|
+ }),
|
|
|
|
|
+ ];
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const fillForm = (data) => {
|
|
const fillForm = (data) => {
|
|
|
Object.assign(form, createEmptyForm(), {
|
|
Object.assign(form, createEmptyForm(), {
|
|
|
id: data?.id ?? null,
|
|
id: data?.id ?? null,
|
|
|
- name: data?.name || "",
|
|
|
|
|
- type: data?.type || "",
|
|
|
|
|
- categories: [...(data?.categories || [])],
|
|
|
|
|
- location: data?.location || "",
|
|
|
|
|
|
|
+ ownerName: data?.ownerName || data?.name || "",
|
|
|
|
|
+ ownerPhone: data?.ownerPhone || data?.phone || "",
|
|
|
|
|
+ residentLocation: data?.residentLocation || data?.location || "",
|
|
|
locationWkt: data?.locationWkt || "",
|
|
locationWkt: data?.locationWkt || "",
|
|
|
- areaLabel: data?.areaLabel || "",
|
|
|
|
|
- selectedAreaIds: [...(data?.selectedAreaIds || [])],
|
|
|
|
|
- selectedAreas: [...(data?.selectedAreas || [])],
|
|
|
|
|
- ownerName: data?.ownerName || "",
|
|
|
|
|
- ownerPhone: data?.ownerPhone || "",
|
|
|
|
|
|
|
+ categories: normalizeCategoriesFromData(data),
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -247,51 +267,126 @@ const close = () => emit("update:modelValue", false);
|
|
|
|
|
|
|
|
const handleClosed = () => {
|
|
const handleClosed = () => {
|
|
|
submitting.value = false;
|
|
submitting.value = false;
|
|
|
- areaDialogVisible.value = false;
|
|
|
|
|
locationDialogVisible.value = false;
|
|
locationDialogVisible.value = false;
|
|
|
|
|
+ locationTarget.value = null;
|
|
|
formRef.value?.resetFields?.();
|
|
formRef.value?.resetFields?.();
|
|
|
Object.assign(form, createEmptyForm());
|
|
Object.assign(form, createEmptyForm());
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleSelectLocation = () => {
|
|
|
|
|
|
|
+const addCategory = () => {
|
|
|
|
|
+ form.categories.push(createCategory({ category: "品类一" }));
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const removeCategory = (gIdx) => {
|
|
|
|
|
+ if (form.categories.length <= 1) return;
|
|
|
|
|
+ form.categories.splice(gIdx, 1);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const addPlanting = (gIdx) => {
|
|
|
|
|
+ form.categories[gIdx]?.plantings.push(createPlanting());
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const removePlanting = (gIdx, pIdx) => {
|
|
|
|
|
+ const group = form.categories[gIdx];
|
|
|
|
|
+ if (!group) return;
|
|
|
|
|
+ if (group.plantings.length <= 1) {
|
|
|
|
|
+ if (form.categories.length > 1) removeCategory(gIdx);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ group.plantings.splice(pIdx, 1);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const openResidentLocation = () => {
|
|
|
|
|
+ locationTarget.value = { type: "resident" };
|
|
|
locationDialogVisible.value = true;
|
|
locationDialogVisible.value = true;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleLocationConfirm = (point) => {
|
|
|
|
|
- form.locationWkt = point.wkt;
|
|
|
|
|
- form.location = point.label;
|
|
|
|
|
- formRef.value?.validateField?.("location");
|
|
|
|
|
|
|
+const openPlantLocation = (categoryIndex, plantingIndex) => {
|
|
|
|
|
+ locationTarget.value = { type: "plant", categoryIndex, plantingIndex };
|
|
|
|
|
+ locationDialogVisible.value = true;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleSelectArea = () => {
|
|
|
|
|
- areaDialogVisible.value = true;
|
|
|
|
|
|
|
+const handleLocationConfirm = (point) => {
|
|
|
|
|
+ const target = locationTarget.value;
|
|
|
|
|
+ if (target?.type === "plant") {
|
|
|
|
|
+ const planting =
|
|
|
|
|
+ form.categories[target.categoryIndex]?.plantings?.[target.plantingIndex];
|
|
|
|
|
+ if (planting) {
|
|
|
|
|
+ planting.plantLocationWkt = point.wkt;
|
|
|
|
|
+ planting.plantLocation = point.label;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ form.locationWkt = point.wkt;
|
|
|
|
|
+ form.residentLocation = point.label;
|
|
|
|
|
+ formRef.value?.validateField?.("residentLocation");
|
|
|
|
|
+ }
|
|
|
|
|
+ locationTarget.value = null;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleAreaConfirm = ({ areas, ids, label }) => {
|
|
|
|
|
- form.selectedAreas = areas;
|
|
|
|
|
- form.selectedAreaIds = ids;
|
|
|
|
|
- form.areaLabel = label;
|
|
|
|
|
- formRef.value?.validateField?.("areaLabel");
|
|
|
|
|
|
|
+const validatePlantings = () => {
|
|
|
|
|
+ for (const group of form.categories) {
|
|
|
|
|
+ if (!group.category) {
|
|
|
|
|
+ ElMessage.warning("请选择种植品类");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (const item of group.plantings) {
|
|
|
|
|
+ if (!item.variety) {
|
|
|
|
|
+ ElMessage.warning("请选择种植品种");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!item.area) {
|
|
|
|
|
+ ElMessage.warning("请填写种植面积");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!item.plantLocation) {
|
|
|
|
|
+ ElMessage.warning("请选择种植点位");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleSubmit = async () => {
|
|
const handleSubmit = async () => {
|
|
|
await formRef.value?.validate?.(async (valid) => {
|
|
await formRef.value?.validate?.(async (valid) => {
|
|
|
if (!valid) return;
|
|
if (!valid) return;
|
|
|
|
|
+ if (!validatePlantings()) return;
|
|
|
|
|
+
|
|
|
submitting.value = true;
|
|
submitting.value = true;
|
|
|
try {
|
|
try {
|
|
|
|
|
+ const categories = form.categories.map(({ category, plantings }) => ({
|
|
|
|
|
+ category,
|
|
|
|
|
+ plantings: plantings.map(
|
|
|
|
|
+ ({ variety, area, startPoint, startDate, plantLocation, plantLocationWkt, phenology }) => ({
|
|
|
|
|
+ variety,
|
|
|
|
|
+ area,
|
|
|
|
|
+ startPoint,
|
|
|
|
|
+ startDate,
|
|
|
|
|
+ plantLocation,
|
|
|
|
|
+ plantLocationWkt,
|
|
|
|
|
+ phenology: phenology || "生育期一",
|
|
|
|
|
+ })
|
|
|
|
|
+ ),
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ const plantings = categories.flatMap((g) =>
|
|
|
|
|
+ g.plantings.map((p) => ({ ...p, cropCategory: g.category }))
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
const payload = {
|
|
const payload = {
|
|
|
id: form.id,
|
|
id: form.id,
|
|
|
- name: form.name,
|
|
|
|
|
- type: form.type,
|
|
|
|
|
- categories: [...form.categories],
|
|
|
|
|
- location: form.location,
|
|
|
|
|
- locationWkt: form.locationWkt,
|
|
|
|
|
- areaLabel: form.areaLabel,
|
|
|
|
|
- selectedAreaIds: [...form.selectedAreaIds],
|
|
|
|
|
- selectedAreas: [...form.selectedAreas],
|
|
|
|
|
ownerName: form.ownerName,
|
|
ownerName: form.ownerName,
|
|
|
ownerPhone: form.ownerPhone,
|
|
ownerPhone: form.ownerPhone,
|
|
|
|
|
+ name: form.ownerName,
|
|
|
|
|
+ phone: form.ownerPhone,
|
|
|
|
|
+ residentLocation: form.residentLocation,
|
|
|
|
|
+ location: form.residentLocation,
|
|
|
|
|
+ locationWkt: form.locationWkt,
|
|
|
|
|
+ cropCategory: categories[0]?.category || "",
|
|
|
|
|
+ categories,
|
|
|
|
|
+ plantings,
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
emit("submit", { mode: props.mode, data: payload });
|
|
emit("submit", { mode: props.mode, data: payload });
|
|
|
ElMessage.success(isEdit.value ? "编辑成功" : "新增成功");
|
|
ElMessage.success(isEdit.value ? "编辑成功" : "新增成功");
|
|
|
close();
|
|
close();
|
|
@@ -304,49 +399,209 @@ const handleSubmit = async () => {
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
|
.device-form {
|
|
.device-form {
|
|
|
- padding: 20px 24px 0 8px;
|
|
|
|
|
- border-top: 1px solid #E5E6EB;
|
|
|
|
|
|
|
+ padding: 16px 18px 0;
|
|
|
|
|
+ border-top: 1px solid #e5e6eb;
|
|
|
|
|
+ max-height: 68vh;
|
|
|
|
|
+ overflow: auto;
|
|
|
|
|
|
|
|
:deep(.el-form-item) {
|
|
:deep(.el-form-item) {
|
|
|
- margin-bottom: 24px;
|
|
|
|
|
|
|
+ margin-bottom: 18px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-form-item__label) {
|
|
|
|
|
+ color: #1d2129;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ padding-right: 0;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.field-block {
|
|
|
|
|
|
|
+.form-item--inline {
|
|
|
|
|
+ :deep(.el-form-item__content) {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.name-phone-row {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: flex-start;
|
|
|
|
|
- gap: 10px;
|
|
|
|
|
|
|
+ gap: 12px;
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
|
|
+
|
|
|
|
|
+ &__name {
|
|
|
|
|
+ width: 190px;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__phone-item {
|
|
|
|
|
+ width: 190px;
|
|
|
|
|
+ margin-bottom: 0 !important;
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-form-item__content) {
|
|
|
|
|
+ margin-left: 0 !important;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.btn-outline {
|
|
|
|
|
- height: 32px;
|
|
|
|
|
- padding: 0 16px;
|
|
|
|
|
- color: $base-color;
|
|
|
|
|
|
|
+.form-section {
|
|
|
|
|
+ margin-bottom: 18px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-section__head {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding-bottom: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-section__label {
|
|
|
|
|
+ color: #1D2129;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-block + .category-block {
|
|
|
|
|
+ border-top: 1px solid #E5E6EB;
|
|
|
|
|
+ padding-top: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-block__head {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-select {
|
|
|
|
|
+ width: 120px;
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-select__wrapper) {
|
|
|
|
|
+ // background: rgba($warning-color, 0.12);
|
|
|
|
|
+ box-shadow: 0 0 0 1px $warning-color inset;
|
|
|
|
|
+ border-radius: 2px;
|
|
|
|
|
+
|
|
|
|
|
+ .el-select__selected-item,
|
|
|
|
|
+ .el-select__placeholder,
|
|
|
|
|
+ .el-select__caret {
|
|
|
|
|
+ color: $warning-color;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.planting-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.planting-card {
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ border: 1px solid #e5e6eb;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
background: #fff;
|
|
background: #fff;
|
|
|
- border-color: $base-color;
|
|
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.planting-card__top {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.variety-select {
|
|
|
|
|
+ width: 120px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.planting-card__row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+
|
|
|
|
|
+ &:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.planting-card__label {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ width: 70px;
|
|
|
|
|
+ color: #1d2129;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ line-height: 22px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.planting-card__input {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.start-point-select {
|
|
|
|
|
+ width: 110px;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.start-date-picker {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ width: auto !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.location-box {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ padding: 0 10px;
|
|
|
|
|
+ height: 32px;
|
|
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
|
|
|
- &:hover,
|
|
|
|
|
- &:focus {
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- background: $base-color;
|
|
|
|
|
- border-color: $base-color;
|
|
|
|
|
|
|
+ &--full {
|
|
|
|
|
+ width: 100%;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.area-preview {
|
|
|
|
|
- min-width: 148px;
|
|
|
|
|
- max-width: 100%;
|
|
|
|
|
- padding: 10px 12px;
|
|
|
|
|
- border: 1px solid #e5e5e5;
|
|
|
|
|
- border-radius: 2px;
|
|
|
|
|
- background: #f7f7f7;
|
|
|
|
|
- color: #666;
|
|
|
|
|
|
|
+.location-box__text {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ color: #1d2129;
|
|
|
font-size: 13px;
|
|
font-size: 13px;
|
|
|
- line-height: 1.5;
|
|
|
|
|
- word-break: break-all;
|
|
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.link-btn {
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+.secondary-link {
|
|
|
|
|
+ padding: 0 6px;
|
|
|
|
|
+ color: #FB8E33;
|
|
|
|
|
+ background: rgba(251, 142, 51, 0.1);
|
|
|
|
|
+ height: 25px;
|
|
|
|
|
+ line-height: 25px;
|
|
|
|
|
+ border-radius: 2px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.add-link {
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-item--resident {
|
|
|
|
|
+ :deep(.el-form-item__content) {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.dialog-footer {
|
|
.dialog-footer {
|