|
@@ -1,5 +1,6 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="task-page" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
|
|
<div class="task-page" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
|
|
|
|
|
+ <farm-header class="task-page__header" />
|
|
|
<div class="task-top">
|
|
<div class="task-top">
|
|
|
<div class="map-container" ref="mapContainer"></div>
|
|
<div class="map-container" ref="mapContainer"></div>
|
|
|
</div>
|
|
</div>
|
|
@@ -7,413 +8,137 @@
|
|
|
<customCalendar ref="calendarRef" @dateSelect="handleDateSelect"></customCalendar>
|
|
<customCalendar ref="calendarRef" @dateSelect="handleDateSelect"></customCalendar>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="task-list">
|
|
<div class="task-list">
|
|
|
- <div class="list-filter">
|
|
|
|
|
|
|
+ <div class="work-task-list">
|
|
|
<div
|
|
<div
|
|
|
- v-for="item in filterTabs"
|
|
|
|
|
- :key="item.value"
|
|
|
|
|
- class="filter-item"
|
|
|
|
|
- :class="{ active: activeIndex === item.value }"
|
|
|
|
|
- @click="handleActiveFilter(item.value)"
|
|
|
|
|
|
|
+ v-for="item in taskList"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ class="task-card"
|
|
|
|
|
+ :class="{ 'is-risk': item.isHighRisk }"
|
|
|
>
|
|
>
|
|
|
- {{ t(item.labelKey) }}({{ taskCounts[item.value] || 0 }})
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="list-box">
|
|
|
|
|
- <div class="list-header">
|
|
|
|
|
- <div class="select-group">
|
|
|
|
|
- <el-select class="select-item" v-model="selectParma.farmWorkTypeId" :placeholder="t('workExecute.farmWorkType')"
|
|
|
|
|
- @change="resetAndLoad">
|
|
|
|
|
- <el-option v-for="item in farmWorkTypeList" :key="item.id"
|
|
|
|
|
- :label="item.id === 0 ? t('workExecute.all') : item.name"
|
|
|
|
|
- :value="item.id" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-select class="select-item" v-model="selectParma.districtCode" :placeholder="t('workExecute.farmFilter')"
|
|
|
|
|
- @change="resetAndLoad">
|
|
|
|
|
- <el-option v-for="item in districtList" :key="item.code" :label="item.name"
|
|
|
|
|
- :value="item.code" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <div v-if="item.cornerTip" class="task-card__corner">{{ item.cornerTip }}</div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="task-card__header">
|
|
|
|
|
+ <img class="task-card__icon" src="@/assets/img/home/task.png" alt="" />
|
|
|
|
|
+ <span class="task-card__title van-ellipsis">{{ item.title }}</span>
|
|
|
|
|
+ <span v-if="item.isHighRisk" class="risk-badge">{{ t("workExecute.highRisk") }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <!-- <div class="action-btn">{{ t("workExecute.batchAccept") }}</div> -->
|
|
|
|
|
- </div>
|
|
|
|
|
- <!-- 任务列表 -->
|
|
|
|
|
- <div class="work-task-list" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.3)">
|
|
|
|
|
- <div class="task-item" v-for="(item, index) in taskList" :key="index"
|
|
|
|
|
- :class="[
|
|
|
|
|
- isAbnormalItem(item) ? 'timeout-item' : '',
|
|
|
|
|
- locale === 'en' ? 'task-item--en' : '',
|
|
|
|
|
- ]" @click="handleItem(item, index)">
|
|
|
|
|
- <div class="item-title" :class="{ 'item-title--en': locale === 'en' }">
|
|
|
|
|
- <img class="task-icon" src="@/assets/img/home/task.png" alt="">
|
|
|
|
|
- <div class="item-title-main">
|
|
|
|
|
- <span class="title-text">{{ item?.operation?.name }}</span>
|
|
|
|
|
- <span class="task-status" :class="`task-status--${getWorkType(item.operation?.type)}`">{{
|
|
|
|
|
- item.operation?.type }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <span class="task-tag timeout" v-if="isAbnormalItem(item)">
|
|
|
|
|
- <el-icon>
|
|
|
|
|
- <WarningFilled />
|
|
|
|
|
- </el-icon>
|
|
|
|
|
- {{ getAbnormalLabel() }}
|
|
|
|
|
- </span>
|
|
|
|
|
- <span class="task-tag" v-else :class="`task-tag--${getStatusTagClass()}`">
|
|
|
|
|
- <el-icon v-if="activeStatus === '已完成'" class="task-tag__icon">
|
|
|
|
|
- <CircleCheck />
|
|
|
|
|
- </el-icon>
|
|
|
|
|
- {{ getStatusLabel(activeStatus) }}
|
|
|
|
|
- </span>
|
|
|
|
|
- <div class="item-content">
|
|
|
|
|
- <div class="item-info">
|
|
|
|
|
- <div class="info-item">
|
|
|
|
|
- <div class="info-name">{{ t("workExecute.responsible") }}</div><span class="val-text">
|
|
|
|
|
- {{ item.operation?.executor }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="info-item" v-if="mode !== 'review'">
|
|
|
|
|
- <div class="info-name">{{ t("workExecute.farmWorkDetail") }}</div><span class="val-text">
|
|
|
|
|
- {{ item.operation?.work_detail }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="info-item">
|
|
|
|
|
- <div class="info-name">{{ t("workExecute.farmSituation") }}</div><span class="val-text">
|
|
|
|
|
- {{ item.operation?.work_reason }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="excutor-info">
|
|
|
|
|
- <div class="executor-stats">
|
|
|
|
|
- <div class="stat-cell">
|
|
|
|
|
- <div class="stat-value">
|
|
|
|
|
- {{ item.time ? formatGMTToYMD(item.time) : "--" }}
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="stat-label">{{ t("workExecute.executeTime") }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="cell-line"></div>
|
|
|
|
|
- <div class="stat-cell">
|
|
|
|
|
- <div class="stat-value">{{ item.geohash_sample || "--" }}</div>
|
|
|
|
|
- <div class="stat-label">{{ t("workExecute.executeArea") }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="cell-line"></div>
|
|
|
|
|
- <div class="stat-cell">
|
|
|
|
|
- <div class="stat-value">{{ item.operation?.machine_code || "--" }}</div>
|
|
|
|
|
- <div class="stat-label">{{ t("workExecute.executeMachine") }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
|
|
|
- <div
|
|
|
|
|
- v-if="activeStatus === '已完成'"
|
|
|
|
|
- class="execute-photos"
|
|
|
|
|
- @click.stop
|
|
|
|
|
- >
|
|
|
|
|
- <div class="execute-photos__wrap">
|
|
|
|
|
- <span class="execute-photos__label">{{ t('workExecute.executePhotos') }}</span>
|
|
|
|
|
- <div class="execute-photos__list">
|
|
|
|
|
- <div
|
|
|
|
|
- v-for="(photo, photoIndex) in getExecutePhotoList(item)"
|
|
|
|
|
- :key="photoIndex"
|
|
|
|
|
- class="execute-photos__item"
|
|
|
|
|
- >
|
|
|
|
|
- <img :src="photo" alt="" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="task-card__body">
|
|
|
|
|
+ <div class="info-line">
|
|
|
|
|
+ {{ t("workExecute.farmSituation") }}{{ item.analysisSummary }}
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
- <div class="unqualified-reason" v-if="isAbnormalItem(item) && activeStatus === '已完成'">
|
|
|
|
|
- {{ t("workExecute.unqualifiedReason") }}{{ item.operation?.reason || t("workExecute.defaultUnqualifiedReason") }}
|
|
|
|
|
|
|
+ <div class="info-line">
|
|
|
|
|
+ {{ t("workExecute.executeAreaLabel") }}{{ item.areaCode }}
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <div class="compare-imgs" v-if="isAbnormalItem(item) && activeStatus === '已完成' && (item.beforeImage || item.afterImage)"
|
|
|
|
|
- @click.stop>
|
|
|
|
|
- <div class="img-tag">{{ t("workExecute.before") }}</div>
|
|
|
|
|
- <div class="img-tag right-tag">{{ t("workExecute.after") }}</div>
|
|
|
|
|
- <div class="img-item" v-if="item.beforeImage">
|
|
|
|
|
- <img :src="getWorkImageUrl(item.beforeImage)" alt="" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="img-item" v-if="item.afterImage">
|
|
|
|
|
- <img :src="getWorkImageUrl(item.afterImage)" alt="" />
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="task-card__footer">
|
|
|
|
|
+ <div class="card-btn card-btn--ghost" @click.stop="handleForward(item)">
|
|
|
|
|
+ {{ t("workExecute.forward") }}
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
- <div class="task-footer">
|
|
|
|
|
- <div class="farm-info">{{ t("workExecute.fromFarm") }}</div>
|
|
|
|
|
- <div class="btn-group" v-if="getItemStatusButtons(item).length">
|
|
|
|
|
- <div v-for="btn in getItemStatusButtons(item)" :key="btn.label" class="edit-btn"
|
|
|
|
|
- :class="btn.type" @click.stop="handleStatusBtn(btn, item, index)">
|
|
|
|
|
- {{ btn.label }}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="card-btn card-btn--primary" @click.stop="handleViewDetail(item)">
|
|
|
|
|
+ {{ t("workExecute.viewDetail") }}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="empty-tip" v-if="!loading && taskList.length === 0">{{ t("workExecute.noData") }}</div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-if="!taskList.length" class="empty-tip">{{ t("workExecute.noData") }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { computed, nextTick, onActivated, onMounted, ref, watch } from "vue";
|
|
|
|
|
|
|
+import { computed, nextTick, onActivated, onMounted, ref } from "vue";
|
|
|
import { useStore } from "vuex";
|
|
import { useStore } from "vuex";
|
|
|
-import IndexMap from "./index";
|
|
|
|
|
import { useRouter } from "vue-router";
|
|
import { useRouter } from "vue-router";
|
|
|
-import { WarningFilled, CircleCheck } from "@element-plus/icons-vue";
|
|
|
|
|
import { ElMessage } from "element-plus";
|
|
import { ElMessage } from "element-plus";
|
|
|
-import config from "@/api/config.js";
|
|
|
|
|
|
|
+import IndexMap from "./index";
|
|
|
import customCalendar from "./components/calendar.vue";
|
|
import customCalendar from "./components/calendar.vue";
|
|
|
|
|
+import farmHeader from "@/components/farmHeader.vue";
|
|
|
import { useI18n } from "@/i18n";
|
|
import { useI18n } from "@/i18n";
|
|
|
-import executePhotoPlaceholder1 from "@/assets/img/common/sd-1.jpg";
|
|
|
|
|
-import executePhotoPlaceholder2 from "@/assets/img/common/sd-2.jpg";
|
|
|
|
|
-import executePhotoPlaceholder3 from "@/assets/img/common/sd-3.jpg";
|
|
|
|
|
-import executePhotoPlaceholder4 from "@/assets/img/common/sd-4.jpg";
|
|
|
|
|
-
|
|
|
|
|
-const EXECUTE_PHOTO_PLACEHOLDERS = [
|
|
|
|
|
- executePhotoPlaceholder1,
|
|
|
|
|
- executePhotoPlaceholder2,
|
|
|
|
|
- executePhotoPlaceholder3,
|
|
|
|
|
- executePhotoPlaceholder4,
|
|
|
|
|
-];
|
|
|
|
|
-
|
|
|
|
|
-const { t, locale } = useI18n();
|
|
|
|
|
|
|
+
|
|
|
|
|
+const { t } = useI18n();
|
|
|
const store = useStore();
|
|
const store = useStore();
|
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
|
const indexMap = new IndexMap();
|
|
const indexMap = new IndexMap();
|
|
|
const mapContainer = ref(null);
|
|
const mapContainer = ref(null);
|
|
|
|
|
+const calendarRef = ref(null);
|
|
|
const tabBarHeight = computed(() => store.state.home.tabBarHeight);
|
|
const tabBarHeight = computed(() => store.state.home.tabBarHeight);
|
|
|
-const selectParma = ref({
|
|
|
|
|
- farmWorkTypeId: null,
|
|
|
|
|
- districtCode: null,
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const taskList = ref([])
|
|
|
|
|
-
|
|
|
|
|
-const mode = "execute";
|
|
|
|
|
-const activeStatus = ref("待接受");
|
|
|
|
|
-
|
|
|
|
|
-const STATUS_BTN_MAP = {
|
|
|
|
|
- 待接受: [
|
|
|
|
|
- { labelKey: "workExecute.modifyInfo", type: "normal", action: "redispatch" },
|
|
|
|
|
- { labelKey: "workExecute.acceptTask", type: "primary", action: "remindAccept" },
|
|
|
|
|
- ],
|
|
|
|
|
- 已接受: [
|
|
|
|
|
- { labelKey: "workExecute.modifyInfo", type: "normal", action: "redispatch" },
|
|
|
|
|
- { labelKey: "workExecute.startExecute", type: "primary-blue", action: "startExecute" },
|
|
|
|
|
- ],
|
|
|
|
|
- 执行中: [{ labelKey: "workExecute.markComplete", type: "primary-green", action: "markComplete" }],
|
|
|
|
|
- 已超时: [
|
|
|
|
|
- { labelKey: "workExecute.redispatch", type: "normal", action: "redispatch" },
|
|
|
|
|
- { labelKey: "workExecute.remindAccept", type: "primary", action: "remindAccept" },
|
|
|
|
|
- ],
|
|
|
|
|
- 未达标: [{ labelKey: "workExecute.redispatchAgain", type: "primary", action: "redispatch" }],
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const STATUS_LABEL_MAP = {
|
|
|
|
|
- 待接受: "workExecute.pending",
|
|
|
|
|
- 已接受: "workExecute.accepted",
|
|
|
|
|
- 执行中: "workExecute.executing",
|
|
|
|
|
- 已完成: "workExecute.completed",
|
|
|
|
|
- 已超时: "workExecute.timeout",
|
|
|
|
|
- 未达标: "workExecute.unqualified",
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const getStatusLabel = (status) => t(STATUS_LABEL_MAP[status] || status);
|
|
|
|
|
-
|
|
|
|
|
-const STATUS_TAG_CLASS_MAP = {
|
|
|
|
|
- 待接受: "pending",
|
|
|
|
|
- 已接受: "accepted",
|
|
|
|
|
- 执行中: "executing",
|
|
|
|
|
- 已完成: "completed",
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const getStatusTagClass = () => STATUS_TAG_CLASS_MAP[activeStatus.value] || "pending";
|
|
|
|
|
-
|
|
|
|
|
-const STATUS_BY_TAB_VALUE = { 0: "待接受", 1: "执行中", 2: "已接受", 3: "已完成" };
|
|
|
|
|
-
|
|
|
|
|
-const isTruthyFlag = (value) => Number(value) === 1 || value === true;
|
|
|
|
|
-
|
|
|
|
|
-const ABNORMAL_FLAG_MAP = {
|
|
|
|
|
- 待接受: ["is_aovertime"],
|
|
|
|
|
- 已接受: ["is_eovertime", "is_aovertime"],
|
|
|
|
|
- 执行中: ["is_covertime", "is_aovertime"],
|
|
|
|
|
- 已完成: ["is_anormal", "is_unqualified", "is_aovertime"],
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const isAbnormalItem = (item, status = activeStatus.value) => {
|
|
|
|
|
- const operation = item?.operation || {};
|
|
|
|
|
- const flags = ABNORMAL_FLAG_MAP[status] || ["is_aovertime"];
|
|
|
|
|
- return flags.some((field) => isTruthyFlag(operation[field]));
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const ABNORMAL_LABEL_KEY_MAP = {
|
|
|
|
|
- 待接受: "workExecute.acceptTimeout",
|
|
|
|
|
- 已接受: "workExecute.executeAbnormal",
|
|
|
|
|
- 执行中: "workExecute.completeAbnormal",
|
|
|
|
|
- 已完成: "workExecute.unqualified",
|
|
|
|
|
-};
|
|
|
|
|
|
|
|
|
|
-const ABNORMAL_STATUS_STR_MAP = {
|
|
|
|
|
- 待接受: "接受异常",
|
|
|
|
|
- 已接受: "执行异常",
|
|
|
|
|
- 执行中: "完成异常",
|
|
|
|
|
- 已完成: "未达标",
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const getAbnormalLabel = () => t(ABNORMAL_LABEL_KEY_MAP[activeStatus.value] || "workExecute.acceptTimeout");
|
|
|
|
|
-
|
|
|
|
|
-const getAbnormalStatusStr = () => ABNORMAL_STATUS_STR_MAP[activeStatus.value] || "接受异常";
|
|
|
|
|
-
|
|
|
|
|
-const mapStatusButtons = (buttons) =>
|
|
|
|
|
- buttons.map((btn) => ({
|
|
|
|
|
- ...btn,
|
|
|
|
|
- label: t(btn.labelKey),
|
|
|
|
|
- }));
|
|
|
|
|
-
|
|
|
|
|
-const getItemStatusButtons = (item) => {
|
|
|
|
|
- if (isAbnormalItem(item)) {
|
|
|
|
|
- if (activeStatus.value === "已完成") {
|
|
|
|
|
- return mapStatusButtons(STATUS_BTN_MAP["未达标"] || []);
|
|
|
|
|
- }
|
|
|
|
|
- return [];
|
|
|
|
|
- }
|
|
|
|
|
- return mapStatusButtons(STATUS_BTN_MAP[activeStatus.value] || []);
|
|
|
|
|
-};
|
|
|
|
|
|
|
+const formatDateYMD = (date = new Date()) =>
|
|
|
|
|
+ `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
|
|
|
|
|
|
-const formatGMTToYMD = (gmtString) => {
|
|
|
|
|
- const date = new Date(gmtString);
|
|
|
|
|
- return date.toISOString().split("T")[0];
|
|
|
|
|
-};
|
|
|
|
|
|
|
+const filterDate = ref(formatDateYMD());
|
|
|
|
|
|
|
|
-const getWorkImageUrl = (image) => {
|
|
|
|
|
- if (!image?.cloud_filename) return "";
|
|
|
|
|
- return config.base_img_url3 + image.cloud_filename;
|
|
|
|
|
-};
|
|
|
|
|
|
|
+/** 假数据:后续可替换为接口 */
|
|
|
|
|
+const taskList = ref([
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ title: "喷施杀菌剂",
|
|
|
|
|
+ isHighRisk: true,
|
|
|
|
|
+ cornerTip: "发现异常后机动执行",
|
|
|
|
|
+ analysisSummary: "近期高温高湿,蒂蛀虫风险上升",
|
|
|
|
|
+ areaCode: "A-01 / 分区一",
|
|
|
|
|
+ wkt: ""
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 2,
|
|
|
|
|
+ title: "叶面追肥",
|
|
|
|
|
+ isHighRisk: false,
|
|
|
|
|
+ cornerTip: "",
|
|
|
|
|
+ analysisSummary: "转色期养分需求增加,需补充钾肥",
|
|
|
|
|
+ areaCode: "B-03 / 分区二",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 3,
|
|
|
|
|
+ title: "灌溉补水",
|
|
|
|
|
+ isHighRisk: true,
|
|
|
|
|
+ cornerTip: "发现异常后机动执行",
|
|
|
|
|
+ analysisSummary: "土壤墒情偏低,局部出现轻度干旱胁迫",
|
|
|
|
|
+ areaCode: "C-02 / 分区三",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 4,
|
|
|
|
|
+ title: "病虫害巡查",
|
|
|
|
|
+ isHighRisk: false,
|
|
|
|
|
+ cornerTip: "",
|
|
|
|
|
+ analysisSummary: "重点关注新梢与果实表面异常斑点",
|
|
|
|
|
+ areaCode: "D-05 / 全园",
|
|
|
|
|
+ },
|
|
|
|
|
+]);
|
|
|
|
|
|
|
|
-const resolveExecutePhotoUrl = (src) => {
|
|
|
|
|
- if (!src) return "";
|
|
|
|
|
- if (typeof src === "object" && src.cloud_filename) {
|
|
|
|
|
- return config.base_img_url3 + src.cloud_filename;
|
|
|
|
|
- }
|
|
|
|
|
- if (typeof src === "string") {
|
|
|
|
|
- if (src.startsWith("http")) return src;
|
|
|
|
|
- return config.base_img_url2 + src;
|
|
|
|
|
- }
|
|
|
|
|
- return "";
|
|
|
|
|
|
|
+const handleDateSelect = (date) => {
|
|
|
|
|
+ filterDate.value = date ?? formatDateYMD();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const getExecutePhotoList = (item) => {
|
|
|
|
|
- const operation = item?.operation || {};
|
|
|
|
|
- const raw =
|
|
|
|
|
- operation.execute_evidence ??
|
|
|
|
|
- operation.executeEvidence ??
|
|
|
|
|
- item.execute_evidence ??
|
|
|
|
|
- item.executeEvidence;
|
|
|
|
|
-
|
|
|
|
|
- let photos = [];
|
|
|
|
|
-
|
|
|
|
|
- if (raw) {
|
|
|
|
|
- if (Array.isArray(raw)) {
|
|
|
|
|
- photos = raw.map(resolveExecutePhotoUrl).filter(Boolean);
|
|
|
|
|
- } else if (typeof raw === "string") {
|
|
|
|
|
- try {
|
|
|
|
|
- const parsed = JSON.parse(raw);
|
|
|
|
|
- if (Array.isArray(parsed)) {
|
|
|
|
|
- photos = parsed.map(resolveExecutePhotoUrl).filter(Boolean);
|
|
|
|
|
- }
|
|
|
|
|
- } catch {
|
|
|
|
|
- const url = resolveExecutePhotoUrl(raw);
|
|
|
|
|
- if (url) photos = [url];
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return (photos.length ? photos : EXECUTE_PHOTO_PLACEHOLDERS).slice(0, 4);
|
|
|
|
|
|
|
+const handleForward = () => {
|
|
|
|
|
+ ElMessage.info(t("workExecute.forwardTip"));
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleItem = (item, index) => {
|
|
|
|
|
- const statusStr = isAbnormalItem(item) ? getAbnormalStatusStr() : activeStatus.value;
|
|
|
|
|
- const status = activeIndex.value;
|
|
|
|
|
|
|
+const handleViewDetail = (item) => {
|
|
|
router.push({
|
|
router.push({
|
|
|
path: "/agri_record_detail",
|
|
path: "/agri_record_detail",
|
|
|
query: {
|
|
query: {
|
|
|
- id: item.operation?.work_id,
|
|
|
|
|
- status,
|
|
|
|
|
- statusStr,
|
|
|
|
|
|
|
+ id: item.id,
|
|
|
|
|
+ statusStr: item.isHighRisk ? "执行异常" : "待接受",
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleStatusBtn = (btn, item, index) => {
|
|
|
|
|
- if (btn.action === "redispatch") {
|
|
|
|
|
- ElMessage.info(t("workExecute.redispatchMsg", { name: item.operation?.name }));
|
|
|
|
|
- } else {
|
|
|
|
|
- ElMessage.success(t("workExecute.remindSuccess"));
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const formatDateYMD = (date = new Date()) =>
|
|
|
|
|
- `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
|
|
|
-
|
|
|
|
|
-const calendarRef = ref(null);
|
|
|
|
|
-const filterDate = ref(formatDateYMD());
|
|
|
|
|
-const fullTaskList = ref([]);
|
|
|
|
|
-
|
|
|
|
|
-const getCalendarTaskList = (list) => {
|
|
|
|
|
- return list.map((item) => ({
|
|
|
|
|
- expectedExecuteDate: item.time,
|
|
|
|
|
- executeDeadlineDate: item.time,
|
|
|
|
|
- }));
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const syncCalendarData = () => {
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- calendarRef.value?.setSolarTerm(getCalendarTaskList(fullTaskList.value));
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const handleDateSelect = (date) => {
|
|
|
|
|
- filterDate.value = date ?? formatDateYMD();
|
|
|
|
|
- resetAndLoad();
|
|
|
|
|
- syncCalendarData();
|
|
|
|
|
-};
|
|
|
|
|
-// 各状态任务数量,key 为 work_operation_type:0待接受 1执行中 2已接受 3已完成
|
|
|
|
|
-const taskCounts = ref({ 0: 0, 1: 0, 2: 0, 3: 0 });
|
|
|
|
|
-const STATUS_TAB_VALUES = [0, 2, 1, 3];
|
|
|
|
|
-const filterTabs = [
|
|
|
|
|
- { value: 0, labelKey: "workExecute.pending" },
|
|
|
|
|
- { value: 2, labelKey: "workExecute.accepted" },
|
|
|
|
|
- { value: 1, labelKey: "workExecute.executing" },
|
|
|
|
|
- { value: 3, labelKey: "workExecute.completed" },
|
|
|
|
|
-];
|
|
|
|
|
-// 当前选中的筛选索引
|
|
|
|
|
-const activeIndex = ref(0);
|
|
|
|
|
-const loading = ref(false);
|
|
|
|
|
-
|
|
|
|
|
-const cityCode = ref("");
|
|
|
|
|
-//根据城市的坐标返回区县列表
|
|
|
|
|
-const districtList = ref([]);
|
|
|
|
|
-
|
|
|
|
|
-//农事类型列表
|
|
|
|
|
-const farmWorkTypeList = ref([]);
|
|
|
|
|
-function getFarmWorkTypeList() {
|
|
|
|
|
- VE_API.z_farm_work_record.getFarmWorkTypeList().then(({ data }) => {
|
|
|
|
|
- farmWorkTypeList.value = data;
|
|
|
|
|
- farmWorkTypeList.value.unshift({ id: 0, name: "全部" });
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
const mapPoint = ref(null);
|
|
const mapPoint = ref(null);
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
mapPoint.value = store.state.home.miniUserLocationPoint;
|
|
mapPoint.value = store.state.home.miniUserLocationPoint;
|
|
|
- getFarmWorkTypeList();
|
|
|
|
|
- syncCalendarData();
|
|
|
|
|
nextTick(() => {
|
|
nextTick(() => {
|
|
|
- indexMap.initMap(mapPoint.value, mapContainer.value, true);
|
|
|
|
|
- indexMap.initData(taskList.value, '', 'farmPoint');
|
|
|
|
|
|
|
+ if (mapContainer.value) {
|
|
|
|
|
+ indexMap.initMap(mapPoint.value, mapContainer.value, true);
|
|
|
|
|
+ indexMap.initData([], "", "farmPoint");
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
onActivated(() => {
|
|
onActivated(() => {
|
|
|
- resetAndLoad();
|
|
|
|
|
nextTick(() => {
|
|
nextTick(() => {
|
|
|
if (!indexMap.kmap) {
|
|
if (!indexMap.kmap) {
|
|
|
if (mapContainer.value) {
|
|
if (mapContainer.value) {
|
|
@@ -422,7 +147,6 @@ onActivated(() => {
|
|
|
}
|
|
}
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
if (mapContainer.value && indexMap.kmap.map) {
|
|
if (mapContainer.value && indexMap.kmap.map) {
|
|
|
const checkAndUpdateSize = () => {
|
|
const checkAndUpdateSize = () => {
|
|
|
const container = mapContainer.value;
|
|
const container = mapContainer.value;
|
|
@@ -438,81 +162,6 @@ onActivated(() => {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
-const getRegionRecordList = (data) => {
|
|
|
|
|
- if (!Array.isArray(data)) return [];
|
|
|
|
|
- return data.filter((item) => item.operation !== null);
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const getRegionRecordParams = (workOperationType) => ({
|
|
|
|
|
- // farm_id: '320',
|
|
|
|
|
- // time: '2026-06-30',
|
|
|
|
|
- farm_id: localStorage.getItem('selectedFarmId'),
|
|
|
|
|
- time: filterDate.value,
|
|
|
|
|
- work_operation_type: workOperationType,
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const taskListsByType = ref({ 0: [], 1: [], 2: [], 3: [] });
|
|
|
|
|
-
|
|
|
|
|
-const sortTaskListByAbnormal = (list, tabValue) => {
|
|
|
|
|
- const status = STATUS_BY_TAB_VALUE[tabValue] || "待接受";
|
|
|
|
|
- return [...list].sort((a, b) => {
|
|
|
|
|
- const aAbnormal = isAbnormalItem(a, status) ? 1 : 0;
|
|
|
|
|
- const bAbnormal = isAbnormalItem(b, status) ? 1 : 0;
|
|
|
|
|
- if (bAbnormal !== aAbnormal) return bAbnormal - aAbnormal;
|
|
|
|
|
- return Number(a.AO ?? 0) - Number(b.AO ?? 0);
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const loadPageData = async () => {
|
|
|
|
|
- loading.value = true;
|
|
|
|
|
- try {
|
|
|
|
|
- const results = await Promise.all(
|
|
|
|
|
- STATUS_TAB_VALUES.map((type) => VE_API.farm.getRegionRecord(getRegionRecordParams(type)))
|
|
|
|
|
- );
|
|
|
|
|
- const counts = { 0: 0, 1: 0, 2: 0, 3: 0 };
|
|
|
|
|
- const listsByType = { 0: [], 1: [], 2: [], 3: [] };
|
|
|
|
|
- STATUS_TAB_VALUES.forEach((type, index) => {
|
|
|
|
|
- const { code, data } = results[index] || {};
|
|
|
|
|
- const list = code === 200 ? getRegionRecordList(data) : [];
|
|
|
|
|
- listsByType[type] = sortTaskListByAbnormal(list, type);
|
|
|
|
|
- counts[type] = list.length;
|
|
|
|
|
- });
|
|
|
|
|
- taskListsByType.value = listsByType;
|
|
|
|
|
- taskCounts.value = counts;
|
|
|
|
|
- taskList.value = listsByType[activeIndex.value] || [];
|
|
|
|
|
- } finally {
|
|
|
|
|
- loading.value = false;
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-// 监听 activeIndex 变化,切换 Tab 时复用已请求的数据
|
|
|
|
|
-watch(activeIndex, () => {
|
|
|
|
|
- taskList.value = taskListsByType.value[activeIndex.value] || [];
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// 重置并重新加载
|
|
|
|
|
-const resetAndLoad = () => {
|
|
|
|
|
- loadPageData();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-function handleActiveFilter(i) {
|
|
|
|
|
- activeIndex.value = i;
|
|
|
|
|
- selectParma.value.districtCode = cityCode.value;
|
|
|
|
|
- selectParma.value.farmWorkTypeId = null;
|
|
|
|
|
- const statusMap = { 0: "待接受", 1: "执行中", 2: "已接受", 3: "已完成" };
|
|
|
|
|
- activeStatus.value = STATUS_BY_TAB_VALUE[i] || statusMap[i] || "待接受";
|
|
|
|
|
- syncCalendarData();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const getWorkType = (type) => {
|
|
|
|
|
- const map = {
|
|
|
|
|
- '标准防治类': 1,
|
|
|
|
|
- '标准施肥类': 2,
|
|
|
|
|
- '标准调节类': 3,
|
|
|
|
|
- }
|
|
|
|
|
- return map[type] || type;
|
|
|
|
|
-}
|
|
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
@@ -521,491 +170,138 @@ const getWorkType = (type) => {
|
|
|
height: calc(100vh - 50px);
|
|
height: calc(100vh - 50px);
|
|
|
overflow: auto;
|
|
overflow: auto;
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
- background: #FFFFFF;
|
|
|
|
|
|
|
+ background: linear-gradient(180deg, #d1ebff 0%, #ffffff 100%);
|
|
|
|
|
|
|
|
.map-container {
|
|
.map-container {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: 162px;
|
|
height: 162px;
|
|
|
- clip-path: inset(0px round 8px);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .list-box {
|
|
|
|
|
- background: #F1F3F4;
|
|
|
|
|
- padding: 12px 12px 8px 12px;
|
|
|
|
|
-
|
|
|
|
|
- .list-header {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
-
|
|
|
|
|
- .action-btn {
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- height: 32px;
|
|
|
|
|
- line-height: 30px;
|
|
|
|
|
- color: #2199F8;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- border-radius: 5px;
|
|
|
|
|
- padding: 0 10px;
|
|
|
|
|
- background: #FFFFFF;
|
|
|
|
|
- border: 1px solid #2199F8;
|
|
|
|
|
- color: #2199F8;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .select-group {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- padding: 0px 12px 0 12px;
|
|
|
|
|
-
|
|
|
|
|
- .select-item {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
-
|
|
|
|
|
- ::v-deep {
|
|
|
|
|
- .el-select__wrapper {
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- gap: 2px;
|
|
|
|
|
- box-shadow: none;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- background: none;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .el-select__selection {
|
|
|
|
|
- flex: none;
|
|
|
|
|
- width: fit-content;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .el-select__placeholder {
|
|
|
|
|
- position: static;
|
|
|
|
|
- transform: none;
|
|
|
|
|
- width: fit-content;
|
|
|
|
|
- color: rgba(0, 0, 0, 0.2);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .el-select__caret {
|
|
|
|
|
- color: rgba(0, 0, 0, 0.2);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ clip-path: inset(0 round 8px);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.calendar-container {
|
|
.calendar-container {
|
|
|
- padding: 4px 12px 8px 12px;
|
|
|
|
|
|
|
+ padding: 4px 12px 8px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.task-top {
|
|
.task-top {
|
|
|
- padding: 10px 12px 0 12px;
|
|
|
|
|
|
|
+ padding: 10px 12px 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .task-content-loading {
|
|
|
|
|
- height: 80px;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 60px;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .task-content {
|
|
|
|
|
- min-height: 80px;
|
|
|
|
|
|
|
+ .task-list {
|
|
|
|
|
+ padding: 0 12px 20px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .empty-data {
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #6f7274;
|
|
|
|
|
- padding: 20px 0;
|
|
|
|
|
|
|
+ .work-task-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 10px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .task-list {
|
|
|
|
|
|
|
+ .task-card {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
|
|
+ padding: 14px 12px 12px;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
background: #fff;
|
|
background: #fff;
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .list-filter {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-around;
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.04);
|
|
|
|
|
+
|
|
|
|
|
+ &__corner {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ max-width: 58%;
|
|
|
|
|
+ padding: 4px 10px;
|
|
|
|
|
+ border-radius: 0 8px 0 8px;
|
|
|
|
|
+ background: rgba(255, 106, 106, 0.1);
|
|
|
|
|
+ color: #FF6A6A;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ line-height: 16px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- .filter-item {
|
|
|
|
|
- padding: 0 6px;
|
|
|
|
|
- height: 32px;
|
|
|
|
|
- color: #1D2129;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- border-radius: 20px;
|
|
|
|
|
- position: relative;
|
|
|
|
|
|
|
+ &__header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ padding-right: 8px;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- &.active {
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
|
|
+ &__icon {
|
|
|
|
|
+ width: 16px;
|
|
|
|
|
+ height: 16px;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- &::after {
|
|
|
|
|
- content: "";
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- bottom: 0;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 2px;
|
|
|
|
|
- background: #2199f8;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ &__title {
|
|
|
|
|
+ max-width: 160px;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: #000;
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- .work-task-list {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
|
|
+ .risk-badge {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ height: 18px;
|
|
|
|
|
+ padding: 0 6px;
|
|
|
|
|
+ border-radius: 2px;
|
|
|
|
|
+ background: rgba(255, 106, 106, 0.12);
|
|
|
|
|
+ color: #ff6a6a;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- .task-item {
|
|
|
|
|
|
|
+ &__body {
|
|
|
margin-top: 10px;
|
|
margin-top: 10px;
|
|
|
- background: #fff;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- padding: 12px 16px;
|
|
|
|
|
- border: 1px solid rgba(0, 0, 0, 0.08);
|
|
|
|
|
- position: relative;
|
|
|
|
|
-
|
|
|
|
|
- &.timeout-item {
|
|
|
|
|
- border: 1px solid #FF6A6A;
|
|
|
|
|
- box-shadow: 0px 0px 4px 0px #FF6A6A66;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &--en {
|
|
|
|
|
- padding-top: 22px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .item-title {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- padding-bottom: 10px;
|
|
|
|
|
- border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
|
|
|
|
- color: #1d2129;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
-
|
|
|
|
|
- .task-icon {
|
|
|
|
|
- width: 16px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .item-title-main {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &--en {
|
|
|
|
|
- align-items: flex-start;
|
|
|
|
|
-
|
|
|
|
|
- .task-icon {
|
|
|
|
|
- margin-top: 2px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .item-title-main {
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: flex-start;
|
|
|
|
|
- gap: 4px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .task-status {
|
|
|
|
|
- margin-left: 0;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .title-text {
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .task-status {
|
|
|
|
|
- margin-left: 0;
|
|
|
|
|
- border-radius: 2px;
|
|
|
|
|
- height: 20px;
|
|
|
|
|
- line-height: 20px;
|
|
|
|
|
- padding: 1px 6px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- color: #ffb32f;
|
|
|
|
|
- background: rgba(255, 179, 47, 0.1);
|
|
|
|
|
-
|
|
|
|
|
- &.task-status--1 {
|
|
|
|
|
- background: rgba(33, 153, 248, 0.1);
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &.task-status--2 {
|
|
|
|
|
- background: rgba(255, 179, 47, 0.1);
|
|
|
|
|
- color: #FFB32F;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &.task-status--3 {
|
|
|
|
|
- background: rgba(58, 173, 148, 0.1);
|
|
|
|
|
- color: #3AAD94;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .task-tag {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- right: 0;
|
|
|
|
|
- top: 0;
|
|
|
|
|
- background: rgba(255, 149, 61, 0.1);
|
|
|
|
|
- color: #F46E00;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- padding: 0 10px;
|
|
|
|
|
- height: 25px;
|
|
|
|
|
- line-height: 25px;
|
|
|
|
|
- border-radius: 0 7px 0 8px;
|
|
|
|
|
-
|
|
|
|
|
- &.timeout {
|
|
|
|
|
- background: #FF6A6A;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 2px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &--pending {
|
|
|
|
|
- background: rgba(255, 149, 61, 0.1);
|
|
|
|
|
- color: #F46E00;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &--accepted {
|
|
|
|
|
- background: rgba(33, 153, 248, 0.1);
|
|
|
|
|
- color: #2199F8;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &--executing {
|
|
|
|
|
- background: rgba(33, 153, 248, 0.1);
|
|
|
|
|
- color: #2199F8;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &--completed {
|
|
|
|
|
- background: rgba(74, 191, 50, 0.1);
|
|
|
|
|
- color: #4ABF32;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 2px;
|
|
|
|
|
-
|
|
|
|
|
- .task-tag__icon {
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .execute-photos {
|
|
|
|
|
- margin: 8px 0 4px;
|
|
|
|
|
-
|
|
|
|
|
- &__wrap {
|
|
|
|
|
- background: #ebf5ff;
|
|
|
|
|
- border: 1px solid rgba(33, 153, 248, 0.15);
|
|
|
|
|
- border-radius: 12px;
|
|
|
|
|
- padding: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__label {
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- padding: 4px 10px;
|
|
|
|
|
- background: #2199f8;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- line-height: 18px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- margin-bottom: 8px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__list {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- padding: 10px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__item {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- aspect-ratio: 1;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
-
|
|
|
|
|
- img {
|
|
|
|
|
- display: block;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- object-fit: cover;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .item-content {
|
|
|
|
|
- padding: 12px 0;
|
|
|
|
|
-
|
|
|
|
|
- .excutor-info {
|
|
|
|
|
- margin-top: 12px;
|
|
|
|
|
- background: rgba(189, 189, 189, 0.1);
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- padding-bottom: 12px;
|
|
|
|
|
-
|
|
|
|
|
- .excutor-info-top {
|
|
|
|
|
- padding: 12px 12px 0 12px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .executor-stats {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- padding-top: 10px;
|
|
|
|
|
- justify-content: space-around;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .cell-line {
|
|
|
|
|
- height: 20px;
|
|
|
|
|
- width: 1px;
|
|
|
|
|
- background: #e5e6eb;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ padding-top: 10px;
|
|
|
|
|
+ border-top: 0.5px solid rgba(0, 0, 0, 0.05);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- .stat-value {
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- color: #0B0B0B;
|
|
|
|
|
- margin-bottom: 4px;
|
|
|
|
|
- word-break: break-all;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ .info-line {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ line-height: 20px;
|
|
|
|
|
+ color: rgba(111, 114, 116, 0.6);
|
|
|
|
|
|
|
|
- .stat-label {
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- line-height: 20px;
|
|
|
|
|
- color: rgba(107, 107, 107, 0.5);
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ & + .info-line {
|
|
|
|
|
+ margin-top: 6px;
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- .item-info {
|
|
|
|
|
- color: rgba(111, 114, 116, 0.6);
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- line-height: 21px;
|
|
|
|
|
-
|
|
|
|
|
- .info-item {
|
|
|
|
|
- display: flex;
|
|
|
|
|
-
|
|
|
|
|
- .info-name {
|
|
|
|
|
- flex: none;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .val-text {
|
|
|
|
|
- // color: #1d2129;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .info-item+.info-item {
|
|
|
|
|
- padding-top: 6px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ &__footer {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ margin-top: 14px;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- .task-footer {
|
|
|
|
|
- padding-top: 4px;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- .farm-info {
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: rgba(32, 32, 32, 0.4);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ .card-btn {
|
|
|
|
|
+ height: 32px;
|
|
|
|
|
+ border-radius: 20px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ line-height: 32px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
|
|
|
- .btn-group {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: end;
|
|
|
|
|
- gap: 10px;
|
|
|
|
|
|
|
+ &--ghost {
|
|
|
|
|
+ background: #F6F6F6;
|
|
|
|
|
+ color: #000000;
|
|
|
|
|
+ padding: 0 20px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .edit-btn {
|
|
|
|
|
- height: 30px;
|
|
|
|
|
|
|
+ &--primary {
|
|
|
|
|
+ background: rgba(33, 153, 248, 0.1);
|
|
|
|
|
+ color: #2199f8;
|
|
|
padding: 0 16px;
|
|
padding: 0 16px;
|
|
|
- border-radius: 24px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- line-height: 30px;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
-
|
|
|
|
|
- &.normal {
|
|
|
|
|
- background: #F6F6F6;
|
|
|
|
|
- color: #000000;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &.primary {
|
|
|
|
|
- background: #FF953D;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &.primary-blue {
|
|
|
|
|
- background: #2199F8;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &.primary-green {
|
|
|
|
|
- background: #37C11B;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .compare-imgs {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 10px;
|
|
|
|
|
- position: relative;
|
|
|
|
|
- margin: 10px 0;
|
|
|
|
|
-
|
|
|
|
|
- .img-tag {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- z-index: 10;
|
|
|
|
|
- top: 0;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- background: rgba(0, 0, 0, 0.7);
|
|
|
|
|
- border-radius: 5px 0 5px 0;
|
|
|
|
|
- padding: 0 8px;
|
|
|
|
|
- height: 15px;
|
|
|
|
|
- line-height: 15px;
|
|
|
|
|
-
|
|
|
|
|
- &.right-tag {
|
|
|
|
|
- right: 0;
|
|
|
|
|
- left: auto;
|
|
|
|
|
- border-radius: 0 5px 0 5px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .img-item {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- height: 120px;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- border: 0.5px solid rgba(0, 0, 0, 0.1);
|
|
|
|
|
- border-radius: 5px;
|
|
|
|
|
-
|
|
|
|
|
- img {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- border-radius: 5px;
|
|
|
|
|
- object-fit: cover;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- .unqualified-reason {
|
|
|
|
|
- color: #f74e4e;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- padding: 4px 10px;
|
|
|
|
|
- background: rgba(247, 78, 78, 0.08);
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- line-height: 18px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .empty-tip {
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- color: #86909c;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- padding: 20px 0;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ .empty-tip {
|
|
|
|
|
+ padding: 40px 0;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ color: #86909c;
|
|
|
|
|
+ font-size: 14px;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|