|
|
@@ -1,1640 +0,0 @@
|
|
|
-<script lang="ts" setup>
|
|
|
-import { ref, onMounted, reactive } from 'vue'
|
|
|
-import { VueDraggable } from 'vue-draggable-plus'
|
|
|
-import PieChart from './components/PieChart.vue'
|
|
|
-import SellerChart from './components/SellerChart.vue'
|
|
|
-import BarChart from './components/BarChart.vue'
|
|
|
-import RevenueChart from './components/RevenueChart.vue'
|
|
|
-import RecentStatus from './components/RecentStatus.vue'
|
|
|
-import ScoringSystem from './components/ScoringSystem.vue'
|
|
|
-import TopMap from './components/TopMap.vue'
|
|
|
-import DashFilters from './components/DashFiters.vue'
|
|
|
-import CustomerFilter from './components/CustomerFilter.vue'
|
|
|
-import { useRouter } from 'vue-router'
|
|
|
-import { ElMessage } from 'element-plus'
|
|
|
-import { formatNumber } from '@/utils/tools'
|
|
|
-import { useFiltersStore, FiltersType } from '@/stores/modules/filtersList'
|
|
|
-import dayjs from 'dayjs'
|
|
|
-import { useUserStore } from '@/stores/modules/user'
|
|
|
-
|
|
|
-const userStore = useUserStore()
|
|
|
-const formatDate = userStore.dateFormat
|
|
|
-const filtersStore = useFiltersStore()
|
|
|
-
|
|
|
-const router = useRouter()
|
|
|
-const activeName = ref('first')
|
|
|
-const SaveVisible = ref(false)
|
|
|
-// 可拖拽模块的列表
|
|
|
-interface ManagementItem {
|
|
|
- title: string
|
|
|
- switchValue: boolean
|
|
|
- isRevenueDisplay: boolean
|
|
|
- text: string
|
|
|
- id: number
|
|
|
- title1: string
|
|
|
- title2: string
|
|
|
-}
|
|
|
-const Management = ref<ManagementItem[]>([])
|
|
|
-
|
|
|
-const changeCancel = (id: any) => {
|
|
|
- Management.value[id - 1].switchValue = false
|
|
|
-}
|
|
|
-
|
|
|
-const customerInfo = ref({
|
|
|
- customerCode: [],
|
|
|
- customerType: []
|
|
|
-})
|
|
|
-const changeCustomerData = (val: string[], type: string) => {
|
|
|
- customerInfo.value[type] = val
|
|
|
-}
|
|
|
-
|
|
|
-//RecentStatusList
|
|
|
-const RecentStatusList = ref()
|
|
|
-const pageInfo = ref({ pageNo: 1, pageSize: 10, total: 10000 })
|
|
|
-const isShowtitle1 = ref(true)
|
|
|
-// 点击tab
|
|
|
-const handleTabClick = (tab: any) => {
|
|
|
- if (tab.props.name == 'first') {
|
|
|
- isShowtitle1.value = true
|
|
|
- } else {
|
|
|
- isShowtitle1.value = false
|
|
|
- }
|
|
|
- GetTop10ODEcharts(top10DefaultData.value)
|
|
|
-}
|
|
|
-const kpiDefaultData = ref()
|
|
|
-const pendingDefaultData = ref()
|
|
|
-const recentDefaultData = ref()
|
|
|
-const etdDefaultData = ref()
|
|
|
-const containerDefaultData = ref()
|
|
|
-const top10DefaultData = ref()
|
|
|
-const co2OriginDefaultData = ref()
|
|
|
-const co2DestinationDefaultData = ref()
|
|
|
-const revenueDefaultData = ref()
|
|
|
-
|
|
|
-const GetDashboardData = () => {
|
|
|
- $api.getDashboardFilters({}).then((res: any) => {
|
|
|
- if (res.code == 200) {
|
|
|
- //给默认筛选条件赋值
|
|
|
- kpiDefaultData.value = res.data.KPIDefaulteData
|
|
|
- pendingDefaultData.value = res.data.PendingDefaultData
|
|
|
- recentDefaultData.value = res.data.RecentDefaultData
|
|
|
- etdDefaultData.value = res.data.ETDDefaultData
|
|
|
- containerDefaultData.value = res.data.ContainerefaultData
|
|
|
- top10DefaultData.value = res.data.Top10faultData
|
|
|
- co2OriginDefaultData.value = res.data.OriginCo2Top10faultData
|
|
|
- co2DestinationDefaultData.value = res.data.DestinationCo2Top10faultData
|
|
|
- revenueDefaultData.value = res.data.RevenueDefaultData
|
|
|
-
|
|
|
- nextTick(() => {
|
|
|
- getTableData(recentDefaultData.value, false)
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-// 获取表单数据
|
|
|
-const getTableData = (val: any, isPage?: any) => {
|
|
|
- const rc = isPage ? pageInfo.value.total : -1
|
|
|
- $api
|
|
|
- .GetDashboardData({
|
|
|
- cp: pageInfo.value.pageNo,
|
|
|
- ps: pageInfo.value.pageSize,
|
|
|
- rc,
|
|
|
- ...val
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- pageInfo.value.total = Number(res.data.rc)
|
|
|
- Management.value = res.data.Management
|
|
|
- RecentStatusList.value = res.data.searchData
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-const KPIobj = reactive({
|
|
|
- ETD_Title: '',
|
|
|
- ETDList: [],
|
|
|
- ETD_Radius: [],
|
|
|
- download_name: ''
|
|
|
-})
|
|
|
-const arrivalObj = reactive({
|
|
|
- ETD_Title: '',
|
|
|
- ETDList: [],
|
|
|
- ETD_Radius: [],
|
|
|
- download_name: ''
|
|
|
-})
|
|
|
-const kpiLoading = ref(false)
|
|
|
-const kpiArrivalLoading = ref(false)
|
|
|
-//查询KPI
|
|
|
-const GetKpiData = (val: any) => {
|
|
|
- kpiLoading.value = true
|
|
|
- kpiArrivalLoading.value = true
|
|
|
- kpiDefaultData.value = val
|
|
|
- // 获取KPI Departure图表数据
|
|
|
- $api
|
|
|
- .GetKPIEcharts({
|
|
|
- r_type: 'atd_r4',
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- KPIobj.ETD_Title = `{a|${res.data.title1}} {b|${res.data.title2}}`
|
|
|
- KPIobj.ETDList = res.data.ETDList
|
|
|
- KPIobj.ETD_Radius = res.data.ETD_Radius
|
|
|
- KPIobj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- kpiLoading.value = false
|
|
|
- })
|
|
|
- // 获取KPI Arrival图表数据
|
|
|
- $api
|
|
|
- .GetKPIEcharts({
|
|
|
- r_type: 'ata_r3',
|
|
|
- ...val
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- arrivalObj.ETD_Title = `{a|${res.data.title1}} {b|${res.data.title2}}`
|
|
|
- arrivalObj.ETDList = res.data.ETDList
|
|
|
- arrivalObj.ETD_Radius = res.data.ETD_Radius
|
|
|
- arrivalObj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- kpiArrivalLoading.value = false
|
|
|
- })
|
|
|
-}
|
|
|
-const pendingObj = reactive({
|
|
|
- ETD_Title: '',
|
|
|
- ETDList: [],
|
|
|
- ETD_Radius: [],
|
|
|
- download_name: ''
|
|
|
-})
|
|
|
-const pendingArrivalObj = reactive({
|
|
|
- ETD_Title: '',
|
|
|
- ETDList: [],
|
|
|
- ETD_Radius: [],
|
|
|
- download_name: ''
|
|
|
-})
|
|
|
-const pendingLoading = ref(false)
|
|
|
-const pendingArrivalLoading = ref(false)
|
|
|
-// 查询Pending
|
|
|
-const GetPendingEcharts = (val: any) => {
|
|
|
- // 获取Pending Departure图表数据
|
|
|
- pendingLoading.value = true
|
|
|
- pendingArrivalLoading.value = true
|
|
|
- pendingDefaultData.value = val
|
|
|
- $api
|
|
|
- .GetPendingEcharts({
|
|
|
- r_type: 'r4',
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- pendingObj.ETD_Title = `{a|${res.data.title1}} {b|${res.data.title2}}`
|
|
|
- pendingObj.ETDList = res.data.ETDList
|
|
|
- pendingObj.ETD_Radius = res.data.ETD_Radius
|
|
|
- pendingObj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- pendingLoading.value = false
|
|
|
- })
|
|
|
- // 获取Pending Arrival图表数据
|
|
|
- $api
|
|
|
- .GetPendingEcharts({
|
|
|
- r_type: 'r3',
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- pendingArrivalObj.ETD_Title = `{a|${res.data.title1}} {b|${res.data.title2}}`
|
|
|
- pendingArrivalObj.ETDList = res.data.ETDList
|
|
|
- pendingArrivalObj.ETD_Radius = res.data.ETD_Radius
|
|
|
- pendingArrivalObj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- pendingArrivalLoading.value = false
|
|
|
- })
|
|
|
-}
|
|
|
-const etdObj = reactive({
|
|
|
- ETD_Title: '',
|
|
|
- ETDList: [],
|
|
|
- ETD_Radius: [],
|
|
|
- download_name: ''
|
|
|
-})
|
|
|
-const etdLoading = ref(false)
|
|
|
-// 获取ETD/ETA 图表数据
|
|
|
-const GetETDEcharts = (val: any) => {
|
|
|
- etdLoading.value = true
|
|
|
- etdDefaultData.value = val
|
|
|
- $api
|
|
|
- .GetETDEcharts({
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- etdObj.ETD_Title = `{a|${res.data.ETD_Title}}`
|
|
|
- etdObj.ETDList = res.data.ETDList
|
|
|
- etdObj.ETD_Radius = res.data.ETD_Radius
|
|
|
- etdObj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- etdLoading.value = false
|
|
|
- })
|
|
|
-}
|
|
|
-// 获取ContainerCount
|
|
|
-const containerObj = reactive({
|
|
|
- bar_title: '',
|
|
|
- barList: [],
|
|
|
- barSeries: [],
|
|
|
- Max: 0,
|
|
|
- interval: 0,
|
|
|
- download_name: ''
|
|
|
-})
|
|
|
-const containerLoading = ref(false)
|
|
|
-const GetContainerCountEcharts = (val: any) => {
|
|
|
- containerLoading.value = true
|
|
|
- containerDefaultData.value = val
|
|
|
- $api
|
|
|
- .GetContainerCountEcharts({
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- containerObj.bar_title = res.data.ContainerCount_Title
|
|
|
- containerObj.barList = res.data.ContainerCountList
|
|
|
- containerObj.barSeries = res.data.ContainerCounSeries
|
|
|
- containerObj.Max = res.data.Max
|
|
|
- containerObj.interval = res.data.interval
|
|
|
- containerObj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- containerLoading.value = false
|
|
|
- })
|
|
|
-}
|
|
|
-const topdestinationinType = ref()
|
|
|
-const toporiginType = ref()
|
|
|
-//获取Top10 Origin/Destination
|
|
|
-const Top10Obj = reactive({
|
|
|
- OriginData: [],
|
|
|
- DestinationData: []
|
|
|
-})
|
|
|
-const top1OInterval = reactive({
|
|
|
- Max: 0,
|
|
|
- interval: 0
|
|
|
-})
|
|
|
-const top1OInterval_dest = reactive({
|
|
|
- Max: 0,
|
|
|
- interval: 0
|
|
|
-})
|
|
|
-const topOriginLoading = ref(false)
|
|
|
-const top10Originref = ref()
|
|
|
-const top10Destinationref = ref()
|
|
|
-const GetTop10ODEcharts = (val: any) => {
|
|
|
- topOriginLoading.value = true
|
|
|
- top10DefaultData.value = val
|
|
|
- $api
|
|
|
- .GetTop10ODEcharts({
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- if (isShowtitle1.value) {
|
|
|
- top10Originref.value[0].updataMapObj(res.data.toporiginMap)
|
|
|
- } else {
|
|
|
- top10Destinationref.value[0].updataMapObj(res.data.topdestinationinMap)
|
|
|
- }
|
|
|
- Top10Obj.DestinationData = res.data.seller_data_list_destination
|
|
|
- Top10Obj.OriginData = res.data.seller_data_list_origin
|
|
|
- top1OInterval.Max = res.data.Max
|
|
|
- top1OInterval.interval = res.data.interval
|
|
|
- top1OInterval_dest.Max = res.data.dest_Max
|
|
|
- top1OInterval_dest.interval = res.data.dest_interval
|
|
|
- topdestinationinType.value = res.data.topdestinationinType
|
|
|
- toporiginType.value = res.data.toporiginType
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- topOriginLoading.value = false
|
|
|
- })
|
|
|
-}
|
|
|
-//获取CO2 Origin
|
|
|
-const emissionLoading = ref(false)
|
|
|
-const emissionObj = reactive({
|
|
|
- bar_title: '',
|
|
|
- barList: [],
|
|
|
- barSeries: [],
|
|
|
- Max: 0,
|
|
|
- interval: 0,
|
|
|
- download_name: ''
|
|
|
-})
|
|
|
-const GetCo2EmissionEcharts = (val: any) => {
|
|
|
- emissionLoading.value = true
|
|
|
- co2OriginDefaultData.value = val
|
|
|
- $api
|
|
|
- .GetCo2EmissionEcharts({
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- emissionObj.bar_title = res.data.ContainerCount_Title
|
|
|
- emissionObj.barList = res.data.ContainerCountList
|
|
|
- emissionObj.barSeries = res.data.ContainerCounSeries
|
|
|
- emissionObj.Max = res.data.Max
|
|
|
- emissionObj.interval = res.data.interval
|
|
|
- emissionObj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- emissionLoading.value = false
|
|
|
- })
|
|
|
-}
|
|
|
-//获取CO2 Destination
|
|
|
-const destinationObj = reactive({
|
|
|
- bar_title: '',
|
|
|
- barList: [],
|
|
|
- barSeries: [],
|
|
|
- Max: 0,
|
|
|
- interval: 0,
|
|
|
- download_name: ''
|
|
|
-})
|
|
|
-const destinationLoading = ref(false)
|
|
|
-const GetCo2DestinationEcharts = (val: any) => {
|
|
|
- destinationLoading.value = true
|
|
|
- co2DestinationDefaultData.value = val
|
|
|
- $api
|
|
|
- .GetCo2DestinationEcharts({
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- destinationObj.bar_title = res.data.ContainerCount_Title
|
|
|
- destinationObj.barList = res.data.ContainerCountList
|
|
|
- destinationObj.barSeries = res.data.ContainerCounSeries
|
|
|
- destinationObj.Max = res.data.Max
|
|
|
- destinationObj.interval = res.data.interval
|
|
|
- destinationObj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- destinationLoading.value = false
|
|
|
- })
|
|
|
-}
|
|
|
-//获取Revenue Spent
|
|
|
-
|
|
|
-const revenueObj = reactive({
|
|
|
- bar_title: '',
|
|
|
- barList: [],
|
|
|
- barSeries: [],
|
|
|
- Max: 0,
|
|
|
- interval: 0,
|
|
|
- download_name: '',
|
|
|
- isShowTooltips: true
|
|
|
-})
|
|
|
-const revenueLoading = ref(false)
|
|
|
-const revenue_date_start = ref()
|
|
|
-const revenue_date_end = ref()
|
|
|
-const GetRevenueEcharts = (val: any) => {
|
|
|
- revenue_date_start.value = val.date_start
|
|
|
- revenue_date_end.value = val.date_end
|
|
|
- revenueLoading.value = true
|
|
|
- revenueDefaultData.value = val
|
|
|
- $api
|
|
|
- .GetRevenueEcharts({
|
|
|
- ...val,
|
|
|
- ...customerInfo.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code === 200) {
|
|
|
- revenueObj.bar_title = res.data.bar_title
|
|
|
- revenueObj.barList = res.data.barList
|
|
|
- revenueObj.barSeries = res.data.barSeries
|
|
|
- revenueObj.Max = res.data.Max
|
|
|
- revenueObj.interval = res.data.interval
|
|
|
- revenueObj.download_name = res.data.download_name
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- revenueLoading.value = false
|
|
|
- })
|
|
|
-}
|
|
|
-onMounted(() => {
|
|
|
- GetDashboardData()
|
|
|
-})
|
|
|
-
|
|
|
-// Save Layout
|
|
|
-const SaveLayout = () => {
|
|
|
- SaveVisible.value = false
|
|
|
- Management.value.forEach((item: any, index: any) => {
|
|
|
- item.id = index + 1
|
|
|
- })
|
|
|
- $api
|
|
|
- .SaveLayout({
|
|
|
- management: Management.value
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code == 200) {
|
|
|
- ElMessage({
|
|
|
- message: res.data.msg,
|
|
|
- duration: 3000,
|
|
|
- type: 'success'
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-//Save Filters
|
|
|
-const SaveFilters = () => {
|
|
|
- SaveVisible.value = false
|
|
|
- Management.value.forEach((item: any, index: any) => {
|
|
|
- item.id = index + 1
|
|
|
- })
|
|
|
- const filterConfig = {
|
|
|
- kpiDefaultData: kpiDefaultData.value,
|
|
|
- pendingDefaultData: pendingDefaultData.value,
|
|
|
- recentDefaultData: recentDefaultData.value,
|
|
|
- etdDefaultData: etdDefaultData.value,
|
|
|
- containerDefaultData: containerDefaultData.value,
|
|
|
- top10DefaultData: top10DefaultData.value,
|
|
|
- co2OriginDefaultData: co2OriginDefaultData.value,
|
|
|
- co2DestinationDefaultData: co2DestinationDefaultData.value,
|
|
|
- revenueDefaultData: revenueDefaultData.value
|
|
|
- }
|
|
|
- $api
|
|
|
- .SaveLayout({
|
|
|
- management: Management.value,
|
|
|
- dashboardObj: filterConfig
|
|
|
- })
|
|
|
- .then((res: any) => {
|
|
|
- if (res.code == 200) {
|
|
|
- ElMessage({
|
|
|
- message: res.data.msg,
|
|
|
- duration: 3000,
|
|
|
- type: 'success'
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-//ETD to ETA(DAYS)点击跳转
|
|
|
-const pie_chart_ETD = ref()
|
|
|
-const pie_chart_pending_arrival = ref()
|
|
|
-const pie_chart_pending_departure = ref()
|
|
|
-const pie_chart_kpi_departure = ref()
|
|
|
-const pie_chart_kpi_arrival = ref()
|
|
|
-const seller_chart_top10_origin = ref()
|
|
|
-const seller_chart_top10_destination = ref()
|
|
|
-const seller_chart_CO2_origin = ref()
|
|
|
-const seller_chart_CO2_destination = ref()
|
|
|
-
|
|
|
-// 定义点击项的配置类型
|
|
|
-interface ClickConfig {
|
|
|
- reportType: string
|
|
|
- reportRefGetter: () => string
|
|
|
- extraFilters?: Record<string, any>
|
|
|
- dateConfig?: {
|
|
|
- type: 'ETD' | 'ETA'
|
|
|
- start: string
|
|
|
- end: string
|
|
|
- formatType?: 'Container' | 'Revenue'
|
|
|
- }
|
|
|
- transportMode: any
|
|
|
-}
|
|
|
-
|
|
|
-// 配置映射表:将 val 映射到具体配置
|
|
|
-const CLICK_CONFIG_MAP: Record<string, (val: string) => ClickConfig> = {
|
|
|
- 'KPI Departure': () => ({
|
|
|
- reportType: 'atd_r4',
|
|
|
- reportRefGetter: () => pie_chart_kpi_departure.value[0]?.paramsdata?.name || '',
|
|
|
- dateConfig: {
|
|
|
- title: 'KPI Departure',
|
|
|
- type: kpiDefaultData.value.date_type,
|
|
|
- start: kpiDefaultData.value.date_start,
|
|
|
- end: kpiDefaultData.value.date_end
|
|
|
- },
|
|
|
- transportMode: kpiDefaultData.value.transportation
|
|
|
- }),
|
|
|
- 'KPI Arrival': () => ({
|
|
|
- reportType: 'ata_r3',
|
|
|
- reportRefGetter: () => pie_chart_kpi_arrival.value[0]?.paramsdata?.name || '',
|
|
|
- dateConfig: {
|
|
|
- title: 'KPI Arrival',
|
|
|
- type: kpiDefaultData.value.date_type,
|
|
|
- start: kpiDefaultData.value.date_start,
|
|
|
- end: kpiDefaultData.value.date_end
|
|
|
- },
|
|
|
- transportMode: kpiDefaultData.value.transportation
|
|
|
- }),
|
|
|
- 'Pending Departure': () => ({
|
|
|
- reportType: 'r4',
|
|
|
-
|
|
|
- reportRefGetter: () => pie_chart_pending_departure.value[0]?.paramsdata?.name || '',
|
|
|
- transportMode: pendingDefaultData.value.transportation
|
|
|
- }),
|
|
|
- 'Pending Arrival': () => ({
|
|
|
- reportType: 'r3',
|
|
|
- reportRefGetter: () => pie_chart_pending_arrival.value[0]?.paramsdata?.name || '',
|
|
|
- transportMode: pendingDefaultData.value.transportation
|
|
|
- }),
|
|
|
- 'ETD to ETA (Days)': () => ({
|
|
|
- reportType: 'r1',
|
|
|
- reportRefGetter: () => pie_chart_ETD.value[0]?.paramsdata?.name || '',
|
|
|
- dateConfig: {
|
|
|
- type: etdDefaultData.value.date_type,
|
|
|
- start: etdDefaultData.value.date_start,
|
|
|
- end: etdDefaultData.value.date_end,
|
|
|
- formatType: 'Container'
|
|
|
- },
|
|
|
- transportMode: etdDefaultData.value.transportation,
|
|
|
- extraFilters: {
|
|
|
- _reportRefe_date: {
|
|
|
- value: [dayjs().startOf('year').format('MM/YYYY'), dayjs().endOf('year').format('MM/YYYY')],
|
|
|
- keyType: 'dateRange'
|
|
|
- }
|
|
|
- }
|
|
|
- }),
|
|
|
- 'Top 10 Origin': () => ({
|
|
|
- reportType: 'top',
|
|
|
- reportRefGetter: () => seller_chart_top10_origin.value[0]?.paramsdata || '',
|
|
|
- dateConfig: {
|
|
|
- type: top10DefaultData.value.date_type,
|
|
|
- start: top10DefaultData.value.date_start,
|
|
|
- end: top10DefaultData.value.date_end
|
|
|
- },
|
|
|
- transportMode: top10DefaultData.value.transportation,
|
|
|
- extraFilters: {
|
|
|
- _reportStationType: {
|
|
|
- value: toporiginType.value,
|
|
|
- keyType: 'normal'
|
|
|
- }
|
|
|
- }
|
|
|
- }),
|
|
|
- 'Top 10 Destination': () => ({
|
|
|
- reportType: 'top',
|
|
|
- reportRefGetter: () => seller_chart_top10_destination.value[0]?.paramsdata || '',
|
|
|
- dateConfig: {
|
|
|
- type: top10DefaultData.value.date_type,
|
|
|
- start: top10DefaultData.value.date_start,
|
|
|
- end: top10DefaultData.value.date_end
|
|
|
- },
|
|
|
- transportMode: top10DefaultData.value.transportation,
|
|
|
- extraFilters: {
|
|
|
- _reportStationType: {
|
|
|
- value: topdestinationinType.value,
|
|
|
- keyType: 'normal'
|
|
|
- },
|
|
|
- consignee_city: {
|
|
|
- value: [seller_chart_top10_destination.value[0]?.paramscityname],
|
|
|
- keyType: 'array'
|
|
|
- }
|
|
|
- }
|
|
|
- }),
|
|
|
- 'CO2e Emission by Origin (Top 10)': () => ({
|
|
|
- reportType: 'co2e',
|
|
|
- reportRefGetter: () => seller_chart_CO2_origin.value[0]?.paramsdata?.name || '',
|
|
|
- dateConfig: {
|
|
|
- type: co2OriginDefaultData.value.date_type,
|
|
|
- start: co2OriginDefaultData.value.date_start,
|
|
|
- end: co2OriginDefaultData.value.date_end
|
|
|
- },
|
|
|
- transportMode: co2OriginDefaultData.value.transportation,
|
|
|
- extraFilters: {
|
|
|
- _reportDataType: {
|
|
|
- value: seller_chart_CO2_origin.value[0]?.paramsdata?.type,
|
|
|
- keyType: 'normal'
|
|
|
- },
|
|
|
- _reportStationType: {
|
|
|
- value: 'origin',
|
|
|
- keyType: 'normal'
|
|
|
- }
|
|
|
- }
|
|
|
- }),
|
|
|
- 'CO2e Emission by Destination (Top 10)': () => ({
|
|
|
- reportType: 'co2e',
|
|
|
- reportRefGetter: () => seller_chart_CO2_destination.value[0]?.paramsdata?.name || '',
|
|
|
- dateConfig: {
|
|
|
- type: co2DestinationDefaultData.value.date_type,
|
|
|
- start: co2DestinationDefaultData.value.date_start,
|
|
|
- end: co2DestinationDefaultData.value.date_end
|
|
|
- },
|
|
|
- transportMode: co2DestinationDefaultData.value.transportation,
|
|
|
- extraFilters: {
|
|
|
- _reportDataType: {
|
|
|
- value: seller_chart_CO2_destination.value[0]?.paramsdata?.type,
|
|
|
- keyType: 'normal'
|
|
|
- },
|
|
|
- _reportStationType: {
|
|
|
- value: 'agent',
|
|
|
- keyType: 'normal'
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 公共工具函数:处理日期转换
|
|
|
-const transformDateRange = (
|
|
|
- type: 'ETD' | 'ETA',
|
|
|
- [start, end]: [string, string],
|
|
|
- formatType?: 'Container' | 'Revenue'
|
|
|
-): [string, string] => {
|
|
|
- const parseFormat = formatType === 'Container' ? 'MM/YYYY' : 'MM/DD/YYYY'
|
|
|
-
|
|
|
- const transform = (dateStr: string): string => {
|
|
|
- let parsed = dayjs(dateStr, parseFormat, true)
|
|
|
- if (formatType === 'Container') {
|
|
|
- parsed = parsed.date(1)
|
|
|
- }
|
|
|
- return parsed.format(formatDate)
|
|
|
- }
|
|
|
-
|
|
|
- return [transform(start), transform(end)]
|
|
|
-}
|
|
|
-
|
|
|
-// 主函数
|
|
|
-const ClickParams = (val: string) => {
|
|
|
- const configFactory = CLICK_CONFIG_MAP[val]
|
|
|
- if (!configFactory) {
|
|
|
- console.warn(`No config found for click value: ${val}`)
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const config = configFactory(val)
|
|
|
-
|
|
|
- // 1. 设置日期范围(如果存在)
|
|
|
- if (config.dateConfig) {
|
|
|
- const { type, start, end, formatType } = config.dateConfig
|
|
|
- const [etdStart, etdEnd] = transformDateRange(type, [start, end], formatType)
|
|
|
-
|
|
|
- const keyMap = { ETD: ['etd_start', 'etd_end'], ETA: ['eta_start', 'eta_end'] }
|
|
|
- filtersStore.updateFilter({
|
|
|
- title: type,
|
|
|
- key: keyMap[type],
|
|
|
- value: [etdStart, etdEnd],
|
|
|
- keyType: 'dateRange'
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // 2. 设置运输方式
|
|
|
- filtersStore.updateFilter({
|
|
|
- title: 'Transport Mode',
|
|
|
- key: 'transport_mode',
|
|
|
- value: config.transportMode,
|
|
|
- keyType: 'array'
|
|
|
- })
|
|
|
-
|
|
|
- // 3. 设置报告引用和类型
|
|
|
- filtersStore.updateFilter({
|
|
|
- title: val,
|
|
|
- key: '_reportRef',
|
|
|
- value: config.reportRefGetter(),
|
|
|
- keyType: 'normal'
|
|
|
- })
|
|
|
-
|
|
|
- filtersStore.updateFilter({
|
|
|
- title: '_reportType',
|
|
|
- key: '_reportType',
|
|
|
- value: config.reportType,
|
|
|
- keyType: 'normal',
|
|
|
- isHide: true
|
|
|
- })
|
|
|
- if (val === 'Top 10 Origin') {
|
|
|
- filtersStore.updateFilter({
|
|
|
- title: 'Origin',
|
|
|
- key: 'shipper_city',
|
|
|
- value: [seller_chart_top10_origin.value[0]?.paramscityname],
|
|
|
- keyType: 'array'
|
|
|
- })
|
|
|
- }
|
|
|
- if (val === 'Top 10 Destination') {
|
|
|
- filtersStore.updateFilter({
|
|
|
- title: 'Destination',
|
|
|
- key: 'consignee_city',
|
|
|
- value: [seller_chart_top10_destination.value[0]?.paramscityname],
|
|
|
- keyType: 'array'
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // 4. 设置额外过滤器(如有)
|
|
|
- if (config.extraFilters) {
|
|
|
- Object.entries(config.extraFilters).forEach(([key, data]) => {
|
|
|
- filtersStore.updateFilter({
|
|
|
- title: key,
|
|
|
- key: key,
|
|
|
- value: data.value,
|
|
|
- keyType: data.keyType,
|
|
|
- isHide: true
|
|
|
- })
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // 5. 跳转
|
|
|
- router.push({ path: '/tracking' })
|
|
|
-}
|
|
|
-
|
|
|
-const isShowCumstomerFilter = ref(false)
|
|
|
-const handleCumstomerFilter = () => {
|
|
|
- isShowCumstomerFilter.value = !isShowCumstomerFilter.value
|
|
|
-}
|
|
|
-
|
|
|
-import DashboardGuide from '../src/components/DashboardGuide.vue'
|
|
|
-import { useGuideStore } from '@/stores/modules/guide'
|
|
|
-import { useThemeStore } from '@/stores/modules/theme'
|
|
|
-
|
|
|
-import kpiChartTipLight from './tipsImage/kpi-chart-tip.png'
|
|
|
-import kpiChartTipDark from './tipsImage/dark-kpi-chart-tip.png'
|
|
|
-import pendingChartTipLight from './tipsImage/pending-chart-tip.png'
|
|
|
-import pendingChartTipDark from './tipsImage/dark-pending-chart-tip.png'
|
|
|
-import etdToEtaChartsTipLight from './tipsImage/etd-to-eta-chart-tip.png'
|
|
|
-import etdToEtaChartsTipDark from './tipsImage/dark-etd-to-eta-chart-tip.png'
|
|
|
-import containerChartTipLight from './tipsImage/container-count-chart-tip.png'
|
|
|
-import containerChartTipDark from './tipsImage/dark-container-count-chart-tip.png'
|
|
|
-import top10ChartTipLight from './tipsImage/top-10-chart-tip.png'
|
|
|
-import top10ChartTipDark from './tipsImage/dark-top-10-chart-tip.png'
|
|
|
-import co2eChartTipLight from './tipsImage/co2e-chart-tip.png'
|
|
|
-import co2eChartTipDark from './tipsImage/dark-co2e-chart-tip.png'
|
|
|
-import revenueSpentChartTipLight from './tipsImage/revenue-spent-chart-tip.png'
|
|
|
-import revenueSpentChartTipDark from './tipsImage/dark-revenue-spent-chart-tip.png'
|
|
|
-import recentStatusChartTipLight from './tipsImage/recent-status-chart-tip.png'
|
|
|
-import recentStatusChartTipDark from './tipsImage/dark-recent-status-chart-tip.png'
|
|
|
-
|
|
|
-// Guide 图片
|
|
|
-import viewManagementLight from './guideImage/view-management.png'
|
|
|
-import viewManagementDark from './guideImage/dark-view-management.png'
|
|
|
-import saveConfigLight from './guideImage/save-config-guide.png'
|
|
|
-import saveConfigDark from './guideImage/dark-save-config-guide.png'
|
|
|
-import kpiChartLight from './guideImage/kpi-chart-guide.png'
|
|
|
-import kpiChartDark from './guideImage/dark-kpi-chart-guide.png'
|
|
|
-
|
|
|
-// ====== 2. 主题 store ======
|
|
|
-const themeStore = useThemeStore()
|
|
|
-const guideStore = useGuideStore()
|
|
|
-
|
|
|
-// ====== 3. 工具函数:根据主题返回对应图片 ======
|
|
|
-const getThemeImage = (light: string, dark: string) => {
|
|
|
- return computed(() => (themeStore.theme === 'dark' ? dark : light))
|
|
|
-}
|
|
|
-
|
|
|
-// ====== 4. 自动生成 computed 图片引用 ======
|
|
|
-// Tips 图片
|
|
|
-const kpiChartTip = getThemeImage(kpiChartTipLight, kpiChartTipDark)
|
|
|
-const pendingChartTip = getThemeImage(pendingChartTipLight, pendingChartTipDark)
|
|
|
-const etdToEtaChartsTip = getThemeImage(etdToEtaChartsTipLight, etdToEtaChartsTipDark)
|
|
|
-const containerChartTip = getThemeImage(containerChartTipLight, containerChartTipDark)
|
|
|
-const top10ChartTip = getThemeImage(top10ChartTipLight, top10ChartTipDark)
|
|
|
-const co2eChartTip = getThemeImage(co2eChartTipLight, co2eChartTipDark)
|
|
|
-const revenueSpentChartTip = getThemeImage(revenueSpentChartTipLight, revenueSpentChartTipDark)
|
|
|
-const recentStatusChartTip = getThemeImage(recentStatusChartTipLight, recentStatusChartTipDark)
|
|
|
-
|
|
|
-// Guide 图片
|
|
|
-const viewManagementImg = getThemeImage(viewManagementLight, viewManagementDark)
|
|
|
-const saveConfigImg = getThemeImage(saveConfigLight, saveConfigDark)
|
|
|
-const kpiChartImg = getThemeImage(kpiChartLight, kpiChartDark)
|
|
|
-
|
|
|
-// ====== 5. 其他逻辑 ======
|
|
|
-const dashboardGuideRef = ref(null)
|
|
|
-const handleGuide = () => {
|
|
|
- dashboardGuideRef.value?.startGuide()
|
|
|
-}
|
|
|
-</script>
|
|
|
-<template>
|
|
|
- <div class="dashboard">
|
|
|
- <!-- 评分 -->
|
|
|
- <ScoringSystem></ScoringSystem>
|
|
|
- <DashboardGuide ref="dashboardGuideRef"></DashboardGuide>
|
|
|
- <!-- Title -->
|
|
|
- <div class="Title">
|
|
|
- <div>
|
|
|
- <span>Dashboard</span>
|
|
|
- <VDriverGuide style="margin-top: -1px" @click="handleGuide"></VDriverGuide>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div style="position: relative">
|
|
|
- <el-popover trigger="click" width="400" popper-style="border-radius: 12px">
|
|
|
- <template #reference>
|
|
|
- <el-button class="el-button--default">
|
|
|
- <span class="iconfont_icon">
|
|
|
- <svg class="iconfont" aria-hidden="true">
|
|
|
- <use xlink:href="#icon-icon_view__management_b"></use>
|
|
|
- </svg>
|
|
|
- </span>
|
|
|
- View Management
|
|
|
- </el-button>
|
|
|
- </template>
|
|
|
-
|
|
|
- <div class="Management">
|
|
|
- <div class="title">View Management</div>
|
|
|
- <div class="management-content">
|
|
|
- <div class="management-item" v-for="(item, index) in Management" :key="index">
|
|
|
- <div class="management_flex">
|
|
|
- <div class="content_title">{{ item.title }}</div>
|
|
|
- <div>
|
|
|
- <el-switch
|
|
|
- v-model="item.switchValue"
|
|
|
- :disabled="
|
|
|
- item.isRevenueDisplay != undefined && item.isRevenueDisplay == false
|
|
|
- "
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="content_text">{{ item.text }}</div>
|
|
|
- <div
|
|
|
- class="content_text_warining"
|
|
|
- v-if="item.isRevenueDisplay != undefined && item.isRevenueDisplay == false"
|
|
|
- >
|
|
|
- *To ensure the accuracy of the data display, this report needs to be configured
|
|
|
- and displayed after communicating clearly with Sales.
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <el-divider style="margin-top: 0; margin-bottom: 8px" />
|
|
|
- <div class="tips">
|
|
|
- <span class="iconfont_icon">
|
|
|
- <svg class="iconfont iconfont_tips" aria-hidden="true">
|
|
|
- <use xlink:href="#icon-icon_info_b"></use>
|
|
|
- </svg>
|
|
|
- </span>
|
|
|
- <div class="tips_text">
|
|
|
- Please remember to click the save button in order to keep the new dashboard layout
|
|
|
- and widgets.
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-popover>
|
|
|
-
|
|
|
- <img
|
|
|
- id="view-management-guide"
|
|
|
- v-if="guideStore.dashboard.isShowViewManagementGuidePhoto"
|
|
|
- class="view-management-guide-class"
|
|
|
- :class="{
|
|
|
- 'view-management-guide-dark-class': themeStore.theme === 'dark'
|
|
|
- }"
|
|
|
- :src="viewManagementImg"
|
|
|
- alt=""
|
|
|
- />
|
|
|
- <el-popover
|
|
|
- :visible="SaveVisible"
|
|
|
- :popper-style="{
|
|
|
- display: 'flex',
|
|
|
- flexDirection: 'column',
|
|
|
- alignItems: 'center',
|
|
|
- padding: '8px 4px',
|
|
|
- borderRadius: '12px',
|
|
|
- width: '142px',
|
|
|
- minWidth: '140px',
|
|
|
- backgroundColor: 'var(--management-bg-color)'
|
|
|
- }"
|
|
|
- >
|
|
|
- <template #reference>
|
|
|
- <el-button
|
|
|
- class="el-button--default"
|
|
|
- @blur="SaveVisible = false"
|
|
|
- @click="SaveVisible = !SaveVisible"
|
|
|
- >
|
|
|
- <span class="iconfont_icon">
|
|
|
- <svg class="iconfont" aria-hidden="true">
|
|
|
- <use xlink:href="#icon-icon_save_b"></use>
|
|
|
- </svg>
|
|
|
- </span>
|
|
|
- Save
|
|
|
- <span class="iconfont_icon">
|
|
|
- <svg class="iconfont" aria-hidden="true">
|
|
|
- <use xlink:href="#icon-icon_dropdown_b"></use>
|
|
|
- </svg>
|
|
|
- </span>
|
|
|
- </el-button>
|
|
|
- </template>
|
|
|
- <div class="Save_filters" @click="SaveFilters">
|
|
|
- <span class="iconfont_icon iconfont_icon_save">
|
|
|
- <svg class="iconfont" aria-hidden="true">
|
|
|
- <use xlink:href="#icon-icon_save_b"></use>
|
|
|
- </svg>
|
|
|
- </span>
|
|
|
- <div>Save Filters</div>
|
|
|
- </div>
|
|
|
- <div class="Save_filters" @click="SaveLayout">
|
|
|
- <span class="iconfont_icon iconfont_icon_save">
|
|
|
- <svg class="iconfont" aria-hidden="true">
|
|
|
- <use xlink:href="#icon-icon_save_b"></use>
|
|
|
- </svg>
|
|
|
- </span>
|
|
|
- <div>Save Layout</div>
|
|
|
- </div>
|
|
|
- </el-popover>
|
|
|
-
|
|
|
- <!-- -->
|
|
|
- <img
|
|
|
- id="save-config-guide"
|
|
|
- v-if="guideStore.dashboard.isShowSaveConfigGuidePhoto"
|
|
|
- class="save-config-guide-class position-absolute-guide"
|
|
|
- :src="saveConfigImg"
|
|
|
- :class="{
|
|
|
- 'save-config-guide-dark-class': themeStore.theme === 'dark'
|
|
|
- }"
|
|
|
- alt=""
|
|
|
- />
|
|
|
- <el-button
|
|
|
- class="el-button--default"
|
|
|
- :class="{ 'customer-filter-focus': isShowCumstomerFilter }"
|
|
|
- style="padding: 8px"
|
|
|
- @click="handleCumstomerFilter"
|
|
|
- >
|
|
|
- <span class="font_family icon-icon_search__user_b"></span>
|
|
|
- <div style="margin-left: 4px" class="count" v-if="customerInfo.customerCode.length">
|
|
|
- <span>{{ customerInfo.customerCode.length }}</span>
|
|
|
- </div>
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <!-- customer-filter -->
|
|
|
- <CustomerFilter
|
|
|
- v-if="isShowCumstomerFilter"
|
|
|
- :data="customerInfo"
|
|
|
- @changeCustomerData="changeCustomerData"
|
|
|
- ></CustomerFilter>
|
|
|
- <!-- 图表 -->
|
|
|
- <div class="echarts">
|
|
|
- <VueDraggable
|
|
|
- style="
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- justify-content: space-between;
|
|
|
- gap: 8px;
|
|
|
- width: 100%;
|
|
|
- "
|
|
|
- ref="infoContentRef"
|
|
|
- ghost-class="ghost-class"
|
|
|
- :forceFallback="true"
|
|
|
- fallback-class="fallback-class"
|
|
|
- v-model="Management"
|
|
|
- handle=".handle-draggable"
|
|
|
- >
|
|
|
- <template v-for="item in Management" :key="item">
|
|
|
- <div v-if="item.title === 'KPI' && item.switchValue" class="filters_left">
|
|
|
- <!-- KPI -->
|
|
|
- <VBox_Dashboard
|
|
|
- style="overflow: visible"
|
|
|
- @changeCancel="changeCancel(item.id)"
|
|
|
- :isShowDragIconGudie="true"
|
|
|
- >
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex" style="position: relative">
|
|
|
- <img
|
|
|
- id="kpi-chart-guide"
|
|
|
- v-if="guideStore.dashboard.isShowKpiChartGuidePhoto"
|
|
|
- class="kpi-chart-guide-class position-absolute-guide"
|
|
|
- :src="kpiChartImg"
|
|
|
- alt=""
|
|
|
- />
|
|
|
- <div>
|
|
|
- {{ item.title }}
|
|
|
- <VTipTooltip
|
|
|
- :img="kpiChartTip"
|
|
|
- :width="410"
|
|
|
- :label="'KPI Report:Day difference between actual and estimate.'"
|
|
|
- placement="bottom-start"
|
|
|
- ></VTipTooltip>
|
|
|
- </div>
|
|
|
- <DashFilters
|
|
|
- :defaultData="kpiDefaultData"
|
|
|
- :isShowTransportModeGuide="true"
|
|
|
- :threeMonthsInterval="true"
|
|
|
- @FilterSearch="GetKpiData"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #content>
|
|
|
- <div class="KPI_Pending">
|
|
|
- <div class="kpi">
|
|
|
- <PieChart
|
|
|
- ref="pie_chart_kpi_departure"
|
|
|
- @ClickParams="ClickParams(item.title + ' Departure')"
|
|
|
- :PieData="KPIobj"
|
|
|
- v-vloading="kpiLoading"
|
|
|
- style="height: 300px"
|
|
|
- ></PieChart>
|
|
|
- </div>
|
|
|
- <div class="kpi">
|
|
|
- <PieChart
|
|
|
- ref="pie_chart_kpi_arrival"
|
|
|
- :PieData="arrivalObj"
|
|
|
- v-vloading="kpiArrivalLoading"
|
|
|
- @ClickParams="ClickParams(item.title + ' Arrival')"
|
|
|
- style="height: 300px"
|
|
|
- ></PieChart>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- <div v-else-if="item.title === 'Pending' && item.switchValue" class="filters_left">
|
|
|
- <!-- Pending -->
|
|
|
- <VBox_Dashboard @changeCancel="changeCancel(item.id)">
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex">
|
|
|
- <div>
|
|
|
- {{ item.title }}
|
|
|
- <VTipTooltip
|
|
|
- :img="pendingChartTip"
|
|
|
- :width="420"
|
|
|
- :placement="'bottom-start'"
|
|
|
- :label="'Pending Report:Showing shipments which are soon to depart/arrive.'"
|
|
|
- ></VTipTooltip>
|
|
|
- </div>
|
|
|
- <DashFilters
|
|
|
- :defaultData="pendingDefaultData"
|
|
|
- :isPending="true"
|
|
|
- :img="'./image/kpi-chart-tip.png'"
|
|
|
- @FilterSearch="GetPendingEcharts"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #content>
|
|
|
- <div class="KPI_Pending">
|
|
|
- <div class="kpi">
|
|
|
- <PieChart
|
|
|
- ref="pie_chart_pending_departure"
|
|
|
- :PieData="pendingObj"
|
|
|
- v-vloading="pendingLoading"
|
|
|
- @ClickParams="ClickParams(item.title + ' Departure')"
|
|
|
- style="height: 300px"
|
|
|
- ></PieChart>
|
|
|
- </div>
|
|
|
- <div class="kpi">
|
|
|
- <PieChart
|
|
|
- ref="pie_chart_pending_arrival"
|
|
|
- @ClickParams="ClickParams(item.title + ' Arrival')"
|
|
|
- :PieData="pendingArrivalObj"
|
|
|
- v-vloading="pendingArrivalLoading"
|
|
|
- style="height: 300px"
|
|
|
- ></PieChart>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- <!-- ETD to ETA -->
|
|
|
- <div
|
|
|
- v-else-if="item.title === 'ETD to ETA (Days)' && item.switchValue"
|
|
|
- class="filters_left"
|
|
|
- >
|
|
|
- <VBox_Dashboard @changeCancel="changeCancel(item.id)">
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex">
|
|
|
- <div>
|
|
|
- {{ item.title }}
|
|
|
- <VTipTooltip
|
|
|
- :img="etdToEtaChartsTip"
|
|
|
- :width="430"
|
|
|
- :placement="'bottom-start'"
|
|
|
- :label="'ETD to ETA (Days):Distribution of Transit Time (ETA-ETD) for All Shipments in Last 12 Months.'"
|
|
|
- ></VTipTooltip>
|
|
|
- </div>
|
|
|
- <DashFilters
|
|
|
- :defaultData="etdDefaultData"
|
|
|
- @FilterSearch="GetETDEcharts"
|
|
|
- :isETDToETA="true"
|
|
|
- :isContainer="true"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #content>
|
|
|
- <PieChart
|
|
|
- ref="pie_chart_ETD"
|
|
|
- @ClickParams="ClickParams(item.title)"
|
|
|
- :PieData="etdObj"
|
|
|
- v-vloading="etdLoading"
|
|
|
- style="height: 300px"
|
|
|
- ></PieChart>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- <!-- Container Count -->
|
|
|
- <div
|
|
|
- v-else-if="item.title === 'Container Count' && item.switchValue"
|
|
|
- class="filters_left"
|
|
|
- >
|
|
|
- <VBox_Dashboard @changeCancel="changeCancel(item.id)">
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex">
|
|
|
- <div>
|
|
|
- {{ item.title }}
|
|
|
- <VTipTooltip
|
|
|
- :img="containerChartTip"
|
|
|
- :placement="'bottom-start'"
|
|
|
- :label="'Container Count:Total Container Volume by Month (Last 12 Months)'"
|
|
|
- ></VTipTooltip>
|
|
|
- </div>
|
|
|
- <DashFilters
|
|
|
- :defaultData="containerDefaultData"
|
|
|
- @FilterSearch="GetContainerCountEcharts"
|
|
|
- :isContainer="true"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #content>
|
|
|
- <BarChart
|
|
|
- ref="seller_chart_Container_count"
|
|
|
- :BarData="containerObj"
|
|
|
- v-vloading="containerLoading"
|
|
|
- style="height: 300px"
|
|
|
- :isRevenue="true"
|
|
|
- save-image-name="Container Count"
|
|
|
- :barHeight="{ height: '300px' }"
|
|
|
- ></BarChart>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- <!-- Top10 Origin/Top 10 Destination -->
|
|
|
- <div
|
|
|
- v-else-if="item.title === 'Top 10 Origin/Destination' && item.switchValue"
|
|
|
- class="KPI_Pending"
|
|
|
- >
|
|
|
- <VBox_Dashboard @changeCancel="changeCancel(item.id)" style="width: 100%">
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex" style="height: 48px">
|
|
|
- <div style="display: flex">
|
|
|
- <el-tabs
|
|
|
- v-model="activeName"
|
|
|
- class="demo-tabs"
|
|
|
- style="height: 48px"
|
|
|
- @tab-click="handleTabClick"
|
|
|
- >
|
|
|
- <el-tab-pane :label="item.title1" name="first"></el-tab-pane>
|
|
|
- <el-tab-pane :label="item.title2" name="second"></el-tab-pane>
|
|
|
- </el-tabs>
|
|
|
- <VTipTooltip
|
|
|
- style="margin-left: 4px"
|
|
|
- :img="top10ChartTip"
|
|
|
- :label="'Top 10 Origin & Destination: Last 12 Months Shipment Volume Rankings: Top 10 Origin Cities and Top 10 Destination Cities'"
|
|
|
- :width="700"
|
|
|
- ></VTipTooltip>
|
|
|
- </div>
|
|
|
- <DashFilters
|
|
|
- :defaultData="top10DefaultData"
|
|
|
- @FilterSearch="GetTop10ODEcharts"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template v-if="isShowtitle1" #content>
|
|
|
- <div class="KPI_Pending">
|
|
|
- <div class="seller_chart">
|
|
|
- <SellerChart
|
|
|
- ref="seller_chart_top10_origin"
|
|
|
- @clickParams="ClickParams(item.title1)"
|
|
|
- :SellerData="Top10Obj.OriginData"
|
|
|
- v-vloading="topOriginLoading"
|
|
|
- :Interval="top1OInterval"
|
|
|
- saveImageName="Top 10 Origin"
|
|
|
- ></SellerChart>
|
|
|
- </div>
|
|
|
- <div class="map">
|
|
|
- <!-- <TopMap :obj="top10DefaultData.value" ref="top10Originref"></TopMap> -->
|
|
|
- <TopMap ref="top10Originref"></TopMap>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template v-else #content2>
|
|
|
- <div class="KPI_Pending">
|
|
|
- <div class="seller_chart">
|
|
|
- <SellerChart
|
|
|
- ref="seller_chart_top10_destination"
|
|
|
- @clickParams="ClickParams(item.title2)"
|
|
|
- :SellerData="Top10Obj.DestinationData"
|
|
|
- :Interval="top1OInterval_dest"
|
|
|
- v-vloading="topOriginLoading"
|
|
|
- saveImageName="Top 10 Destination"
|
|
|
- style="height: 310px"
|
|
|
- ></SellerChart>
|
|
|
- </div>
|
|
|
- <div class="map" style="height: 310px">
|
|
|
- <!-- <TopMap :obj="top10DefaultData.value" ref="top10Destinationref"></TopMap> -->
|
|
|
- <TopMap ref="top10Destinationref"></TopMap>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- <!-- CO2e Emission by Origin (Top 10) -->
|
|
|
- <div
|
|
|
- v-else-if="item.title === 'CO2e Emission by Origin (Top 10)' && item.switchValue"
|
|
|
- class="filters_left"
|
|
|
- >
|
|
|
- <VBox_Dashboard @changeCancel="changeCancel(item.id)">
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex">
|
|
|
- <div>
|
|
|
- {{ item.title }}
|
|
|
- <VTipTooltip
|
|
|
- :img="co2eChartTip"
|
|
|
- :label="'CO2e Emission by Origin or Destination: Last 12 Months CO2e Emission Rankings: Top 10 Origin Cities and Top 10 Destination Cities'"
|
|
|
- :width="700"
|
|
|
- :placement="'bottom-start'"
|
|
|
- ></VTipTooltip>
|
|
|
- </div>
|
|
|
- <DashFilters
|
|
|
- :defaultData="co2OriginDefaultData"
|
|
|
- @FilterSearch="GetCo2EmissionEcharts"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #content>
|
|
|
- <BarChart
|
|
|
- ref="seller_chart_CO2_origin"
|
|
|
- :BarData="emissionObj"
|
|
|
- save-image-name="CO2e Emission by Origin (Top 10)"
|
|
|
- @clickParams="ClickParams(item.title)"
|
|
|
- v-vloading="emissionLoading"
|
|
|
- style="height: 250px"
|
|
|
- :isRevenue="true"
|
|
|
- :barHeight="{ height: '250px' }"
|
|
|
- ></BarChart>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- <!-- CO2e Emission by Destination (Top 10) -->
|
|
|
- <div
|
|
|
- v-else-if="item.title === 'CO2e Emission by Destination (Top 10)' && item.switchValue"
|
|
|
- class="filters_left"
|
|
|
- >
|
|
|
- <VBox_Dashboard @changeCancel="changeCancel(item.id)">
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex">
|
|
|
- <div>
|
|
|
- {{ item.title }}
|
|
|
- <!-- <VTipTooltip :img="co2eChartTip"></VTipTooltip> -->
|
|
|
- </div>
|
|
|
- <DashFilters
|
|
|
- :defaultData="co2DestinationDefaultData"
|
|
|
- @FilterSearch="GetCo2DestinationEcharts"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #content>
|
|
|
- <BarChart
|
|
|
- ref="seller_chart_CO2_destination"
|
|
|
- :BarData="destinationObj"
|
|
|
- v-vloading="destinationLoading"
|
|
|
- style="height: 250px"
|
|
|
- :isRevenue="true"
|
|
|
- save-image-name="CO2e Emission by Destination (Top 10)"
|
|
|
- @clickParams="ClickParams(item.title)"
|
|
|
- :barHeight="{ height: '250px' }"
|
|
|
- ></BarChart>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- <!-- <div
|
|
|
- v-else-if="item.title === 'Revenue' && item.switchValue"
|
|
|
- class="KPI_Pending"
|
|
|
- > -->
|
|
|
- <div
|
|
|
- v-else-if="
|
|
|
- item.title === 'Recent Status' && item.switchValue && RecentStatusList.length != 0
|
|
|
- "
|
|
|
- class="KPI_Pending"
|
|
|
- >
|
|
|
- <!-- Recent Status -->
|
|
|
- <VBox_Dashboard @changeCancel="changeCancel(item.id)" style="width: 100%">
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex">
|
|
|
- <div>
|
|
|
- {{ item.title }}
|
|
|
- <VTipTooltip
|
|
|
- :img="recentStatusChartTip"
|
|
|
- :label="'Recent Status: Active shipment list with ETD within the past three months and the next month.'"
|
|
|
- :width="700"
|
|
|
- :placement="'bottom-start'"
|
|
|
- ></VTipTooltip>
|
|
|
- </div>
|
|
|
- <DashFilters
|
|
|
- :defaultData="recentDefaultData"
|
|
|
- @FilterSearch="getTableData"
|
|
|
- :isRecent="true"
|
|
|
- :threeMonthsInterval="true"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #content>
|
|
|
- <RecentStatus :RecentStatusList="RecentStatusList"></RecentStatus>
|
|
|
- <div class="pagination">
|
|
|
- <span>Total {{ formatNumber(pageInfo.total) }}</span>
|
|
|
- <el-pagination
|
|
|
- v-model:current-page="pageInfo.pageNo"
|
|
|
- v-model:page-size="pageInfo.pageSize"
|
|
|
- :page-sizes="[10, 50, 100, 200]"
|
|
|
- layout="prev, pager, next"
|
|
|
- :pager-count="3"
|
|
|
- :total="pageInfo.total"
|
|
|
- @size-change="getTableData(recentDefaultData, true)"
|
|
|
- @current-change="getTableData(recentDefaultData, true)"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- <!-- Revenue -->
|
|
|
- <div v-else-if="item.title === 'Revenue Spent' && item.switchValue" class="KPI_Pending">
|
|
|
- <VBox_Dashboard @changeCancel="changeCancel(item.id)" style="width: 100%">
|
|
|
- <template #header>
|
|
|
- <div class="Title_flex">
|
|
|
- <div>
|
|
|
- Revenue Spent
|
|
|
- <VTipTooltip
|
|
|
- :img="revenueSpentChartTip"
|
|
|
- :label="'Revenue Spent: Based on the billto object, display the corresponding revenue data. '"
|
|
|
- :placement="'bottom-start'"
|
|
|
- :width="700"
|
|
|
- ></VTipTooltip>
|
|
|
- </div>
|
|
|
-
|
|
|
- <DashFilters
|
|
|
- :defaultData="revenueDefaultData"
|
|
|
- @FilterSearch="GetRevenueEcharts"
|
|
|
- :isRevenue="true"
|
|
|
- :isContainer="true"
|
|
|
- ></DashFilters>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #content>
|
|
|
- <RevenueChart
|
|
|
- :BarData="revenueObj"
|
|
|
- v-vloading="revenueLoading"
|
|
|
- :RevenueStartDate="revenue_date_start"
|
|
|
- :RevenueEndDate="revenue_date_end"
|
|
|
- style="height: 300px"
|
|
|
- :barHeight="{ height: '300px' }"
|
|
|
- ></RevenueChart>
|
|
|
- </template>
|
|
|
- </VBox_Dashboard>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </VueDraggable>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-<style lang="scss" scoped>
|
|
|
-.Title {
|
|
|
- display: flex;
|
|
|
- background-color: var(--color-mode);
|
|
|
- height: 68px;
|
|
|
- font-size: var(--font-size-6);
|
|
|
- font-weight: 700;
|
|
|
- padding: 0 24px;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
-}
|
|
|
-
|
|
|
-.iconfont {
|
|
|
- vertical-align: -2px;
|
|
|
-}
|
|
|
-
|
|
|
-.view-management-guide-class {
|
|
|
- position: absolute;
|
|
|
- top: 0px;
|
|
|
- right: 85px;
|
|
|
- width: 437px;
|
|
|
- height: 603px;
|
|
|
- z-index: 1500;
|
|
|
- &.view-management-guide-dark-class {
|
|
|
- width: 439px;
|
|
|
- height: 622px;
|
|
|
- }
|
|
|
-}
|
|
|
-.save-config-guide-class {
|
|
|
- position: absolute;
|
|
|
- top: -1px;
|
|
|
- right: -13px;
|
|
|
- width: 183px;
|
|
|
- height: 160px;
|
|
|
- z-index: 1500;
|
|
|
- transform: translate(-0.8px, 0px);
|
|
|
- &.save-config-guide-dark-class {
|
|
|
- width: 182px;
|
|
|
- height: 157px;
|
|
|
- right: -12px;
|
|
|
- }
|
|
|
-}
|
|
|
-.dashboard {
|
|
|
- .count {
|
|
|
- display: inline-flex;
|
|
|
- height: 17px;
|
|
|
- padding-left: 5px;
|
|
|
- padding-right: 5px;
|
|
|
- background-color: var(--color-theme);
|
|
|
- border-radius: 9px;
|
|
|
- font-size: 12px;
|
|
|
- line-height: 19px;
|
|
|
- text-align: center;
|
|
|
- span {
|
|
|
- color: var(--color-white) !important;
|
|
|
- font-weight: 700;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-.customer-filter-focus {
|
|
|
- border: 1px solid var(--color-btn-default-bg-hover);
|
|
|
- background-color: var(--color-btn-default-bg-hover);
|
|
|
- fill: var(--color-theme);
|
|
|
- span {
|
|
|
- color: var(--color-theme);
|
|
|
- }
|
|
|
-}
|
|
|
-.kpi-chart-guide-class {
|
|
|
- top: -2px;
|
|
|
- left: -50px;
|
|
|
- width: 589px;
|
|
|
- height: 478px;
|
|
|
- z-index: 3500;
|
|
|
-}
|
|
|
-
|
|
|
-.Management {
|
|
|
- max-height: 640px;
|
|
|
- overflow: hidden;
|
|
|
- border-radius: 12px;
|
|
|
- background-color: var(--management-bg-color);
|
|
|
-}
|
|
|
-
|
|
|
-.management-content {
|
|
|
- overflow-y: auto;
|
|
|
- max-height: 533px;
|
|
|
-}
|
|
|
-.title {
|
|
|
- font-weight: 700;
|
|
|
- font-size: var(--font-size-5);
|
|
|
- background-color: var(--color-header-bg);
|
|
|
- height: 48px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding-left: 16px;
|
|
|
-}
|
|
|
-.management-item {
|
|
|
- width: 368px;
|
|
|
- min-height: 54px;
|
|
|
- margin: 10px auto;
|
|
|
- background-color: var(--color-header-bg);
|
|
|
- border-radius: var(--border-radius-6);
|
|
|
- padding: 8px 16px;
|
|
|
-}
|
|
|
-.management_flex {
|
|
|
- display: flex;
|
|
|
- height: 20px;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
-}
|
|
|
-.content_title {
|
|
|
- font-weight: 700;
|
|
|
- font-size: var(--font-size-3);
|
|
|
-}
|
|
|
-.content_text {
|
|
|
- color: var(--color-neutral-2);
|
|
|
- font-size: var(--font-size-2);
|
|
|
- line-height: 16px;
|
|
|
-}
|
|
|
-.content_text_warining {
|
|
|
- color: var(--color-warning);
|
|
|
- font-size: var(--font-size-2);
|
|
|
- line-height: 16px;
|
|
|
-}
|
|
|
-.tips {
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- padding-bottom: 8px;
|
|
|
-}
|
|
|
-.iconfont_tips {
|
|
|
- fill: var(--color-neutral-2);
|
|
|
-}
|
|
|
-.tips_text {
|
|
|
- width: 278.43px;
|
|
|
- text-align: center;
|
|
|
- font-size: var(--font-size-2);
|
|
|
- color: var(--color-neutral-2);
|
|
|
-}
|
|
|
-
|
|
|
-.Save_filters {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- height: 40px;
|
|
|
- font-size: var(--font-size-3);
|
|
|
- width: 126px;
|
|
|
- border-radius: 6px;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-.iconfont_icon_save {
|
|
|
- margin-right: 16px;
|
|
|
- fill: var(--color-neutral-1);
|
|
|
-}
|
|
|
-.Save_filters:hover {
|
|
|
- border-color: var(--color-btn-default-bg-hover);
|
|
|
- background-color: var(--color-btn-default-bg-hover);
|
|
|
- .iconfont_icon_save {
|
|
|
- fill: var(--color-theme);
|
|
|
- }
|
|
|
- div {
|
|
|
- color: var(--color-theme);
|
|
|
- }
|
|
|
-}
|
|
|
-.filters {
|
|
|
- display: flex;
|
|
|
- padding: 0 24px;
|
|
|
- height: 32px;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 8px;
|
|
|
- justify-content: space-between;
|
|
|
-}
|
|
|
-.KPI_Pending {
|
|
|
- display: flex;
|
|
|
- width: 100%;
|
|
|
-}
|
|
|
-.filters_left {
|
|
|
- border-radius: var(--border-radius-6);
|
|
|
- width: calc(50% - 4px);
|
|
|
- flex: 0 0 calc(50% - 4px);
|
|
|
- min-width: 0;
|
|
|
- box-sizing: border-box;
|
|
|
-}
|
|
|
-
|
|
|
-:deep(.ETD_title) {
|
|
|
- margin-bottom: 0 !important;
|
|
|
-}
|
|
|
-:deep(:where(.css-dev-only-do-not-override-19iuou).ant-picker-range) {
|
|
|
- height: 32px;
|
|
|
-}
|
|
|
-.echarts {
|
|
|
- padding: 0 22px;
|
|
|
- background-color: var(--color-mode);
|
|
|
- :deep(> div) {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- justify-content: space-between;
|
|
|
- gap: 8px;
|
|
|
- width: 100%;
|
|
|
- > * {
|
|
|
- box-sizing: border-box;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-.kpi {
|
|
|
- width: 50%;
|
|
|
- border-right: 1px solid var(--color-border);
|
|
|
-}
|
|
|
-.kpi:last-child {
|
|
|
- border-right: none;
|
|
|
-}
|
|
|
-.ghost-class {
|
|
|
- opacity: 0;
|
|
|
-}
|
|
|
-.fallback-class {
|
|
|
- opacity: 1 !important;
|
|
|
- background-color: var(--color-v-box-content-drag-bg);
|
|
|
- cursor: move !important;
|
|
|
- box-shadow: 4px 4px 32px 0px rgba(0, 0, 0, 0.2);
|
|
|
- border-radius: 12px;
|
|
|
-}
|
|
|
-.pagination {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- border-top: 1px solid var(--color-border);
|
|
|
- padding: 4px 8px;
|
|
|
-}
|
|
|
-
|
|
|
-.seller_chart {
|
|
|
- width: 30%;
|
|
|
- border-right: 1px solid var(--color-border);
|
|
|
-}
|
|
|
-.map {
|
|
|
- width: 70%;
|
|
|
-}
|
|
|
-.Title_flex {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- margin-right: 30px;
|
|
|
-}
|
|
|
-
|
|
|
-.dashboard {
|
|
|
- z-index: 2014;
|
|
|
- position: relative;
|
|
|
- background-color: var(--color-mode);
|
|
|
- padding-bottom: 40px;
|
|
|
-}
|
|
|
-:deep(.el-tabs__header) {
|
|
|
- height: 48px;
|
|
|
- margin-bottom: 0;
|
|
|
-}
|
|
|
-</style>
|
|
|
-<style lang="scss">
|
|
|
-:not(body):has(> img.driver-active-element) {
|
|
|
- overflow: visible !important;
|
|
|
-}
|
|
|
-</style>
|