Commit 097317d3 by qinjianhui

Merge branch 'dev'

parents 4cd242e7 95ca82ad
...@@ -16,6 +16,7 @@ declare module 'vue' { ...@@ -16,6 +16,7 @@ declare module 'vue' {
ElCarousel: typeof import('element-plus/es')['ElCarousel'] ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem'] ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxButton: typeof import('element-plus/es')['ElCheckboxButton']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
...@@ -54,7 +55,6 @@ declare module 'vue' { ...@@ -54,7 +55,6 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline'] ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree'] ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload'] ElUpload: typeof import('element-plus/es')['ElUpload']
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"axios": "^1.6.7", "axios": "^1.6.7",
"bignumber.js": "^9.3.0", "bignumber.js": "^9.3.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^6.0.0",
"element-plus": "^2.6.0", "element-plus": "^2.6.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"luxon": "^3.7.1", "luxon": "^3.7.1",
...@@ -3635,6 +3636,16 @@ ...@@ -3635,6 +3636,16 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/echarts": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-6.0.0.tgz",
"integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "2.3.0",
"zrender": "6.0.0"
}
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.157", "version": "1.5.157",
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.157.tgz", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.157.tgz",
...@@ -6009,6 +6020,12 @@ ...@@ -6009,6 +6020,12 @@
"typescript": ">=4.2.0" "typescript": ">=4.2.0"
} }
}, },
"node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
"license": "0BSD"
},
"node_modules/type": { "node_modules/type": {
"version": "2.7.3", "version": "2.7.3",
"resolved": "https://registry.npmmirror.com/type/-/type-2.7.3.tgz", "resolved": "https://registry.npmmirror.com/type/-/type-2.7.3.tgz",
...@@ -6848,6 +6865,15 @@ ...@@ -6848,6 +6865,15 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/zrender": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-6.0.0.tgz",
"integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==",
"license": "BSD-3-Clause",
"dependencies": {
"tslib": "2.3.0"
}
} }
} }
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
"axios": "^1.6.7", "axios": "^1.6.7",
"bignumber.js": "^9.3.0", "bignumber.js": "^9.3.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^6.0.0",
"element-plus": "^2.6.0", "element-plus": "^2.6.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"luxon": "^3.7.1", "luxon": "^3.7.1",
......
import axios from './axios'
import { BaseRespData } from '@/types/api'
import { statisticData, trendType } from '@/types/api/statistic'
//1、核心数据看板
export function getStatisticData() {
return axios.get<never, BaseRespData<statisticData>>(
'factory/overview/statistic',
)
}
//2、趋势图(订单趋势+24/48发货率图+超时发货率图)
export function trendApi(params: {
timeUnit: string | number
trendType: string
}) {
return axios.post<never, BaseRespData<trendType[]>>(
'/factory/overview/trend',
params,
)
}
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
> >
复制店铺单号 复制店铺单号
</div> </div>
<div v-if="showCopyCount" class="menu-item" @click="onChangeCopy('count')"> <div v-if="showCopyCount" class="menu-item" @click="onChangeCopy('count')">
统计数量 统计数量
</div> </div>
...@@ -28,7 +29,7 @@ ...@@ -28,7 +29,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, defineExpose, onMounted, onUnmounted } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
const visible = ref(false) const visible = ref(false)
const position = ref({ x: 0, y: 0 }) const position = ref({ x: 0, y: 0 })
......
export interface statisticData {
todayNewOrderNum: number
todayNewProductNum: number
todayConfirmOrderNum: number
todayConfirmProductNum: number
notShipmentOrderNum: number
overTimeNotShipmentOrderNum: number
yesterdayShipmentOrderNum: number
beforeYesterdayShipmentOrderNum: number
compareYesterdayShipmentOrderNum: number
shipmentRateOf24Hour: number
compareLastDayShipmentRateOf24Hour: number
shipmentRateOf48Hour: number
compareLastDayShipmentRateOf48Hour: number
overTimeShipmentRate: number
compareLastDayOverTimeShipmentRate: number
waitCreateLogisticOrderNum: number
waitArrangeOrderNum: number
producingOrderNum: number
waitShipmentNum: number
blockApplyOrderNum: number
exceptionOrderNum: number
outOfStockOrderNum: number
outOfStock24HourOrderNum: number
outOfStock48HourOrderNum: number
outOfStockOver48HourOrderNum: number
outOfStockSkuNum: number
outOfStockProductNum: number
yesterdayOverTimeShipmentOrderNum: number
}
export interface trendType {
confirmNum: number // 接单数
produceNum: number // 生产数
shipmentNum: number // 发货数
shipmentRateOf24Hour: number // 24小时发货率
shipmentRateOf48Hour: number // 48小时发货率
overtimeShipmentRate: number // 超48小时发货率
overtimeShipmentOrderNum: number // 超时发货单数
startTime: string // 周期开始时间
endTime: string // 周期结束时间
}
<template> <template>
<div class="card h-100 dashboard"> <div class="dashboard-wrapper">
<div class="card-wrapper">
<div class="dashboard-title">核心数据看板</div>
<div class="card-container-wrapper">
<div class="card-container">
<div class="card-item">
<div class="card-item-header">
<div class="card-title">今日新单(件/单)</div>
<el-tooltip
class="item"
effect="light"
content="今日新增订单的订单数量"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{
(statisticData?.todayNewProductNum ?? '-') +
'/' +
(statisticData?.todayNewOrderNum ?? '-')
}}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">今日接单(件/单)</div>
<el-tooltip
class="item"
effect="light"
content="今日新增确认生产的订单数量"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{
(statisticData?.todayConfirmProductNum ?? '-') +
'/' +
(statisticData?.todayConfirmOrderNum ?? '-')
}}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">未发货订单(单)</div>
<el-tooltip
class="item"
effect="light"
content="目前确认生产后还未转为已发货状态的总订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{ statisticData?.notShipmentOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">超时未发订单(单)</div>
<el-tooltip
class="item"
effect="light"
content="目前确认生产后超过48H还未转为已发货状态的总订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-red">
{{ statisticData?.overTimeNotShipmentOrderNum ?? '-' }}
</div>
</div>
</div>
</div>
<div class="card-container" :gutter="20">
<div class="card-item">
<div class="card-item-header">
<div class="card-title">昨日发货数(单)</div>
<el-tooltip
class="item"
effect="light"
content="昨日转为已发货状态的总订单数(昨日北京时间0-24点数据)"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{ statisticData?.yesterdayShipmentOrderNum ?? '-' }}
</div>
<div class="card-item-content-text">
较前天
<div <div
v-if="userInfo" v-if="
class="factory-info flex h-100 flex-center" statisticData &&
statisticData.compareYesterdayShipmentOrderNum >= 0
"
style="display: flex; margin-left: 10px"
> >
<span class="title">{{ userInfo.factory.title }}</span> <div class="text-red">
<span>欢迎您!</span> {{ statisticData?.compareYesterdayShipmentOrderNum }}
</div>
<div class="up-icon"></div>
</div>
<div v-else style="display: flex; margin-left: 10px">
<div class="text-green">
{{
Math.abs(
statisticData?.compareYesterdayShipmentOrderNum || 0,
)
}}
</div>
<div class="down-icon"></div>
</div>
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">24h发货率</div>
<el-tooltip
class="item"
effect="light"
content="昨日已发货订单中从接单转为已发货状态在24小时内的比例(昨日北京时间0-24点数据)"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{ (statisticData?.shipmentRateOf24Hour ?? '-') + '%' }}
</div>
<div class="card-item-content-text">
较前天
<div
v-if="
statisticData &&
statisticData.compareLastDayShipmentRateOf24Hour >= 0
"
style="display: flex; margin-left: 10px"
>
<div class="text-red">
{{
(statisticData?.compareLastDayShipmentRateOf24Hour ??
'-') + '%'
}}
</div>
<div class="up-icon"></div>
</div>
<div v-else style="display: flex; margin-left: 10px">
<div class="text-green">
{{
Math.abs(
statisticData?.compareLastDayShipmentRateOf24Hour || 0,
) + '%'
}}
</div>
<div class="down-icon"></div>
</div>
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">48h发货率</div>
<el-tooltip
class="item"
effect="light"
content="昨日已发货订单中从接单转为已发货状态在48小时内的比例(昨日北京时间0-24点数据)"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{ (statisticData?.shipmentRateOf48Hour ?? '-') + '%' }}
</div>
<div class="card-item-content-text">
较前天
<div
v-if="
statisticData &&
statisticData.compareLastDayShipmentRateOf48Hour >= 0
"
style="display: flex; margin-left: 10px"
>
<div class="text-red">
{{
(statisticData?.compareLastDayShipmentRateOf48Hour ??
'-') + '%'
}}
</div>
<div class="up-icon"></div>
</div>
<div v-else style="display: flex; margin-left: 10px">
<div class="text-green">
{{
Math.abs(
statisticData?.compareLastDayShipmentRateOf48Hour || 0,
) + '%'
}}
</div>
<div class="down-icon"></div>
</div>
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">发货超时率</div>
<el-tooltip
class="item"
effect="light"
content="昨日已发货订单中从接单转为已发货状态大于48小时的比例(昨日北京时间0-24点数据)"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-red">
{{
(statisticData?.overTimeShipmentRate ?? '-') +
'%(' +
(statisticData?.yesterdayOverTimeShipmentOrderNum ?? '-') +
')'
}}
</div>
<div class="card-item-content-text">
较前天
<div
v-if="
statisticData &&
statisticData.compareLastDayOverTimeShipmentRate >= 0
"
style="display: flex; margin-left: 10px"
>
<div class="text-red">
{{
(statisticData?.compareLastDayOverTimeShipmentRate ??
'-') + '%'
}}
</div>
<div class="up-icon"></div>
</div>
<div v-else style="display: flex; margin-left: 10px">
<div class="text-green">
{{
Math.abs(
statisticData?.compareLastDayOverTimeShipmentRate || 0,
) + '%'
}}
</div>
<div class="down-icon"></div>
</div>
</div>
</div>
</div>
</div>
<div class="card-container">
<div class="card-item">
<div class="card-item-header">
<div class="card-title">待创建物流(单)</div>
<el-tooltip
class="item"
effect="light"
content="目前订单中待创建物流的总订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{ statisticData?.waitCreateLogisticOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">待排单(单)</div>
<el-tooltip
class="item"
effect="light"
content="目前订单中待排单的总生产数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{ statisticData?.waitArrangeOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">生产中(单)</div>
<el-tooltip
class="item"
effect="light"
content="目前订单中排单完成后到发货前的总生产单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{ statisticData?.producingOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">待发货(单)</div>
<el-tooltip
class="item"
effect="light"
content="目前订单中待发货状态的总订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-blue">
{{ statisticData?.waitShipmentNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">拦截申请(单)</div>
<el-tooltip
class="item"
effect="light"
content="目前在生产拦截申请和发货拦截申请中的总订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-yellow">
{{ statisticData?.blockApplyOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">异常单(单)</div>
<el-tooltip
class="item"
effect="light"
content="所有订单中的异常单总单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div class="card-item-content text-red">
{{ statisticData?.exceptionOrderNum ?? '-' }}
</div>
</div>
</div>
</div>
<div class="card-container">
<div class="card-item">
<div class="card-item-header">
<div class="card-title">缺货中订单(单)</div>
<el-tooltip
class="item"
effect="light"
content="缺货统计中的商品总订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-yellow">
{{ statisticData?.outOfStockOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">缺货&lt;24h单(单)</div>
<el-tooltip
class="item"
effect="light"
content="缺货总订单中的缺货时长在24h内的订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-yellow">
{{ statisticData?.outOfStock24HourOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">缺货&lt;48h单(单)</div>
<el-tooltip
class="item"
effect="light"
content="缺货总订单中的缺货时长在48h内的订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div class="card-item-content text-yellow">
{{ statisticData?.outOfStock48HourOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">缺货≥48h单(单)</div>
<el-tooltip
class="item"
effect="light"
content="缺货总订单中的缺货时长大于48h内的订单数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-red">
{{ statisticData?.outOfStockOver48HourOrderNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">缺货库存SKU数</div>
<el-tooltip
class="item"
effect="light"
content="缺货的库存SKU的总数目"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-red">
{{ statisticData?.outOfStockSkuNum ?? '-' }}
</div>
</div>
</div>
<div class="card-item">
<div class="card-item-header">
<div class="card-title">缺货总件数</div>
<el-tooltip
class="item"
effect="light"
content="缺货的货物总件数"
placement="bottom"
>
<div class="card-icon"></div>
</el-tooltip>
</div>
<div class="card-item-content-box">
<div v-auto-fit-text class="card-item-content text-yellow">
{{ statisticData?.outOfStockProductNum ?? '-' }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="chart-wrapper" style="width: 100%">
<el-radio-group
v-model="chartTimes1"
size="small"
class="chart-controls"
@change="getchartTimes($event, 'ORDER_TREND')"
>
<el-radio-button
v-for="time in timeOptions"
:key="time.type"
:value="time.type"
:label="time.name"
>
</el-radio-button>
</el-radio-group>
<div
ref="orderChart1"
v-loading="orderChart1Loading"
class="chart-container"
style="width: 100%"
/>
</div>
<div class="charts-row">
<div class="chart-wrapper chart-half">
<el-radio-group
v-model="chartTimes2"
size="small"
class="chart-controls"
@change="getchartTimes($event, 'SHIPMENT_TREND')"
>
<el-radio-button
v-for="time in timeOptions"
:key="time.type"
:value="time.type"
:label="time.name"
>
</el-radio-button>
</el-radio-group>
<div
ref="orderChart2"
v-loading="orderChart2Loading"
class="chart-container"
style="width: 100%"
/>
</div>
<div class="chart-wrapper chart-half">
<el-radio-group
v-model="chartTimes3"
size="small"
class="chart-controls"
@change="getchartTimes($event, 'OVERTIME_SHIPMENT_TREND')"
>
<el-radio-button
v-for="time in timeOptions"
:key="time.type"
:value="time.type"
:label="time.name"
>
</el-radio-button>
</el-radio-group>
<div
ref="orderChart3"
v-loading="orderChart3Loading"
class="chart-container"
style="width: 100%"
/>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useUserStore from '@/store/user' import { ref, onMounted, onBeforeUnmount, nextTick, type Ref } from 'vue'
const userInfo = useUserStore().user import * as echarts from 'echarts/core'
import {
TitleComponent,
GridComponent,
LegendComponent,
} from 'echarts/components'
import { LineChart } from 'echarts/charts'
import { UniversalTransition } from 'echarts/features'
import { CanvasRenderer } from 'echarts/renderers'
import { getStatisticData, trendApi } from '@/api/statistic'
import type {
statisticData as StatisticData,
trendType as trendType,
} from '@/types/api/statistic'
import { TooltipComponent } from 'echarts/components'
echarts.use([TooltipComponent])
echarts.use([
TitleComponent,
GridComponent,
LegendComponent,
LineChart,
CanvasRenderer,
UniversalTransition,
])
// 获取统计数据
const statisticData = ref<StatisticData | null>(null)
// 图表 loading 状态(无数据时展示 loading)
const orderChart1Loading = ref<boolean>(true)
const orderChart2Loading = ref<boolean>(true)
const orderChart3Loading = ref<boolean>(true)
const timeOptions = ref<{ type: string; name: string }[]>([
{ type: '0', name: '日数据' },
{ type: '1', name: '周数据' },
{ type: '2', name: '月数据' },
])
const chartTimes1 = ref<string>('0')
const chartTimes2 = ref<string>('0')
const chartTimes3 = ref<string>('0')
// 图表容器引用
const orderChart1 = ref<HTMLDivElement | null>(null)
const orderChart2 = ref<HTMLDivElement | null>(null)
const orderChart3 = ref<HTMLDivElement | null>(null)
// 图表实例数组
const chartInstances: (echarts.ECharts | null)[] = [null, null, null]
type ChartSeriesItem = {
name: string
data: (number | { value: number; count?: number })[]
color: string
}
type ChartConfig = {
title: string
legend: string[]
xAxisData: string[]
series: ChartSeriesItem[]
}
// 图表配置
const chartConfig1 = ref<ChartConfig>({
title: '订单趋势统计(单)',
legend: ['接单数', '生产数', '发货数'],
xAxisData: [''],
series: [
{
name: '接单数',
data: [0],
color: '#30d2cb',
},
{
name: '生产数',
data: [0],
color: '#9e95ff',
},
{
name: '发货数',
data: [0],
color: '#6bb3fd',
},
],
})
const chartConfig2 = ref<ChartConfig>({
title: '订单发货效率',
legend: ['24H发货率', '48H发货率'],
xAxisData: [''],
series: [
{
name: '24H发货率',
data: [0],
color: '#3e9fff',
},
{
name: '48H发货率',
data: [0],
color: '#ac84ff',
},
],
})
const chartConfig3 = ref<ChartConfig>({
title: '订单超时情况',
legend: ['发货超时率'],
xAxisData: [''],
series: [
{
name: '发货超时率',
data: [{ value: 0, count: 0 }],
color: '#ff6845',
},
],
})
const overtimeTooltipFormatter = (params: TooltipParam[]) =>
params
.map((item) => {
const dataObj =
item.data && typeof item.data === 'object'
? (item.data as { value: number | string; count?: number })
: null
const count =
dataObj && typeof dataObj.count === 'number' ? dataObj.count : null
const valueText = `${item.value}%`
return `${item.marker}${item.seriesName}: ${valueText}${
count !== null ? `(${count}单)` : ''
}`
})
.join('<br/>')
// 创建图表配置的函数
type TooltipParam = {
marker: string
seriesName: string
value: number | string
data?: unknown
}
const createChartOption = (
config: Ref<ChartConfig>,
isPercent = false,
customFormatter?: (params: TooltipParam[]) => string,
) => {
const data = config.value
return {
title: {
text: data.title,
left: 'left',
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
type: 'dashed',
},
label: {
backgroundColor: '#6a7985',
},
},
formatter:
customFormatter ??
((params: TooltipParam[]) =>
params
.map(
(item) =>
`${item.marker}${item.seriesName}: ${
isPercent ? `${item.value}%` : item.value
}`,
)
.join('<br/>')),
},
legend: {
data: data.legend,
top: 'top',
left: 'center',
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: data.xAxisData,
axisLabel: {
// 关键配置:添加省略号
overflow: 'truncate', // 截断显示
width: 120, // 每个标签的最大宽度(像素)
formatter: function (value: string) {
// 手动截断,超过6个字符加省略号
if (value.length > 10) {
return value.substring(0, 10) + '...'
}
return value
},
},
},
yAxis: {
type: 'value',
axisLabel: isPercent
? {
formatter: '{value}%',
color: '#666',
}
: {
color: '#666',
},
axisLine: {
lineStyle: { color: '#ccc' },
},
splitLine: {
lineStyle: { color: '#eee' },
},
},
series: data.series.map((item) => ({
name: item.name,
type: 'line',
data: item.data,
smooth: true,
lineStyle: {
color: item.color,
width: 3,
},
itemStyle: {
color: item.color,
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: item.color,
},
{
offset: 1,
color: item.color + '00', // 添加透明度,00 表示完全透明
},
],
globalCoord: false,
},
},
})),
}
}
// 自动缩放文字:保证卡片内数字不超出边框宽度,超出则减小字号
const adjustTextSize = (el: HTMLElement) => {
const maxFontSize = 45
const minFontSize = 16
const card = el.closest('.card-item') as HTMLElement | null
const containerWidth = card?.clientWidth ?? el.parentElement?.clientWidth ?? 0
// 获取 card-item-content-text 的实际宽度
const contentBox = el.parentElement
const contentText = contentBox?.querySelector(
'.card-item-content-text',
) as HTMLElement | null
let contentTextWidth = 0
let gap = 0
let marginRight = 0
if (contentText && contentBox) {
contentTextWidth = contentText.offsetWidth
// 获取 gap 值
const contentBoxStyle = window.getComputedStyle(contentBox)
gap = parseFloat(contentBoxStyle.gap) || 0
// 获取 margin-right 值
const contentTextStyle = window.getComputedStyle(contentText)
marginRight = parseFloat(contentTextStyle.marginRight) || 0
}
const limitWidth = containerWidth - contentTextWidth - gap - marginRight - 20
el.style.fontSize = `${maxFontSize}px`
let current = maxFontSize
while (current > minFontSize && el.scrollWidth > limitWidth) {
current -= 1
el.style.fontSize = `${current}px`
}
}
const vAutoFitText = {
mounted(el: HTMLElement) {
nextTick(() => {
adjustTextSize(el)
})
const card = el.closest('.card-item') as HTMLElement | null
if (card && 'ResizeObserver' in window) {
const observer = new ResizeObserver(() => {
adjustTextSize(el)
})
observer.observe(card)
;(el as { __autoFitObserver__?: ResizeObserver }).__autoFitObserver__ =
observer
}
},
updated(el: HTMLElement) {
nextTick(() => {
adjustTextSize(el)
})
},
unmounted(el: HTMLElement) {
const observer = (el as { __autoFitObserver__?: ResizeObserver })
.__autoFitObserver__
if (observer) {
observer.disconnect()
delete (el as { __autoFitObserver__?: ResizeObserver })
.__autoFitObserver__
}
},
}
const getStatisticDataApi = async () => {
// 统计数据
getStatisticData()
.then((res) => {
if (res.code === 200) {
statisticData.value = res.data
}
})
.catch((error) => {
console.error('获取统计数据失败:', error)
})
// 订单趋势统计
trendApi({
timeUnit: 0,
trendType: 'ORDER_TREND',
})
.then((res) => {
if (res.code === 200) {
updateOrderTrendChart(res.data, 0)
chartInstances[0]?.setOption(createChartOption(chartConfig1))
// 无数据时保持 loading,有数据则关闭
orderChart1Loading.value = !(res.data && res.data.length > 0)
}
})
.catch((error) => {
console.error('获取订单趋势数据失败:', error)
})
// 订单发货效率
trendApi({
timeUnit: 0,
trendType: 'SHIPMENT_TREND',
})
.then((res) => {
if (res.code === 200) {
updateShipmentTrendChart(res.data, 0)
chartInstances[1]?.setOption(createChartOption(chartConfig2, true))
orderChart2Loading.value = !(res.data && res.data.length > 0)
}
})
.catch((error) => {
console.error('获取发货趋势数据失败:', error)
})
// 订单生产效率
trendApi({
timeUnit: 0,
trendType: 'OVERTIME_SHIPMENT_TREND',
})
.then((res) => {
if (res.code === 200) {
updateOvertimeTrendChart(res.data, 0)
chartInstances[2]?.setOption(
createChartOption(chartConfig3, true, overtimeTooltipFormatter),
)
orderChart3Loading.value = !(res.data && res.data.length > 0)
}
})
.catch((error) => {
console.error('获取超时发货趋势数据失败:', error)
})
}
onMounted(async () => {
await nextTick()
// 订单趋势统计
if (orderChart1.value) {
chartInstances[0] = echarts.init(orderChart1.value)
chartInstances[0]?.setOption(createChartOption(chartConfig1))
}
// 订单发货效率
if (orderChart2.value) {
chartInstances[1] = echarts.init(orderChart2.value)
chartInstances[1]?.setOption(createChartOption(chartConfig2, true))
}
// 订单生产效率
if (orderChart3.value) {
chartInstances[2] = echarts.init(orderChart3.value)
chartInstances[2]?.setOption(
createChartOption(chartConfig3, true, overtimeTooltipFormatter),
)
}
// 请求成功后立即更新对应的图表
getStatisticDataApi()
// 监听窗口大小变化,自动调整图表大小
window.addEventListener('resize', handleResize)
})
const getchartTimes = async (v: string, type: string) => {
try {
// 切换时间维度时先开启对应 loading,等数据返回后根据是否有数据决定是否保留
if (type === 'ORDER_TREND') {
orderChart1Loading.value = true
} else if (type === 'SHIPMENT_TREND') {
orderChart2Loading.value = true
} else if (type === 'OVERTIME_SHIPMENT_TREND') {
orderChart3Loading.value = true
}
const { data } = await trendApi({
timeUnit: v,
trendType: type,
})
if (type == 'ORDER_TREND') {
updateOrderTrendChart(data, v)
chartInstances[0]?.setOption(createChartOption(chartConfig1))
orderChart1Loading.value = !(data && data.length > 0)
} else if (type == 'SHIPMENT_TREND') {
updateShipmentTrendChart(data, v)
chartInstances[1]?.setOption(createChartOption(chartConfig2, true))
orderChart2Loading.value = !(data && data.length > 0)
} else {
updateOvertimeTrendChart(data, v)
chartInstances[2]?.setOption(
createChartOption(chartConfig3, true, overtimeTooltipFormatter),
)
orderChart3Loading.value = !(data && data.length > 0)
}
} catch (error) {
console.log(error)
}
}
const updateOrderTrendChart = (data: trendType[], type: number | string) => {
const confirmNums = data.map((el) => el.confirmNum)
const produceNums = data.map((el) => el.produceNum)
const shipmentNums = data.map((el) => el.shipmentNum)
const startTimes = data.map((el) => el.startTime)
const timerange = data.map((el) => `${el.startTime}——${el.endTime}`)
if (type == 0) {
chartConfig1.value.xAxisData = startTimes
} else {
chartConfig1.value.xAxisData = timerange
}
chartConfig1.value.series[0].data = confirmNums
chartConfig1.value.series[1].data = produceNums
chartConfig1.value.series[2].data = shipmentNums
}
const updateShipmentTrendChart = (data: trendType[], type: number | string) => {
const shipmentRateOf24Hours = data.map((el) => el.shipmentRateOf24Hour)
const shipmentRateOf48Hours = data.map((el) => el.shipmentRateOf48Hour)
const startTimes = data.map((el) => el.startTime)
const timerange = data.map((el) => `${el.startTime}-${el.endTime}`)
if (type == 0) {
chartConfig2.value.xAxisData = startTimes
} else {
chartConfig2.value.xAxisData = timerange
}
chartConfig2.value.series[0].data = shipmentRateOf24Hours
chartConfig2.value.series[1].data = shipmentRateOf48Hours
}
const updateOvertimeTrendChart = (data: trendType[], type: number | string) => {
const overtimeShipmentOrderNums = data.map(
(el) => el.overtimeShipmentOrderNum,
)
const overtimeShipmentRates = data.map((el) => el.overtimeShipmentRate)
const startTimes = data.map((el) => el.startTime)
const timerange = data.map((el) => `${el.startTime}-${el.endTime}`)
if (type == 0) {
chartConfig3.value.xAxisData = startTimes
} else {
chartConfig3.value.xAxisData = timerange
}
chartConfig3.value.series[0].data = overtimeShipmentRates.map(
(rate, idx) => ({
value: rate,
count: overtimeShipmentOrderNums[idx],
}),
)
}
const handleResize = () => {
chartInstances.forEach((instance) => {
instance?.resize()
})
}
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
chartInstances.forEach((instance, index) => {
instance?.dispose()
chartInstances[index] = null
})
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.factory-info { .dashboard-title {
font-size: 20px;
font-weight: 600;
color: #333;
padding: 10px 20px;
}
.card-wrapper {
background-color: #fff;
}
.card-container-wrapper {
padding: 0 20px 20px;
}
.card-container {
width: 100%;
background-color: #fff;
border-radius: 5px;
box-sizing: border-box;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
&:not(:last-child) {
margin-bottom: 20px;
}
font-size: 24px; .card-item {
.title { min-width: 270px;
margin-right: 10px; height: 120px;
background-color: #f7f7f7;
border-radius: 5px;
padding: 8px 15px;
display: flex;
flex-direction: column;
justify-content: space-between;
.card-item-header {
display: flex;
align-items: center;
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.card-icon {
width: 20px;
height: 20px;
cursor: pointer;
background-size: 100% 100%;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256' viewBox='0 0 256 256'%3E%3Cpath fill='%238e8d8d' d='M140 180a12 12 0 1 1-12-12a12 12 0 0 1 12 12M128 72c-22.06 0-40 16.15-40 36v4a8 8 0 0 0 16 0v-4c0-11 10.77-20 24-20s24 9 24 20s-10.77 20-24 20a8 8 0 0 0-8 8v8a8 8 0 0 0 16 0v-.72c18.24-3.35 32-17.9 32-35.28c0-19.85-17.94-36-40-36m104 56A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88'/%3E%3C/svg%3E");
}
}
.card-item-content-box {
display: flex;
align-items: flex-end;
gap: 10px;
.card-item-content {
font-size: 50px;
font-weight: 500; font-weight: 500;
color: green; white-space: nowrap;
}
.card-item-content-text {
font-size: 14px;
color: #666;
display: flex;
align-items: center;
margin-right: 10px;
width: 100%;
white-space: nowrap;
margin-bottom: 3px;
}
.up-icon {
width: 18px;
height: 18px;
background-size: 100% 100%;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256' viewBox='0 0 24 24'%3E%3Cpath fill='%23f56c6c' d='M12.725 2.451a.75.75 0 0 0-1.007-.013L3.504 9.69A.75.75 0 0 0 4 11.003h4.382c.138 1.255.057 2.576-.252 3.969c-.318 1.434-1.722 3.25-4.515 5.438a.75.75 0 0 0 .535 1.337c3.311-.322 6.112-1.537 8.365-3.662c2.08-1.963 3.228-4.334 3.422-7.082H20a.75.75 0 0 0 .511-1.299z'/%3E%3C/svg%3E");
}
.down-icon {
width: 18px;
height: 18px;
background-size: 100% 100%;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256' viewBox='0 0 24 24'%3E%3Cpath fill='%2367C23A' d='M4.15 2.253a.75.75 0 0 0-.535 1.337C6.408 5.778 7.812 7.594 8.13 9.028c.309 1.393.39 2.714.252 3.969H4a.75.75 0 0 0-.496 1.312l8.214 7.253a.75.75 0 0 0 1.007-.013l7.786-7.253a.75.75 0 0 0-.511-1.3h-4.063c-.194-2.747-1.342-5.118-3.422-7.08C10.262 3.79 7.46 2.574 4.15 2.252'/%3E%3C/svg%3E");
}
}
}
}
.text-blue {
color: #409eff;
}
.text-green {
color: #67c23a;
}
.text-red {
color: #f56c6c;
}
.text-yellow {
color: #e6a23c;
}
.dashboard-wrapper {
height: 100%;
width: 100%;
margin: 0 auto;
overflow-x: hidden;
}
.chart-wrapper {
position: relative;
margin-top: 20px;
}
.chart-container {
min-height: 500px;
height: 500px;
background-color: #fff;
border-radius: 5px;
padding: 20px;
box-sizing: border-box;
}
.chart-controls {
position: absolute;
top: 30px;
right: 30px;
z-index: 10;
}
.charts-row {
display: flex;
gap: 10px;
}
.chart-half {
width: 50%;
}
@media (max-width: 1000px) {
.charts-row {
flex-direction: column;
}
.chart-half {
width: 100%;
} }
} }
</style> </style>
...@@ -706,6 +706,44 @@ function changeChinaTime(zone: string) { ...@@ -706,6 +706,44 @@ function changeChinaTime(zone: string) {
}) })
} }
const handleRadioGroupClick = (event: Event) => {
const target = event.target as HTMLElement
const radioButton = target.closest('.el-radio-button')
if (radioButton) {
const input = radioButton.querySelector(
'input[type="radio"]',
) as HTMLInputElement
if (input) {
const value = input.value
if (searchForm.value.customizedQuantity === value) {
event.preventDefault()
event.stopPropagation()
searchForm.value.customizedQuantity = ''
}
}
}
}
const handleMultiRadioGroupClick = (event: Event) => {
const target = event.target as HTMLElement
const radioButton = target.closest('.el-radio-button')
if (radioButton) {
const input = radioButton.querySelector(
'input[type="radio"]',
) as HTMLInputElement
if (input) {
const value = input.value === 'true' ? true : false
if (searchForm.value.multi === value) {
event.preventDefault()
event.stopPropagation()
searchForm.value.multi = null
}
}
}
}
const resultConfirm = () => { const resultConfirm = () => {
search() search()
loadTabData() loadTabData()
...@@ -796,7 +834,7 @@ onMounted(() => { ...@@ -796,7 +834,7 @@ onMounted(() => {
clearable clearable
filterable filterable
popper-class="customize-select-style" popper-class="customize-select-style"
style="width: 150px" style="width: 180px"
> >
<ElOption <ElOption
v-for="(item, index) in platformJson" v-for="(item, index) in platformJson"
...@@ -878,16 +916,27 @@ onMounted(() => { ...@@ -878,16 +916,27 @@ onMounted(() => {
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
<ElFormItem label="类型"> <ElFormItem
label="印刷"
@click.stop="(e: Event) => handleRadioGroupClick(e)"
>
<el-radio-group v-model="searchForm.customizedQuantity"> <el-radio-group v-model="searchForm.customizedQuantity">
<el-radio-button value="single">单面</el-radio-button> <el-radio-button value="single">单面</el-radio-button>
<el-radio-button value="multiple">多面</el-radio-button> <el-radio-button value="multiple">多面</el-radio-button>
<el-radio-button value="normal">普品</el-radio-button>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem label="数量"> <ElFormItem label="数量">
<el-radio-group v-model="searchForm.multi"> <el-radio-group
<el-radio-button :value="false">单件</el-radio-button> v-model="searchForm.multi"
<el-radio-button :value="true">多件</el-radio-button> @click.stop="(e: Event) => handleMultiRadioGroupClick(e)"
>
<el-radio-button :value="false" class="radioBtn"
>单件</el-radio-button
>
<el-radio-button :value="true" class="radioBtn"
>多件</el-radio-button
>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem> <ElFormItem>
...@@ -1283,12 +1332,9 @@ onMounted(() => { ...@@ -1283,12 +1332,9 @@ onMounted(() => {
} }
} }
} }
.search-form { .radioBtn {
::v-deep .el-radio-button { ::v-deep .el-radio-button__inner {
width: 75px; width: 75px;
.el-radio-button__inner {
width: 100%;
}
} }
} }
</style> </style>
...@@ -102,7 +102,8 @@ const tableColumns = computed(() => { ...@@ -102,7 +102,8 @@ const tableColumns = computed(() => {
prop: 'replaceShipment', prop: 'replaceShipment',
width: 120, width: 120,
align: 'center', align: 'center',
},{ },
{
label: '物流类型', label: '物流类型',
slot: 'shipmentType', slot: 'shipmentType',
prop: 'shipmentType', prop: 'shipmentType',
...@@ -297,6 +298,24 @@ const goodsColumns = computed(() => { ...@@ -297,6 +298,24 @@ const goodsColumns = computed(() => {
}, },
] ]
}) })
const productMarkList = [
{
label: '普通商品',
value: 'normal',
},
{
label: 'pod商品',
value: 'pod',
},
{
label: '一件定制局部印',
value: 'custom_part',
},
{
label: '一件定制满印',
value: 'custom_full',
},
]
const tableData = ref<PodCnOrderListData[]>([]) const tableData = ref<PodCnOrderListData[]>([])
const goodsData = ref<ProductList[]>([]) const goodsData = ref<ProductList[]>([])
const searchVisible = ref(false) const searchVisible = ref(false)
...@@ -377,7 +396,7 @@ const search = () => { ...@@ -377,7 +396,7 @@ const search = () => {
goodsData.value = [] goodsData.value = []
logList.value = [] logList.value = []
getOrderListFn() getOrderListFn()
if(tabValue.value==='2'){ if (tabValue.value === '2') {
zoneType.value = 'Asia/Shanghai' zoneType.value = 'Asia/Shanghai'
operationLog() operationLog()
} }
...@@ -652,7 +671,7 @@ const refreshAddress = async () => { ...@@ -652,7 +671,7 @@ const refreshAddress = async () => {
ElMessage.success('操作成功') ElMessage.success('操作成功')
search() search()
await loadTabData() await loadTabData()
if(tabValue.value==='2'){ if (tabValue.value === '2') {
zoneType.value = 'Asia/Shanghai' zoneType.value = 'Asia/Shanghai'
await operationLog() await operationLog()
} }
...@@ -663,7 +682,6 @@ const refreshAddress = async () => { ...@@ -663,7 +682,6 @@ const refreshAddress = async () => {
} }
} }
/** /**
* @description: 获取打印面单 * @description: 获取打印面单
*/ */
...@@ -751,6 +769,44 @@ const handleSelectionChange = (val: PodCnOrderListData[]) => { ...@@ -751,6 +769,44 @@ const handleSelectionChange = (val: PodCnOrderListData[]) => {
selection.value = val selection.value = val
} }
const handleRadioGroupClick = (event: Event) => {
const target = event.target as HTMLElement
const radioButton = target.closest('.el-radio-button')
if (radioButton) {
const input = radioButton.querySelector(
'input[type="radio"]',
) as HTMLInputElement
if (input) {
const value = input.value
if (searchForm.value.customizedQuantity === value) {
event.preventDefault()
event.stopPropagation()
searchForm.value.customizedQuantity = ''
}
}
}
}
const handleMultiRadioGroupClick = (event: Event) => {
const target = event.target as HTMLElement
const radioButton = target.closest('.el-radio-button')
if (radioButton) {
const input = radioButton.querySelector(
'input[type="radio"]',
) as HTMLInputElement
if (input) {
const value = input.value === 'true' ? true : false
if (searchForm.value.multi === value) {
event.preventDefault()
event.stopPropagation()
searchForm.value.multi = null
}
}
}
}
/** /**
* @description: 页面添加回车监听 * @description: 页面添加回车监听
*/ */
...@@ -814,7 +870,7 @@ onMounted(() => { ...@@ -814,7 +870,7 @@ onMounted(() => {
clearable clearable
filterable filterable
placeholder="请输入" placeholder="请输入"
style="width: 150px" style="width: 180px"
> >
<el-option <el-option
v-for="item in warehouseList" v-for="item in warehouseList"
...@@ -900,16 +956,27 @@ onMounted(() => { ...@@ -900,16 +956,27 @@ onMounted(() => {
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
<!-- <ElFormItem label="类型"> <ElFormItem label="印刷">
<el-radio-group v-model="searchForm.customizedQuantity"> <el-radio-group
v-model="searchForm.customizedQuantity"
@click.stop="(e: Event) => handleRadioGroupClick(e)"
>
<el-radio-button value="single">单面</el-radio-button> <el-radio-button value="single">单面</el-radio-button>
<el-radio-button value="multiple">多面</el-radio-button> <el-radio-button value="multiple">多面</el-radio-button>
<el-radio-button value="normal">普品</el-radio-button>
</el-radio-group> </el-radio-group>
</ElFormItem> --> </ElFormItem>
<ElFormItem label="数量"> <ElFormItem label="数量">
<el-radio-group v-model="searchForm.multi"> <el-radio-group
<el-radio-button :value="false">单件</el-radio-button> v-model="searchForm.multi"
<el-radio-button :value="true">多件</el-radio-button> @click.stop="(e: Event) => handleMultiRadioGroupClick(e)"
>
<el-radio-button :value="false" class="radioBtn"
>单件</el-radio-button
>
<el-radio-button :value="true" class="radioBtn"
>多件</el-radio-button
>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem> <ElFormItem>
...@@ -1042,7 +1109,32 @@ onMounted(() => { ...@@ -1042,7 +1109,32 @@ onMounted(() => {
></ElOption> ></ElOption>
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
<ElFormItem v-if="searchForm.replaceShipment || searchForm.replaceShipment===0" label="物流类型"> <ElFormItem label="商品类型">
<ElSelect
v-model="searchForm.productMark"
placeholder="请选择商品类型"
clearable
filterable
collapse-tags
collapse-tags-tooltip
:teleported="false"
style="width: 150px"
>
<ElOption
v-for="(item, index) in productMarkList"
:key="index"
:value="item.value"
:label="item.label"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem
v-if="
searchForm.replaceShipment ||
searchForm.replaceShipment === 0
"
label="物流类型"
>
<ElSelect <ElSelect
v-model="searchForm.shipmentType" v-model="searchForm.shipmentType"
placeholder="物流类型" placeholder="物流类型"
...@@ -1050,7 +1142,9 @@ onMounted(() => { ...@@ -1050,7 +1142,9 @@ onMounted(() => {
style="width: 150px" style="width: 150px"
> >
<ElOption <ElOption
v-for="(item, index) in searchForm.replaceShipment === 0? ['自提', '快递']:['自有物流', '工厂物流']" v-for="(item, index) in searchForm.replaceShipment === 0
? ['自提', '快递']
: ['自有物流', '工厂物流']"
:key="index" :key="index"
:value="index" :value="index"
:label="item" :label="item"
...@@ -1136,10 +1230,14 @@ onMounted(() => { ...@@ -1136,10 +1230,14 @@ onMounted(() => {
<div>{{ getStatus(row.status) }}</div> <div>{{ getStatus(row.status) }}</div>
</template> </template>
<template #shipmentType="{ row }"> <template #shipmentType="{ row }">
{{ (row.replaceShipment === 0 ? ['自提', '快递'] : ['自有物流', '工厂物流'])[row.shipmentType] }} {{
(row.replaceShipment === 0
? ['自提', '快递']
: ['自有物流', '工厂物流'])[row.shipmentType]
}}
</template> </template>
<template #replaceShipment="{ row }"> <template #replaceShipment="{ row }">
{{['不代发', '代发'][row.replaceShipment] }} {{ ['不代发', '代发'][row.replaceShipment] }}
</template> </template>
</TableView> </TableView>
</div> </div>
...@@ -1185,8 +1283,7 @@ onMounted(() => { ...@@ -1185,8 +1283,7 @@ onMounted(() => {
style="margin-left: 10px" style="margin-left: 10px"
@click="changeChinaTime('Asia/Shanghai')" @click="changeChinaTime('Asia/Shanghai')"
>北京时间 >北京时间
</el-button </el-button>
>
<el-button <el-button
:type="zoneType === 'America/New_York' ? 'primary' : ''" :type="zoneType === 'America/New_York' ? 'primary' : ''"
size="small" size="small"
...@@ -1194,8 +1291,7 @@ onMounted(() => { ...@@ -1194,8 +1291,7 @@ onMounted(() => {
style="margin-left: 10px" style="margin-left: 10px"
@click="changeChinaTime('America/New_York')" @click="changeChinaTime('America/New_York')"
>新泽西时间 >新泽西时间
</el-button </el-button>
>
<el-button <el-button
:type="zoneType === 'America/Los_Angeles' ? 'primary' : ''" :type="zoneType === 'America/Los_Angeles' ? 'primary' : ''"
size="small" size="small"
...@@ -1203,8 +1299,7 @@ onMounted(() => { ...@@ -1203,8 +1299,7 @@ onMounted(() => {
style="margin-left: 10px" style="margin-left: 10px"
@click="changeChinaTime('America/Los_Angeles')" @click="changeChinaTime('America/Los_Angeles')"
>洛杉矶时间 >洛杉矶时间
</el-button </el-button>
>
</div> </div>
<LogList <LogList
...@@ -1334,14 +1429,9 @@ onMounted(() => { ...@@ -1334,14 +1429,9 @@ onMounted(() => {
} }
} }
} }
.radioBtn {
.search-form { ::v-deep .el-radio-button__inner {
::v-deep .el-radio-button {
width: 75px; width: 75px;
.el-radio-button__inner {
width: 100%;
}
} }
} }
</style> </style>
...@@ -167,7 +167,7 @@ ...@@ -167,7 +167,7 @@
v-model.trim="searchForm.thirdSkuCode" v-model.trim="searchForm.thirdSkuCode"
placeholder="库存SKU" placeholder="库存SKU"
clearable clearable
style="width: 150px" style="width: 180px"
></ElInput> ></ElInput>
</ElFormItem> </ElFormItem>
<ElFormItem label="款号" v-if="status !== 'BATCH_DOWNLOAD'"> <ElFormItem label="款号" v-if="status !== 'BATCH_DOWNLOAD'">
...@@ -229,13 +229,14 @@ ...@@ -229,13 +229,14 @@
<el-option value="desc" label="倒序"></el-option> <el-option value="desc" label="倒序"></el-option>
</el-select> </el-select>
</ElFormItem> </ElFormItem>
<ElFormItem label="类型" v-if="status !== 'BATCH_DOWNLOAD'"> <ElFormItem label="印刷" v-if="status !== 'BATCH_DOWNLOAD'">
<el-radio-group <el-radio-group
v-model="searchForm.customizedQuantity" v-model="searchForm.customizedQuantity"
@click.stop="(e: Event) => handleRadioGroupClick(e)" @click.stop="(e: Event) => handleRadioGroupClick(e)"
> >
<el-radio-button value="single">单面</el-radio-button> <el-radio-button label="single">单面</el-radio-button>
<el-radio-button value="multiple">多面</el-radio-button> <el-radio-button label="multiple">多面</el-radio-button>
<el-radio-button label="normal">普品</el-radio-button>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem label="数量" v-if="status !== 'BATCH_DOWNLOAD'"> <ElFormItem label="数量" v-if="status !== 'BATCH_DOWNLOAD'">
...@@ -243,8 +244,12 @@ ...@@ -243,8 +244,12 @@
v-model="searchForm.multi" v-model="searchForm.multi"
@click.stop="(e: Event) => handleMultiRadioGroupClick(e)" @click.stop="(e: Event) => handleMultiRadioGroupClick(e)"
> >
<el-radio-button :value="false">单件</el-radio-button> <el-radio-button :value="false" class="radioBtn"
<el-radio-button :value="true">多件</el-radio-button> >单件</el-radio-button
>
<el-radio-button :value="true" class="radioBtn"
>多件</el-radio-button
>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem v-if="status !== 'BATCH_DOWNLOAD'"> <ElFormItem v-if="status !== 'BATCH_DOWNLOAD'">
...@@ -372,6 +377,7 @@ ...@@ -372,6 +377,7 @@
></ElOption> ></ElOption>
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
<ElFormItem <ElFormItem
v-if="searchForm.replaceShipment === 0" v-if="searchForm.replaceShipment === 0"
label="物流类型" label="物流类型"
...@@ -1653,6 +1659,15 @@ ...@@ -1653,6 +1659,15 @@
{{ row.prepaidAmount || 0 }} {{ row.prepaidAmount || 0 }}
</span> </span>
</div> </div>
<div
v-if="['COMPLETE'].includes(status)"
class="order-price-item"
>
<span class="order-price-item-label">实际运费¥:</span>
<span class="order-price-item-value">
{{ row.actualAmount || 0 }}
</span>
</div>
<!-- <div <!-- <div
v-if=" v-if="
['WAIT_TRACK', 'IN_TRANSIT', 'COMPLETE', 'CANCEL'].includes( ['WAIT_TRACK', 'IN_TRANSIT', 'COMPLETE', 'CANCEL'].includes(
...@@ -2296,6 +2311,9 @@ ...@@ -2296,6 +2311,9 @@
<div class="menu-item" @click="rightChange('order-number')"> <div class="menu-item" @click="rightChange('order-number')">
复制订单号 复制订单号
</div> </div>
<div class="menu-item" @click="rightChange('factorySubOrderNumber')">
复制生产单号
</div>
</template> </template>
</RightClickMenu> </RightClickMenu>
<el-dialog <el-dialog
...@@ -2873,7 +2891,11 @@ const totalAmountPrice = (item: PodCnOrderListData): string => { ...@@ -2873,7 +2891,11 @@ const totalAmountPrice = (item: PodCnOrderListData): string => {
const productAmount = new BigNumber(item?.productAmount ?? 0) const productAmount = new BigNumber(item?.productAmount ?? 0)
const serviceAmount = new BigNumber(item?.serviceAmount ?? 0) const serviceAmount = new BigNumber(item?.serviceAmount ?? 0)
// const freightAmount = usePrepaid ? item?.prepaidAmount : item?.actualAmount // const freightAmount = usePrepaid ? item?.prepaidAmount : item?.actualAmount
const actualFreight = new BigNumber(item?.actualAmount ?? 0)
const payFreight = new BigNumber(item?.prepaidAmount ?? 0) const payFreight = new BigNumber(item?.prepaidAmount ?? 0)
if (item.status === 'COMPLETE') {
return productAmount.plus(serviceAmount).plus(actualFreight).toString()
}
return productAmount.plus(serviceAmount).plus(payFreight).toString() return productAmount.plus(serviceAmount).plus(payFreight).toString()
} }
const countryList = ref([]) const countryList = ref([])
...@@ -4567,13 +4589,40 @@ const rightChange = async (code: string) => { ...@@ -4567,13 +4589,40 @@ const rightChange = async (code: string) => {
} else if (code === 'copy_shopNumber') { } else if (code === 'copy_shopNumber') {
const str = (tableData.value as ProductList[] | PodCnOrderListData[]) const str = (tableData.value as ProductList[] | PodCnOrderListData[])
.map((item) => item?.shopNumber) .map((item) => item?.shopNumber)
.join() .filter(Boolean)
.join(',')
if (!str) return ElMessage.warning('当前数据没有店铺单号')
navigator.clipboard.writeText(str) navigator.clipboard.writeText(str)
ElMessage.success('复制成功') ElMessage.success('复制成功')
} else if (code === 'order-number') { } else if (code === 'order-number') {
const str = (tableData.value as ProductList[] | PodCnOrderListData[]) const str = (tableData.value as ProductList[] | PodCnOrderListData[])
.map((item) => item?.factoryOrderNumber) .map((item) => item?.factoryOrderNumber)
.join() .filter(Boolean)
.join(',')
if (!str) return ElMessage.warning('当前数据没有订单号')
navigator.clipboard.writeText(str)
ElMessage.success('复制成功')
} else if (code === 'factorySubOrderNumber') {
let str
if (flat) {
str = (tableData.value as (ProductList | PodCnOrderListData)[])
.flatMap(
(item) =>
(item as PodCnOrderListData)?.productList
?.map((product) => product.factorySubOrderNumber)
?.filter(Boolean) ?? [],
)
.filter(Boolean)
.join(',')
} else {
str = (tableData.value as ProductList[])
.map((item) => item?.factorySubOrderNumber)
.filter(Boolean)
.join(',')
}
if (!str) return ElMessage.warning('当前数据没有生产单号')
navigator.clipboard.writeText(str) navigator.clipboard.writeText(str)
ElMessage.success('复制成功') ElMessage.success('复制成功')
} }
...@@ -6166,14 +6215,14 @@ useEnterKeyTrigger({ ...@@ -6166,14 +6215,14 @@ useEnterKeyTrigger({
color: white; color: white;
font-weight: bold; font-weight: bold;
} }
.search-form { // .search-form {
::v-deep .el-radio-button { // ::v-deep .el-radio-button {
width: 75px; // width: 75px;
.el-radio-button__inner { // .el-radio-button__inner {
width: 100%; // width: 100%;
} // }
} // }
} // }
.triangle-container-wrap { .triangle-container-wrap {
position: absolute; position: absolute;
top: 0; top: 0;
...@@ -6202,6 +6251,11 @@ useEnterKeyTrigger({ ...@@ -6202,6 +6251,11 @@ useEnterKeyTrigger({
font-size: 12px; font-size: 12px;
} }
} }
.radioBtn {
::v-deep .el-radio-button__inner {
width: 75px;
}
}
</style> </style>
<style lang="scss"> <style lang="scss">
.customize-select-style { .customize-select-style {
......
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
<LogisticsWaySelect <LogisticsWaySelect
v-model="searchForm.craftCode" v-model="searchForm.craftCode"
:company-list="craftList" :company-list="craftList"
:start-width="'178px'" :start-width="'150px'"
search-placeholder="搜索工艺名称" search-placeholder="搜索工艺名称"
start-placeholder="请选择工艺名称" start-placeholder="请选择工艺名称"
></LogisticsWaySelect> ></LogisticsWaySelect>
...@@ -142,7 +142,7 @@ ...@@ -142,7 +142,7 @@
v-model.trim="searchForm.thirdSkuCode" v-model.trim="searchForm.thirdSkuCode"
placeholder="库存SKU" placeholder="库存SKU"
clearable clearable
style="width: 150px" style="width: 180px"
></ElInput> ></ElInput>
</ElFormItem> </ElFormItem>
<ElFormItem label="款号" v-if="status !== 'BATCH_DOWNLOAD'"> <ElFormItem label="款号" v-if="status !== 'BATCH_DOWNLOAD'">
...@@ -219,7 +219,7 @@ ...@@ -219,7 +219,7 @@
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
<!-- </div> --> <!-- </div> -->
<ElFormItem label="类型" v-if="status !== 'BATCH_DOWNLOAD'"> <ElFormItem label="印刷" v-if="status !== 'BATCH_DOWNLOAD'">
<el-radio-group <el-radio-group
v-model="searchForm.customizedQuantity" v-model="searchForm.customizedQuantity"
@click.stop="(e: Event) => handleRadioGroupClick(e)" @click.stop="(e: Event) => handleRadioGroupClick(e)"
...@@ -234,8 +234,12 @@ ...@@ -234,8 +234,12 @@
v-model="searchForm.multi" v-model="searchForm.multi"
@click.stop="(e: Event) => handleMultiRadioGroupClick(e)" @click.stop="(e: Event) => handleMultiRadioGroupClick(e)"
> >
<el-radio-button :label="false">单件</el-radio-button> <el-radio-button :label="false" class="radioBtn"
<el-radio-button :label="true">多件</el-radio-button> >单件</el-radio-button
>
<el-radio-button :label="true" class="radioBtn"
>多件</el-radio-button
>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem> <ElFormItem>
...@@ -2542,6 +2546,9 @@ ...@@ -2542,6 +2546,9 @@
<div class="menu-item" @click="rightChange('order-number')"> <div class="menu-item" @click="rightChange('order-number')">
复制订单号 复制订单号
</div> </div>
<div class="menu-item" @click="rightChange('factorySubOrderNumber')">
复制生产单号
</div>
</template> </template>
</RightClickMenu> </RightClickMenu>
<el-dialog <el-dialog
...@@ -5178,13 +5185,38 @@ const rightChange = async (code: string) => { ...@@ -5178,13 +5185,38 @@ const rightChange = async (code: string) => {
} else if (code === 'copy_shopNumber') { } else if (code === 'copy_shopNumber') {
const str = (tableData.value as ProductList[] | PodUsOrderListData[]) const str = (tableData.value as ProductList[] | PodUsOrderListData[])
.map((item) => item?.shopNumber) .map((item) => item?.shopNumber)
.join() .filter(Boolean)
.join(',')
if (!str) return ElMessage.warning('当前数据没有店铺单号')
navigator.clipboard.writeText(str) navigator.clipboard.writeText(str)
ElMessage.success('复制成功') ElMessage.success('复制成功')
} else if (code === 'order-number') { } else if (code === 'order-number') {
const str = (tableData.value as ProductList[] | PodUsOrderListData[]) const str = (tableData.value as ProductList[] | PodUsOrderListData[])
.map((item) => item?.factoryOrderNumber) .map((item) => item?.factoryOrderNumber)
.join() .filter(Boolean)
.join(',')
if (!str) return ElMessage.warning('当前数据没有订单号')
navigator.clipboard.writeText(str)
ElMessage.success('复制成功')
} else if (code === 'factorySubOrderNumber') {
let str
if (flat) {
str = (tableData.value as (ProductList | PodUsOrderListData)[])
.flatMap(
(item) =>
(item as PodUsOrderListData)?.productList
?.map((product) => product.factorySubOrderNumber)
?.filter(Boolean) ?? [],
)
.filter(Boolean)
.join(',')
} else {
str = (tableData.value as ProductList[])
.map((item) => item?.factorySubOrderNumber)
.filter(Boolean)
.join(',')
}
if (!str) return ElMessage.warning('当前数据没有生产单号')
navigator.clipboard.writeText(str) navigator.clipboard.writeText(str)
ElMessage.success('复制成功') ElMessage.success('复制成功')
} }
...@@ -7218,6 +7250,11 @@ const printNormal = async () => { ...@@ -7218,6 +7250,11 @@ const printNormal = async () => {
font-size: 12px; font-size: 12px;
} }
} }
.radioBtn {
::v-deep .el-radio-button__inner {
width: 75px;
}
}
</style> </style>
<style lang="scss"> <style lang="scss">
.customize-select-style { .customize-select-style {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment