From 4aa86b1ec441c4e358e1cc488d8f021fb80f1355 Mon Sep 17 00:00:00 2001 From: riku <risaku@163.com> Date: 星期三, 17 九月 2025 17:34:35 +0800 Subject: [PATCH] 2025.9.17 数据产品(待完成) --- src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue | 318 +++++++++++++++++++++++++++++++--- src/api/index.js | 4 src/components.d.ts | 4 src/views/fysp/data-product/base-data-product/ProdManage.vue | 48 +++- src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue | 83 +++++++- src/utils/css-util.js | 21 ++ src/router/index.js | 8 src/views/fysp/data-product/base-data-product/components/ProdDownload.vue | 2 src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue | 7 9 files changed, 422 insertions(+), 73 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index 7b8275a..bf34904 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -13,8 +13,8 @@ let ip2_file = 'https://fyami.com.cn/'; if (debug) { - // ip1 = 'http://192.168.0.103:9001/'; - ip1 = 'http://localhost:9001/'; + ip1 = 'http://192.168.0.103:9001/'; + // ip1 = 'http://localhost:9001/'; // ip1_file = 'http://192.168.0.138:8080/'; // ip2 = 'http://192.168.0.138:8080/'; // ip2_file = 'https://fyami.com.cn/'; diff --git a/src/components.d.ts b/src/components.d.ts index 6076c52..fdde43f 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -20,11 +20,8 @@ ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] ElButton: typeof import('element-plus/es')['ElButton'] - ElCalendar: typeof import('element-plus/es')['ElCalendar'] ElCard: typeof import('element-plus/es')['ElCard'] ElCascader: typeof import('element-plus/es')['ElCascader'] - ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] - ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] ElCol: typeof import('element-plus/es')['ElCol'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] @@ -45,7 +42,6 @@ ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] ElInput: typeof import('element-plus/es')['ElInput'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] - ElLink: typeof import('element-plus/es')['ElLink'] ElMain: typeof import('element-plus/es')['ElMain'] ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] diff --git a/src/router/index.js b/src/router/index.js index 925417d..5d81136 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -93,7 +93,7 @@ // 鍩虹浜у搧-鍦烘櫙娓呭崟 path: 'scene', name: 'ProdSceneInfo', - meta: { keepAlive: true, key: 'ProdManage' }, + meta: { keepAlive: false, key: 'ProdManage' }, component: () => import( '@/views/fysp/data-product/base-data-product/ProdSceneInfo.vue' @@ -103,7 +103,7 @@ // 鍩虹浜у搧-瑙勮寖鎬ц瘎浼� path: 'evaluate', name: 'ProdEvaluationInfo', - meta: { keepAlive: true, key: 'ProdManage' }, + meta: { keepAlive: false, key: 'ProdManage' }, component: () => import( '@/views/fysp/data-product/base-data-product/ProdEvaluationInfo.vue' @@ -113,7 +113,7 @@ // 鍩虹浜у搧-宸℃煡淇℃伅 path: 'inspection', name: 'ProdInspectionInfo', - meta: { keepAlive: true, key: 'ProdManage' }, + meta: { keepAlive: false, key: 'ProdManage' }, component: () => import( '@/views/fysp/data-product/base-data-product/ProdInspectionInfo.vue' @@ -123,7 +123,7 @@ // 鍩虹浜у搧-鐩戞祴鏁版嵁 path: 'monitordata', name: 'ProdMonitorDataInfo', - meta: { keepAlive: true, key: 'ProdManage' }, + meta: { keepAlive: false, key: 'ProdManage' }, component: () => import( '@/views/fysp/data-product/base-data-product/ProdMonitorDataInfo.vue' diff --git a/src/utils/css-util.js b/src/utils/css-util.js new file mode 100644 index 0000000..bf826b1 --- /dev/null +++ b/src/utils/css-util.js @@ -0,0 +1,21 @@ +/** + * 鎻愬彇css涓甫鏈塩alc鍑芥暟鐨勮〃杈惧紡鐨勫弬鏁伴儴鍒� + * @param {String} str css涓甫鏈塩alc鍑芥暟鐨勮〃杈惧紡 + */ +function unCalc(str) { + if (str.startsWith('calc(')) { + let _str = str.replace('calc(', '') + _str = _str.replace(/\)/g, (match, offset, string) => { + if (offset === string.lastIndexOf(match)) { + return '' + } else { + return match + } + }) + return _str + } else { + return str + } +} + +export { unCalc } diff --git a/src/views/fysp/data-product/base-data-product/ProdManage.vue b/src/views/fysp/data-product/base-data-product/ProdManage.vue index 8ca321f..0f1ff6b 100644 --- a/src/views/fysp/data-product/base-data-product/ProdManage.vue +++ b/src/views/fysp/data-product/base-data-product/ProdManage.vue @@ -1,19 +1,22 @@ <template> - <el-menu - default-active="scene" - ellipsis - mode="horizontal" - style="max-width: 600px" - > - <el-menu-item - v-for="item in menu" - :key="item.path" - :index="item.path" - @click="navPage" - >{{ item.name }}</el-menu-item + <el-affix> + <el-menu + ref="menuRef" + default-active="scene" + ellipsis + mode="horizontal" + style="max-width: 600px; background-color: aliceblue" > - </el-menu> - <router-view v-slot="{ Component, route }"> + <el-menu-item + v-for="item in menu" + :key="item.path" + :index="item.path" + @click="navPage" + >{{ item.name }}</el-menu-item + > + </el-menu> + </el-affix> + <router-view v-slot="{ Component, route }" :style="'height: ' + height"> <keep-alive> <component v-if="route.meta.keepAlive" @@ -25,11 +28,14 @@ </router-view> </template> <script setup> -import { ref, onMounted } from 'vue'; +import { ref, onMounted, provide } from 'vue'; import { useRouter, useRoute } from 'vue-router'; const router = useRouter(); const route = useRoute(); + +const menuRef = ref(null); +const height = ref('calc(100vh - 64px)'); const menu = ref([ { @@ -58,5 +64,17 @@ }); } }; + +function calcTableHeight() { + const h = menuRef.value.$el.offsetHeight; + return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`; +} + +onMounted(() => { + height.value = calcTableHeight(); +}); + +// 鎻愪緵缁欏唴閮ㄧ粍浠惰鍥炬渶澶ч珮搴� +provide('viewHeight', height); </script> <style scoped></style> diff --git a/src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue b/src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue index 7412ddf..0f817a5 100644 --- a/src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue +++ b/src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue @@ -1,31 +1,90 @@ <template> - <BaseProdProcess :active="active"> - <template #step1> + <BaseProdProcess + v-model:active="active" + @onStep1="onStep1" + :loading="loading" + > + <!-- <template #step1> <ProdQueryOpt :loading="loading" @submit="onSearch"> </ProdQueryOpt> + </template> --> + <template #step2="{ contentHeight }"> + <el-table + :data="tableData" + v-loading="loading" + :height="viewHeight" + table-layout="fixed" + :show-overflow-tooltip="true" + size="small" + border + > + <el-table-column fixed="left" prop="index" label="缂栧彿" width="40"> + </el-table-column> + <el-table-column + fixed="left" + prop="name" + label="鍚嶇О" + :show-overflow-tooltip="true" + min-width="200" + > + </el-table-column> + <el-table-column prop="type" label="绫诲瀷" width="50" /> + <el-table-column prop="status" label="鐘舵��" width="60" /> + <el-table-column prop="stage" label="闃舵" width="90" /> + <!-- <el-table-column prop="provincename" label="鐪�" width="90" /> + <el-table-column prop="cityname" label="甯�" width="90" /> + <el-table-column prop="districtname" label="鍖哄幙" width="90" /> --> + <el-table-column prop="townname" label="琛楅亾" width="110" /> + <el-table-column prop="location" label="鍦板潃" width="200" /> + <!-- <el-table-column prop="longitude" label="缁忓害" width="110" /> + <el-table-column prop="latitude" label="绾害" width="110" /> --> + <!-- <el-table-column + prop="updatedate" + label="鏇存柊鏃堕棿" + width="140" + :formatter="timeFormat" + /> --> + </el-table> </template> - <template #step2></template> - <template #step3></template> + <!-- <template #step3></template> --> </BaseProdProcess> </template> <script setup> -import { ref } from 'vue'; +import { ref, inject } from 'vue'; +import dayjs from 'dayjs'; import BaseProdProcess from '@/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue'; import ProdQueryOpt from '@/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue'; +import dataprodbaseApi from '@/api/fysp/dataprodbaseApi.js'; const active = ref(1); -const loading = ref(false) +const loading = ref(false); +const tableData = ref([]); +const viewHeight = inject('viewHeight'); function changeActive() { active.value++; active.value = active.value > 3 ? 1 : active.value; } -function onSearch(opt) { - console.log(opt); +function onStep1(opt) { loading.value = true; - setTimeout(() => { - changeActive() - loading.value = false; - }, 1000); + dataprodbaseApi + .fetchProdSceneInfo(opt) + .then((res) => { + if (res.success) { + tableData.value = res.data.map((item) => ({ + status: item.status, + stage: item.stage, + ...item.scene + })); + } + changeActive(); + }) + .finally(() => { + loading.value = false; + }); +} + +function timeFormat(row, column, cellValue, index) { + return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss'); } </script> diff --git a/src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue b/src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue index fb18256..356a41d 100644 --- a/src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue +++ b/src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue @@ -1,62 +1,310 @@ <template> - <!-- <el-button type="primary" @click="changeActive">change</el-button> --> - <!-- <el-row> - <el-col - :span="active == 1 ? 16 : 4" - :class="active == 1 ? 'prod-active' : 'prod-inactive'" - >step1</el-col> - <el-col - :span="active == 2 ? 16 : 4" - :class="active == 2 ? 'prod-active' : 'prod-inactive'" - >step2</el-col> - <el-col - :span="active == 3 ? 16 : 4" - :class="active == 3 ? 'prod-active' : 'prod-inactive'" - >step3</el-col> - </el-row> --> <el-row> - <div :class="active == 1 ? 'prod-active' : 'prod-inactive'"> - <slot name="step1"></slot> + <!-- 姝ラ1 鏁版嵁浜у搧鐢熸垚閫夐」 --> + <div :class="active == 1 ? 'prod-active' : 'prod-inactive'" ref="step1Ref"> + <transition + name="el-fade-in" + @after-leave="handleTransitionContentEnd(1)" + > + <div v-show="showStep1Content"> + <template v-if="$slots.step1"> + <slot name="step1"></slot> + </template> + <template v-else> + <ProdQueryOpt :loading="loading" @submit="onSearch"> </ProdQueryOpt> + </template> + </div> + </transition> + <transition + name="el-fade-in" + @after-leave="handleTransitionThumbnailEnd(1)" + > + <div + v-show="showStep1Thumbnail" + class="prod-thumbnail-wrapper" + :style="{ height: viewHeight }" + @click="changeActive(1)" + > + <div class="prod-thumbnail">鈶犱慨鏀归�夐」</div> + </div> + </transition> </div> - <div :class="active == 2 ? 'prod-active' : 'prod-inactive'"> - <slot name="step2"></slot> + <!-- 姝ラ2 鏁版嵁浜у搧缁撴灉棰勮 --> + <div :class="active == 2 ? 'prod-active' : 'prod-inactive'" ref="step2Ref"> + <transition + name="el-fade-in" + @after-leave="handleTransitionContentEnd(2)" + > + <div v-show="showStep2Content"> + <div ref="titleRef" class="prod-title"> + <el-text tag="b" size="large">鏁版嵁浜у搧棰勮</el-text> + </div> + <slot name="step2" :contentHeight="contentHeight"></slot> + </div> + </transition> + <transition + name="el-fade-in" + @after-leave="handleTransitionThumbnailEnd(2)" + > + <div + v-show="showStep2Thumbnail" + class="prod-thumbnail-wrapper" + :style="{ height: viewHeight }" + @click="changeActive(2)" + > + <div + :class=" + 'prod-thumbnail prod-thumbnail_middle ' + + (active < 2 ? 'prod-thumbnail-disabled' : '') + " + > + 鈶℃暟鎹骇鍝侀瑙� + </div> + </div> + </transition> </div> - <div :class="active == 3 ? 'prod-active' : 'prod-inactive'"> - <slot name="step3"></slot> + <!-- 姝ラ3 鏁版嵁浜у搧琛ㄥ崟涓嬭浇 --> + <div :class="active == 3 ? 'prod-active' : 'prod-inactive'" ref="step3Ref"> + <transition + name="el-fade-in" + @after-leave="handleTransitionContentEnd(3)" + > + <div v-show="showStep3Content"> + <template v-if="$slots.step3"> + <slot name="step3"></slot> + </template> + <template v-else> + <ProdDownload></ProdDownload> + </template> + </div> + </transition> + <transition + name="el-fade-in" + @after-leave="handleTransitionThumbnailEnd(3)" + > + <div + v-show="showStep3Thumbnail" + class="prod-thumbnail-wrapper" + :style="{ height: viewHeight }" + @click="changeActive(3)" + > + <div + :class=" + 'prod-thumbnail prod-thumbnail_end ' + + (active < 3 ? 'prod-thumbnail-disabled' : '') + " + > + 鈶㈡暟鎹骇鍝佷笅杞� + </div> + </div> + </transition> </div> </el-row> </template> <script setup> -import { ref } from 'vue'; +import { computed, inject, ref, watch, onMounted } from 'vue'; +import { unCalc } from '@/utils/css-util'; +import ProdQueryOpt from '@/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue'; +import ProdDownload from '@/views/fysp/data-product/base-data-product/components/ProdDownload.vue'; const props = defineProps({ active: { type: Number, default: 1 + }, + loading: { + type: Boolean, + default: false } }); -// function changeActive() { -// active.value++; -// active.value = active.value > 3 ? 1 : active.value; -// } +const emit = defineEmits(['update:active', 'onStep1']); + +const viewHeight = inject('viewHeight'); + +const btnDisabled = ref(false); + +const titleRef = ref(null); +const contentHeight = ref('50vh'); + +function calContentHeight() { + console.log(titleRef.value.offsetHeight); + contentHeight.value = `calc(${unCalc(viewHeight.value)} - ${ + titleRef.value?.offsetHeight || 0 + }px)`; + console.log(contentHeight.value); + +} + +// 姝ラ寮曠敤 +const step1Ref = ref(null); +const step2Ref = ref(null); +const step3Ref = ref(null); + +// 鎺у埗鏄剧ず/闅愯棌鐨勭姸鎬� +const showStep1Content = ref(props.active === 1); +const showStep1Thumbnail = ref(props.active != 1); +const showStep2Content = ref(props.active === 2); +const showStep2Thumbnail = ref(props.active != 2); +const showStep3Content = ref(props.active === 3); +const showStep3Thumbnail = ref(props.active != 3); + +// 璁板綍鍔ㄧ敾鏄惁姝e湪杩涜涓� +const isAnimating = ref({}); + +// 鐩戝惉active鍙樺寲 +watch( + () => props.active, + (newActive, oldActive) => { + // 鏍囪鍔ㄧ敾寮�濮� + isAnimating.value[oldActive] = true; + isAnimating.value[newActive] = true; + + // 鍏堥殣钘忔墍鏈夊唴瀹癸紝绛夊緟鍔ㄧ敾缁撴潫鍚庡啀鏄剧ず姝g‘鐨勫唴瀹� + if (oldActive === 1) { + showStep1Content.value = false; + } else if (oldActive === 2) { + showStep2Content.value = false; + } else if (oldActive === 3) { + showStep3Content.value = false; + } + + if (newActive === 1) { + showStep1Thumbnail.value = false; + } else if (newActive === 2) { + showStep2Thumbnail.value = false; + } else if (newActive === 3) { + showStep3Thumbnail.value = false; + } + } +); + +// 澶勭悊鍔ㄧ敾缁撴潫浜嬩欢 +function handleTransitionThumbnailEnd(stepIndex) { + // 妫�鏌ュ姩鐢绘槸鍚︾‘瀹炵粨鏉燂紙閬垮厤閲嶅瑙﹀彂锛� + if (isAnimating.value[stepIndex]) { + isAnimating.value[stepIndex] = false; + + // setTimeout(() => { + // 鍔ㄧ敾缁撴潫鍚庯紝鏇存柊鏄剧ず鐘舵�� + if (stepIndex === 1) { + showStep1Content.value = props.active === 1; + } else if (stepIndex === 2) { + showStep2Content.value = props.active === 2; + } else if (stepIndex === 3) { + showStep3Content.value = props.active === 3; + } + // }, 50); + } +} + +function handleTransitionContentEnd(stepIndex) { + // 妫�鏌ュ姩鐢绘槸鍚︾‘瀹炵粨鏉燂紙閬垮厤閲嶅瑙﹀彂锛� + if (isAnimating.value[stepIndex]) { + isAnimating.value[stepIndex] = false; + + // setTimeout(() => { + // 鍔ㄧ敾缁撴潫鍚庯紝鏇存柊鏄剧ず鐘舵�� + if (stepIndex === 1) { + showStep1Thumbnail.value = props.active != 1; + } else if (stepIndex === 2) { + showStep2Thumbnail.value = props.active != 2; + } else if (stepIndex === 3) { + showStep3Thumbnail.value = props.active != 3; + } + // }, 50); + } +} + +function onSearch(opt) { + emit('onStep1', opt); +} +function changeActive(index) { + let isAnimate = false; + Object.values(isAnimating.value).forEach((item) => { + isAnimate = isAnimate || item; + }); + if (!isAnimate && !btnDisabled.value && props.active >= index) { + emit('update:active', index); + btnDisabled.value = true; + setTimeout(() => { + btnDisabled.value = false; + }, 500); + } + // emit('update:active', index); +} + +onMounted(() => { + calContentHeight(); +}); </script> <style scoped> .prod-active { - width: 66.667%; + /* width: 66.667%; */ + width: 90%; + transition: + width 0.5s ease, + box-shadow 0.3s ease; + /* background-color: #409eff; */ + margin: 5px 0; + border-radius: 4px; + box-shadow: + -3px 0 6px rgba(0, 0, 0, 0.1), + 3px 0 6px rgba(0, 0, 0, 0.1); +} + +.prod-inactive { + /* width: 16.667%; */ + width: 5%; transition: width 0.5s ease; - background-color: #409eff; - color: white; + /* background-color: #e4e7ed; */ margin: 5px 0; border-radius: 4px; } -.prod-inactive { - width: 16.667%; - transition: width 0.5s ease; +.prod-title { + padding: 10px; +} + +.prod-thumbnail-wrapper { + display: flex; + justify-content: center; + align-items: center; + padding: 0 2px; +} + +.prod-thumbnail { + height: 90%; + width: 100%; + background-color: #409eff; + color: white; + display: flex; + justify-content: center; + align-items: center; + writing-mode: vertical-rl; + text-orientation: upright; + letter-spacing: 8px; + font-size: 18px; + font-weight: 500; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + cursor: pointer; +} + +.prod-thumbnail_middle { + border-radius: 0px; +} + +.prod-thumbnail_end { + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.prod-thumbnail-disabled { background-color: #e4e7ed; - color: #606266; - margin: 5px 0; - border-radius: 4px; + color: #c0c4cc; + cursor: not-allowed; } </style> diff --git a/src/views/fysp/data-product/base-data-product/components/ProdDownload.vue b/src/views/fysp/data-product/base-data-product/components/ProdDownload.vue new file mode 100644 index 0000000..54642c5 --- /dev/null +++ b/src/views/fysp/data-product/base-data-product/components/ProdDownload.vue @@ -0,0 +1,2 @@ +<template>ProdDownload</template> +<script setup></script> diff --git a/src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue b/src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue index 55ed176..8f9618d 100644 --- a/src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue +++ b/src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue @@ -4,6 +4,7 @@ <div><el-text tag="b" size="large">浜у搧鐢熸垚閫夐」</el-text></div> </template> <SearchBar + v-show="active" ref="refSearchBar" :btn-show="false" :init="false" @@ -11,7 +12,7 @@ > </SearchBar> <template #footer> - <el-row justify="space-around"> + <el-row v-show="active" justify="space-around"> <el-button type="primary" size="default" @@ -31,6 +32,10 @@ loading: { type: Boolean, default: false + }, + active:{ + type: Boolean, + default: true } }); const emit = defineEmits(['submit']); -- Gitblit v1.9.3