增加首页

This commit is contained in:
Mrking 2024-09-22 21:19:23 +08:00
parent fe54bd79b6
commit 3d64b4eaac
16 changed files with 1143 additions and 63 deletions

View File

@ -0,0 +1,48 @@
package cn.hangtag.module.oms.enums;
import cn.hangtag.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 时间范围类型的枚举
*
* @author owen
*/
@AllArgsConstructor
@Getter
public enum TimeRangeTypeEnum implements IntArrayValuable {
/**
*
*/
DAY(1),
/**
*
*/
WEEK(7),
/**
*
*/
MONTH(30),
/**
*
*/
YEAR(365),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TimeRangeTypeEnum::getType).toArray();
/**
* 类型
*/
private final Integer type;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -0,0 +1,21 @@
package cn.hangtag.module.oms.controller.admin.saleorder.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 订单备注 Request VO")
@Data
public class SaleOrderRemarkReqVO {
@Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "订单编号不能为空")
private Long id;
@Schema(description = "商家备注", example = "你猜一下")
@NotEmpty(message = "订单备注不能为空")
private String remark;
}

View File

@ -2,6 +2,7 @@ import request from '@/config/axios'
// 销售订单 VO
export interface SaleOrderVO {
id: number // 客户id
customerId: number // 客户id
bizdate: Date // 业务日期
remark: string // 备注
@ -89,6 +90,19 @@ export const SaleOrderApi = {
})
},
// 修改订单地址
updateOrderAddress: async (data: any) => {
return await request.put({ url: `/oms/sale-order/update-address`, data })
},
// 订单备注
updateOrderRemark: async (data: any) => {
return await request.put({ url: `/oms/sale-order/update-remark`, data })
},
// 更新分录数据
updateOrderEntrys: async (data: SaleOrderVO) => {
return await request.put({ url: `/oms/sale-order/update-entrys`, data })
}
}

View File

@ -2,4 +2,6 @@
export interface DataComparisonRespVO<T> {
value: T
reference: T
value2: T
reference2: T
}

View File

