!238 完善订单详情和售后退款 TODO 提到的问题

Merge pull request !238 from puhui999/dev-to-dev
This commit is contained in:
芋道源码 2023-09-08 17:29:05 +00:00 committed by Gitee
commit dbfabcbbb5
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
7 changed files with 222 additions and 47 deletions

View File

@ -51,12 +51,13 @@ export interface OrderVO {
avatar?: string avatar?: string
} }
// 订单操作日志 // 订单操作日志
orderLog: orderLog[] logs?: logs[]
} }
export interface orderLog { export interface logs {
content?: string content?: string
createTime?: Date createTime?: Date
userType?: number
} }
export interface OrderItemRespVO { export interface OrderItemRespVO {

View File

@ -191,6 +191,10 @@ service.interceptors.response.use(
} }
return Promise.reject('error') return Promise.reject('error')
} else { } else {
// 前端处理 data 为 null 的情况,进行提示
if (data.msg !== '') {
ElNotification.error({ title: msg })
}
return data return data
} }
}, },

View File

@ -60,13 +60,19 @@ export const getBoolDictOptions = (dictType: string) => {
return dictOption return dictOption
} }
export const getDictObj = (dictType: string, value: any) => { /**
*
* @param dictType
* @param value
* @return DictDataType
*/
export const getDictObj = (dictType: string, value: any): DictDataType | undefined => {
const dictOptions: DictDataType[] = getDictOptions(dictType) const dictOptions: DictDataType[] = getDictOptions(dictType)
dictOptions.forEach((dict: DictDataType) => { for (const dict of dictOptions) {
if (dict.value === value.toString()) { if (dict.value === value + '') {
return dict return dict
} }
}) }
} }
/** /**
@ -74,12 +80,13 @@ export const getDictObj = (dictType: string, value: any) => {
* *
* @param dictType * @param dictType
* @param value * @param value
* @return
*/ */
export const getDictLabel = (dictType: string, value: any) => { export const getDictLabel = (dictType: string, value: any): string => {
const dictOptions: DictDataType[] = getDictOptions(dictType) const dictOptions: DictDataType[] = getDictOptions(dictType)
const dictLabel = ref('') const dictLabel = ref('')
dictOptions.forEach((dict: DictDataType) => { dictOptions.forEach((dict: DictDataType) => {
if (dict.value === value) { if (dict.value === value + '') {
dictLabel.value = dict.label dictLabel.value = dict.label
} }
}) })

View File

@ -41,7 +41,7 @@ import { SpuProperty } from '@/views/mall/promotion/components/index'
defineOptions({ name: 'PromotionSpuAndSkuList' }) defineOptions({ name: 'PromotionSpuAndSkuList' })
const props = defineProps<{ const props = defineProps<{
spuList: T[] // TODO 便 spu spu spu spuList: T[]
ruleConfig: RuleConfig[] ruleConfig: RuleConfig[]
spuPropertyListP: SpuProperty<T>[] spuPropertyListP: SpuProperty<T>[]
}>() }>()

View File

@ -126,14 +126,12 @@
<el-descriptions-item labelClassName="no-colon"> <el-descriptions-item labelClassName="no-colon">
<el-timeline> <el-timeline>
<el-timeline-item <el-timeline-item
v-for="saleLog in formData.afterSaleLog" v-for="saleLog in formData.logs"
:key="saleLog.id" :key="saleLog.id"
:timestamp="formatDate(saleLog.createTime)" :timestamp="formatDate(saleLog.createTime)"
placement="top" placement="top"
> >
<el-card> <div class="el-timeline-right-content">
<span>用户类型</span>
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="saleLog.userType" class="mr-10px" />
<span>售后状态(之前)</span> <span>售后状态(之前)</span>
<dict-tag <dict-tag
:type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS"
@ -147,7 +145,15 @@
class="mr-10px" class="mr-10px"
/> />
<span>操作明细{{ saleLog.content }}</span> <span>操作明细{{ saleLog.content }}</span>
</el-card> </div>
<template #dot>
<span
:style="{ backgroundColor: updateStyles(saleLog.userType) }"
class="dot-node-style"
>
{{ getDictLabel(DICT_TYPE.USER_TYPE, saleLog.userType)[0] || '系' }}
</span>
</template>
</el-timeline-item> </el-timeline-item>
</el-timeline> </el-timeline>
</el-descriptions-item> </el-descriptions-item>
@ -160,28 +166,50 @@
<script lang="ts" setup> <script lang="ts" setup>
import * as AfterSaleApi from '@/api/mall/trade/afterSale/index' import * as AfterSaleApi from '@/api/mall/trade/afterSale/index'
import { floatToFixed2 } from '@/utils' import { floatToFixed2 } from '@/utils'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import UpdateAuditReasonForm from '@/views/mall/trade/afterSale/form/AfterSaleDisagreeForm.vue' import UpdateAuditReasonForm from '@/views/mall/trade/afterSale/form/AfterSaleDisagreeForm.vue'
import { createImageViewer } from '@/components/ImageViewer' import { createImageViewer } from '@/components/ImageViewer'
import { isArray } from '@/utils/is' import { isArray } from '@/utils/is'
import { useTagsViewStore } from '@/store/modules/tagsView'
defineOptions({ name: 'TradeAfterSaleDetail' }) defineOptions({ name: 'TradeAfterSaleDetail' })
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
const { params } = useRoute() // const { params } = useRoute() //
const { push, currentRoute } = useRouter() //
const formData = ref({ const formData = ref({
order: {}, order: {},
afterSaleLog: [] logs: []
}) })
const updateAuditReasonFormRef = ref() // Ref const updateAuditReasonFormRef = ref() // Ref
const updateStyles = (type: number) => {
const dict = getDictObj(DICT_TYPE.USER_TYPE, type)
switch (dict?.colorType) {
case 'success':
return '#67C23A'
case 'info':
return '#909399'
case 'warning':
return '#E6A23C'
case 'danger':
return '#F56C6C'
}
return '#409EFF'
}
/** 获得详情 */ /** 获得详情 */
const getDetail = async () => { const getDetail = async () => {
const id = params.orderId as unknown as number const id = params.orderId as unknown as number
if (id) { if (id) {
formData.value = await AfterSaleApi.getAfterSale(id) const res = await AfterSaleApi.getAfterSale(id)
//
if (res === null) {
close()
}
formData.value = res
} }
} }
@ -240,7 +268,12 @@ const imagePreview = (args) => {
urlList urlList
}) })
} }
const { delView } = useTagsViewStore() //
/** 关闭 tag */
const close = () => {
delView(unref(currentRoute))
push({ name: 'TradeAfterSale' })
}
onMounted(async () => { onMounted(async () => {
await getDetail() await getDetail()
}) })
@ -277,4 +310,51 @@ onMounted(async () => {
} }
} }
} }
// 线
:deep(.el-timeline) {
margin: 10px 0px 0px 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 {
content: '';
position: absolute;
top: 10px;
left: 13px;
border-width: 8px; /* 调整尖角大小 */
border-style: solid;
border-color: transparent #f7f8fa transparent transparent; /* 尖角颜色,左侧朝向 */
}
}
.dot-node-style {
width: 20px;
height: 20px;
position: absolute;
left: -5px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
color: #fff;
font-size: 10px;
}
}
</style> </style>

