|
|
@@ -1,804 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="agri-record-detail">
|
|
|
- <custom-header :name="t('agriRecordDetail.title')"></custom-header>
|
|
|
- <div class="content" :class="{ 'content--no-footer': !showBottomButtons }">
|
|
|
- <div class="status-banner" :class="`status-banner--${statusBannerTheme}`">
|
|
|
- <div class="status-banner__title">{{ statusBannerTitle }}</div>
|
|
|
- <div class="status-banner__deadline">{{ t('agriRecordDetail.deadlinePrefix') }} {{ workDetail.work_time }}</div>
|
|
|
- </div>
|
|
|
- <div class="box-wrap execution-area-card">
|
|
|
- <div class="execution-area-card__header">
|
|
|
- <span class="title">{{ t('agriRecordDetail.executionArea') }}</span>
|
|
|
- <span class="code">ws0gefwg9tdn</span>
|
|
|
- </div>
|
|
|
- <div class="map-container" ref="mapContainer"></div>
|
|
|
- <div class="execution-area-card__footer">
|
|
|
- <div class="address">
|
|
|
- <div class="address-title">{{ farmInfo.farm_name }}</div>
|
|
|
- <div class="address-detail">{{ farmInfo.rawAddress }}</div>
|
|
|
- </div>
|
|
|
- <div class="nav-btn" @click="handleNavigate">{{ t('agriRecordDetail.navigateHere') }}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div v-if="isCompletedStatus" class="box-wrap execute-photos-card">
|
|
|
- <div class="execute-photos-card__header">
|
|
|
- <span class="execute-photos-card__title">{{ t('workExecute.executePhotos') }}</span>
|
|
|
- <span class="execute-photos-card__status">{{ t('agriRecordDetail.auditApproved') }}</span>
|
|
|
- </div>
|
|
|
- <div class="execute-photos-card__list">
|
|
|
- <div
|
|
|
- v-for="(photo, index) in executePhotoList"
|
|
|
- :key="index"
|
|
|
- class="execute-photos-card__item"
|
|
|
- >
|
|
|
- <img :src="photo" alt="" />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="box-wrap work-summary-card">
|
|
|
- <div class="header" :class="{ 'header--en': locale === 'en' }">
|
|
|
- <img class="icon" src="@/assets/img/home/task.png" alt="">
|
|
|
- <div class="header-main">
|
|
|
- <span class="title">{{ workDetail.work_name }}</span>
|
|
|
- <div class="tags" v-if="workTagLabel">
|
|
|
- <span
|
|
|
- class="work-tag"
|
|
|
- :class="`work-tag--${getWorkTagTheme(workDetail.farm_work_type)}`"
|
|
|
- >{{ workTagLabel }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="body">
|
|
|
- <div
|
|
|
- v-for="(row, index) in detailRows"
|
|
|
- :key="index"
|
|
|
- class="detail-row"
|
|
|
- :class="{
|
|
|
- 'detail-row--field': isFieldRow(row),
|
|
|
- 'detail-row--textarea': getEffectiveRowType(row) === 'textarea',
|
|
|
- }"
|
|
|
- >
|
|
|
- <span class="detail-row__label">{{ t(row.labelKey) }}</span>
|
|
|
- <span v-if="isTextRow(row)" class="detail-row__value">{{ formatRowValue(row) }}</span>
|
|
|
- <el-date-picker
|
|
|
- v-else-if="getEffectiveRowType(row) === 'date'"
|
|
|
- v-model="row.value"
|
|
|
- class="detail-row__control"
|
|
|
- type="date"
|
|
|
- format="YYYY.MM.DD"
|
|
|
- value-format="YYYY-MM-DD"
|
|
|
- :placeholder="t('agriRecordDetail.selectDate')"
|
|
|
- :editable="false"
|
|
|
- style="width: 100%"
|
|
|
- />
|
|
|
- <el-input
|
|
|
- v-else-if="getEffectiveRowType(row) === 'input'"
|
|
|
- v-model="row.value"
|
|
|
- class="detail-row__control"
|
|
|
- :placeholder="t('agriRecordDetail.pleaseInput')"
|
|
|
- />
|
|
|
- <el-input
|
|
|
- v-else-if="getEffectiveRowType(row) === 'textarea'"
|
|
|
- v-model="row.value"
|
|
|
- class="detail-row__control"
|
|
|
- type="textarea"
|
|
|
- :rows="3"
|
|
|
- :placeholder="t('agriRecordDetail.pleaseInput')"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="box-wrap">
|
|
|
- <div class="info-item">
|
|
|
- <div class="info-title"><span class="title-block"></span>{{ t('agriRecordDetail.workPurpose') }}</div>
|
|
|
- <div class="info-value">{{ workDetail?.work_purpose }}</div>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <div class="info-title"><span class="title-block"></span>{{ t('agriRecordDetail.drugPrescription') }}</div>
|
|
|
- <div class="info-value">{{ workDetail?.drug_prescription }}</div>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <div class="info-title"><span class="title-block"></span>{{ t('agriRecordDetail.executionParams') }}</div>
|
|
|
- <div class="info-value">{{ workDetail?.execution_method || '--' }}</div>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <div class="info-title"><span class="title-block"></span>{{ t('agriRecordDetail.precautions') }}</div>
|
|
|
- <div class="info-value">{{ workDetail?.precautions }}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div
|
|
|
- class="custom-bottom-fixed-btns"
|
|
|
- :class="{ 'custom-bottom-fixed-btns--single': isExecutingStatus && !isEditing }"
|
|
|
- v-if="showBottomButtons"
|
|
|
- >
|
|
|
- <template v-if="isExecutingStatus && !isEditing">
|
|
|
- <div class="bottom-btn primary-btn green-btn" @click="handleAccept">{{ t('workExecute.markComplete') }}</div>
|
|
|
- </template>
|
|
|
- <template v-else>
|
|
|
- <div class="bottom-btn secondary-btn" @click="handleToggleEdit">
|
|
|
- {{ isEditing ? t('agriRecordDetail.cancelEdit') : t('agriRecordDetail.modifyInfo') }}
|
|
|
- </div>
|
|
|
- <div class="bottom-btn primary-btn" :class="{ 'orange-btn': !isEditing && !isAcceptedStatus }" @click="handleAccept">{{ primaryActionText }}</div>
|
|
|
- </template>
|
|
|
- </div>
|
|
|
- <tip-popup
|
|
|
- v-model:show="showAcceptSuccessPopup"
|
|
|
- type="success"
|
|
|
- :text="t('agriRecordDetail.acceptSuccessTitle')"
|
|
|
- :text2="t('agriRecordDetail.acceptSuccessDesc')"
|
|
|
- hideBtn
|
|
|
- />
|
|
|
- <tip-popup
|
|
|
- v-model:show="showExecuteSuccessPopup"
|
|
|
- type="executeSuccess"
|
|
|
- :text="t('agriRecordDetail.executeSuccessTitle')"
|
|
|
- :text2="t('agriRecordDetail.executeSuccessDesc')"
|
|
|
- :notice-text="t('agriRecordDetail.executeSuccessNotice')"
|
|
|
- hideBtn
|
|
|
- />
|
|
|
- <complete-popup v-model:show="showCompletePopup" @confirm="handleCompleteConfirm" />
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script setup>
|
|
|
-import { ref, computed, onActivated, onDeactivated, onBeforeUnmount, nextTick } from 'vue';
|
|
|
-import { School } from '@element-plus/icons-vue';
|
|
|
-import * as util from '@/common/ol_common.js';
|
|
|
-import customHeader from '@/components/customHeader.vue';
|
|
|
-import { useI18n } from '@/i18n';
|
|
|
-import AreaMap from '../work_detail/components/areaMap.js';
|
|
|
-import tipPopup from '@/components/popup/tipPopup.vue';
|
|
|
-import completePopup from './components/completePopup.vue';
|
|
|
-import { ElMessage } from 'element-plus';
|
|
|
-import { useRoute } from 'vue-router';
|
|
|
-import config from '@/api/config.js';
|
|
|
-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 route = useRoute();
|
|
|
-const DEFAULT_FARM_POINT = 'POINT(113.6142086995688 23.585836479509055)';
|
|
|
-const mapContainer = ref(null);
|
|
|
-const recordDetailMap = new AreaMap();
|
|
|
-
|
|
|
-const workDetail = ref({});
|
|
|
-
|
|
|
-const farmZones = computed(() => {
|
|
|
- const zones = workDetail.value?.zones;
|
|
|
- if (!Array.isArray(zones)) return [];
|
|
|
- return zones.filter(
|
|
|
- (z) => z && typeof z.zone_geometry === 'string' && z.zone_geometry.trim()
|
|
|
- );
|
|
|
-});
|
|
|
-
|
|
|
-function wktCenterPointFromZones(zones) {
|
|
|
- const list = Array.isArray(zones) ? zones : [];
|
|
|
- for (const zone of list) {
|
|
|
- const wkt = zone?.zone_geometry;
|
|
|
- if (!wkt || typeof wkt !== 'string') continue;
|
|
|
- try {
|
|
|
- const c = util.wktCastGeom(wkt.trim()).getFirstCoordinate();
|
|
|
- return `POINT(${c[0]} ${c[1]})`;
|
|
|
- } catch {
|
|
|
- /* try next zone */
|
|
|
- }
|
|
|
- }
|
|
|
- return DEFAULT_FARM_POINT;
|
|
|
-}
|
|
|
-
|
|
|
-function destroyExecutionAreaMap() {
|
|
|
- recordDetailMap.destroyMap();
|
|
|
-}
|
|
|
-
|
|
|
-function initExecutionAreaMap() {
|
|
|
- nextTick(() => {
|
|
|
- if (!mapContainer.value) return;
|
|
|
- const zones = farmZones.value;
|
|
|
- recordDetailMap.destroyMap();
|
|
|
- recordDetailMap.initMap(wktCenterPointFromZones(zones), mapContainer.value);
|
|
|
- recordDetailMap.initZones(zones);
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-const farmInfo = ref({});
|
|
|
-onActivated(async () => {
|
|
|
- farmInfo.value = JSON.parse(localStorage.getItem('selectedFarmData') || '{}');
|
|
|
- isEditing.value = false;
|
|
|
- detailRowsSnapshot = null;
|
|
|
- await getRegionWorkDetail();
|
|
|
-});
|
|
|
-
|
|
|
-onDeactivated(() => {
|
|
|
- destroyExecutionAreaMap();
|
|
|
-});
|
|
|
-
|
|
|
-onBeforeUnmount(() => {
|
|
|
- destroyExecutionAreaMap();
|
|
|
-});
|
|
|
-
|
|
|
-const getRegionWorkDetail = async () => {
|
|
|
- const params = {
|
|
|
- id: route.query.id,
|
|
|
- work_operations_type: route.query.status
|
|
|
- };
|
|
|
- const { code, data } = await VE_API.farm.getRegionWorkDetail(params);
|
|
|
- if (code === 200 && data && data.length > 0) {
|
|
|
- workDetail.value = data[0];
|
|
|
- detailRows.value = buildDetailRows(workDetail.value);
|
|
|
- initExecutionAreaMap();
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const EDITABLE_FIELD_MAP = {
|
|
|
- work_time: 'date',
|
|
|
- execution_method: 'textarea',
|
|
|
-};
|
|
|
-
|
|
|
-const isEditing = ref(false);
|
|
|
-const showAcceptSuccessPopup = ref(false);
|
|
|
-const showExecuteSuccessPopup = ref(false);
|
|
|
-const showCompletePopup = ref(false);
|
|
|
-let detailRowsSnapshot = null;
|
|
|
-
|
|
|
-const getEffectiveRowType = (row) => {
|
|
|
- if (!isEditing.value) return undefined;
|
|
|
- return EDITABLE_FIELD_MAP[row.field] || undefined;
|
|
|
-};
|
|
|
-
|
|
|
-const isTextRow = (row) => !getEffectiveRowType(row);
|
|
|
-const isFieldRow = (row) => !!getEffectiveRowType(row);
|
|
|
-
|
|
|
-const formatRowValue = (row) => row?.value ?? '';
|
|
|
-
|
|
|
-const normalizeDateValue = (value) => {
|
|
|
- if (!value) return value;
|
|
|
- return String(value).replace(/\./g, '-');
|
|
|
-};
|
|
|
-
|
|
|
-const DETAIL_ROW_SCHEMA = [
|
|
|
- { labelKey: 'agriRecordDetail.fields.executor', field: 'executor' },
|
|
|
- { labelKey: 'agriRecordDetail.fields.workReasonDetail', field: 'work_reason_detail' },
|
|
|
- { labelKey: 'agriRecordDetail.fields.cropConditionAnalysis', field: 'crop_condition_analysis' },
|
|
|
- { labelKey: 'agriRecordDetail.fields.workTime', field: 'work_time', type: 'date' },
|
|
|
- { labelKey: 'agriRecordDetail.fields.executionMethod', field: 'execution_method', type: 'textarea' },
|
|
|
-];
|
|
|
-
|
|
|
-const buildDetailRows = (data = {}) =>
|
|
|
- DETAIL_ROW_SCHEMA.map(({ labelKey, field, type }) => ({
|
|
|
- labelKey,
|
|
|
- field,
|
|
|
- type,
|
|
|
- value: data[field] ?? '',
|
|
|
- }));
|
|
|
-
|
|
|
-const detailRows = ref([]);
|
|
|
-
|
|
|
-const STATUS_BANNER_MAP = {
|
|
|
- 待接受: 'workExecute.pending',
|
|
|
- 已接受: 'workExecute.accepted',
|
|
|
- 执行中: 'workExecute.executing',
|
|
|
- 已完成: 'workExecute.completed',
|
|
|
- 接受异常: 'workExecute.acceptTimeout',
|
|
|
- 执行异常: 'workExecute.executeAbnormal',
|
|
|
- 完成异常: 'workExecute.completeAbnormal',
|
|
|
- 未达标: 'workExecute.unqualified',
|
|
|
-};
|
|
|
-
|
|
|
-const statusBannerTitle = computed(() => {
|
|
|
- const statusStr = route.query.statusStr;
|
|
|
- const key = STATUS_BANNER_MAP[statusStr];
|
|
|
- return key ? t(key) : statusStr || '';
|
|
|
-});
|
|
|
-
|
|
|
-const statusBannerTheme = computed(() => {
|
|
|
- const statusStr = route.query.statusStr;
|
|
|
- if (['接受异常', '执行异常', '完成异常', '未达标'].includes(statusStr)) return 'overtime';
|
|
|
- if (statusStr === '执行中') return 'executing';
|
|
|
- if (statusStr === '已完成') return 'completed';
|
|
|
- if (statusStr === '已接受') return 'accepted';
|
|
|
- const status = Number(route.query.status);
|
|
|
- if (status === 0) return 'pending';
|
|
|
- if (status === 2) return 'accepted';
|
|
|
- if (status === 3) return 'completed';
|
|
|
- if (status === 3) return 'completed';
|
|
|
- return 'pending';
|
|
|
-});
|
|
|
-
|
|
|
-const workTagLabel = computed(() => {
|
|
|
- const type = workDetail.value?.farm_work_type;
|
|
|
- if (type == null || type === '') return '';
|
|
|
- return t(`agriRecordDetail.farmWorkType.${type}`);
|
|
|
-});
|
|
|
-
|
|
|
-const ABNORMAL_STATUS_FLAG_MAP = {
|
|
|
- 接受异常: ["is_aovertime"],
|
|
|
- 执行异常: ["is_eovertime", "is_aovertime"],
|
|
|
- 完成异常: ["is_covertime", "is_aovertime"],
|
|
|
- 未达标: ["is_anormal", "is_unqualified", "is_aovertime"],
|
|
|
-};
|
|
|
-
|
|
|
-const isTruthyFlag = (value) => Number(value) === 1 || value === true;
|
|
|
-
|
|
|
-const isAbnormalDetail = computed(() => {
|
|
|
- const statusStr = route.query.statusStr;
|
|
|
- if (["接受异常", "执行异常", "完成异常", "未达标"].includes(statusStr)) {
|
|
|
- const flags = ABNORMAL_STATUS_FLAG_MAP[statusStr] || ["is_aovertime"];
|
|
|
- const detail = workDetail.value || {};
|
|
|
- return flags.some((field) => isTruthyFlag(detail[field]));
|
|
|
- }
|
|
|
- return Object.values(ABNORMAL_STATUS_FLAG_MAP).some((flags) =>
|
|
|
- flags.some((field) => isTruthyFlag(workDetail.value?.[field]))
|
|
|
- );
|
|
|
-});
|
|
|
-
|
|
|
-const isAcceptedStatus = computed(() =>
|
|
|
- route.query.statusStr === '已接受' || Number(route.query.status) === 2
|
|
|
-);
|
|
|
-
|
|
|
-const isExecutingStatus = computed(() => route.query.statusStr === '执行中');
|
|
|
-
|
|
|
-const isCompletedStatus = computed(() =>
|
|
|
- route.query.statusStr === '已完成' || Number(route.query.status) === 3
|
|
|
-);
|
|
|
-
|
|
|
-const showBottomButtons = computed(() => !isAbnormalDetail.value && !isCompletedStatus.value);
|
|
|
-
|
|
|
-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 executePhotoList = computed(() => {
|
|
|
- const raw = workDetail.value?.execute_evidence ?? workDetail.value?.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 primaryActionText = computed(() => {
|
|
|
- if (isEditing.value) return t('agriRecordDetail.saveInfo');
|
|
|
- if (isAcceptedStatus.value) return t('workExecute.startExecute');
|
|
|
- return t('agriRecordDetail.acceptTask');
|
|
|
-});
|
|
|
-
|
|
|
-const getWorkTagTheme = (type) => {
|
|
|
- const n = Number(type);
|
|
|
- if (n === 95) return 'green';
|
|
|
- if (n >= 91 && n <= 98) return 'orange';
|
|
|
- return 'blue';
|
|
|
-};
|
|
|
-
|
|
|
-const handleNavigate = () => {};
|
|
|
-
|
|
|
-const handleToggleEdit = () => {
|
|
|
- if (!isEditing.value) {
|
|
|
- detailRowsSnapshot = JSON.parse(JSON.stringify(detailRows.value));
|
|
|
- detailRows.value = detailRows.value.map((row) => {
|
|
|
- if (row.field === 'work_time' && row.value) {
|
|
|
- return { ...row, value: normalizeDateValue(row.value) };
|
|
|
- }
|
|
|
- return row;
|
|
|
- });
|
|
|
- isEditing.value = true;
|
|
|
- return;
|
|
|
- }
|
|
|
- if (detailRowsSnapshot) {
|
|
|
- detailRows.value = detailRowsSnapshot;
|
|
|
- detailRowsSnapshot = null;
|
|
|
- }
|
|
|
- isEditing.value = false;
|
|
|
-};
|
|
|
-
|
|
|
-const handleAccept = () => {
|
|
|
- if (isEditing.value) {
|
|
|
- // TODO: 提交保存农事信息接口
|
|
|
- isEditing.value = false;
|
|
|
- detailRowsSnapshot = null;
|
|
|
- ElMessage.success(t('agriRecordDetail.saveSuccess'));
|
|
|
- return;
|
|
|
- }
|
|
|
- if (isAcceptedStatus.value) {
|
|
|
- // TODO: 开始执行接口
|
|
|
- showExecuteSuccessPopup.value = true;
|
|
|
- return;
|
|
|
- }
|
|
|
- if (isExecutingStatus.value) {
|
|
|
- showCompletePopup.value = true;
|
|
|
- return;
|
|
|
- }
|
|
|
- // TODO: 提交接受农事接口
|
|
|
- showAcceptSuccessPopup.value = true;
|
|
|
-};
|
|
|
-
|
|
|
-const handleCompleteConfirm = () => {
|
|
|
- // TODO: 提交完成农事接口,payload: { executeDate, executeEvidence }
|
|
|
- ElMessage.success(t('agriRecordDetail.saveSuccess'));
|
|
|
-};
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped lang="scss">
|
|
|
-.agri-record-detail {
|
|
|
- width: 100%;
|
|
|
- height: 100vh;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- background: #f2f3f5;
|
|
|
- overflow: hidden;
|
|
|
-
|
|
|
- .content {
|
|
|
- flex: 1;
|
|
|
- min-height: 0;
|
|
|
- width: 100%;
|
|
|
- overflow-y: auto;
|
|
|
- -webkit-overflow-scrolling: touch;
|
|
|
- padding-bottom: 90px;
|
|
|
- box-sizing: border-box;
|
|
|
-
|
|
|
- &--no-footer {
|
|
|
- padding-bottom: 12px;
|
|
|
- }
|
|
|
-
|
|
|
- .status-banner {
|
|
|
- padding: 20px 12px 72px;
|
|
|
- color: #fff;
|
|
|
-
|
|
|
- &--0,
|
|
|
- &--pending {
|
|
|
- background: #FF9838;
|
|
|
- }
|
|
|
-
|
|
|
- &--2,
|
|
|
- &--accepted,
|
|
|
- &--executing {
|
|
|
- background: #2199F8;
|
|
|
- }
|
|
|
-
|
|
|
- &--3,
|
|
|
- &--completed {
|
|
|
- background: #4ABF32;
|
|
|
- }
|
|
|
-
|
|
|
- &--1,
|
|
|
- &--overtime {
|
|
|
- background: #FF6A6A;
|
|
|
- }
|
|
|
-
|
|
|
- &__title {
|
|
|
- font-size: 22px;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .box-wrap {
|
|
|
- background: #ffffff;
|
|
|
- border-radius: 8px;
|
|
|
- padding: 10px;
|
|
|
- margin: 10px 12px 0 12px;
|
|
|
-
|
|
|
- &.execution-area-card {
|
|
|
- position: relative;
|
|
|
- z-index: 1;
|
|
|
- margin-top: -52px;
|
|
|
-
|
|
|
- .execution-area-card__header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- .title {
|
|
|
- font-size: 16px;
|
|
|
- }
|
|
|
- .code {
|
|
|
- color: #565656;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .map-container {
|
|
|
- width: 100%;
|
|
|
- height: 166px;
|
|
|
- margin: 12px 0;
|
|
|
- border-radius: 8px;
|
|
|
- overflow: hidden;
|
|
|
- }
|
|
|
-
|
|
|
- .execution-area-card__footer {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- .address {
|
|
|
- .address-title {
|
|
|
- font-size: 16px;
|
|
|
- color: #1D2129;
|
|
|
- // overflow: hidden;
|
|
|
- // text-overflow: ellipsis;
|
|
|
- // white-space: nowrap;
|
|
|
- }
|
|
|
-
|
|
|
- .address-detail {
|
|
|
- margin-top: 2px;
|
|
|
- font-size: 12px;
|
|
|
- color: rgba(29, 33, 41, 0.6);
|
|
|
- // overflow: hidden;
|
|
|
- // text-overflow: ellipsis;
|
|
|
- // white-space: nowrap;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .nav-btn {
|
|
|
- padding: 6px 10px;
|
|
|
- border: 1px solid #2199F8;
|
|
|
- border-radius: 25px;
|
|
|
- color: #2199F8;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.execute-photos-card {
|
|
|
- padding: 12px 10px 10px;
|
|
|
-
|
|
|
- .execute-photos-card__header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- margin-bottom: 12px;
|
|
|
- }
|
|
|
-
|
|
|
- .execute-photos-card__title {
|
|
|
- font-size: 16px;
|
|
|
- }
|
|
|
-
|
|
|
- .execute-photos-card__status {
|
|
|
- padding: 2px 8px;
|
|
|
- font-size: 12px;
|
|
|
- line-height: 18px;
|
|
|
- color: #4ABF32;
|
|
|
- background: rgba(74, 191, 50, 0.1);
|
|
|
- border-radius: 4px;
|
|
|
- }
|
|
|
-
|
|
|
- .execute-photos-card__list {
|
|
|
- display: flex;
|
|
|
- gap: 8px;
|
|
|
- }
|
|
|
-
|
|
|
- .execute-photos-card__item {
|
|
|
- flex: 1;
|
|
|
- aspect-ratio: 1;
|
|
|
- border-radius: 8px;
|
|
|
- overflow: hidden;
|
|
|
-
|
|
|
- img {
|
|
|
- display: block;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- object-fit: cover;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.work-summary-card {
|
|
|
- padding: 12px 10px 10px;
|
|
|
-
|
|
|
- .header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
- border-bottom: 1px solid rgba(245, 245, 245, 0.99);
|
|
|
- padding-bottom: 8px;
|
|
|
- margin-bottom: 8px;
|
|
|
- .icon {
|
|
|
- width: 16px;
|
|
|
- height: 16px;
|
|
|
- flex-shrink: 0;
|
|
|
- }
|
|
|
-
|
|
|
- .header-main {
|
|
|
- flex: 1;
|
|
|
- min-width: 0;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
- }
|
|
|
-
|
|
|
- &--en {
|
|
|
- align-items: flex-start;
|
|
|
-
|
|
|
- .icon {
|
|
|
- margin-top: 2px;
|
|
|
- }
|
|
|
-
|
|
|
- .header-main {
|
|
|
- flex-direction: column;
|
|
|
- align-items: flex-start;
|
|
|
- gap: 4px;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .title {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 500;
|
|
|
- }
|
|
|
-
|
|
|
- .tags {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
- }
|
|
|
-
|
|
|
- .work-tag {
|
|
|
- padding: 0 6px;
|
|
|
- border-radius: 2px;
|
|
|
- font-size: 12px;
|
|
|
-
|
|
|
- &--orange {
|
|
|
- background: rgba(255, 179, 47, 0.1);
|
|
|
- color: #FFB32F;
|
|
|
- }
|
|
|
-
|
|
|
- &--blue {
|
|
|
- background: rgba(33, 153, 248, 0.1);
|
|
|
- color: #2199F8;
|
|
|
- }
|
|
|
-
|
|
|
- &--green {
|
|
|
- background: rgba(58, 173, 148, 0.1);
|
|
|
- color: #3AAD94;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .body {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 6px;
|
|
|
- .detail-row {
|
|
|
- display: flex;
|
|
|
- gap: 8px;
|
|
|
-
|
|
|
- &__label {
|
|
|
- width: 72px;
|
|
|
- min-width: 72px;
|
|
|
- flex-shrink: 0;
|
|
|
- color: rgba(111, 114, 116, 0.6);
|
|
|
- }
|
|
|
-
|
|
|
- &__value {
|
|
|
- color: #565656;
|
|
|
- }
|
|
|
-
|
|
|
- &__control {
|
|
|
- flex: 1;
|
|
|
- min-width: 0;
|
|
|
- }
|
|
|
-
|
|
|
- &--field {
|
|
|
- align-items: center;
|
|
|
-
|
|
|
- :deep(.el-input__wrapper) {
|
|
|
- border: 1px solid rgba(33, 153, 248, 0.5);
|
|
|
- border-radius: 4px;
|
|
|
- box-shadow: none;
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.el-input__inner) {
|
|
|
- color: #2199F8;
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.el-input__prefix),
|
|
|
- :deep(.el-input__suffix) {
|
|
|
- color: #2199F8;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &--textarea {
|
|
|
- align-items: flex-start;
|
|
|
-
|
|
|
- .detail-row__label {
|
|
|
- padding-top: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.el-textarea__inner) {
|
|
|
- border: 1px solid rgba(33, 153, 248, 0.5);
|
|
|
- border-radius: 4px;
|
|
|
- box-shadow: none;
|
|
|
- color: #2199F8;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- .info-item {
|
|
|
- .info-title {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
- color: #2199F8;
|
|
|
- font-weight: 500;
|
|
|
-
|
|
|
- .title-block {
|
|
|
- width: 6px;
|
|
|
- height: 6px;
|
|
|
- background: #2199F8;
|
|
|
- border-radius: 1px;
|
|
|
- transform: rotate(45deg);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .info-value {
|
|
|
- padding-top: 4px;
|
|
|
- color: #ACACAC;
|
|
|
- line-height: 21px;
|
|
|
- white-space: pre-line;
|
|
|
- }
|
|
|
-
|
|
|
- .blod-text {
|
|
|
- font-weight: 500;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .info-item+.info-item {
|
|
|
- margin-top: 10px;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .custom-bottom-fixed-btns {
|
|
|
- justify-content: space-between;
|
|
|
-
|
|
|
- &--single {
|
|
|
- justify-content: center;
|
|
|
- }
|
|
|
-
|
|
|
- .secondary-btn{
|
|
|
- background: #F6F6F6;
|
|
|
- border: none;
|
|
|
- }
|
|
|
- .primary-btn{
|
|
|
- background: #2199F8;
|
|
|
- border: none;
|
|
|
- }
|
|
|
- .orange-btn{
|
|
|
- background: #FF953D;
|
|
|
- color: #fff;
|
|
|
- }
|
|
|
- .green-btn{
|
|
|
- background: #4ABF32;
|
|
|
- color: #fff;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</style>
|