@ -16,36 +16,24 @@ export interface TradeTrendReqVO {
times: [dayjs.ConfigType, dayjs.ConfigType]
}
/** 交易状况统计 Response VO */
export interface TradeTrendSummaryRespVO {
time: string
turnoverPrice: number
orderPayPrice: number
rechargePrice: number
expensePrice: number
walletPayPrice: number
brokerageSettlementPrice: number
afterSaleRefundPrice: number
}
/** 交易订单数量 Response VO */
export interface TradeOrderCountRespVO {
/** 待发货 */
undelivered?: number
/** 待核销 */
pickUp?: number
/** 退款中 */
afterSaleApply?: number
/** 提现待审核 */
auditingWithdraw?: number
/** 驳回 */
orderCountAA?: number
/** 待提交 */
orderCountA?: number
/** 提交中 */
orderCountB?: number
/** 已审核 */
orderCountC?: number
}
/** 交易订单统计 Response VO */
export interface TradeOrderSummaryRespVO {
/** 支付订单商品数 */
orderPayCount?: number
orderCount?: number
/** 总支付金额,单位:分 */
orderPayPrice?: number
orderAmount?: number
}
/** 订单量趋势统计 Response VO */
@ -53,9 +41,9 @@ export interface TradeOrderTrendRespVO {
/** 日期 */
date: string
/** 订单数量 */
orderPayCount: number
orderCount: number
/** 订单支付金额 */
orderPayPrice: number
orderAmount: number
}
// 查询交易统计
@ -65,13 +53,7 @@ export const getTradeStatisticsSummary = () => {
})
}
// 获得交易状况统计
export const getTradeStatisticsAnalyse = (params: TradeTrendReqVO) => {
return request.get<DataComparisonRespVO<TradeTrendSummaryRespVO>>({
url: '/statistics/trade/analyse',
params: formatDateParam(params)
})
}
// 获得交易状况明细
export const getTradeStatisticsList = (params: TradeTrendReqVO) => {
@ -91,7 +73,7 @@ export const exportTradeStatisticsExcel = (params: TradeTrendReqVO) => {
// 获得交易订单数量
export const getOrderCount = async () => {
return await request.get<TradeOrderCountRespVO>({ url: `/statistics/trade/order-count` })
return await request.get<TradeOrderCountRespVO>({ url: `/oms/statistics/trade/order-count` })
}
// 获得交易订单数量对照
@ -108,7 +90,7 @@ export const getOrderCountTrendComparison = (
endTime: dayjs.ConfigType
) => {
return request.get<DataComparisonRespVO<TradeOrderTrendRespVO>[]>({
url: '/statistics/trade/order-count-trend',
url: '/oms/statistics/trade/order-count-trend',
params: { type, beginTime: formatDate(beginTime), endTime: formatDate(endTime) }
})
}

View File

@ -237,11 +237,21 @@ const remainingRouter: AppRouteRecordRaw[] = [
{
path: '/oms/produceorder', // 订单管理
component: Layout,
name: 'ProduceOrderCenter',
name: 'OrderCenter',
meta: {
hidden: true
},
children: [
{
path: 'order/detail/:id(\\d+)',
component: () => import('@/views/oms/saleorder/detail/index.vue'),
name: 'SaleOrderDetail',
meta: {
title: '订单详情',
icon: 'ep:view',
activeMenu: '/oms/saleorder'
}
},
{
path: 'produceorder/add',
component: () => import('@/views/oms/produceorder/form/index.vue'),

View File

@ -1,49 +1,101 @@
<template>
<div class="flex flex-col">
<!--
&lt;!&ndash; 数据对照 &ndash;&gt;
<!-- 数据对照 -->
<el-row :gutter="16" class="row">
<el-col :md="6" :sm="12" :xs="24" :loading="loading">
<ComparisonCard
tag="今日"
lasttag="昨日数据"
title="订单量"
:value="orderComparison?.value?.orderPayCount || 0"
:reference="orderComparison?.reference?.orderPayCount || 0"
:value="orderComparison?.value?.orderCount || 0"
:reference="orderComparison?.reference?.orderCount || 0"
/>
</el-col>
<el-col :md="6" :sm="12" :xs="24" :loading="loading">
<ComparisonCard
tag="今日"
lasttag="昨日数据"
title="销售额"
prefix="¥"
:decimals="2"
:value="orderComparison?.value?.orderAmount || 0"
:reference="orderComparison?.reference?.orderAmount || 0"
/>
</el-col>
<el-col :md="6" :sm="12" :xs="24" :loading="loading">
<ComparisonCard
tag="本周"
lasttag="上周数据"
title="订单量"
:value="orderComparison?.value?.orderPayCount || 0"
:reference="orderComparison?.reference?.orderPayCount || 0"
:value="orderComparison?.value2?.orderCount || 0"
:reference="orderComparison?.reference2?.orderCount || 0"
/>
</el-col>
<el-col :md="6" :sm="12" :xs="24" :loading="loading">
<ComparisonCard
tag="本月"
title="订单量"
:value="orderComparison?.value?.orderPayCount || 0"
:reference="orderComparison?.reference?.orderPayCount || 0"
/>
</el-col>
<el-col :md="6" :sm="12" :xs="24" :loading="loading">
<ComparisonCard
tag="本年"
title="订单量"
:value="orderComparison?.value?.orderPayCount || 0"
:reference="orderComparison?.reference?.orderPayCount || 0"
tag="本周"
lasttag="上周数据"
title="销售额"
prefix="¥"
:decimals="2"
:value="orderComparison?.value2?.orderAmount || 0"
:reference="orderComparison?.reference2?.orderAmount || 0"
/>
</el-col>
</el-row>
-->
<el-row :gutter="16" class="row">
<el-col :md="12">
<!-- 快捷入口 -->
<ShortcutCard />
</el-col>
<el-col :md="12">
<!-- 运营数据 -->
<OperationDataCard />
</el-col>
</el-row>
<!-- 交易量趋势 -->
<TradeTrendCard class="mb-4" />
</div>
</template>
<script lang="ts" setup>
import * as TradeStatisticsApi from '@/api/oms/statistics/trade'
import {TradeOrderSummaryRespVO} from '@/api/oms/statistics/trade'
import {DataComparisonRespVO} from '@/api/oms/statistics/common'
import ComparisonCard from './components/ComparisonCard.vue'
import ShortcutCard from './components/ShortcutCard.vue'
import OperationDataCard from './components/OperationDataCard.vue'
import TradeTrendCard from './components/TradeTrendCard.vue'
import { fenToYuan } from '@/utils'
/** 商城首页 */
defineOptions({ name: 'MallHome' })
const loading = ref(true) //
const orderComparison = ref<DataComparisonRespVO<TradeOrderSummaryRespVO>>() //
/** 查询交易对照卡片数据 */
const getOrderComparison = async () => {
orderComparison.value = await TradeStatisticsApi.getOrderComparison()
}
/** 查询会员用户数量对照卡片数据 */
/*const getUserCountComparison = async () => {
userComparison.value = await MemberStatisticsApi.getUserCountComparison()
}*/
/** 初始化 **/
onMounted(async () => {
loading.value = true
await Promise.all([getOrderComparison()])
loading.value = false
})
</script>
<style lang="scss" scoped>
.row {

View File

@ -0,0 +1,43 @@
<template>
<div class="flex flex-col gap-2 bg-[var(--el-bg-color-overlay)] p-6">
<div class="flex items-center justify-between text-gray-500">
<span>{{ title }}</span>
<el-tag>{{ tag }}</el-tag>
</div>
<div class="flex flex-row items-baseline justify-between">
<CountTo :prefix="prefix" :end-val="value" :decimals="decimals" class="text-3xl" />
<span :class="toNumber(percent) > 0 ? 'text-red-500' : 'text-green-500'">
{{ Math.abs(toNumber(percent)) }}%
<Icon :icon="toNumber(percent) > 0 ? 'ep:caret-top' : 'ep:caret-bottom'" class="!text-sm" />
</span>
</div>
<el-divider class="mb-1! mt-2!" />
<div class="flex flex-row items-center justify-between text-sm">
<span class="text-gray-500">{{lasttag}}</span>
<span>{{ prefix || '' }}{{ reference }}</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { toNumber } from 'lodash-es'
import { calculateRelativeRate } from '@/utils'
/** 交易对照卡片 */
defineOptions({ name: 'ComparisonCard' })
const props = defineProps({
title: propTypes.string.def('').isRequired,
tag: propTypes.string.def(''),
lasttag: propTypes.string.def(''),
prefix: propTypes.string.def(''),
value: propTypes.number.def(0).isRequired,
reference: propTypes.number.def(0).isRequired,
decimals: propTypes.number.def(0)
})
//
const percent = computed(() =>
calculateRelativeRate(props.value as number, props.reference as number)
)
</script>

View File

@ -0,0 +1,76 @@
<template>
<el-card shadow="never">
<template #header>
<CardTitle title="运营数据" />
</template>
<div class="flex flex-row flex-wrap items-center gap-8 p-4">
<div
v-for="item in data"
:key="item.name"
class="h-20 w-20% flex flex-col cursor-pointer items-center justify-center gap-2"
@click="handleClick(item.routerName)"
>
<CountTo
:prefix="item.prefix"
:end-val="item.value"
:decimals="item.decimals"
class="text-3xl"
/>
<span class="text-center">{{ item.name }}</span>
</div>
</div>
</el-card>
</template>
<script lang="ts" setup>
import * as TradeStatisticsApi from '@/api/oms/statistics/trade'
import { CardTitle } from '@/components/Card'
/** 运营数据卡片 */
defineOptions({ name: 'OperationDataCard' })
const router = useRouter() //
/** 数据 */
const data = reactive({
orderCountAA: { name: '已驳回订单', value: 0, routerName: 'SaleOrder' },
orderCountA: { name: '待提交订单', value: 0, routerName: 'SaleOrder' },
orderCountB: { name: '待审核订单', value: 0, routerName: 'SaleOrder' },
orderCountC: { name: '已完成订单', value: 0, routerName: 'SaleOrder' }
})
/** 查询订单数据 */
const getOrderData = async () => {
const orderCount = await TradeStatisticsApi.getOrderCount()
if (orderCount.orderCountAA != null) {
data.orderCountAA.value = orderCount.orderCountAA
}
if (orderCount.orderCountA != null) {
data.orderCountA.value = orderCount.orderCountA
}
if (orderCount.orderCountB != null) {
data.orderCountB.value = orderCount.orderCountB
}
if (orderCount.orderCountC != null) {
data.orderCountC.value = orderCount.orderCountC
}
}
/**
* 跳转到对应页面
*
* @param routerName 路由页面组件的名称
*/
const handleClick = (routerName: string) => {
router.push({ name: routerName })
}
/** 激活时 */
onActivated(() => {
getOrderData()
})
/** 初始化 **/
onMounted(() => {
getOrderData()
})
</script>

View File

@ -0,0 +1,58 @@
<template>
<el-card shadow="never">
<template #header>
<CardTitle title="快捷入口" />
</template>
<div class="flex flex-row flex-wrap gap-8 p-4">
<div
v-for="menu in menuList"
:key="menu.name"
class="h-20 w-20% flex flex-col cursor-pointer items-center justify-center gap-2"
@click="handleMenuClick(menu.routerName)"
>
<div
:class="menu.bgColor"
class="h-48px w-48px flex items-center justify-center rounded text-white"
>
<Icon :icon="menu.icon" class="text-7.5!" />
</div>
<span>{{ menu.name }}</span>
</div>
</div>
</el-card>
</template>
<script lang="ts" setup>
/** 快捷入口卡片 */
import { CardTitle } from '@/components/Card'
defineOptions({ name: 'ShortcutCard' })
const router = useRouter() //
/** 菜单列表 */
const menuList = [
{ name: '客户管理', icon: 'ep:user-filled', bgColor: 'bg-red-400', routerName: 'Customer' },
{
name: '产品管理',
icon: 'fluent-mdl2:product',
bgColor: 'bg-orange-400',
routerName: 'ProductInfo'
},
{ name: '订单管理', icon: 'ep:list', bgColor: 'bg-yellow-500', routerName: 'SaleOrder' },
{
name: '生产制单',
icon: 'fa-solid:project-diagram',
bgColor: 'bg-green-600',
routerName: 'ProduceOrder'
}
]
/**
* 跳转到菜单对应页面
*
* @param routerName 路由页面组件的名称
*/
const handleMenuClick = (routerName: string) => {
router.push({ name: routerName })
}
</script>

View File

@ -0,0 +1,207 @@
<template>
<el-card shadow="never">
<template #header>
<div class="flex flex-row items-center justify-between">
<CardTitle title="交易量趋势" />
<!-- 查询条件 -->
<div class="flex flex-row items-center gap-2">
<el-radio-group v-model="timeRangeType" @change="handleTimeRangeTypeChange">
<el-radio-button v-for="[key, value] in timeRange.entries()" :key="key" :label="key">
{{ value.name }}
</el-radio-button>
</el-radio-group>
</div>
</div>
</template>
<!-- 折线图 -->
<Echart :height="300" :options="eChartOptions" />
</el-card>
</template>
<script lang="ts" setup>
import dayjs, { Dayjs } from 'dayjs'
import { EChartsOption } from 'echarts'
import * as TradeStatisticsApi from '@/api/oms/statistics/trade'
import { formatDate } from '@/utils/formatTime'
import { CardTitle } from '@/components/Card'
/** 交易量趋势 */
defineOptions({ name: 'TradeTrendCard' })
enum TimeRangeTypeEnum {
DAY30 = 1,
WEEK = 7,
MONTH = 30,
YEAR = 365
} //
const timeRangeType = ref(TimeRangeTypeEnum.DAY30) // , 30
const loading = ref(true) //
// Map
const timeRange = new Map()
.set(TimeRangeTypeEnum.DAY30, {
name: '30天',
series: [
{ name: '订单金额', type: 'bar', smooth: true, data: [] },
{ name: '订单数量', type: 'line', smooth: true, data: [] }
]
})
.set(TimeRangeTypeEnum.WEEK, {
name: '周',
series: [
{ name: '上周金额', type: 'bar', smooth: true, data: [] },
{ name: '本周金额', type: 'bar', smooth: true, data: [] },
{ name: '上周数量', type: 'line', smooth: true, data: [] },
{ name: '本周数量', type: 'line', smooth: true, data: [] }
]
})
.set(TimeRangeTypeEnum.MONTH, {
name: '月',
series: [
{ name: '上月金额', type: 'bar', smooth: true, data: [] },
{ name: '本月金额', type: 'bar', smooth: true, data: [] },
{ name: '上月数量', type: 'line', smooth: true, data: [] },
{ name: '本月数量', type: 'line', smooth: true, data: [] }
]
})
.set(TimeRangeTypeEnum.YEAR, {
name: '年',
series: [
{ name: '去年金额', type: 'bar', smooth: true, data: [] },
{ name: '今年金额', type: 'bar', smooth: true, data: [] },
{ name: '去年数量', type: 'line', smooth: true, data: [] },
{ name: '今年数量', type: 'line', smooth: true, data: [] }
]
})
/** 图表配置 */
const eChartOptions = reactive<EChartsOption>({
grid: {
left: 20,
right: 20,
bottom: 20,
top: 80,
containLabel: true
},
legend: {
top: 50,
data: []
},
series: [],
toolbox: {
feature: {
//
dataZoom: {
yAxisIndex: false // Y
},
brush: {
type: ['lineX', 'clear'] //
},
saveAsImage: { show: true, name: '订单量趋势' } //
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
xAxis: {
type: 'category',
inverse: true,
boundaryGap: false,
axisTick: {
show: false
},
data: [],
axisLabel: {
formatter: (date: string) => {
switch (timeRangeType.value) {
case TimeRangeTypeEnum.DAY30:
return formatDate(date, 'MM-DD')
case TimeRangeTypeEnum.WEEK:
let weekDay = formatDate(date, 'ddd')
if (weekDay == '0') weekDay = '日'
return '周' + weekDay
case TimeRangeTypeEnum.MONTH:
return formatDate(date, 'D')
case TimeRangeTypeEnum.YEAR:
return formatDate(date, 'M') + '月'
default:
return date
}
}
}
},
yAxis: {
axisTick: {
show: false
}
}
}) as EChartsOption
/** 时间范围类型单选按钮选中 */
const handleTimeRangeTypeChange = async () => {
//
let beginTime: Dayjs
let endTime: Dayjs
switch (timeRangeType.value) {
case TimeRangeTypeEnum.WEEK:
beginTime = dayjs().startOf('week')
endTime = dayjs().endOf('week')
break
case TimeRangeTypeEnum.MONTH:
beginTime = dayjs().startOf('month')
endTime = dayjs().endOf('month')
break
case TimeRangeTypeEnum.YEAR:
beginTime = dayjs().startOf('year')
endTime = dayjs().endOf('year')
break
case TimeRangeTypeEnum.DAY30:
default:
beginTime = dayjs().subtract(30, 'day').startOf('d')
endTime = dayjs().endOf('d')
break
}
//
await getOrderCountTrendComparison(beginTime, endTime)
}
/** 查询订单数量趋势对照数据 */
const getOrderCountTrendComparison = async (
beginTime: dayjs.ConfigType,
endTime: dayjs.ConfigType
) => {
loading.value = true
//
const list = await TradeStatisticsApi.getOrderCountTrendComparison(
timeRangeType.value,
beginTime,
endTime
)
//
const dates: string[] = []
const series = [...timeRange.get(timeRangeType.value).series]
for (let item of list) {
dates.push(item.value.date)
if (series.length === 2) {
series[0].data.push(item?.value?.orderAmount || 0) //
series[1].data.push(item?.value?.orderCount || 0) //
} else {
series[0].data.push(item?.reference?.orderAmount || 0) //
series[1].data.push(item?.value?.orderAmount || 0) //
series[2].data.push(item?.reference?.orderCount || 0) //
series[3].data.push(item?.value?.orderCount || 0) //
}
}
eChartOptions.xAxis!['data'] = dates
eChartOptions.series = series
// legend424
eChartOptions.legend['data'] = series.map((item) => item.name)
loading.value = false
}
/** 初始化 **/
onMounted(() => {
handleTimeRangeTypeChange()
})
</script>

View File

@ -1,12 +1,12 @@
<template>
<Dialog v-model="dialogVisible" title="品牌权限">
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
<!-- <el-form-item label="角色名称">
<el-form-item label="客户编码">
<el-tag>{{ formData.number }}</el-tag>
</el-form-item>
<el-form-item label="客户名称">
<el-tag>{{ formData.name }}</el-tag>
</el-form-item>
<el-form-item label="角色标识">
<el-tag>{{ formData.code }}</el-tag>
</el-form-item>-->
<el-form-item label="品牌权限">
<el-card class="cardHeight">
<!-- <template #header>
@ -49,6 +49,7 @@ import { defaultProps, handleTree } from '@/utils/tree'
import * as CustomerApi from '@/api/oms/customer'
import * as CustomerBrandApi from '@/api/oms/customerbrand'
import * as BrandApi from '@/api/oms/brand'
import {CustomerVO} from "@/api/oms/customer";
defineOptions({ name: 'SystemRoleAssignMenuForm' })
@ -60,7 +61,7 @@ const formLoading = ref(false) // 表单的加载中1修改时的数据加
const formData = reactive({
id: undefined,
name: '',
code: '',
number: '',
menuIds: []
})
const formRef = ref() // Ref
@ -70,7 +71,7 @@ const treeRef = ref() // 菜单树组件 Ref
const treeNodeAll = ref(false) // /
/** 打开弹窗 */
const open = async (row: RoleApi.RoleVO) => {
const open = async (row: CustomerApi.CustomerVO) => {
dialogVisible.value = true
resetForm()
// Menu setChecked
@ -78,7 +79,7 @@ const open = async (row: RoleApi.RoleVO) => {
//
formData.id = row.id
formData.name = row.name
formData.code = row.code
formData.number = row.number
formLoading.value = true
try {
formData.value.menuIds = await CustomerBrandApi.getCustomerBrandList(row.id)

View File

@ -0,0 +1,357 @@
<template>
<ContentWrap>
<el-button
v-if="formData.billStatus != 'C'"
type="primary"
@click="handleSave"
>保存</el-button>
<el-button
v-if="formData.billStatus == 'B'"
type="primary"
@click="handleAudit"
>审核</el-button>
<el-button
v-if="formData.billStatus == 'A' || formData.billStatus == 'B' "
type="primary"
@click="remark">备注</el-button>
<!-- 订单信息 -->
<el-descriptions title="订单信息">
<el-descriptions-item label="订单号: ">{{ formData.billno }}</el-descriptions-item>
<el-descriptions-item label="业务日期: ">{{ formatDate(formData.bizdate,'YYYY-MM-DD') }}</el-descriptions-item>
<el-descriptions-item label="确认日期: ">{{ formatDate(formData.confirmdate,'YYYY-MM-DD') }}</el-descriptions-item>
<el-descriptions-item label="计划日期: ">{{ formatDate(formData.plansenddate,'YYYY-MM-DD') }}</el-descriptions-item>
<el-descriptions-item label="手机: ">{{ formData.phone }}</el-descriptions-item>
<el-descriptions-item label="传真: ">{{ formData.fax }}</el-descriptions-item>
<el-descriptions-item label="邮件: ">{{ formData.emails }}</el-descriptions-item>
<el-descriptions-item label="客户编号: ">{{ formData?.customer?.number }}</el-descriptions-item>
<el-descriptions-item label="客户名称: ">{{ formData?.customer?.name }}</el-descriptions-item>
<el-descriptions-item label="客户公司: ">{{ formData?.customer?.company }}</el-descriptions-item>
<el-descriptions-item label="订单备注: ">{{ formData.remark }}</el-descriptions-item>
</el-descriptions>
<!-- 订单状态 -->
<el-descriptions :column="3" title="订单状态">
<el-descriptions-item label="单据状态: ">
<dict-tag :type="DICT_TYPE.OMS_BILL_STATUS" :value="formData.billStatus" />
</el-descriptions-item>
<el-descriptions-item label="订单状态: ">
<dict-tag :type="DICT_TYPE.OMS_ORDER_STATUS" :value="formData.orderStatus" />
</el-descriptions-item>
<el-descriptions-item v-if="formData.billStatus == 'AA' " label="驳回原因: ">{{ formData?.rejectReason }}</el-descriptions-item>
<el-descriptions-item v-hasPermi="['oms:sale-order:update']" label-class-name="no-colon">
<!-- 待发货 -->
<template >
<!-- 快递发货 -->
<el-button
type="primary"
@click="delivery"
>
发货
</el-button>
<el-button
type="primary"
@click="updateAddress"
>
修改地址
</el-button>
<!-- 到店自提 -->
<el-button
type="primary"
@click="handleAudit"
>
核销
</el-button>
</template>
</el-descriptions-item>
<!-- <el-descriptions-item>
<template #label><span style="color: red">提醒: </span></template>
买家付款成功后货款将直接进入您的商户号微信支付宝<br />
请及时关注你发出的包裹状态确保可以配送至买家手中 <br />
如果买家表示没收到货或货物有问题请及时联系买家处理友好协商
</el-descriptions-item>-->
</el-descriptions>
<!-- 商品信息 -->
<el-descriptions title="产品信息">
<el-descriptions-item labelClassName="no-colon">
<el-row :gutter="20">
<el-col :span="544">
<el-form
ref="itemFormEntrysRef"
:model="formData.entrys"
:rules="itemFormEntrysRules"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData.entrys"
border
:on-cell-click="cellClick"
>
<el-table-column label="产品编码" prop="materialName" width="150"/>
<el-table-column label="产品名称" prop="materialName" width="150"/>
<el-table-column label="产品规格" prop="materialSpec" width="150" />
<el-table-column label="数量" prop="qty" width="150" />
<el-table-column label="单价" width="250">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.price`" :rules="itemFormEntrysRules.price" class="mb-0px!"
v-if="row.billStatus!='C'"
>
<el-input-number
style="width: 100%"
v-model.number="row.price"
:step="0.001"
:controls="false"
@change="changeRow(row,'price')"
placeholder="请输入单价"
/>
</el-form-item>
<span v-else @click="editRow($index)">{{ row.price }}</span>
</template>
</el-table-column>
<el-table-column label="折扣(%)" prop="discount" width="250" >
<template #default="{ row, $index }">
<el-input-number
style="width: 100%"
v-if="row.billStatus!='C'"
v-model.number="row.discount"
:step="0.001"
:controls="false"
min="0"
max="100"
@change="changeRow(row,'discount')"
placeholder="请输入折扣(%)"
/>
<span v-else @click="editRow($index)">{{ row.discount }}</span>
</template>
</el-table-column>
<el-table-column label="金额" prop="amount" width="250" />
<!-- <el-table-column label="合计" prop="payPrice" width="150">
<template #default="{ row }">{{ fenToYuan(row.payPrice) }}</template>
</el-table-column>-->
</el-table>
</el-form>
</el-col>
<el-col :span="10" />
</el-row>
</el-descriptions-item>
</el-descriptions>
<!-- 订单信息 -->
<el-descriptions title="发票信息">
<el-descriptions-item label="发票抬头: ">{{ formData.invoiceCode }}</el-descriptions-item>
<el-descriptions-item label="发票名称: ">{{ formData.invoiceName }}</el-descriptions-item>
<el-descriptions-item label="货币: ">{{ formData.currency }}</el-descriptions-item>
<el-descriptions-item label="发票备注: ">{{ formData.invoiceRemarks }}</el-descriptions-item>
</el-descriptions>
<!-- 订单信息 -->
<el-descriptions title="制单信息">
<el-descriptions-item label="创建时间: ">{{ formatDate(formData.createTime) }}</el-descriptions-item>
<el-descriptions-item label="修改人: ">{{ formData.updaterName }}</el-descriptions-item>
<el-descriptions-item label="修改时间: ">{{ formatDate(formData.updateTime) }}</el-descriptions-item>
<el-descriptions-item label="审核人: ">{{ formData.auditorName }}</el-descriptions-item>
<el-descriptions-item label="审核时间: ">{{ formatDate(formData.auditorTime) }}</el-descriptions-item>
</el-descriptions>
</ContentWrap>
<!-- 各种操作的弹窗 -->
<OrderUpdateRemarkForm ref="updateRemarkForm" @success="getDetail" />
<OrderUpdateAddressForm ref="updateAddressFormRef" @success="getDetail" />
</template>
<script lang="ts" setup>
import { fenToYuan } from '@/utils'
import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime'
import OrderUpdateRemarkForm from '@/views/oms/saleorder/form/OrderUpdateRemarkForm.vue'
import OrderUpdateAddressForm from '@/views/oms/saleorder/form/OrderUpdateAddressForm.vue'
import { useTagsViewStore } from '@/store/modules/tagsView'
import { propTypes } from '@/utils/propTypes'
import {SaleOrderApi, SaleOrderVO} from "@/api/oms/saleorder";
defineOptions({ name: 'TradeOrderDetail' })
const message = useMessage() //
const itemFormEntrysRef = ref() // Ref
const itemFormEntrysRules = reactive({
price: [{ required: true, message: '单价不能为空', trigger: 'blur' }],
})
//
const formData = ref<SaleOrderVO>({
logs: []
})
/** 各种操作 */
const updateRemarkForm = ref() // Ref
const remark = () => {
updateRemarkForm.value?.open(formData.value)
}
const updateAddressFormRef = ref() // Ref
const updateAddress = () => {
updateAddressFormRef.value?.open(formData.value)
}
const changeRow = async (row,key:string) => {
if('price' == key || 'discount' == key ){
let price = row.price
let qty = row.qty
let discount = row.discount
row.amount = (price*qty) * (1-(discount/100))
//100*(1-(2/100))
}
}
const handleSave = async () => {
try {
//
await itemFormEntrysRef.value.validate()
//
await SaleOrderApi.updateOrderEntrys(formData.value)
message.success('保存成功')
//
await getDetail()
} catch {}
}
/** 审核 */
const handleAudit = async () => {
try {
//
await itemFormEntrysRef.value.validate()
//
await message.confirm('确认审核订单吗?')
//
await SaleOrderApi.updateSaleOrderBillStatus([formData.value.id],'audit')
message.success('审核成功')
//
await getDetail()
} catch {}
}
/** 获得详情 */
const { params } = useRoute() //
const props = defineProps({
id: propTypes.number.def(undefined), // ID
showPickUp: propTypes.bool.def(true) //
})
const id = (params.id || props.id) as unknown as number
const getDetail = async () => {
if (id) {
const res = (await SaleOrderApi.getSaleOrder(id)) as SaleOrderVO
//
if (!res) {
message.error('销售订单不存在')
close()
}
formData.value = res
}
}
/** 关闭 tag */
const { delView } = useTagsViewStore() //
const { push, currentRoute } = useRouter() //
const close = () => {
delView(unref(currentRoute))
push({ name: 'SaleOrder' })
}
/** 初始化 **/
onMounted(async () => {
await getDetail()
})
</script>
<style lang="scss" scoped>
:deep(.el-descriptions) {
&:not(:nth-child(1)) {
margin-top: 20px;
}
.el-descriptions__title {
display: flex;
align-items: center;
&::before {
display: inline-block;
width: 3px;
height: 20px;
margin-right: 10px;
background-color: #409eff;
content: '';
}
}
.el-descriptions-item__container {
margin: 0 10px;
.no-colon {
margin: 0;
&::after {
content: '';
}
}
}
}
// 线
:deep(.el-timeline) {
margin: 10px 0 0 160px;
.el-timeline-item__wrapper {
position: relative;
top: -20px;
.el-timeline-item__timestamp {
position: absolute !important;
top: 10px;
left: -150px;
}
}
.el-timeline-right-content {
display: flex;
align-items: center;
min-height: 30px;
padding: 10px;
background-color: #f7f8fa;
&::before {
position: absolute;
top: 10px;
left: 13px; /* 将伪元素水平居中 */
border-color: transparent #f7f8fa transparent transparent; /* 尖角颜色,左侧朝向 */
border-style: solid;
border-width: 8px; /* 调整尖角大小 */
content: ''; /* 必须设置 content 属性 */
}
}
.dot-node-style {
position: absolute;
left: -5px;
display: flex;
width: 20px;
height: 20px;
font-size: 10px;
color: #fff;
border-radius: 50%;
justify-content: center;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,98 @@
<template>
<Dialog v-model="dialogVisible" title="修改订单收货地址" width="35%">
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="120px">
<el-form-item label="收件人">
<el-input v-model="formData.receiverName" placeholder="请输入收件人名称" />
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="formData.receiverMobile" placeholder="请输入收件人手机号" />
</el-form-item>
<el-form-item label="所在地">
<el-tree-select
v-model="formData.receiverAreaId"
:data="areaList"
:props="defaultProps"
:render-after-expand="true"
/>
</el-form-item>
<el-form-item label="详细地址">
<el-input
v-model="formData.receiverDetailAddress"
:rows="3"
placeholder="请输入收件人详细地址"
type="textarea"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { getAreaTree } from '@/api/system/area'
import { copyValueToTarget } from '@/utils'
import { defaultProps } from '@/utils/tree'
import {SaleOrderApi,SaleOrderVO} from "@/api/oms/saleorder";
defineOptions({ name: 'OrderUpdateAddressForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const formLoading = ref(false) // 12
const formData = ref({
id: undefined, //
receiverName: '', //
receiverMobile: '', //
receiverAreaId: null, //
receiverDetailAddress: '' //
})
const areaList = ref([]) //
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (row: SaleOrderVO) => {
resetForm()
//
copyValueToTarget(formData.value, row)
dialogVisible.value = true
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
formLoading.value = true
try {
const data = unref(formData)
await SaleOrderApi.updateOrderAddress(data)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
//
emit('success', true)
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined, //
receiverName: '', //
receiverMobile: '', //
receiverAreaId: null, //
receiverDetailAddress: '' //
}
formRef.value?.resetFields()
}
onMounted(async () => {
//
areaList.value = await getAreaTree()
})
</script>

View File

@ -0,0 +1,70 @@
<template>
<Dialog v-model="dialogVisible" title="订单备注" width="45%">
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
<el-form-item label="备注">
<el-input
v-model="formData.remark"
:rows="3"
placeholder="请输入订单备注"
type="textarea"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import {SaleOrderApi,SaleOrderVO} from "@/api/oms/saleorder";
defineOptions({ name: 'OrderUpdateRemarkForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const formLoading = ref(false) // 12
const formData = ref({
id: undefined, //
remark: '' //
})
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (row: SaleOrderVO) => {
resetForm()
//
formData.value.id = row.id
formData.value.remark = row.remark
dialogVisible.value = true
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
formLoading.value = true
try {
const data = unref(formData)
await SaleOrderApi.updateOrderRemark(data)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
//
emit('success', true)
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined, //
remark: '' //
}
formRef.value?.resetFields()
}
</script>

View File

@ -179,6 +179,7 @@
<el-table v-loading="loading" :data="list" :stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleSelectionChange"
:row-class-name="tableRowClassName"
>
<el-table-column width="30" label="选择" type="selection" />
<el-table-column label="单据编号" align="center" prop="billno" width="120px"/>
@ -235,12 +236,13 @@
<el-table-column label="操作" align="center" width="150" fixed="right">
<template #default="scope">
<el-button
v-hasPermi="['oms:sale-order:query']"
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['oms:sale-order:update']"
@click="openDetail(scope.row.id)"
>
编辑
<Icon icon="ep:notification" />
详情
</el-button>
<el-button
link
@ -293,6 +295,7 @@ import {checkPermi} from "@/utils/permission";
/** 销售订单 列表 */
defineOptions({ name: 'SaleOrder' })
const { currentRoute, push } = useRouter() //
const message = useMessage() //
const { t } = useI18n() //
@ -536,6 +539,34 @@ const rejectCancel = () =>{
}
/** 查看订单详情 */
const openDetail = (id: number) => {
push({ name: 'SaleOrderDetail', params: { id } })
}
const tableRowClassName = ({
row,
rowIndex,
}: {
row: SaleOrderVO
rowIndex: number
}) => {
if (row.billStatus === 'AA') {
return 'warning-row'
}
return ''
}
// /
watch(
() => currentRoute.value,
() => {
getList()
}
)
/** 激活时 */
onActivated(() => {
getList()
@ -547,3 +578,13 @@ onMounted(() => {
getTabsCount()
})
</script>
<style>
.el-table .warning-row {
--el-table-tr-bg-color: #D4D4D4;
}
.el-table .success-row {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
</style>