View File

@ -40,12 +40,17 @@
<el-descriptions-item label="订单状态: "> <el-descriptions-item label="订单状态: ">
<dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="formData.status!" /> <dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="formData.status!" />
</el-descriptions-item> </el-descriptions-item>
<!-- TODO @puhui999根据状态进行展示按钮 -->
<el-descriptions-item label-class-name="no-colon"> <el-descriptions-item label-class-name="no-colon">
<el-button type="primary" @click="openForm('updatePrice')">调整价格</el-button> <el-button v-if="formData.status! === 0" type="primary" @click="updatePrice">
<el-button type="primary" @click="openForm('remark')">备注</el-button> 调整价格
<el-button type="primary" @click="openForm('delivery')">发货</el-button> </el-button>
<el-button type="primary" @click="openForm('updateAddress')">修改地址</el-button> <el-button type="primary" @click="remark">备注</el-button>
<el-button v-if="formData.status! === 10" type="primary" @click="delivery">
发货
</el-button>
<el-button v-if="formData.status! === 10" type="primary" @click="updateAddress">
修改地址
</el-button>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label><span style="color: red">提醒: </span></template> <template #label><span style="color: red">提醒: </span></template>
@ -152,11 +157,22 @@
<el-descriptions-item labelClassName="no-colon"> <el-descriptions-item labelClassName="no-colon">
<el-timeline> <el-timeline>
<el-timeline-item <el-timeline-item
v-for="(log, index) in formData.orderLog" v-for="(log, index) in formData.logs"
:key="index" :key="index"
:timestamp="formatDate(log.createTime!)" :timestamp="formatDate(log.createTime!)"
placement="top"
> >
<div class="el-timeline-right-content">
{{ log.content }} {{ log.content }}
</div>
<template #dot>
<span
:style="{ backgroundColor: updateStyles(log.userType!) }"
class="dot-node-style"
>
{{ getDictLabel(DICT_TYPE.USER_TYPE, log.userType)[0] }}
</span>
</template>
</el-timeline-item> </el-timeline-item>
</el-timeline> </el-timeline>
</el-descriptions-item> </el-descriptions-item>
@ -173,54 +189,74 @@
import * as TradeOrderApi from '@/api/mall/trade/order' import * as TradeOrderApi from '@/api/mall/trade/order'
import { floatToFixed2 } from '@/utils' import { floatToFixed2 } from '@/utils'
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
import OrderUpdateRemarkForm from '@/views/mall/trade/order/form/OrderUpdateRemarkForm.vue' import OrderUpdateRemarkForm from '@/views/mall/trade/order/form/OrderUpdateRemarkForm.vue'
import OrderDeliveryForm from '@/views/mall/trade/order/form/OrderDeliveryForm.vue' import OrderDeliveryForm from '@/views/mall/trade/order/form/OrderDeliveryForm.vue'
import OrderUpdateAddressForm from '@/views/mall/trade/order/form/OrderUpdateAddressForm.vue' import OrderUpdateAddressForm from '@/views/mall/trade/order/form/OrderUpdateAddressForm.vue'
import OrderUpdatePriceForm from '@/views/mall/trade/order/form/OrderUpdatePriceForm.vue' import OrderUpdatePriceForm from '@/views/mall/trade/order/form/OrderUpdatePriceForm.vue'
import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express' import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express'
import { useTagsViewStore } from '@/store/modules/tagsView'
defineOptions({ name: 'TradeOrderDetail' }) defineOptions({ name: 'TradeOrderDetail' })
const message = useMessage() // const message = useMessage() //
const updateStyles = (type: number) => {
const dict = getDictObj(DICT_TYPE.USER_TYPE, type)
switch (dict?.colorType) {
case 'success':
return '#67C23A'
case 'info':
return '#909399'
case 'warning':
return '#E6A23C'
case 'danger':
return '#F56C6C'
}
return '#409EFF'
}
// //
const formData = ref<TradeOrderApi.OrderVO>({ const formData = ref<TradeOrderApi.OrderVO>({
orderLog: [] // TODO @puhui999orderLogs logs: []
}) })
// TODO @puhui999
const deliveryFormRef = ref() // Ref const deliveryFormRef = ref() // Ref
const updateRemarkForm = ref() // Ref const updateRemarkForm = ref() // Ref
const updateAddressFormRef = ref() // Ref const updateAddressFormRef = ref() // Ref
const updatePriceFormRef = ref() // Ref const updatePriceFormRef = ref() // Ref
const openForm = (type: string) => { const remark = () => {
switch (type) {
case 'remark':
updateRemarkForm.value?.open(formData.value) updateRemarkForm.value?.open(formData.value)
break }
case 'delivery': const delivery = () => {
deliveryFormRef.value?.open(formData.value) deliveryFormRef.value?.open(formData.value)
break }
case 'updateAddress': const updateAddress = () => {
updateAddressFormRef.value?.open(formData.value) updateAddressFormRef.value?.open(formData.value)
break }
case 'updatePrice': const updatePrice = () => {
updatePriceFormRef.value?.open(formData.value) updatePriceFormRef.value?.open(formData.value)
break
} }
}
/** 获得详情 */ /** 获得详情 */
const { params } = useRoute() // const { params } = useRoute() //
const getDetail = async () => { const getDetail = async () => {
const id = params.orderId as unknown as number const id = params.orderId as unknown as number
if (id) { if (id) {
const res = (await TradeOrderApi.getOrder(id)) as TradeOrderApi.OrderVO const res = (await TradeOrderApi.getOrder(id)) as TradeOrderApi.OrderVO
//
if (res === null) {
close()
}
formData.value = res formData.value = res
} }
} }
const { delView } = useTagsViewStore() //
const { push, currentRoute } = useRouter() //
/** 关闭 tag */
const close = () => {
delView(unref(currentRoute))
push({ name: 'TradeAfterSale' })
}
/** 复制 */ /** 复制 */
const clipboardSuccess = () => { const clipboardSuccess = () => {
message.success('复制成功') message.success('复制成功')
@ -267,4 +303,51 @@ onMounted(async () => {
} }
} }
} }
// 线
:deep(.el-timeline) {
margin: 10px 0px 0px 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 {
content: ''; /* 必须设置 content 属性 */
position: absolute;
top: 10px;
left: 13px; /* 将伪元素水平居中 */
border-width: 8px; /* 调整尖角大小 */
border-style: solid;
border-color: transparent #f7f8fa transparent transparent; /* 尖角颜色,左侧朝向 */
}
}
.dot-node-style {
width: 20px;
height: 20px;
position: absolute;
left: -5px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
color: #fff;
font-size: 10px;
}
}
</style> </style>

View File

@ -100,7 +100,7 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- TODO 聚合搜索等售后结束后实现--> <!-- TODO puhui 聚合搜索等售后结束后实现-->
<el-form-item label="聚合搜索"> <el-form-item label="聚合搜索">
<el-input <el-input
v-show="true" v-show="true"
@ -139,7 +139,7 @@
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column class-name="order-table-col"> <el-table-column class-name="order-table-col">
<template #header> <template #header>
<!-- TODO @phui999小屏幕下会有偏移后续看看 --> <!-- TODO @puhui999小屏幕下会有偏移后续看看 -->
<div class="flex items-center" style="width: 100%"> <div class="flex items-center" style="width: 100%">
<div class="ml-100px mr-200px">商品信息</div> <div class="ml-100px mr-200px">商品信息</div>
<div class="mr-60px">单价()/数量</div> <div class="mr-60px">单价()/数量</div>