|
|
@@ -0,0 +1,794 @@
|
|
|
+<template>
|
|
|
+ <view class="page report-page">
|
|
|
+ <view class="mode-tabs">
|
|
|
+ <view
|
|
|
+ v-for="item in modeOptions"
|
|
|
+ :key="item.value"
|
|
|
+ class="mode-tab"
|
|
|
+ :class="{ active: currentMode === item.value }"
|
|
|
+ @click="switchMode(item.value)">
|
|
|
+ {{ item.label }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <z-paging
|
|
|
+ ref="paging"
|
|
|
+ v-model="dataList"
|
|
|
+ class="report-paging"
|
|
|
+ :fixed="false"
|
|
|
+ :default-page-size="10"
|
|
|
+ @query="queryList">
|
|
|
+ <view class="report-list">
|
|
|
+ <view class="report-card" v-for="item in dataList" :key="item.id">
|
|
|
+ <view class="card-header">
|
|
|
+ <view>
|
|
|
+ <view class="card-date">{{ formatDate(item.createTime) }}</view>
|
|
|
+ </view>
|
|
|
+ <view class="status-tag">
|
|
|
+ {{ rdStatusDict[item.rdStatus] || item.rdStatus || "--" }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="card-body">
|
|
|
+ <view class="field-row">
|
|
|
+ <text class="field-label">施工队伍</text>
|
|
|
+ <text class="field-value">{{ item.deptName || "--" }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="field-row">
|
|
|
+ <text class="field-label">任务</text>
|
|
|
+ <text class="field-value">{{ item.taskName || "--" }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="field-row brief-row">
|
|
|
+ <text class="field-label">施工简报</text>
|
|
|
+ <UniTooltip
|
|
|
+ :content="item.constructionBrief || '--'"
|
|
|
+ placement="top">
|
|
|
+ <text class="field-value brief-text">{{
|
|
|
+ item.constructionBrief || "--"
|
|
|
+ }}</text>
|
|
|
+ </UniTooltip>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view v-if="item.lastGroupIdFlag" class="summary-panel">
|
|
|
+ <view class="summary-header" @click="toggleSummary(item.id)">
|
|
|
+ <text>组汇总</text>
|
|
|
+ <uni-icons
|
|
|
+ :type="isSummaryExpanded(item.id) ? 'up' : 'down'"
|
|
|
+ color="#004098"
|
|
|
+ size="16" />
|
|
|
+ </view>
|
|
|
+ <view v-if="isSummaryExpanded(item.id)" class="summary-grid">
|
|
|
+ <view
|
|
|
+ class="summary-item"
|
|
|
+ v-for="field in summaryFields"
|
|
|
+ :key="field.key">
|
|
|
+ <text class="summary-label">{{ field.label }}</text>
|
|
|
+ <text class="summary-value">{{
|
|
|
+ formatValue(item[field.key])
|
|
|
+ }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </z-paging>
|
|
|
+
|
|
|
+ <UniFab
|
|
|
+ :pattern="fabPattern"
|
|
|
+ horizontal="right"
|
|
|
+ vertical="bottom"
|
|
|
+ direction="horizontal"
|
|
|
+ :popMenu="false"
|
|
|
+ @fabClick="openFilterPopup" />
|
|
|
+
|
|
|
+ <uni-popup
|
|
|
+ ref="filterPopup"
|
|
|
+ type="bottom"
|
|
|
+ background-color="#fff"
|
|
|
+ border-radius="10px 10px 0 0">
|
|
|
+ <view class="filter-popup">
|
|
|
+ <view class="filter-header">
|
|
|
+ <text class="filter-action" @click="closeFilterPopup">取消</text>
|
|
|
+ <text class="filter-title">筛选条件</text>
|
|
|
+ <text class="filter-action primary" @click="applyFilter">确定</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="filter-body">
|
|
|
+ <view class="filter-item">
|
|
|
+ <view class="filter-label">项目</view>
|
|
|
+ <uni-easyinput
|
|
|
+ v-model="filterForm.contractName"
|
|
|
+ :inputBorder="false"
|
|
|
+ :styles="inputStyles"
|
|
|
+ placeholder="请输入项目" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view v-if="currentMode === 'team'" class="filter-item">
|
|
|
+ <view class="filter-label">任务</view>
|
|
|
+ <uni-easyinput
|
|
|
+ v-model="filterForm.taskName"
|
|
|
+ :inputBorder="false"
|
|
|
+ :styles="inputStyles"
|
|
|
+ placeholder="请输入任务" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="filter-item">
|
|
|
+ <view class="filter-label">创建时间</view>
|
|
|
+ <uni-datetime-picker
|
|
|
+ v-model="filterForm.createTime"
|
|
|
+ type="datetimerange"
|
|
|
+ return-type="string"
|
|
|
+ :border="false"
|
|
|
+ placeholder="请选择" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view v-if="currentMode === 'well'" class="filter-item well-tree-item">
|
|
|
+ <view class="filter-label">井</view>
|
|
|
+ <uni-easyinput
|
|
|
+ v-model="wellSearchKey"
|
|
|
+ :inputBorder="false"
|
|
|
+ :styles="inputStyles"
|
|
|
+ placeholder="请输入井名" />
|
|
|
+ <view class="dept-selected">
|
|
|
+ {{ selectedWellText || "请选择" }}
|
|
|
+ </view>
|
|
|
+ <view class="tree well-tree">
|
|
|
+ <DaTree
|
|
|
+ :key="wellTreeRenderKey"
|
|
|
+ :data="wellTreeData"
|
|
|
+ labelField="label"
|
|
|
+ valueField="value"
|
|
|
+ childrenField="children"
|
|
|
+ defaultExpandAll
|
|
|
+ checkedDisabled
|
|
|
+ :filterValue="wellSearchKey"
|
|
|
+ :defaultCheckedKeys="selectedWellKey"
|
|
|
+ @change="handleWellTreeChange" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view v-if="currentMode === 'team'" class="filter-item dept-item">
|
|
|
+ <view class="filter-label">队伍</view>
|
|
|
+ <view class="dept-selected">
|
|
|
+ {{ selectedDeptName || "请选择" }}
|
|
|
+ </view>
|
|
|
+ <view class="tree">
|
|
|
+ <DaTree
|
|
|
+ :data="treeData"
|
|
|
+ labelField="name"
|
|
|
+ valueField="id"
|
|
|
+ disabledField="disabled"
|
|
|
+ defaultExpandAll
|
|
|
+ checkedDisabled
|
|
|
+ :defaultCheckedKeys="filterForm.deptId"
|
|
|
+ @change="handleTreeChange" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="filter-footer">
|
|
|
+ <button class="filter-button reset" @click="resetFilter">重置</button>
|
|
|
+ <button class="filter-button" type="primary" @click="applyFilter"
|
|
|
+ >搜索</button
|
|
|
+ >
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </uni-popup>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { computed, onMounted, reactive, ref } from "vue";
|
|
|
+import dayjs from "dayjs";
|
|
|
+import DaTree from "@/components/da-tree/index.vue";
|
|
|
+import UniFab from "@/uni_modules/uni-fab/components/uni-fab/uni-fab.vue";
|
|
|
+import UniTooltip from "@/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue";
|
|
|
+import {
|
|
|
+ getTaskWellNames,
|
|
|
+ getRuiduReportTeamPage,
|
|
|
+ getRuiduReportWellPage,
|
|
|
+} from "@/api/ruiduReport";
|
|
|
+import { specifiedSimpleDepts } from "@/api";
|
|
|
+import { getDeptId } from "@/utils/auth";
|
|
|
+import { useDataDictStore } from "@/store/modules/dataDict";
|
|
|
+
|
|
|
+const paging = ref(null);
|
|
|
+const filterPopup = ref(null);
|
|
|
+const dataList = ref([]);
|
|
|
+const deptOptions = ref([]);
|
|
|
+const treeData = ref([]);
|
|
|
+const wellTreeData = ref([]);
|
|
|
+const wellTreeRenderKey = ref(0);
|
|
|
+const selectedWellKey = ref("");
|
|
|
+const wellSearchKey = ref("");
|
|
|
+const expandedSummaryIds = ref([]);
|
|
|
+const currentMode = ref("well");
|
|
|
+const dictStore = useDataDictStore();
|
|
|
+const rdStatusDict = reactive({});
|
|
|
+
|
|
|
+const modeOptions = [
|
|
|
+ { label: "井", value: "well" },
|
|
|
+ { label: "队伍", value: "team" },
|
|
|
+];
|
|
|
+
|
|
|
+const summaryFields = [
|
|
|
+ { label: "桥塞", key: "groupIdBridgePlug" },
|
|
|
+ { label: "趟数", key: "groupIdRunCount" },
|
|
|
+ { label: "井数", key: "groupIdCumulativeWorkingWell" },
|
|
|
+ { label: "小时H", key: "groupIdHourCount" },
|
|
|
+ { label: "油耗L", key: "groupIdFuel" },
|
|
|
+ { label: "水方量", key: "groupIdWaterVolume" },
|
|
|
+ { label: "泵车台次", key: "groupIdPumpTrips" },
|
|
|
+ { label: "段数", key: "groupIdCumulativeWorkingLayers" },
|
|
|
+ { label: "仪表/混砂", key: "groupIdMixSand" },
|
|
|
+];
|
|
|
+
|
|
|
+const inputStyles = reactive({
|
|
|
+ backgroundColor: "#f7f8fa",
|
|
|
+ color: "#333",
|
|
|
+});
|
|
|
+
|
|
|
+const fabPattern = reactive({
|
|
|
+ color: "#fff",
|
|
|
+ backgroundColor: "#fff",
|
|
|
+ selectedColor: "#fff",
|
|
|
+ buttonColor: "#004098",
|
|
|
+ iconColor: "#fff",
|
|
|
+ icon: "search",
|
|
|
+});
|
|
|
+
|
|
|
+const getDefaultCreateTime = () => {
|
|
|
+ const end = dayjs().endOf("day").format("YYYY-MM-DD HH:mm:ss");
|
|
|
+ const start = dayjs().subtract(6, "day").startOf("day").format("YYYY-MM-DD HH:mm:ss");
|
|
|
+ return [start, end];
|
|
|
+};
|
|
|
+
|
|
|
+const filterForm = reactive({
|
|
|
+ contractName: "",
|
|
|
+ taskName: "",
|
|
|
+ wellName: "",
|
|
|
+ deptId: getDeptId(),
|
|
|
+ createTime: getDefaultCreateTime(),
|
|
|
+});
|
|
|
+
|
|
|
+const selectedDeptName = computed(() => {
|
|
|
+ const current = deptOptions.value.find(
|
|
|
+ (item) => String(item.value) === String(filterForm.deptId)
|
|
|
+ );
|
|
|
+ return current?.text || "";
|
|
|
+});
|
|
|
+
|
|
|
+const selectedWellText = computed(() => {
|
|
|
+ if (filterForm.wellName) return `井号:${filterForm.wellName}`;
|
|
|
+ if (filterForm.contractName) return `项目:${filterForm.contractName}`;
|
|
|
+ return "";
|
|
|
+});
|
|
|
+
|
|
|
+const handleTree = (
|
|
|
+ data,
|
|
|
+ id = "id",
|
|
|
+ parentId = "parentId",
|
|
|
+ children = "children"
|
|
|
+) => {
|
|
|
+ if (!Array.isArray(data)) return [];
|
|
|
+
|
|
|
+ const childrenListMap = {};
|
|
|
+ const nodeIds = {};
|
|
|
+ const tree = [];
|
|
|
+
|
|
|
+ for (const item of data) {
|
|
|
+ const itemParentId = item[parentId];
|
|
|
+ if (childrenListMap[itemParentId] == null) {
|
|
|
+ childrenListMap[itemParentId] = [];
|
|
|
+ }
|
|
|
+ nodeIds[item[id]] = item;
|
|
|
+ childrenListMap[itemParentId].push(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (const item of data) {
|
|
|
+ if (nodeIds[item[parentId]] == null) {
|
|
|
+ tree.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const adaptToChildrenList = (node) => {
|
|
|
+ if (childrenListMap[node[id]] != null) {
|
|
|
+ node[children] = childrenListMap[node[id]];
|
|
|
+ }
|
|
|
+ if (node[children]) {
|
|
|
+ node[children].forEach(adaptToChildrenList);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ tree.forEach(adaptToChildrenList);
|
|
|
+ return tree;
|
|
|
+};
|
|
|
+
|
|
|
+const sortDeptTree = (nodes) => {
|
|
|
+ if (!Array.isArray(nodes)) return [];
|
|
|
+
|
|
|
+ return [...nodes]
|
|
|
+ .sort((a, b) => (a.sort ?? 999999) - (b.sort ?? 999999))
|
|
|
+ .map((node) => ({
|
|
|
+ ...node,
|
|
|
+ children: sortDeptTree(node.children),
|
|
|
+ }));
|
|
|
+};
|
|
|
+
|
|
|
+const loadDeptOptions = async () => {
|
|
|
+ try {
|
|
|
+ const response = await specifiedSimpleDepts(getDeptId());
|
|
|
+ const list = response?.data || [];
|
|
|
+ deptOptions.value = list.map((item) => ({
|
|
|
+ text: item.name,
|
|
|
+ value: item.id,
|
|
|
+ }));
|
|
|
+ treeData.value = sortDeptTree(handleTree(list));
|
|
|
+ } catch (error) {
|
|
|
+ treeData.value = [];
|
|
|
+ deptOptions.value = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const loadWellOptions = async () => {
|
|
|
+ try {
|
|
|
+ const response = await getTaskWellNames({
|
|
|
+ companyId: 163,
|
|
|
+ wellName: "",
|
|
|
+ });
|
|
|
+ const list = response?.data || [];
|
|
|
+ const parentMap = new Map();
|
|
|
+ const tree = [];
|
|
|
+
|
|
|
+ list.forEach((item) => {
|
|
|
+ if (item.type === "1") {
|
|
|
+ const node = {
|
|
|
+ label: item.projectName,
|
|
|
+ value: `project-${item.projectId}`,
|
|
|
+ type: "1",
|
|
|
+ rawData: item,
|
|
|
+ children: [],
|
|
|
+ };
|
|
|
+ parentMap.set(item.projectId, node);
|
|
|
+ tree.push(node);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ list.forEach((item) => {
|
|
|
+ if (item.type === "2") {
|
|
|
+ const parent = parentMap.get(item.projectId);
|
|
|
+ const node = {
|
|
|
+ label: item.wellName,
|
|
|
+ value: `well-${item.projectId}-${item.wellName}`,
|
|
|
+ type: "2",
|
|
|
+ rawData: item,
|
|
|
+ };
|
|
|
+ if (parent) {
|
|
|
+ parent.children.push(node);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ wellTreeData.value = tree;
|
|
|
+ } catch (error) {
|
|
|
+ wellTreeData.value = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const queryList = (pageNo, pageSize) => {
|
|
|
+ const request =
|
|
|
+ currentMode.value === "well"
|
|
|
+ ? getRuiduReportTeamPage({
|
|
|
+ pageNo,
|
|
|
+ pageSize,
|
|
|
+ contractName: filterForm.contractName,
|
|
|
+ taskName: filterForm.wellName,
|
|
|
+ createTime: filterForm.createTime,
|
|
|
+ })
|
|
|
+ : getRuiduReportWellPage({
|
|
|
+ pageNo,
|
|
|
+ pageSize,
|
|
|
+ deptId: filterForm.deptId,
|
|
|
+ contractName: filterForm.contractName,
|
|
|
+ taskName: filterForm.taskName,
|
|
|
+ createTime: filterForm.createTime,
|
|
|
+ });
|
|
|
+
|
|
|
+ request
|
|
|
+ .then((res) => {
|
|
|
+ const list = res.data?.list || [];
|
|
|
+ const summaryIds = list
|
|
|
+ .filter((item) => item.lastGroupIdFlag)
|
|
|
+ .map((item) => String(item.id));
|
|
|
+ expandedSummaryIds.value =
|
|
|
+ pageNo === 1
|
|
|
+ ? summaryIds
|
|
|
+ : Array.from(new Set([...expandedSummaryIds.value, ...summaryIds]));
|
|
|
+ paging.value?.complete(list);
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ paging.value?.complete(false);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const switchMode = (mode, reload = true) => {
|
|
|
+ if (currentMode.value === mode) return;
|
|
|
+ currentMode.value = mode;
|
|
|
+ expandedSummaryIds.value = [];
|
|
|
+ if (reload) {
|
|
|
+ paging.value?.reload();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const openFilterPopup = () => {
|
|
|
+ filterPopup.value?.open();
|
|
|
+};
|
|
|
+
|
|
|
+const closeFilterPopup = () => {
|
|
|
+ filterPopup.value?.close();
|
|
|
+};
|
|
|
+
|
|
|
+const applyFilter = () => {
|
|
|
+ closeFilterPopup();
|
|
|
+ paging.value?.reload();
|
|
|
+};
|
|
|
+
|
|
|
+const resetFilter = () => {
|
|
|
+ filterForm.contractName = "";
|
|
|
+ filterForm.taskName = "";
|
|
|
+ filterForm.wellName = "";
|
|
|
+ filterForm.deptId = getDeptId();
|
|
|
+ filterForm.createTime = getDefaultCreateTime();
|
|
|
+ selectedWellKey.value = "";
|
|
|
+ wellSearchKey.value = "";
|
|
|
+ wellTreeRenderKey.value += 1;
|
|
|
+};
|
|
|
+
|
|
|
+const handleTreeChange = (value) => {
|
|
|
+ filterForm.deptId = value;
|
|
|
+};
|
|
|
+
|
|
|
+const handleWellTreeChange = (value, item) => {
|
|
|
+ const node = item?.originItem;
|
|
|
+ selectedWellKey.value = value;
|
|
|
+ if (node?.type === "1") {
|
|
|
+ filterForm.contractName = node.rawData?.projectName || node.label || "";
|
|
|
+ filterForm.wellName = "";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (node?.type === "2") {
|
|
|
+ filterForm.wellName = node.rawData?.wellName || node.label || "";
|
|
|
+ filterForm.contractName = "";
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const toggleSummary = (id) => {
|
|
|
+ const key = String(id);
|
|
|
+ if (expandedSummaryIds.value.includes(key)) {
|
|
|
+ expandedSummaryIds.value = expandedSummaryIds.value.filter(
|
|
|
+ (item) => item !== key
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ expandedSummaryIds.value = [...expandedSummaryIds.value, key];
|
|
|
+};
|
|
|
+
|
|
|
+const isSummaryExpanded = (id) => {
|
|
|
+ return expandedSummaryIds.value.includes(String(id));
|
|
|
+};
|
|
|
+
|
|
|
+const formatDate = (time) => {
|
|
|
+ return time ? dayjs(time).format("YYYY-MM-DD") : "--";
|
|
|
+};
|
|
|
+
|
|
|
+const formatValue = (value) => {
|
|
|
+ if (value === 0) return "0";
|
|
|
+ return value ?? "--";
|
|
|
+};
|
|
|
+
|
|
|
+const loadRdStatusDict = async () => {
|
|
|
+ if (dictStore.dataDict.length <= 0) {
|
|
|
+ await dictStore.loadDataDictList();
|
|
|
+ }
|
|
|
+ dictStore.getStrDictOptions("rdStatus").forEach((item) => {
|
|
|
+ rdStatusDict[item.value] = item.label;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ loadDeptOptions();
|
|
|
+ loadWellOptions();
|
|
|
+ loadRdStatusDict();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.report-page {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ min-height: 0;
|
|
|
+ padding: 10px !important;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.mode-tabs {
|
|
|
+ flex-shrink: 0;
|
|
|
+ height: 42px;
|
|
|
+ padding: 4px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ display: flex;
|
|
|
+ gap: 4px;
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.mode-tab {
|
|
|
+ flex: 1;
|
|
|
+ height: 34px;
|
|
|
+ line-height: 34px;
|
|
|
+ text-align: center;
|
|
|
+ color: #5c6675;
|
|
|
+ font-size: 14px;
|
|
|
+ border-radius: 6px;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ color: #ffffff;
|
|
|
+ background: #004098;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.report-paging {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ height: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.report-list {
|
|
|
+ padding: 8px 6px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.report-card {
|
|
|
+ margin-bottom: 14px;
|
|
|
+ overflow: hidden;
|
|
|
+ background: #ffffff;
|
|
|
+ border: 1px solid rgba(0, 64, 152, 0.08);
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 6px 18px rgba(35, 54, 79, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.card-header {
|
|
|
+ min-height: 52px;
|
|
|
+ padding: 14px 16px 10px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border-bottom: 1px solid #edf1f7;
|
|
|
+}
|
|
|
+
|
|
|
+.card-date {
|
|
|
+ color: #000000;
|
|
|
+ font-weight: 700;
|
|
|
+ font-size: 18px;
|
|
|
+ line-height: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.status-tag {
|
|
|
+ max-width: 112px;
|
|
|
+ height: 24px;
|
|
|
+ line-height: 24px;
|
|
|
+ padding: 0 9px;
|
|
|
+ border-radius: 12px;
|
|
|
+ background: #004098;
|
|
|
+ color: #ffffff;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.card-body {
|
|
|
+ padding: 12px 16px 6px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.field-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ min-height: 30px;
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.field-label {
|
|
|
+ width: 70px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ color: #000000;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 13px;
|
|
|
+ line-height: 22px;
|
|
|
+}
|
|
|
+
|
|
|
+.field-value {
|
|
|
+ min-width: 0;
|
|
|
+ flex: 1;
|
|
|
+ color: #233044;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 22px;
|
|
|
+}
|
|
|
+
|
|
|
+.summary-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.summary-item {
|
|
|
+ min-width: 0;
|
|
|
+ padding: 9px 10px;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #f7f8fa;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.summary-label {
|
|
|
+ display: block;
|
|
|
+ color: #7a8494;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 17px;
|
|
|
+}
|
|
|
+
|
|
|
+.summary-value {
|
|
|
+ display: block;
|
|
|
+ margin-top: 4px;
|
|
|
+ color: #233044;
|
|
|
+ font-weight: 700;
|
|
|
+ font-size: 15px;
|
|
|
+ line-height: 20px;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.brief-row {
|
|
|
+ padding-top: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.brief-row :deep(.uni-tooltip) {
|
|
|
+ min-width: 0;
|
|
|
+ flex: 1;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+.brief-text {
|
|
|
+ display: block;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.summary-panel {
|
|
|
+ margin: 0 16px 14px;
|
|
|
+ border: 1px solid rgba(0, 64, 152, 0.12);
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.summary-header {
|
|
|
+ height: 38px;
|
|
|
+ padding: 0 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ color: #004098;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 14px;
|
|
|
+ background: #eef5ff;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.summary-grid {
|
|
|
+ padding: 10px;
|
|
|
+ background: #ffffff;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-popup {
|
|
|
+ height: 86vh;
|
|
|
+ max-height: 86vh;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ background: #fff;
|
|
|
+ padding-bottom: env(safe-area-inset-bottom);
|
|
|
+}
|
|
|
+
|
|
|
+.filter-header {
|
|
|
+ height: 48px;
|
|
|
+ padding: 0 16px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-title {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ font-size: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-action {
|
|
|
+ min-width: 48px;
|
|
|
+ color: #666;
|
|
|
+ font-size: 14px;
|
|
|
+
|
|
|
+ &.primary {
|
|
|
+ color: #004098;
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.filter-body {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 12px 16px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-item {
|
|
|
+ margin-bottom: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-label {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ color: #333;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.dept-selected {
|
|
|
+ min-height: 36px;
|
|
|
+ line-height: 36px;
|
|
|
+ padding: 0 10px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ background: #f7f8fa;
|
|
|
+ color: #666;
|
|
|
+ border-radius: 4px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.tree {
|
|
|
+ height: 360px;
|
|
|
+ overflow: hidden;
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-footer {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ padding: 10px 16px 14px;
|
|
|
+ border-top: 1px solid #f0f0f0;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-button {
|
|
|
+ flex: 1;
|
|
|
+ height: 38px;
|
|
|
+ line-height: 38px;
|
|
|
+ font-size: 14px;
|
|
|
+ margin: 0;
|
|
|
+
|
|
|
+ &.reset {
|
|
|
+ color: #004098;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #004098;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|