mirror of
https://gitee.com/hhyykk/ipms-sjy-ui.git
synced 2025-07-16 11:55:07 +08:00
Merge remote-tracking branch 'yudao/dev' into dev-to-dev
This commit is contained in:
@ -5,7 +5,7 @@
|
||||
<el-row :gutter="20" justify="space-between">
|
||||
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex items-center">
|
||||
<img :src="avatar" alt="" class="w-70px h-70px rounded-[50%] mr-20px" />
|
||||
<img :src="avatar" alt="" class="mr-20px h-70px w-70px rounded-[50%]" />
|
||||
<div>
|
||||
<div class="text-20px">
|
||||
{{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
|
||||
@ -17,9 +17,9 @@
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex h-70px items-center justify-end lt-sm:mt-10px">
|
||||
<div class="h-70px flex items-center justify-end lt-sm:mt-10px">
|
||||
<div class="px-8px text-right">
|
||||
<div class="text-14px text-gray-400 mb-20px">{{ t('workplace.project') }}</div>
|
||||
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.project') }}</div>
|
||||
<CountTo
|
||||
class="text-20px"
|
||||
:start-val="0"
|
||||
@ -29,7 +29,7 @@
|
||||
</div>
|
||||
<el-divider direction="vertical" />
|
||||
<div class="px-8px text-right">
|
||||
<div class="text-14px text-gray-400 mb-20px">{{ t('workplace.toDo') }}</div>
|
||||
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.toDo') }}</div>
|
||||
<CountTo
|
||||
class="text-20px"
|
||||
:start-val="0"
|
||||
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<div class="px-8px text-right">
|
||||
<div class="text-14px text-gray-400 mb-20px">{{ t('workplace.access') }}</div>
|
||||
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.access') }}</div>
|
||||
<CountTo
|
||||
class="text-20px"
|
||||
:start-val="0"
|
||||
@ -58,7 +58,7 @@
|
||||
<el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-10px">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="flex justify-between h-3">
|
||||
<div class="h-3 flex justify-between">
|
||||
<span>{{ t('workplace.project') }}</span>
|
||||
<el-link type="primary" :underline="false">{{ t('action.more') }}</el-link>
|
||||
</div>
|
||||
@ -80,7 +80,7 @@
|
||||
<span class="text-16px">{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="mt-15px text-14px text-gray-400">{{ t(item.message) }}</div>
|
||||
<div class="mt-20px text-12px text-gray-400 flex justify-between">
|
||||
<div class="mt-20px flex justify-between text-12px text-gray-400">
|
||||
<span>{{ item.personal }}</span>
|
||||
<span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span>
|
||||
</div>
|
||||
@ -114,7 +114,7 @@
|
||||
<el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-10px">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="flex justify-between h-3">
|
||||
<div class="h-3 flex justify-between">
|
||||
<span>{{ t('workplace.shortcutOperation') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
@ -133,7 +133,7 @@
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mt-10px">
|
||||
<template #header>
|
||||
<div class="flex justify-between h-3">
|
||||
<div class="h-3 flex justify-between">
|
||||
<span>{{ t('workplace.notice') }}</span>
|
||||
<el-link type="primary" :underline="false">{{ t('action.more') }}</el-link>
|
||||
</div>
|
||||
@ -141,7 +141,7 @@
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<div v-for="(item, index) in notice" :key="`dynamics-${index}`">
|
||||
<div class="flex items-center">
|
||||
<img :src="avatar" alt="" class="w-35px h-35px rounded-[50%] mr-20px" />
|
||||
<img :src="avatar" alt="" class="mr-20px h-35px w-35px rounded-[50%]" />
|
||||
<div>
|
||||
<div class="text-14px">
|
||||
<Highlight :keys="item.keys.map((v) => t(v))">
|
||||
|
@ -20,7 +20,7 @@
|
||||
:duration="2600"
|
||||
:end-val="102400"
|
||||
:start-val="0"
|
||||
class="text-20px font-700 text-right"
|
||||
class="text-right text-20px font-700"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -49,7 +49,7 @@
|
||||
:duration="2600"
|
||||
:end-val="81212"
|
||||
:start-val="0"
|
||||
class="text-20px font-700 text-right"
|
||||
class="text-right text-20px font-700"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -78,7 +78,7 @@
|
||||
:duration="2600"
|
||||
:end-val="9280"
|
||||
:start-val="0"
|
||||
class="text-20px font-700 text-right"
|
||||
class="text-right text-20px font-700"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -107,7 +107,7 @@
|
||||
:duration="2600"
|
||||
:end-val="13600"
|
||||
:start-val="0"
|
||||
class="text-20px font-700 text-right"
|
||||
class="text-right text-20px font-700"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div
|
||||
:class="prefixCls"
|
||||
class="h-[100%] relative lt-xl:bg-[var(--login-bg-color)] lt-sm:px-10px lt-xl:px-10px lt-md:px-10px"
|
||||
class="relative h-[100%] lt-xl:bg-[var(--login-bg-color)] lt-md:px-10px lt-sm:px-10px lt-xl:px-10px"
|
||||
>
|
||||
<div class="relative h-full flex mx-auto">
|
||||
<div class="relative mx-auto h-full flex">
|
||||
<div
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden`"
|
||||
>
|
||||
<!-- 左上角的 logo + 系统标题 -->
|
||||
<div class="flex items-center relative text-white">
|
||||
<img alt="" class="w-48px h-48px mr-10px" src="@/assets/imgs/logo.png" />
|
||||
<div class="relative flex items-center text-white">
|
||||
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
|
||||
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
|
||||
</div>
|
||||
<!-- 左边的背景图 + 欢迎语 -->
|
||||
<div class="flex justify-center items-center h-[calc(100%-60px)]">
|
||||
<div class="h-[calc(100%-60px)] flex items-center justify-center">
|
||||
<TransitionGroup
|
||||
appear
|
||||
enter-active-class="animate__animated animate__bounceInLeft"
|
||||
@ -21,41 +21,41 @@
|
||||
>
|
||||
<img key="1" alt="" class="w-350px" src="@/assets/svgs/login-box-bg.svg" />
|
||||
<div key="2" class="text-3xl text-white">{{ t('login.welcome') }}</div>
|
||||
<div key="3" class="mt-5 font-normal text-white text-14px">
|
||||
<div key="3" class="mt-5 text-14px font-normal text-white">
|
||||
{{ t('login.message') }}
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 p-30px lt-sm:p-10px dark:bg-[var(--login-bg-color)] relative">
|
||||
<div class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px">
|
||||
<!-- 右上角的主题、语言选择 -->
|
||||
<div
|
||||
class="flex justify-between items-center text-white at-2xl:justify-end at-xl:justify-end"
|
||||
class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
|
||||
>
|
||||
<div class="flex items-center at-2xl:hidden at-xl:hidden">
|
||||
<img alt="" class="w-48px h-48px mr-10px" src="@/assets/imgs/logo.png" />
|
||||
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
|
||||
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-end items-center space-x-10px">
|
||||
<div class="flex items-center justify-end space-x-10px">
|
||||
<ThemeSwitch />
|
||||
<LocaleDropdown class="lt-xl:text-white dark:text-white" />
|
||||
<LocaleDropdown class="dark:text-white lt-xl:text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右边的登录界面 -->
|
||||
<Transition appear enter-active-class="animate__animated animate__bounceInRight">
|
||||
<div
|
||||
class="h-full flex items-center m-auto w-[100%] at-2xl:max-w-500px at-xl:max-w-500px at-md:max-w-500px at-lg:max-w-500px"
|
||||
class="m-auto h-full w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
|
||||
>
|
||||
<!-- 账号登录 -->
|
||||
<LoginForm class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<LoginForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<!-- 手机登录 -->
|
||||
<MobileForm class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<MobileForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<!-- 二维码登录 -->
|
||||
<QrCodeForm class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<QrCodeForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<!-- 注册 -->
|
||||
<RegisterForm class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<RegisterForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<!-- 三方登录 -->
|
||||
<SSOLoginVue class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
@ -112,13 +112,13 @@
|
||||
<el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<div class="flex justify-between w-[100%]">
|
||||
<div class="w-[100%] flex justify-between">
|
||||
<Icon
|
||||
v-for="(item, key) in socialList"
|
||||
:key="key"
|
||||
:icon="item.icon"
|
||||
:size="30"
|
||||
class="cursor-pointer anticon"
|
||||
class="anticon cursor-pointer"
|
||||
color="#999"
|
||||
@click="doSocialLogin(item.type)"
|
||||
/>
|
||||
@ -128,7 +128,7 @@
|
||||
<el-divider content-position="center">萌新必读</el-divider>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<div class="flex justify-between w-[100%]">
|
||||
<div class="w-[100%] flex justify-between">
|
||||
<el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>
|
||||
<el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link>
|
||||
<el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<h2 class="mb-3 text-2xl font-bold text-center xl:text-3xl enter-x xl:text-center">
|
||||
<h2 class="enter-x mb-3 text-center text-2xl font-bold xl:text-center xl:text-3xl">
|
||||
{{ getFormTitle }}
|
||||
</h2>
|
||||
</template>
|
||||
|
@ -10,7 +10,7 @@
|
||||
</el-col>
|
||||
<el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<div class="w-[100%] mt-15px">
|
||||
<div class="mt-15px w-[100%]">
|
||||
<XButton :title="t('login.backLogin')" class="w-[100%]" @click="handleBackLogin()" />
|
||||
</div>
|
||||
</el-col>
|
||||
|
@ -29,7 +29,7 @@
|
||||
@click="loginRegister()"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-[100%] mt-15px">
|
||||
<div class="mt-15px w-[100%]">
|
||||
<XButton :title="t('login.hasUser')" class="w-[100%]" @click="handleBackLogin()" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<el-card class="w-1/3 user" shadow="hover">
|
||||
<el-card class="user w-1/3" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ t('profile.user.title') }}</span>
|
||||
@ -8,7 +8,7 @@
|
||||
</template>
|
||||
<ProfileUser />
|
||||
</el-card>
|
||||
<el-card class="w-2/3 user ml-3" shadow="hover">
|
||||
<el-card class="user ml-3 w-2/3" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ t('profile.info.title') }}</span>
|
||||
|
@ -69,6 +69,9 @@ const getTimelineItemIcon = (item) => {
|
||||
if (item.result === 4) {
|
||||
return 'el-icon-remove-outline'
|
||||
}
|
||||
if (item.result === 5) {
|
||||
return 'el-icon-back'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
@ -86,6 +89,12 @@ const getTimelineItemType = (item) => {
|
||||
if (item.result === 4) {
|
||||
return 'info'
|
||||
}
|
||||
if (item.result === 5) {
|
||||
return 'warning'
|
||||
}
|
||||
if (item.result === 6) {
|
||||
return 'default'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
</script>
|
||||
|
86
src/views/bpm/processInstance/detail/TaskDelegateForm.vue
Normal file
86
src/views/bpm/processInstance/detail/TaskDelegateForm.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="委派任务" width="500">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
>
|
||||
<el-form-item label="接收人" prop="delegateUserId">
|
||||
<el-select v-model="formData.delegateUserId" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="委派理由" prop="reason">
|
||||
<el-input v-model="formData.reason" clearable placeholder="请输入委派理由" />
|
||||
</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 * as TaskApi from '@/api/bpm/task'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'BpmTaskDelegateForm' })
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref({
|
||||
id: '',
|
||||
delegateUserId: undefined
|
||||
})
|
||||
const formRules = ref({
|
||||
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }]
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
const userList = ref<any[]>([]) // 用户列表
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (id: string) => {
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
formData.value.id = id
|
||||
// 获得用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
await TaskApi.delegateTask(formData.value)
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: '',
|
||||
delegateUserId: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="回退" width="500">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
>
|
||||
<el-form-item label="退回节点" prop="targetDefinitionKey">
|
||||
<el-select v-model="formData.targetDefinitionKey" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in returnList"
|
||||
:key="item.definitionKey"
|
||||
:label="item.name"
|
||||
:value="item.definitionKey"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="回退理由" prop="reason">
|
||||
<el-input v-model="formData.reason" clearable placeholder="请输入回退理由" />
|
||||
</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" name="TaskRollbackDialogForm" setup>
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref({
|
||||
id: '',
|
||||
targetDefinitionKey: undefined,
|
||||
reason: ''
|
||||
})
|
||||
const formRules = ref({
|
||||
targetDefinitionKey: [{ required: true, message: '必须选择回退节点', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '回退理由不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
const returnList = ref([])
|
||||
/** 打开弹窗 */
|
||||
const open = async (id: string) => {
|
||||
returnList.value = await TaskApi.getReturnList({ taskId: id })
|
||||
if (returnList.value.length === 0) {
|
||||
message.warning('当前没有可回退的节点')
|
||||
return false
|
||||
}
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
formData.value.id = id
|
||||
}
|
||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
await TaskApi.returnTask(formData.value)
|
||||
message.success('回退成功')
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: '',
|
||||
targetDefinitionKey: undefined,
|
||||
reason: ''
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
@ -91,6 +91,10 @@
|
||||
|
||||
<!-- 弹窗:转派审批人 -->
|
||||
<TaskUpdateAssigneeForm ref="taskUpdateAssigneeFormRef" @success="getDetail" />
|
||||
<!-- 弹窗,回退节点 -->
|
||||
<TaskReturnDialog ref="taskReturnDialogRef" @success="getDetail" />
|
||||
<!-- 委派,将任务委派给别人处理,处理完成后,会重新回到原审批人手中-->
|
||||
<TaskDelegateForm ref="taskDelegateForm" @success="getDetail" />
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@ -103,6 +107,8 @@ import * as TaskApi from '@/api/bpm/task'
|
||||
import TaskUpdateAssigneeForm from './TaskUpdateAssigneeForm.vue'
|
||||
import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
|
||||
import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
|
||||
import TaskReturnDialog from './TaskReturnDialogForm.vue'
|
||||
import TaskDelegateForm from './taskDelegateForm.vue'
|
||||
import { registerComponent } from '@/utils/routerHelper'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceDetail' })
|
||||
@ -166,16 +172,17 @@ const openTaskUpdateAssigneeForm = (id: string) => {
|
||||
taskUpdateAssigneeFormRef.value.open(id)
|
||||
}
|
||||
|
||||
const taskDelegateForm = ref()
|
||||
/** 处理审批退回的操作 */
|
||||
const handleDelegate = async (task) => {
|
||||
message.error('暂不支持【委派】功能,可以使用【转派】替代!')
|
||||
console.log(task)
|
||||
taskDelegateForm.value.open(task.id)
|
||||
}
|
||||
|
||||
//回退弹框组件
|
||||
const taskReturnDialogRef = ref()
|
||||
/** 处理审批退回的操作 */
|
||||
const handleBack = async (task) => {
|
||||
message.error('暂不支持【退回】功能!')
|
||||
console.log(task)
|
||||
taskReturnDialogRef.value.open(task.id)
|
||||
}
|
||||
|
||||
/** 获得详情 */
|
||||
@ -256,7 +263,7 @@ const getTaskList = async () => {
|
||||
auditForms.value = []
|
||||
tasks.value.forEach((task) => {
|
||||
// 2.1 只有待处理才需要
|
||||
if (task.result !== 1) {
|
||||
if (task.result !== 1 && task.result !== 6) {
|
||||
return
|
||||
}
|
||||
// 2.2 自己不是处理人
|
||||
|
@ -2,7 +2,7 @@
|
||||
<ContentWrap>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<div class="mb-2 float-right">
|
||||
<div class="float-right mb-2">
|
||||
<el-button size="small" type="primary" @click="showJson">生成 JSON</el-button>
|
||||
<el-button size="small" type="success" @click="showOption">生成 Options</el-button>
|
||||
<el-button size="small" type="danger" @click="showTemplate">生成组件</el-button>
|
||||
|
@ -20,8 +20,8 @@
|
||||
ref="treeRef"
|
||||
:data="preview.fileTree"
|
||||
:expand-on-click-node="false"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
@ -31,7 +31,7 @@
|
||||
<el-card
|
||||
v-loading="loading"
|
||||
:gutter="12"
|
||||
class="w-2/3 ml-3"
|
||||
class="ml-3 w-2/3"
|
||||
element-loading-text="加载代码中..."
|
||||
shadow="hover"
|
||||
>
|
||||
|
@ -7,7 +7,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center">
|
||||
<span class="text-lg font-medium mr-4"> 连接状态: </span>
|
||||
<span class="mr-4 text-lg font-medium"> 连接状态: </span>
|
||||
<el-tag :color="getTagColor">{{ status }}</el-tag>
|
||||
</div>
|
||||
<hr class="my-4" />
|
||||
@ -20,7 +20,7 @@
|
||||
{{ getIsOpen ? '关闭连接' : '开启连接' }}
|
||||
</el-button>
|
||||
</div>
|
||||
<p class="text-lg font-medium mt-4">设置</p>
|
||||
<p class="mt-4 text-lg font-medium">设置</p>
|
||||
<hr class="my-4" />
|
||||
<el-input
|
||||
v-model="sendValue"
|
||||
@ -43,7 +43,7 @@
|
||||
<ul>
|
||||
<li v-for="item in getList" :key="item.time" class="mt-2">
|
||||
<div class="flex items-center">
|
||||
<span class="mr-2 text-primary font-medium">收到消息:</span>
|
||||
<span class="text-primary mr-2 font-medium">收到消息:</span>
|
||||
<span>{{ formatDate(item.time) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -35,14 +35,14 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" row-key="id" default-expand-all>
|
||||
<el-table-column label="分类名称" prop="name" sortable />
|
||||
<el-table-column label="移动端分类图" align="center" prop="picUrl">
|
||||
<el-table-column label="名称" min-width="240" prop="name" sortable />
|
||||
<el-table-column label="分类图标" align="center" min-width="80" prop="picUrl">
|
||||
<template #default="scope">
|
||||
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="移动端分类图" class="h-30px" />
|
||||
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="移动端分类图" class="h-36px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分类排序" align="center" prop="sort" />
|
||||
<el-table-column label="开启状态" align="center" prop="status">
|
||||
<el-table-column label="排序" align="center" min-width="150" prop="sort" />
|
||||
<el-table-column label="状态" align="center" min-width="150" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
|
@ -8,7 +8,7 @@
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="商品" prop="spuId">
|
||||
<div @click="handleSelectSpu" class="w-60px h-60px">
|
||||
<div @click="handleSelectSpu" class="h-60px w-60px">
|
||||
<div v-if="spuData && spuData.picUrl">
|
||||
<el-image :src="spuData.picUrl" />
|
||||
</div>
|
||||
@ -18,7 +18,7 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品规格" prop="skuId" v-if="formData.spuId">
|
||||
<div @click="handleSelectSku" class="w-60px h-60px">
|
||||
<div @click="handleSelectSku" class="h-60px w-60px">
|
||||
<div v-if="skuData && skuData.picUrl">
|
||||
<el-image :src="skuData.picUrl" />
|
||||
</div>
|
||||
@ -150,6 +150,7 @@ const resetForm = () => {
|
||||
userNickname: undefined,
|
||||
userAvatar: undefined,
|
||||
spuId: undefined,
|
||||
spuName: undefined,
|
||||
skuId: undefined,
|
||||
descriptionScores: 5,
|
||||
benefitScores: 5,
|
||||
@ -182,11 +183,11 @@ const handleSkuChange = (sku: ProductSpuApi.Sku) => {
|
||||
<style>
|
||||
.select-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
@ -59,16 +59,15 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="false">
|
||||
<el-table-column label="评论编号" align="center" prop="id" min-width="60" />
|
||||
<el-table-column label="用户名称" align="center" prop="userNickname" width="80" />
|
||||
<el-table-column label="商品信息" align="center" min-width="300">
|
||||
<el-table-column label="评论编号" align="center" prop="id" min-width="50" />
|
||||
<el-table-column label="商品信息" align="center" min-width="400">
|
||||
<template #default="scope">
|
||||
<div class="flex row items-center gap-x-4px">
|
||||
<div class="row flex items-center gap-x-4px">
|
||||
<el-image
|
||||
v-if="scope.row.skuPicUrl"
|
||||
:src="scope.row.skuPicUrl"
|
||||
:preview-src-list="[scope.row.skuPicUrl]"
|
||||
class="w-40px h-40px shrink-0"
|
||||
class="h-40px w-40px shrink-0"
|
||||
preview-teleported
|
||||
/>
|
||||
<div>{{ scope.row.spuName }}</div>
|
||||
@ -82,10 +81,10 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分星级" align="center" prop="scores" width="80" />
|
||||
<el-table-column label="描述星级" align="center" prop="descriptionScores" width="80" />
|
||||
<el-table-column label="服务星级" align="center" prop="benefitScores" width="80" />
|
||||
<el-table-column label="评论内容" align="center" prop="content" min-width="80">
|
||||
<el-table-column label="用户名称" align="center" prop="userNickname" width="100" />
|
||||
<el-table-column label="商品评分" align="center" prop="descriptionScores" width="90" />
|
||||
<el-table-column label="服务评分" align="center" prop="benefitScores" width="90" />
|
||||
<el-table-column label="评论内容" align="center" prop="content" min-width="210">
|
||||
<template #default="scope">
|
||||
<p>{{ scope.row.content }}</p>
|
||||
<div class="flex justify-center gap-x-4px">
|
||||
@ -95,7 +94,7 @@
|
||||
:src="picUrl"
|
||||
:preview-src-list="scope.row.picUrls"
|
||||
:initial-index="index"
|
||||
class="w-40px h-40px"
|
||||
class="h-40px w-40px"
|
||||
preview-teleported
|
||||
/>
|
||||
</div>
|
||||
@ -105,7 +104,7 @@
|
||||
label="回复内容"
|
||||
align="center"
|
||||
prop="replyContent"
|
||||
min-width="100"
|
||||
min-width="250"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
@ -113,7 +112,7 @@
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column label="是否展示" align="center" width="80px">
|
||||
<template #default="scope">
|
||||
|
@ -53,8 +53,8 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column align="center" label="编号" prop="id" />
|
||||
<el-table-column align="center" label="名称" prop="name" />
|
||||
<el-table-column align="center" label="编号" min-width="60" prop="id" />
|
||||
<el-table-column align="center" label="属性名称" prop="name" min-width="150" />
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" label="备注" prop="remark" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
@ -165,7 +165,7 @@ const handleDelete = async (id: number) => {
|
||||
|
||||
/** 跳转商品属性列表 */
|
||||
const goValueList = (id: number) => {
|
||||
push({ path: '/product/property/value/' + id })
|
||||
push({ name: 'ProductPropertyValue', params: { propertyId: id } })
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
|
@ -45,8 +45,8 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名称" align="center" prop="name" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="编号" align="center" min-width="60" prop="id" />
|
||||
<el-table-column label="属性值名称" align="center" min-width="150" prop="name" />
|
||||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
|
@ -124,7 +124,7 @@
|
||||
<el-table-column v-if="isComponent" type="selection" width="45" />
|
||||
<el-table-column align="center" label="图片" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
|
||||
<el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-if="formData!.specType && !isBatch">
|
||||
@ -204,7 +204,7 @@
|
||||
<el-table-column v-if="isComponent" type="selection" width="45" />
|
||||
<el-table-column align="center" label="图片" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
|
||||
<el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-if="formData!.specType">
|
||||
|
@ -12,7 +12,7 @@
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
:src="row.picUrl"
|
||||
class="w-30px h-30px"
|
||||
class="h-30px w-30px"
|
||||
:preview-src-list="[row.picUrl]"
|
||||
preview-teleported
|
||||
/>
|
||||
@ -25,7 +25,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="销售价(元)" min-width="80">
|
||||
<template #default="{ row }">
|
||||
{{ row.price }}
|
||||
{{ fenToYuan(row.price) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -36,6 +36,7 @@
|
||||
import { ElTable } from 'element-plus'
|
||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { fenToYuan } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'SkuTableSelect' })
|
||||
|
||||
|
@ -73,7 +73,7 @@
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
:src="row.picUrl"
|
||||
class="w-30px h-30px"
|
||||
class="h-30px w-30px"
|
||||
:preview-src-list="[row.picUrl]"
|
||||
preview-teleported
|
||||
/>
|
||||
|
@ -15,15 +15,14 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="商品分类" prop="categoryId">
|
||||
<el-tree-select
|
||||
<el-cascader
|
||||
v-model="formData.categoryId"
|
||||
:data="categoryList"
|
||||
:options="categoryList"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
class="w-1/1"
|
||||
node-key="id"
|
||||
clearable
|
||||
placeholder="请选择商品分类"
|
||||
@change="categoryNodeClick"
|
||||
filterable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -74,8 +73,6 @@
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<!-- TODO 可能情况:善品录入后选择运费发现下拉选择中没有对应的模版 这里需不需要做添加运费模版后选择的功能 -->
|
||||
<!-- <el-button class="ml-20px">运费模板</el-button>-->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -102,7 +99,7 @@
|
||||
<el-form-item label="分销类型" props="subCommissionType">
|
||||
<el-radio-group v-model="formData.subCommissionType" @change="changeSubCommissionType">
|
||||
<el-radio :label="false">默认设置</el-radio>
|
||||
<el-radio :label="true" class="radio">自行设置</el-radio>
|
||||
<el-radio :label="true" class="radio">单独设置</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -117,7 +114,7 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.specType" label="商品属性">
|
||||
<el-button class="mr-15px mb-10px" @click="attributesAddFormRef.open">添加规格</el-button>
|
||||
<el-button class="mb-10px mr-15px" @click="attributesAddFormRef.open">添加属性</el-button>
|
||||
<ProductAttributes :propertyList="propertyList" @success="generateSkus" />
|
||||
</el-form-item>
|
||||
<template v-if="formData.specType && propertyList.length > 0">
|
||||
@ -139,7 +136,7 @@
|
||||
|
||||
<!-- 情况二:详情 -->
|
||||
<Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
|
||||
<template #categoryId="{ row }"> {{ categoryString(row.categoryId) }}</template>
|
||||
<template #categoryId="{ row }"> {{ formatCategoryName(row.categoryId) }}</template>
|
||||
<template #brandId="{ row }">
|
||||
{{ brandList.find((item) => item.id === row.brandId)?.name }}
|
||||
</template>
|
||||
@ -150,17 +147,17 @@
|
||||
{{ row.specType ? '多规格' : '单规格' }}
|
||||
</template>
|
||||
<template #subCommissionType="{ row }">
|
||||
{{ row.subCommissionType ? '自行设置' : '默认设置' }}
|
||||
{{ row.subCommissionType ? '单独设置' : '默认设置' }}
|
||||
</template>
|
||||
<template #picUrl="{ row }">
|
||||
<el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
|
||||
<el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" />
|
||||
</template>
|
||||
<template #sliderPicUrls="{ row }">
|
||||
<el-image
|
||||
v-for="(item, index) in row.sliderPicUrls"
|
||||
:key="index"
|
||||
:src="item.url"
|
||||
class="w-60px h-60px mr-10px"
|
||||
class="mr-10px h-60px w-60px"
|
||||
@click="imagePreview(row.sliderPicUrls)"
|
||||
/>
|
||||
</template>
|
||||
@ -206,17 +203,17 @@ const ruleConfig: RuleConfig[] = [
|
||||
{
|
||||
name: 'price',
|
||||
rule: (arg) => arg >= 0.01,
|
||||
message: '商品销售价格必须大于等于 0.01 !!!'
|
||||
message: '商品销售价格必须大于等于 0.01 元!!!'
|
||||
},
|
||||
{
|
||||
name: 'marketPrice',
|
||||
rule: (arg) => arg >= 0.01,
|
||||
message: '商品市场价格必须大于等于 0.01 !!!'
|
||||
message: '商品市场价格必须大于等于 0.01 元!!!'
|
||||
},
|
||||
{
|
||||
name: 'costPrice',
|
||||
rule: (arg) => arg >= 0.01,
|
||||
message: '商品成本价格必须大于等于 0.01 !!!'
|
||||
message: '商品成本价格必须大于等于 0.01 元!!!'
|
||||
}
|
||||
]
|
||||
|
||||
@ -359,23 +356,11 @@ const onChangeSpec = () => {
|
||||
}
|
||||
|
||||
const categoryList = ref([]) // 分类树
|
||||
/**
|
||||
* 选择分类时触发校验
|
||||
*/
|
||||
const categoryNodeClick = () => {
|
||||
if (!checkSelectedNode(categoryList.value, formData.categoryId)) {
|
||||
formData.categoryId = null
|
||||
message.warning('必须选择二级及以下节点!!')
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取分类的节点的完整结构
|
||||
*
|
||||
* @param categoryId 分类id
|
||||
*/
|
||||
const categoryString = (categoryId) => {
|
||||
/** 获取分类的节点的完整结构 */
|
||||
const formatCategoryName = (categoryId) => {
|
||||
return treeToString(categoryList.value, categoryId)
|
||||
}
|
||||
|
||||
const brandList = ref([]) // 精简商品品牌列表
|
||||
const deliveryTemplateList = ref([]) // 运费模版
|
||||
onMounted(async () => {
|
||||
|
@ -41,7 +41,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<!-- TODO tag展示暂时不考虑排序 -->
|
||||
<!-- TODO @puhui999:tag展示暂时不考虑排序;支持拖动排序 -->
|
||||
<el-form-item label="活动优先级">
|
||||
<el-tag>默认</el-tag>
|
||||
<el-tag class="ml-2" type="success">秒杀</el-tag>
|
||||
|
@ -102,7 +102,7 @@ const getDetail = async () => {
|
||||
if ('ProductSpuDetail' === name) {
|
||||
isDetail.value = true
|
||||
}
|
||||
const id = params.spuId as unknown as number
|
||||
const id = params.id as unknown as number
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
@ -161,7 +161,7 @@ const submitForm = async () => {
|
||||
deepCopyFormData.sliderPicUrls = newSliderPicUrls
|
||||
// 校验都通过后提交表单
|
||||
const data = deepCopyFormData as ProductSpuApi.Spu
|
||||
const id = params.spuId as unknown as number
|
||||
const id = params.id as unknown as number
|
||||
if (!id) {
|
||||
await ProductSpuApi.createSpu(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
|
@ -18,15 +18,14 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品分类" prop="categoryId">
|
||||
<el-tree-select
|
||||
<el-cascader
|
||||
v-model="queryParams.categoryId"
|
||||
:data="categoryList"
|
||||
:options="categoryList"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
class="w-1/1"
|
||||
node-key="id"
|
||||
clearable
|
||||
placeholder="请选择商品分类"
|
||||
@change="nodeClick"
|
||||
filterable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
@ -78,7 +77,7 @@
|
||||
/>
|
||||
</el-tabs>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column type="expand" width="30">
|
||||
<el-table-column type="expand">
|
||||
<template #default="{ row }">
|
||||
<el-form class="spu-table-expand" label-position="left">
|
||||
<el-row>
|
||||
@ -86,17 +85,17 @@
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="商品分类:">
|
||||
<span>{{ categoryString(row.categoryId) }}</span>
|
||||
<span>{{ formatCategoryName(row.categoryId) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="市场价:">
|
||||
<span>{{ floatToFixed2(row.marketPrice) }}元</span>
|
||||
<span>{{ fenToYuan(row.marketPrice) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="成本价:">
|
||||
<span>{{ floatToFixed2(row.costPrice) }}元</span>
|
||||
<span>{{ fenToYuan(row.costPrice) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -106,9 +105,8 @@
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="收藏:">
|
||||
<!-- TODO 没有这个属性,暂时写死 5 个 -->
|
||||
<span>5</span>
|
||||
<el-form-item label="浏览量:">
|
||||
<span>{{ row.browseCount }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
@ -122,15 +120,15 @@
|
||||
</el-form>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column key="id" align="center" label="商品编号" prop="id" />
|
||||
<el-table-column align="center" label="商品编号" min-width="60" prop="id" />
|
||||
<el-table-column label="商品图" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="row.picUrl" class="w-30px h-30px" @click="imagePreview(row.picUrl)" />
|
||||
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
|
||||
<el-table-column align="center" label="商品售价" min-width="90" prop="price">
|
||||
<template #default="{ row }"> {{ floatToFixed2(row.price) }}元</template>
|
||||
<template #default="{ row }"> {{ fenToYuan(row.price) }}元</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
|
||||
<el-table-column align="center" label="库存" min-width="90" prop="stock" />
|
||||
@ -152,7 +150,7 @@
|
||||
active-text="上架"
|
||||
inactive-text="下架"
|
||||
inline-prompt
|
||||
@change="changeStatus(row)"
|
||||
@change="handleStatusChange(row)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
@ -191,7 +189,7 @@
|
||||
v-hasPermi="['product:spu:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="changeStatus(row, ProductSpuStatusEnum.DISABLE.status)"
|
||||
@click="handleStatus02Change(row, ProductSpuStatusEnum.DISABLE.status)"
|
||||
>
|
||||
恢复到仓库
|
||||
</el-button>
|
||||
@ -201,7 +199,7 @@
|
||||
v-hasPermi="['product:spu:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="changeStatus(row, ProductSpuStatusEnum.RECYCLE.status)"
|
||||
@click="handleStatus02Change(row, ProductSpuStatusEnum.RECYCLE.status)"
|
||||
>
|
||||
加入回收站
|
||||
</el-button>
|
||||
@ -220,12 +218,11 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { TabsPaneContext } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree'
|
||||
import { defaultProps, handleTree, treeToString } from '@/utils/tree'
|
||||
import { ProductSpuStatusEnum } from '@/utils/constants'
|
||||
import { floatToFixed2 } from '@/utils'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import download from '@/utils/download'
|
||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||
import * as ProductCategoryApi from '@/api/mall/product/category'
|
||||
@ -254,7 +251,7 @@ const tabsData = ref([
|
||||
},
|
||||
{
|
||||
count: 0,
|
||||
name: '已经售空商品',
|
||||
name: '已售罄商品',
|
||||
type: 2
|
||||
},
|
||||
{
|
||||
@ -303,43 +300,37 @@ const getList = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改 SPU 状态
|
||||
*
|
||||
* @param row
|
||||
* @param status 更改前的值
|
||||
*/
|
||||
const changeStatus = async (row, status?: number) => {
|
||||
const deepCopyValue = cloneDeep(unref(row))
|
||||
if (typeof status !== 'undefined') deepCopyValue.status = status
|
||||
/** 添加到仓库 / 回收站的状态 */
|
||||
const handleStatus02Change = async (row, newStatus: number) => {
|
||||
try {
|
||||
let text = ''
|
||||
switch (deepCopyValue.status) {
|
||||
case ProductSpuStatusEnum.DISABLE.status:
|
||||
text = ProductSpuStatusEnum.DISABLE.name
|
||||
break
|
||||
case ProductSpuStatusEnum.ENABLE.status:
|
||||
text = ProductSpuStatusEnum.ENABLE.name
|
||||
break
|
||||
case ProductSpuStatusEnum.RECYCLE.status:
|
||||
text = `加入${ProductSpuStatusEnum.RECYCLE.name}`
|
||||
break
|
||||
}
|
||||
await message.confirm(
|
||||
deepCopyValue.status === -1
|
||||
? `确认要将[${row.name}]${text}吗?`
|
||||
: row.status === -1 // 再判断一次原对象是否等于-1,例: 把回收站中的商品恢复到仓库中,事件触发时原对象status为-1 深拷贝对象status被赋值为0
|
||||
? `确认要将[${row.name}]恢复到仓库吗?`
|
||||
: `确认要${text}[${row.name}]吗?`
|
||||
)
|
||||
await ProductSpuApi.updateStatus({ id: deepCopyValue.id, status: deepCopyValue.status })
|
||||
message.success('更新状态成功')
|
||||
// 二次确认
|
||||
const text = newStatus === ProductSpuStatusEnum.RECYCLE.status ? '加入到回收站' : '恢复到仓库'
|
||||
await message.confirm(`确认要"${row.name}"${text}吗?`)
|
||||
// 发起修改
|
||||
await ProductSpuApi.updateStatus({ id: row.id, status: newStatus })
|
||||
message.success(text + '成功')
|
||||
// 刷新 tabs 数据
|
||||
await getTabsCount()
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 更新上架/下架状态 */
|
||||
const handleStatusChange = async (row) => {
|
||||
try {
|
||||
// 二次确认
|
||||
const text = row.status ? '上架' : '下架'
|
||||
await message.confirm(`确认要${text}"${row.name}"吗?`)
|
||||
// 发起修改
|
||||
await ProductSpuApi.updateStatus({ id: row.id, status: row.status })
|
||||
message.success(text + '成功')
|
||||
// 刷新 tabs 数据
|
||||
await getTabsCount()
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {
|
||||
// 取消更改状态时回显数据
|
||||
// 异常时,需要重置回之前的值
|
||||
row.status =
|
||||
row.status === ProductSpuStatusEnum.DISABLE.status
|
||||
? ProductSpuStatusEnum.ENABLE.status
|
||||
@ -380,26 +371,20 @@ const resetQuery = () => {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增或修改
|
||||
*
|
||||
* @param id 商品 SPU 编号
|
||||
*/
|
||||
/** 新增或修改 */
|
||||
const openForm = (id?: number) => {
|
||||
// 修改
|
||||
if (typeof id === 'number') {
|
||||
push({ name: 'ProductSpuEdit', params: { spuId: id } })
|
||||
push({ name: 'ProductSpuEdit', params: { id } })
|
||||
return
|
||||
}
|
||||
// 新增
|
||||
push({ name: 'ProductSpuAdd' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看商品详情
|
||||
*/
|
||||
/** 查看商品详情 */
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'ProductSpuDetail', params: { spuId: id } })
|
||||
push({ name: 'ProductSpuDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
@ -417,6 +402,12 @@ const handleExport = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const categoryList = ref() // 分类树
|
||||
/** 获取分类的节点的完整结构 */
|
||||
const formatCategoryName = (categoryId) => {
|
||||
return treeToString(categoryList.value, categoryId)
|
||||
}
|
||||
|
||||
// 监听路由变化更新列表,解决商品保存后,列表不刷新的问题。
|
||||
watch(
|
||||
() => currentRoute.value,
|
||||
@ -425,25 +416,6 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
const categoryList = ref() // 分类树
|
||||
/**
|
||||
* 获取分类的节点的完整结构
|
||||
* @param categoryId 分类id
|
||||
*/
|
||||
const categoryString = (categoryId) => {
|
||||
return treeToString(categoryList.value, categoryId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验所选是否为二级及以下节点
|
||||
*/
|
||||
const nodeClick = () => {
|
||||
if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) {
|
||||
queryParams.value.categoryId = null
|
||||
message.warning('必须选择二级及以下节点!!')
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getTabsCount()
|
||||
|
@ -30,7 +30,7 @@
|
||||
<el-table-column align="center" label="砍价底价(元)" min-width="168">
|
||||
<template #default="{ row: sku }">
|
||||
<el-input-number
|
||||
v-model="sku.productConfig.bargainPrice"
|
||||
v-model="sku.productConfig.bargainMinPrice"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:step="0.1"
|
||||
@ -86,7 +86,7 @@ const ruleConfig: RuleConfig[] = [
|
||||
message: '商品砍价起始价格不能小于 0 !!!'
|
||||
},
|
||||
{
|
||||
name: 'productConfig.bargainPrice',
|
||||
name: 'productConfig.bargainMinPrice',
|
||||
rule: (arg) => arg >= 0,
|
||||
message: '商品砍价底价不能小于 0 !!!'
|
||||
},
|
||||
@ -123,14 +123,14 @@ const getSpuDetails = async (
|
||||
spuId: spu.id!,
|
||||
skuId: sku.id!,
|
||||
bargainFirstPrice: 1,
|
||||
bargainPrice: 1,
|
||||
bargainMinPrice: 1,
|
||||
stock: 1
|
||||
}
|
||||
if (typeof products !== 'undefined') {
|
||||
const product = products.find((item) => item.skuId === sku.id)
|
||||
if (product) {
|
||||
product.bargainFirstPrice = formatToFraction(product.bargainFirstPrice)
|
||||
product.bargainPrice = formatToFraction(product.bargainPrice)
|
||||
product.bargainMinPrice = formatToFraction(product.bargainMinPrice)
|
||||
}
|
||||
config = product || config
|
||||
}
|
||||
@ -173,7 +173,7 @@ const open = async (type: string, id?: number) => {
|
||||
spuId: data.spuId!,
|
||||
skuId: data.skuId,
|
||||
bargainFirstPrice: data.bargainFirstPrice, // 砍价起始价格,单位分
|
||||
bargainPrice: data.bargainPrice, // 砍价底价
|
||||
bargainMinPrice: data.bargainMinPrice, // 砍价底价
|
||||
stock: data.stock // 活动库存
|
||||
}
|
||||
]
|
||||
@ -204,12 +204,13 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
// TODO @puhui999: 这样要深克隆
|
||||
const data = formRef.value.formModel as BargainActivityApi.BargainActivityVO
|
||||
const products = spuAndSkuListRef.value.getSkuConfigs('productConfig')
|
||||
products.forEach((item: BargainProductVO) => {
|
||||
// 砍价价格元转分
|
||||
item.bargainFirstPrice = convertToInteger(item.bargainFirstPrice)
|
||||
item.bargainPrice = convertToInteger(item.bargainPrice)
|
||||
item.bargainMinPrice = convertToInteger(item.bargainMinPrice)
|
||||
})
|
||||
// 用户每次砍价金额分转元, 元转分
|
||||
data.randomMinPrice = convertToInteger(data.randomMinPrice)
|
||||
|
@ -6,7 +6,7 @@ export const rules = reactive({
|
||||
name: [required],
|
||||
startTime: [required],
|
||||
endTime: [required],
|
||||
userSize: [required],
|
||||
helpMaxCount: [required],
|
||||
bargainCount: [required],
|
||||
singleLimitCount: [required]
|
||||
})
|
||||
@ -72,7 +72,7 @@ const crudSchemas = reactive<CrudSchema[]>([
|
||||
},
|
||||
{
|
||||
label: '砍价人数',
|
||||
field: 'userSize',
|
||||
field: 'helpMaxCount',
|
||||
isSearch: false,
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
@ -132,20 +132,6 @@ const crudSchemas = reactive<CrudSchema[]>([
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '砍价成功数量',
|
||||
field: 'successCount',
|
||||
isSearch: false,
|
||||
isForm: false
|
||||
},
|
||||
{
|
||||
label: '活动状态',
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
dictClass: 'number',
|
||||
isSearch: true,
|
||||
isForm: false
|
||||
},
|
||||
{
|
||||
label: '拼团商品',
|
||||
field: 'spuId',
|
||||
@ -155,11 +141,6 @@ const crudSchemas = reactive<CrudSchema[]>([
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '操作',
|
||||
field: 'action',
|
||||
isForm: false
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
|
@ -1,90 +1,195 @@
|
||||
<template>
|
||||
<!-- 搜索工作栏 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @reset="setSearchParams" @search="setSearchParams">
|
||||
<!-- 新增等操作按钮 -->
|
||||
<template #actionMore>
|
||||
<el-button
|
||||
v-hasPermi="['promotion:bargain-activity:create']"
|
||||
plain
|
||||
type="primary"
|
||||
@click="openForm('create')"
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入活动名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择活动状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['promotion:bargain-activity:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</template>
|
||||
</Search>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<Table
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
:columns="allSchemas.tableColumns"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
>
|
||||
<template #spuId="{ row }">
|
||||
<el-image
|
||||
:src="row.picUrl"
|
||||
class="w-30px h-30px align-middle mr-5px"
|
||||
@click="imagePreview(row.picUrl)"
|
||||
/>
|
||||
<span class="align-middle">{{ row.spuName }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
v-hasPermi="['promotion:bargain-activity:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['promotion:bargain-activity:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="活动编号" prop="id" min-width="80" />
|
||||
<el-table-column label="活动名称" prop="name" min-width="140" />
|
||||
<el-table-column label="活动时间" min-width="210">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.startTime, 'YYYY-MM-DD') }}
|
||||
~ {{ formatDate(scope.row.endTime, 'YYYY-MM-DD') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品图片" prop="spuName" min-width="80">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
:src="scope.row.picUrl"
|
||||
class="h-40px w-40px"
|
||||
:preview-src-list="[scope.row.picUrl]"
|
||||
preview-teleported
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品标题" prop="spuName" min-width="300" />
|
||||
<el-table-column
|
||||
label="起始价格"
|
||||
prop="bargainFirstPrice"
|
||||
min-width="100"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column
|
||||
label="砍价底价"
|
||||
prop="bargainMinPrice"
|
||||
min-width="100"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column label="总砍价人数" prop="recordUserCount" min-width="100" />
|
||||
<el-table-column label="成功砍价人数" prop="recordSuccessUserCount" min-width="110" />
|
||||
<el-table-column label="助力人数" prop="helpUserCount" min-width="100" />
|
||||
<el-table-column label="活动状态" align="center" prop="status" min-width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库存" align="center" prop="stock" min-width="80" />
|
||||
<el-table-column label="总库存" align="center" prop="totalStock" min-width="80" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="150px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['promotion:bargain-activity:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleClose(scope.row.id)"
|
||||
v-if="scope.row.status === 0"
|
||||
v-hasPermi="['promotion:bargain-activity:close']"
|
||||
>
|
||||
关闭
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-else
|
||||
v-hasPermi="['promotion:bargain-activity:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<BargainActivityForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { allSchemas } from './bargainActivity.data'
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as BargainActivityApi from '@/api/mall/promotion/bargain/bargainActivity'
|
||||
import BargainActivityForm from './BargainActivityForm.vue'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import { sortTableColumns } from '@/hooks/web/useCrudSchemas'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { fenToYuan } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'PromotionBargainActivity' })
|
||||
|
||||
// tableObject:表格的属性对象,可获得分页大小、条数等属性
|
||||
// tableMethods:表格的操作对象,可进行获得分页、删除记录等操作
|
||||
// 详细可见:https://doc.iocoder.cn/vue3/crud-schema/
|
||||
const { tableObject, tableMethods } = useTable({
|
||||
getListApi: BargainActivityApi.getBargainActivityPage, // 分页接口
|
||||
delListApi: BargainActivityApi.deleteBargainActivity // 删除接口
|
||||
})
|
||||
// 获得表格的各种操作
|
||||
const { getList, setSearchParams } = tableMethods
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
/** 商品图预览 */
|
||||
const imagePreview = (imgUrl: string) => {
|
||||
createImageViewer({
|
||||
urlList: [imgUrl]
|
||||
})
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await BargainActivityApi.getBargainActivityPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
@ -93,15 +198,35 @@ const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
// TODO 芋艿:这里要改下
|
||||
/** 关闭按钮操作 */
|
||||
const handleClose = async (id: number) => {
|
||||
try {
|
||||
// 关闭的二次确认
|
||||
await message.confirm('确认关闭该秒杀活动吗?')
|
||||
// 发起关闭
|
||||
await BargainActivityApi.closeSeckillActivity(id)
|
||||
message.success('关闭成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (id: number) => {
|
||||
tableMethods.delList(id, false)
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await BargainActivityApi.deleteBargainActivity(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
// 获得活动列表
|
||||
sortTableColumns(allSchemas.tableColumns, 'spuId')
|
||||
getList()
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
})
|
||||
</script>
|
||||
|
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="助力列表">
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="用户编号" prop="userId" min-width="80px" />
|
||||
<el-table-column label="用户头像" prop="avatar" min-width="80px">
|
||||
<template #default="scope">
|
||||
<el-avatar :src="scope.row.avatar" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="用户昵称" prop="nickname" min-width="100px" />
|
||||
<el-table-column
|
||||
label="砍价金额"
|
||||
prop="reducePrice"
|
||||
min-width="100px"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column
|
||||
label="助力时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as BargainHelpApi from '@/api/mall/promotion/bargain/bargainHelp'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
|
||||
/** 助力列表 */
|
||||
defineOptions({ name: 'BargainRecordListDialog' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
recordId: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 打开弹窗 */
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const open = async (recordId: any) => {
|
||||
dialogVisible.value = true
|
||||
queryParams.recordId = recordId
|
||||
resetQuery()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await BargainHelpApi.getBargainHelpPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
</script>
|
195
src/views/mall/promotion/bargain/record/index.vue
Normal file
195
src/views/mall/promotion/bargain/record/index.vue
Normal file
@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="砍价状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择砍价状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_BARGAIN_RECORD_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['promotion:bargain-record:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['promotion:bargain-record:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" min-width="50" prop="id" />
|
||||
<el-table-column label="发起用户" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
:src="scope.row.avatar"
|
||||
class="h-20px w-20px"
|
||||
:preview-src-list="[scope.row.avatar]"
|
||||
preview-teleported
|
||||
/>
|
||||
{{ scope.row.nickname }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="发起时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="砍价活动" min-width="150" prop="activity.name" />
|
||||
<el-table-column
|
||||
label="最低价"
|
||||
min-width="100"
|
||||
prop="activity.bargainMinPrice"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column
|
||||
label="当前价"
|
||||
min-width="100"
|
||||
prop="bargainPrice"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column label="总砍价次数" min-width="100" prop="activity.helpMaxCount" />
|
||||
<el-table-column label="剩余砍价次数" min-width="100" prop="helpCount" />
|
||||
<el-table-column label="砍价状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_BARGAIN_RECORD_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="结束时间"
|
||||
align="center"
|
||||
prop="endTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="订单编号" align="center" prop="orderId" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openRecordListDialog(scope.row.id)"
|
||||
v-hasPermi="['promotion:bargain-help:query']"
|
||||
>
|
||||
助力
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗 -->
|
||||
<BargainRecordListDialog ref="recordListDialogRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as BargainRecordApi from '@/api/mall/promotion/bargain/bargainRecord'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import BargainRecordListDialog from './BargainRecordListDialog.vue'
|
||||
|
||||
defineOptions({ name: 'PromotionBargainRecord' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
status: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await BargainRecordApi.getBargainRecordPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开[助力]弹窗 */
|
||||
const recordListDialogRef = ref()
|
||||
const openRecordListDialog = (id?: number) => {
|
||||
recordListDialogRef.value.open(id)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
@ -167,6 +167,7 @@ const submitForm = async () => {
|
||||
products.forEach((item: CombinationActivityApi.CombinationProductVO) => {
|
||||
item.combinationPrice = convertToInteger(item.combinationPrice)
|
||||
})
|
||||
// TODO @puhui999: 这样要深克隆
|
||||
const data = formRef.value.formModel as CombinationActivityApi.CombinationActivityVO
|
||||
data.products = products
|
||||
// 真正提交
|
||||
|
@ -33,7 +33,7 @@
|
||||
<template #spuId="{ row }">
|
||||
<el-image
|
||||
:src="row.picUrl"
|
||||
class="w-30px h-30px align-middle mr-5px"
|
||||
class="mr-5px h-30px w-30px align-middle"
|
||||
@click="imagePreview(row.picUrl)"
|
||||
/>
|
||||
<span class="align-middle">{{ row.spuName }}</span>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<el-table-column key="id" align="center" label="商品编号" prop="id" />
|
||||
<el-table-column label="商品图" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="row.picUrl" class="w-30px h-30px" @click="imagePreview(row.picUrl)" />
|
||||
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
|
||||
|
@ -70,7 +70,7 @@
|
||||
<el-table-column key="id" align="center" label="商品编号" prop="id" />
|
||||
<el-table-column label="商品图" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="row.picUrl" class="w-30px h-30px" @click="imagePreview(row.picUrl)" />
|
||||
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
|
@ -9,7 +9,7 @@ export const discountFormat = (row: CouponTemplateVO) => {
|
||||
return `¥${floatToFixed2(row.discountPrice)}`
|
||||
}
|
||||
if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
|
||||
return `${row.discountPrice}%`
|
||||
return `${row.discountPercent}%`
|
||||
}
|
||||
return '未知【' + row.discountType + '】'
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
@keyup="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-form-item label="领取时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
@ -50,12 +50,17 @@
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="会员信息" align="center" prop="nickname" />
|
||||
<!-- TODO 芋艿:以后支持头像,支持跳转 -->
|
||||
<el-table-column label="优惠劵" align="center" prop="name" />
|
||||
<el-table-column label="优惠券类型" align="center" prop="discountType">
|
||||
<el-table-column label="会员昵称" align="center" min-width="100" prop="nickname" />
|
||||
<el-table-column label="优惠券名称" align="center" min-width="140" prop="name" />
|
||||
<el-table-column label="类型" align="center" prop="discountType">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_PRODUCT_SCOPE" :value="scope.row.productScope" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="优惠" min-width="100" prop="discount">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
|
||||
{{ discountFormat(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="领取方式" align="center" prop="takeType">
|
||||
@ -109,6 +114,7 @@
|
||||
import { deleteCoupon, getCouponPage } from '@/api/mall/promotion/coupon/coupon'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { discountFormat } from '@/views/mall/promotion/coupon/formatter'
|
||||
|
||||
defineOptions({ name: 'PromotionCoupon' })
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
v-if="formData.productScope === PromotionProductScopeEnum.SPU.scope"
|
||||
prop="productSpuIds"
|
||||
>
|
||||
<div class="flex items-center gap-1 flex-wrap">
|
||||
<div class="flex flex-wrap items-center gap-1">
|
||||
<div class="select-box spu-pic" v-for="(spu, index) in productSpus" :key="spu.id">
|
||||
<el-image :src="spu.picUrl" />
|
||||
<Icon icon="ep:circle-close-filled" class="del-icon" @click="handleRemoveSpu(index)" />
|
||||
@ -62,7 +62,7 @@
|
||||
<el-input-number
|
||||
v-model="formData.discountPrice"
|
||||
placeholder="请输入优惠金额,单位:元"
|
||||
class="!w-400px mr-2"
|
||||
class="mr-2 !w-400px"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
/>
|
||||
@ -76,7 +76,7 @@
|
||||
<el-input-number
|
||||
v-model="formData.discountPercent"
|
||||
placeholder="优惠券折扣不能小于 1 折,且不可大于 9.9 折"
|
||||
class="!w-400px mr-2"
|
||||
class="mr-2 !w-400px"
|
||||
:precision="1"
|
||||
:min="1"
|
||||
:max="9.9"
|
||||
@ -91,7 +91,7 @@
|
||||
<el-input-number
|
||||
v-model="formData.discountLimitPrice"
|
||||
placeholder="请输入最多优惠"
|
||||
class="!w-400px mr-2"
|
||||
class="mr-2 !w-400px"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
/>
|
||||
@ -101,7 +101,7 @@
|
||||
<el-input-number
|
||||
v-model="formData.usePrice"
|
||||
placeholder="无门槛请设为 0"
|
||||
class="!w-400px mr-2"
|
||||
class="mr-2 !w-400px"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
/>
|
||||
@ -117,7 +117,7 @@
|
||||
<el-input-number
|
||||
v-model="formData.totalCount"
|
||||
placeholder="发放数量,没有之后不能领取或发放,-1 为不限制"
|
||||
class="!w-400px mr-2"
|
||||
class="mr-2 !w-400px"
|
||||
:precision="0"
|
||||
:min="-1"
|
||||
/>
|
||||
@ -127,7 +127,7 @@
|
||||
<el-input-number
|
||||
v-model="formData.takeLimitCount"
|
||||
placeholder="设置为 -1 时,可无限领取"
|
||||
class="!w-400px mr-2"
|
||||
class="mr-2 !w-400px"
|
||||
:precision="0"
|
||||
:min="-1"
|
||||
/>
|
||||
@ -423,22 +423,24 @@ const handleRemoveSpu = (index: number) => {
|
||||
<style scoped lang="scss">
|
||||
.select-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: 8px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.spu-pic {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.del-icon {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
z-index: 1;
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
right: -10px;
|
||||
top: -10px;
|
||||
}
|
||||
</style>
|
||||
|
@ -19,7 +19,7 @@
|
||||
@keyup="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="优惠券类型" prop="discountType">
|
||||
<el-form-item label="优惠类型" prop="discountType">
|
||||
<el-select
|
||||
v-model="queryParams.discountType"
|
||||
class="!w-240px"
|
||||
@ -71,14 +71,6 @@
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:plus" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
type="success"
|
||||
@click="$router.push('/promotion/coupon')"
|
||||
v-hasPermi="['promotion:coupon:query']"
|
||||
>
|
||||
<Icon icon="ep:operation" class="mr-5px" />会员优惠劵
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
@ -86,17 +78,29 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="优惠券名称" align="center" prop="name" />
|
||||
<el-table-column label="优惠券类型" align="center" prop="discountType">
|
||||
<el-table-column label="优惠券名称" min-width="140" prop="name" />
|
||||
<el-table-column label="类型" min-width="80" prop="productScope">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_PRODUCT_SCOPE" :value="scope.row.productScope" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="优惠" min-width="100" prop="discount">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
|
||||
{{ discountFormat(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="领取方式" min-width="100" prop="takeType">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE" :value="scope.row.takeType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="优惠金额 / 折扣"
|
||||
label="使用时间"
|
||||
align="center"
|
||||
prop="discount"
|
||||
:formatter="discountFormat"
|
||||
prop="validityType"
|
||||
width="185"
|
||||
:formatter="validityTypeFormat"
|
||||
/>
|
||||
<el-table-column label="发放数量" align="center" prop="totalCount" />
|
||||
<el-table-column
|
||||
@ -111,13 +115,6 @@
|
||||
prop="takeLimitCount"
|
||||
:formatter="takeLimitCountFormat"
|
||||
/>
|
||||
<el-table-column
|
||||
label="有效期限"
|
||||
align="center"
|
||||
prop="validityType"
|
||||
width="190"
|
||||
:formatter="validityTypeFormat"
|
||||
/>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
|
@ -1,94 +1,205 @@
|
||||
<template>
|
||||
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @reset="setSearchParams" @search="setSearchParams">
|
||||
<!-- 新增等操作按钮 -->
|
||||
<template #actionMore>
|
||||
<el-button
|
||||
v-hasPermi="['promotion:seckill-activity:create']"
|
||||
plain
|
||||
type="primary"
|
||||
@click="openForm('create')"
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入活动名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择活动状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:plus" /> 新增
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['promotion:seckill-activity:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</template>
|
||||
</Search>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<Table
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
:columns="allSchemas.tableColumns"
|
||||
:data="tableObject.tableList"
|
||||
:expand="true"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
@expand-change="expandChange"
|
||||
>
|
||||
<template #expand> 展示活动商品和商品相关属性活动配置</template>
|
||||
<template #spuId="{ row }">
|
||||
<el-image
|
||||
:src="row.picUrl"
|
||||
class="w-30px h-30px align-middle mr-5px"
|
||||
@click="imagePreview(row.picUrl)"
|
||||
/>
|
||||
<span class="align-middle">{{ row.spuName }}</span>
|
||||
</template>
|
||||
<template #configIds="{ row }">
|
||||
<el-tag v-for="(name, index) in convertSeckillConfigNames(row)" :key="index" class="mr-5px">
|
||||
{{ name }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button
|
||||
v-hasPermi="['promotion:seckill-activity:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['promotion:seckill-activity:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</Table>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="活动编号" prop="id" min-width="80" />
|
||||
<el-table-column label="活动名称" prop="name" min-width="140" />
|
||||
<el-table-column
|
||||
label="秒杀时段"
|
||||
prop="configIds"
|
||||
width="220px"
|
||||
:show-overflow-tooltip="false"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-tag v-for="(configId, index) in scope.row.configIds" :key="index" class="mr-5px">
|
||||
{{ formatConfigNames(configId) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动时间" min-width="210">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.startTime, 'YYYY-MM-DD') }}
|
||||
~ {{ formatDate(scope.row.endTime, 'YYYY-MM-DD') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品图片" prop="spuName" min-width="80">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
:src="scope.row.picUrl"
|
||||
class="h-40px w-40px"
|
||||
:preview-src-list="[scope.row.picUrl]"
|
||||
preview-teleported
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品标题" prop="spuName" min-width="300" />
|
||||
<el-table-column
|
||||
label="原价"
|
||||
prop="marketPrice"
|
||||
min-width="100"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column label="秒杀价" prop="marketPrice" min-width="100">
|
||||
<template #default="scope">
|
||||
{{ formatSeckillPrice(scope.row.products) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动状态" align="center" prop="status" min-width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库存" align="center" prop="stock" min-width="80" />
|
||||
<el-table-column label="总库存" align="center" prop="totalStock" min-width="80" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="150px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['promotion:seckill-activity:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleClose(scope.row.id)"
|
||||
v-if="scope.row.status === 0"
|
||||
v-hasPermi="['promotion:seckill-activity:close']"
|
||||
>
|
||||
关闭
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-else
|
||||
v-hasPermi="['promotion:seckill-activity:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<SeckillActivityForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { allSchemas } from './seckillActivity.data'
|
||||
import { getSimpleSeckillConfigList } from '@/api/mall/promotion/seckill/seckillConfig'
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
||||
import * as SeckillConfigApi from '@/api/mall/promotion/seckill/seckillConfig'
|
||||
import SeckillActivityForm from './SeckillActivityForm.vue'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import { sortTableColumns } from '@/hooks/web/useCrudSchemas'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { fenToYuan } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'PromotionSeckillActivity' })
|
||||
defineOptions({ name: 'SeckillActivity' })
|
||||
|
||||
// tableObject:表格的属性对象,可获得分页大小、条数等属性
|
||||
// tableMethods:表格的操作对象,可进行获得分页、删除记录等操作
|
||||
// 详细可见:https://doc.iocoder.cn/vue3/crud-schema/
|
||||
const { tableObject, tableMethods } = useTable({
|
||||
getListApi: SeckillActivityApi.getSeckillActivityPage, // 分页接口
|
||||
delListApi: SeckillActivityApi.deleteSeckillActivity // 删除接口
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null
|
||||
})
|
||||
// 获得表格的各种操作
|
||||
const { getList, setSearchParams } = tableMethods
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await SeckillActivityApi.getSeckillActivityPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
@ -96,37 +207,47 @@ const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (id: number) => {
|
||||
tableMethods.delList(id, false)
|
||||
/** 关闭按钮操作 */
|
||||
const handleClose = async (id: number) => {
|
||||
try {
|
||||
// 关闭的二次确认
|
||||
await message.confirm('确认关闭该秒杀活动吗?')
|
||||
// 发起关闭
|
||||
await SeckillActivityApi.closeSeckillActivity(id)
|
||||
message.success('关闭成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 商品图预览 */
|
||||
const imagePreview = (imgUrl: string) => {
|
||||
createImageViewer({
|
||||
urlList: [imgUrl]
|
||||
})
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await SeckillActivityApi.deleteSeckillActivity(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const configList = ref([]) // 时段配置精简列表
|
||||
const convertSeckillConfigNames = computed(
|
||||
() => (row) =>
|
||||
configList.value
|
||||
?.filter((item) => row.configIds.includes(item.id))
|
||||
?.map((config) => config.name)
|
||||
)
|
||||
const formatConfigNames = (configId) => {
|
||||
const config = configList.value.find((item) => item.id === configId)
|
||||
return config != null ? `${config.name}[${config.startTime} ~ ${config.endTime}]` : ''
|
||||
}
|
||||
|
||||
const expandChange = (row, expandedRows) => {
|
||||
// TODO puhui:等 CRUD 完事后弄
|
||||
console.log(row, expandedRows)
|
||||
const formatSeckillPrice = (products) => {
|
||||
const seckillPrice = Math.min(...products.map((item) => item.seckillPrice))
|
||||
return `¥${fenToYuan(seckillPrice)}`
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 获得活动列表
|
||||
sortTableColumns(allSchemas.tableColumns, 'spuId')
|
||||
await getList()
|
||||
// 获得秒杀时间段
|
||||
configList.value = await getSimpleSeckillConfigList()
|
||||
configList.value = await SeckillConfigApi.getSimpleSeckillConfigList()
|
||||
})
|
||||
</script>
|
||||
|
@ -94,42 +94,6 @@ const crudSchemas = reactive<CrudSchema[]>([
|
||||
width: 300
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '新增订单数',
|
||||
field: 'orderCount',
|
||||
isForm: false,
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
value: 0
|
||||
},
|
||||
table: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '付款人数',
|
||||
field: 'userCount',
|
||||
isForm: false,
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
value: 0
|
||||
},
|
||||
table: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '订单实付金额',
|
||||
field: 'totalPrice',
|
||||
isForm: false,
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
value: 0
|
||||
},
|
||||
table: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '总限购数量',
|
||||
field: 'totalLimitCount',
|
||||
@ -163,26 +127,6 @@ const crudSchemas = reactive<CrudSchema[]>([
|
||||
width: 80
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '秒杀库存',
|
||||
field: 'stock',
|
||||
isForm: false,
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
value: 0
|
||||
},
|
||||
table: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '秒杀总库存',
|
||||
field: 'totalStock',
|
||||
isForm: false,
|
||||
table: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '秒杀活动商品',
|
||||
field: 'spuId',
|
||||
@ -197,37 +141,6 @@ const crudSchemas = reactive<CrudSchema[]>([
|
||||
width: 300
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
field: 'createTime',
|
||||
formatter: dateFormatter,
|
||||
search: {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
type: 'daterange',
|
||||
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
|
||||
}
|
||||
},
|
||||
isForm: false,
|
||||
table: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
field: 'status',
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
dictClass: 'number',
|
||||
isForm: false,
|
||||
isSearch: true,
|
||||
form: {
|
||||
component: 'Radio'
|
||||
},
|
||||
table: {
|
||||
width: 80
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'remark',
|
||||
@ -245,15 +158,6 @@ const crudSchemas = reactive<CrudSchema[]>([
|
||||
table: {
|
||||
width: 300
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '操作',
|
||||
field: 'action',
|
||||
isForm: false,
|
||||
table: {
|
||||
width: 120,
|
||||
fixed: 'right'
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
|
@ -36,7 +36,7 @@
|
||||
v-for="(item, index) in row.sliderPicUrls"
|
||||
:key="index"
|
||||
:src="item"
|
||||
class="w-60px h-60px mr-10px"
|
||||
class="mr-10px h-60px w-60px"
|
||||
@click="imagePreview(row.sliderPicUrls)"
|
||||
/>
|
||||
</template>
|
||||
|
@ -6,7 +6,6 @@
|
||||
<el-descriptions-item label="配送方式: ">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_DELIVERY_TYPE" :value="formData.order.deliveryType" />
|
||||
</el-descriptions-item>
|
||||
<!-- TODO 营销活动待实现 -->
|
||||
<el-descriptions-item label="订单类型: ">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="formData.order.type" />
|
||||
</el-descriptions-item>
|
||||
@ -29,8 +28,7 @@
|
||||
<el-descriptions-item label="付款方式: ">
|
||||
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="formData.order.payChannelCode" />
|
||||
</el-descriptions-item>
|
||||
<!-- TODO 芋艿:待实现:跳转会员 -->
|
||||
<!-- <el-descriptions-item label="买家: ">{{ formData.user.nickname }}</el-descriptions-item> -->
|
||||
<el-descriptions-item label="买家: ">{{ formData?.user?.nickname }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 售后信息 -->
|
||||
@ -46,7 +44,7 @@
|
||||
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="formData.way" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="退款金额: ">
|
||||
{{ floatToFixed2(formData.refundPrice) }}
|
||||
{{ fenToYuan(formData.refundPrice) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="退款原因: ">{{ formData.applyReason }}</el-descriptions-item>
|
||||
<el-descriptions-item label="补充描述: ">
|
||||
@ -57,7 +55,7 @@
|
||||
v-for="(item, index) in formData.applyPicUrls"
|
||||
:key="index"
|
||||
:src="item.url"
|
||||
class="w-60px h-60px mr-10px"
|
||||
class="mr-10px h-60px w-60px"
|
||||
@click="imagePreview(formData.applyPicUrls)"
|
||||
/>
|
||||
</el-descriptions-item>
|
||||
@ -92,7 +90,7 @@
|
||||
<el-descriptions-item labelClassName="no-colon">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="15">
|
||||
<el-table :data="formData.items" border>
|
||||
<el-table :data="[formData.orderItem]" border>
|
||||
<el-table-column label="商品" prop="spuName" width="auto">
|
||||
<template #default="{ row }">
|
||||
{{ row.spuName }}
|
||||
@ -102,19 +100,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品原价" prop="price" width="150">
|
||||
<template #default="{ row }">{{ floatToFixed2(row.price) }}元</template>
|
||||
<template #default="{ row }">{{ fenToYuan(row.price) }} 元</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" prop="count" width="100" />
|
||||
<el-table-column label="合计" prop="payPrice" width="150">
|
||||
<template #default="{ row }">{{ floatToFixed2(row.payPrice) }}元</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="售后状态" prop="afterSaleStatus" width="120">
|
||||
<template #default="{ row }">
|
||||
<dict-tag
|
||||
:type="DICT_TYPE.TRADE_ORDER_ITEM_AFTER_SALE_STATUS"
|
||||
:value="row.afterSaleStatus"
|
||||
/>
|
||||
</template>
|
||||
<template #default="{ row }">{{ fenToYuan(row.payPrice) }} 元</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
@ -122,6 +112,8 @@
|
||||
</el-row>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 操作日志 -->
|
||||
<el-descriptions title="售后日志">
|
||||
<el-descriptions-item labelClassName="no-colon">
|
||||
<el-timeline>
|
||||
@ -153,7 +145,7 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as AfterSaleApi from '@/api/mall/trade/afterSale/index'
|
||||
import { floatToFixed2 } from '@/utils'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import UpdateAuditReasonForm from '@/views/mall/trade/afterSale/form/AfterSaleDisagreeForm.vue'
|
||||
@ -191,7 +183,7 @@ const getUserTypeColor = (type: number) => {
|
||||
|
||||
/** 获得详情 */
|
||||
const getDetail = async () => {
|
||||
const id = params.orderId as unknown as number
|
||||
const id = params.id as unknown as number
|
||||
if (id) {
|
||||
const res = await AfterSaleApi.getAfterSale(id)
|
||||
// 没有表单信息则关闭页面返回
|
||||
@ -204,44 +196,56 @@ const getDetail = async () => {
|
||||
}
|
||||
|
||||
/** 同意售后 */
|
||||
const agree = () => {
|
||||
message.confirm('是否同意售后?').then(() => {
|
||||
AfterSaleApi.agree(formData.value.id)
|
||||
const agree = async () => {
|
||||
try {
|
||||
// 二次确认
|
||||
await message.confirm('是否同意售后?')
|
||||
await AfterSaleApi.agree(formData.value.id)
|
||||
// 提示成功
|
||||
message.success(t('common.success'))
|
||||
getDetail()
|
||||
})
|
||||
await getDetail()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 拒绝售后 */
|
||||
const disagree = () => {
|
||||
const disagree = async () => {
|
||||
updateAuditReasonFormRef.value?.open(formData.value)
|
||||
}
|
||||
|
||||
/** 确认收货 */
|
||||
const receive = () => {
|
||||
message.confirm('是否确认收货?').then(() => {
|
||||
AfterSaleApi.receive(formData.value.id)
|
||||
const receive = async () => {
|
||||
try {
|
||||
// 二次确认
|
||||
await message.confirm('是否确认收货?')
|
||||
await AfterSaleApi.receive(formData.value.id)
|
||||
// 提示成功
|
||||
message.success(t('common.success'))
|
||||
getDetail()
|
||||
})
|
||||
await getDetail()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 拒绝收货 */
|
||||
const refuse = () => {
|
||||
message.confirm('是否拒绝收货?').then(() => {
|
||||
AfterSaleApi.refuse(formData.value.id)
|
||||
const refuse = async () => {
|
||||
try {
|
||||
// 二次确认
|
||||
await message.confirm('是否拒绝收货?')
|
||||
await AfterSaleApi.refuse(formData.value.id)
|
||||
// 提示成功
|
||||
message.success(t('common.success'))
|
||||
getDetail()
|
||||
})
|
||||
await getDetail()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 确认退款 */
|
||||
const refund = () => {
|
||||
message.confirm('是否确认退款?').then(() => {
|
||||
AfterSaleApi.refund(formData.value.id)
|
||||
const refund = async () => {
|
||||
try {
|
||||
// 二次确认
|
||||
await message.confirm('是否确认退款?')
|
||||
await AfterSaleApi.refund(formData.value.id)
|
||||
// 提示成功
|
||||
message.success(t('common.success'))
|
||||
getDetail()
|
||||
})
|
||||
await getDetail()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 图片预览 */
|
||||
|
@ -123,7 +123,7 @@
|
||||
<div class="flex items-center">
|
||||
<el-image
|
||||
:src="row.picUrl"
|
||||
class="w-30px h-30px mr-10px"
|
||||
class="mr-10px h-30px w-30px"
|
||||
@click="imagePreview(row.picUrl)"
|
||||
/>
|
||||
<span class="mr-10px">{{ row.spuName }}</span>
|
||||
@ -135,17 +135,16 @@
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="订单金额" prop="refundPrice">
|
||||
<template #default="scope">
|
||||
<span>{{ floatToFixed2(scope.row.refundPrice) }}元</span>
|
||||
<span>{{ fenToYuan(scope.row.refundPrice) }} 元</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO 芋艿:未来要加个会员链接 -->
|
||||
<el-table-column align="center" label="买家" prop="user.nickname" />
|
||||
<el-table-column align="center" label="申请时间" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ formatDate(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="售后状态">
|
||||
<el-table-column align="center" label="售后状态" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
@ -177,7 +176,7 @@ import { formatDate } from '@/utils/formatTime'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import { TabsPaneContext } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { floatToFixed2 } from '@/utils'
|
||||
import { fenToYuan } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'TradeAfterSale' })
|
||||
|
||||
@ -240,7 +239,7 @@ const tabClick = async (tab: TabsPaneContext) => {
|
||||
|
||||
/** 处理退款 */
|
||||
const openAfterSaleDetail = (id: number) => {
|
||||
push({ name: 'TradeAfterSaleDetail', params: { orderId: id } })
|
||||
push({ name: 'TradeAfterSaleDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 查看订单详情 */
|
||||
|
@ -96,14 +96,14 @@
|
||||
align="center"
|
||||
prop="unfreezeTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170px"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170px"
|
||||
width="180px"
|
||||
/>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
|
@ -77,7 +77,7 @@
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170px"
|
||||
width="180px"
|
||||
/>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
|
@ -67,7 +67,7 @@
|
||||
align="center"
|
||||
prop="bindUserTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170px"
|
||||
width="180px"
|
||||
/>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
|
@ -19,6 +19,7 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 展示上级推广人的信息 -->
|
||||
<el-descriptions v-if="bindUser" :column="1" border>
|
||||
<el-descriptions-item label="头像">
|
||||
<el-avatar :src="bindUser.avatar" />
|
||||
@ -79,7 +80,7 @@ const submitForm = async () => {
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 未查找到合适的上级
|
||||
if (!bindUser.value) {
|
||||
message.error('请先查询并确认推广人')
|
||||
return
|
||||
@ -116,7 +117,6 @@ const handleGetUser = async () => {
|
||||
message.error('不能绑定自己为推广人')
|
||||
return
|
||||
}
|
||||
|
||||
formLoading.value = true
|
||||
bindUser.value = await BrokerageUserApi.getBrokerageUser(formData.value.bindUserId)
|
||||
if (!bindUser.value) {
|
||||
|
@ -109,7 +109,7 @@
|
||||
align="center"
|
||||
prop="brokerageTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170px"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="上级推广员编号" align="center" prop="bindUserId" width="150px" />
|
||||
<el-table-column
|
||||
@ -117,7 +117,7 @@
|
||||
align="center"
|
||||
prop="bindUserTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170px"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="150px" fixed="right">
|
||||
<template #default="scope">
|
||||
@ -204,7 +204,7 @@ const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
bindUserId: null,
|
||||
brokerageEnabled: null,
|
||||
brokerageEnabled: true,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
@ -281,7 +281,7 @@ const handleClearBindUser = async (row: BrokerageUserApi.BrokerageUserVO) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 推广资格 开通/关闭 */
|
||||
/** 推广资格:开通/关闭 */
|
||||
const handleBrokerageEnabledChange = async (row: BrokerageUserApi.BrokerageUserVO) => {
|
||||
try {
|
||||
// 二次确认
|
||||
|
@ -102,11 +102,11 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="提现方式" align="left" prop="type" min-width="120px">
|
||||
<template #default="scope">
|
||||
<div
|
||||
>{{ getDictLabel(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE, scope.row.type) }}账号:{{
|
||||
scope.row.accountNo
|
||||
}}</div
|
||||
>
|
||||
<div v-if="scope.row.type === BrokerageWithdrawTypeEnum.WALLET.type"> 余额 </div>
|
||||
<div v-else>
|
||||
{{ getDictLabel(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE, scope.row.type) }}
|
||||
<span v-if="scope.row.accountNo">账号:{{ scope.row.accountNo }}</span>
|
||||
</div>
|
||||
<template v-if="scope.row.type === BrokerageWithdrawTypeEnum.BANK.type">
|
||||
<div>真实姓名:{{ scope.row.name }}</div>
|
||||
<div>
|
||||
@ -117,14 +117,16 @@
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收款码" align="left" prop="accountQrCodeUrl" width="70px">
|
||||
<el-table-column label="收款码" align="left" prop="accountQrCodeUrl" min-width="70px">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
v-if="scope.row.accountQrCodeUrl"
|
||||
:src="scope.row.accountQrCodeUrl"
|
||||
class="w-40px h-40px"
|
||||
:preview-src-list="[scope.row.accountQrCodeUrl]"
|
||||
preview-teleported
|
||||
/>
|
||||
<span v-else>无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@ -132,7 +134,7 @@
|
||||
align="left"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170px"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="备注" align="left" prop="remark" />
|
||||
<el-table-column label="状态" align="left" prop="status" min-width="120px">
|
||||
|
@ -10,8 +10,65 @@
|
||||
<el-form-item label="hideId" v-show="false">
|
||||
<el-input v-model="formData.id" />
|
||||
</el-form-item>
|
||||
|
||||
<el-tabs>
|
||||
<!-- 售后 -->
|
||||
<el-tab-pane label="售后">
|
||||
<el-form-item label="退款理由" prop="afterSaleRefundReasons">
|
||||
<el-select
|
||||
v-model="formData.afterSaleRefundReasons"
|
||||
allow-create
|
||||
filterable
|
||||
multiple
|
||||
placeholder="请直接输入退款理由"
|
||||
>
|
||||
<el-option
|
||||
v-for="reason in formData.afterSaleRefundReasons"
|
||||
:key="reason"
|
||||
:label="reason"
|
||||
:value="reason"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="退货理由" prop="afterSaleReturnReasons">
|
||||
<el-select
|
||||
v-model="formData.afterSaleReturnReasons"
|
||||
allow-create
|
||||
filterable
|
||||
multiple
|
||||
placeholder="请直接输入退货理由"
|
||||
>
|
||||
<el-option
|
||||
v-for="reason in formData.afterSaleReturnReasons"
|
||||
:key="reason"
|
||||
:label="reason"
|
||||
:value="reason"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<!-- 配送 -->
|
||||
<el-tab-pane label="配送">
|
||||
<el-form-item label="启用包邮" prop="deliveryExpressFreeEnabled">
|
||||
<el-switch v-model="formData.deliveryExpressFreeEnabled" style="user-select: none" />
|
||||
<el-text class="w-full" size="small" type="info"> 商城是否启用全场包邮 </el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="满额包邮" prop="deliveryExpressFreePrice">
|
||||
<el-input-number
|
||||
v-model="formData.deliveryExpressFreePrice"
|
||||
placeholder="请输入满额包邮"
|
||||
class="!w-xs"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
/>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
商城商品满多少金额即可包邮,单位:元
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="启用门店自提" prop="deliveryPickUpEnabled">
|
||||
<el-switch v-model="formData.deliveryPickUpEnabled" style="user-select: none" />
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<!-- 分销 -->
|
||||
<el-tab-pane label="分销">
|
||||
<el-form-item label="分佣启用" prop="brokerageEnabled">
|
||||
<el-switch v-model="formData.brokerageEnabled" style="user-select: none" />
|
||||
@ -45,22 +102,25 @@
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
没有推广人:只要用户没有推广人,随时都可以绑定推广关系
|
||||
首次绑定:只要用户没有推广人,随时都可以绑定推广关系
|
||||
</el-text>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
新用户:只有新用户注册时或首次进入系统时才可以绑定推广关系
|
||||
注册绑定:只有新用户注册时或首次进入系统时才可以绑定推广关系
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="分销海报图">
|
||||
<UploadImgs v-model="formData.brokeragePostUrls" width="75px" height="125px" />
|
||||
<UploadImgs v-model="formData.brokeragePosterUrls" width="75px" height="125px" />
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
个人中心分销海报图片,建议尺寸600x1000
|
||||
个人中心分销海报图片,建议尺寸 600x1000
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="一级返佣比例" prop="brokerageFirstPercent">
|
||||
<el-input-number
|
||||
v-model="formData.brokerageFirstPercent"
|
||||
placeholder="请输入一级返佣比例"
|
||||
class="!w-xs"
|
||||
:min="0"
|
||||
:max="100"
|
||||
/>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
订单交易成功后给推广人返佣的百分比
|
||||
@ -70,6 +130,9 @@
|
||||
<el-input-number
|
||||
v-model="formData.brokerageSecondPercent"
|
||||
placeholder="请输入二级返佣比例"
|
||||
class="!w-xs"
|
||||
:min="0"
|
||||
:max="100"
|
||||
/>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
订单交易成功后给推广人的推荐人返佣的百分比
|
||||
@ -79,6 +142,8 @@
|
||||
<el-input-number
|
||||
v-model="formData.brokerageFrozenDays"
|
||||
placeholder="请输入佣金冻结天数"
|
||||
class="!w-xs"
|
||||
:min="0"
|
||||
/>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
防止用户退款,佣金被提现了,所以需要设置佣金冻结时间,单位:天
|
||||
@ -87,14 +152,30 @@
|
||||
<el-form-item label="提现最低金额" prop="brokerageWithdrawMinPrice">
|
||||
<el-input-number
|
||||
v-model="formData.brokerageWithdrawMinPrice"
|
||||
placeholder="请输入用户提现最低金额"
|
||||
placeholder="请输入提现最低金额"
|
||||
class="!w-xs"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
/>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
用户提现最低金额限制,单位:元
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="提现方式" prop="brokerageWithdrawType">
|
||||
<el-checkbox-group v-model="formData.brokerageWithdrawType">
|
||||
<el-form-item label="提现手续费" prop="brokerageWithdrawFeePercent">
|
||||
<el-input-number
|
||||
v-model="formData.brokerageWithdrawFeePercent"
|
||||
placeholder="请输入提现手续费"
|
||||
class="!w-xs"
|
||||
:min="0"
|
||||
:max="100"
|
||||
/>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
提现手续费百分比,范围 0-100,0 为无提现手续费。例:设置 10,即收取 10% 手续费,提现
|
||||
10 元,到账 9 元,1 元手续费
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="提现方式" prop="brokerageWithdrawTypes">
|
||||
<el-checkbox-group v-model="formData.brokerageWithdrawTypes">
|
||||
<el-checkbox
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE)"
|
||||
:key="dict.value"
|
||||
@ -118,7 +199,7 @@
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 保存 -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm" :loading="formLoading"> 保存 </el-button>
|
||||
</el-form-item>
|
||||
@ -128,7 +209,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import * as ConfigApi from '@/api/mall/trade/config'
|
||||
import { BrokerageBindModeEnum, BrokerageEnabledConditionEnum } from '@/utils/constants'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
defineOptions({ name: 'TradeConfig' })
|
||||
@ -138,18 +218,26 @@ const message = useMessage() // 消息弹窗
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formRef = ref()
|
||||
const formData = ref({
|
||||
brokerageEnabled: true,
|
||||
brokerageEnabledCondition: BrokerageEnabledConditionEnum.ALL.condition,
|
||||
brokerageBindMode: BrokerageBindModeEnum.ANYTIME.mode,
|
||||
brokeragePostUrls: [],
|
||||
id: null,
|
||||
afterSaleRefundReasons: [],
|
||||
afterSaleReturnReasons: [],
|
||||
deliveryExpressFreeEnabled: false,
|
||||
deliveryExpressFreePrice: 0,
|
||||
deliveryPickUpEnabled: false,
|
||||
brokerageEnabled: false,
|
||||
brokerageEnabledCondition: undefined,
|
||||
brokerageBindMode: undefined,
|
||||
brokeragePosterUrls: [],
|
||||
brokerageFirstPercent: 0,
|
||||
brokerageSecondPercent: 0,
|
||||
brokerageWithdrawMinPrice: 0,
|
||||
brokerageWithdrawFeePercent: 0,
|
||||
brokerageBankNames: [],
|
||||
brokerageFrozenDays: 0,
|
||||
brokerageWithdrawType: []
|
||||
brokerageWithdrawTypes: []
|
||||
})
|
||||
const formRules = reactive({
|
||||
deliveryExpressFreePrice: [{ required: true, message: '满额包邮不能为空', trigger: 'blur' }],
|
||||
brokerageEnabledCondition: [{ required: true, message: '分佣模式不能为空', trigger: 'blur' }],
|
||||
brokerageBindMode: [{ required: true, message: '分销关系绑定模式不能为空', trigger: 'blur' }],
|
||||
brokerageFirstPercent: [{ required: true, message: '一级返佣比例不能为空', trigger: 'blur' }],
|
||||
@ -157,9 +245,10 @@ const formRules = reactive({
|
||||
brokerageWithdrawMinPrice: [
|
||||
{ required: true, message: '用户提现最低金额不能为空', trigger: 'blur' }
|
||||
],
|
||||
brokerageWithdrawFeePercent: [{ required: true, message: '提现手续费不能为空', trigger: 'blur' }],
|
||||
brokerageBankNames: [{ required: true, message: '提现银行不能为空', trigger: 'blur' }],
|
||||
brokerageFrozenDays: [{ required: true, message: '佣金冻结时间不能为空', trigger: 'blur' }],
|
||||
brokerageWithdrawType: [
|
||||
brokerageWithdrawTypes: [
|
||||
{
|
||||
required: true,
|
||||
message: '提现方式不能为空',
|
||||
@ -177,10 +266,15 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as ConfigApi.ConfigVO
|
||||
data.brokeragePostUrls = formData.value.brokeragePostUrls.map((item: any) => {
|
||||
const data = {
|
||||
...formData.value
|
||||
} as unknown as ConfigApi.ConfigVO
|
||||
data.brokeragePosterUrls = formData.value.brokeragePosterUrls.map((item: any) => {
|
||||
return item?.url ? item.url : item
|
||||
})
|
||||
// 金额放大
|
||||
data.deliveryExpressFreePrice = data.deliveryExpressFreePrice * 100
|
||||
data.brokerageWithdrawMinPrice = data.brokerageWithdrawMinPrice * 100
|
||||
await ConfigApi.saveTradeConfig(data)
|
||||
message.success('保存成功')
|
||||
} finally {
|
||||
@ -194,8 +288,11 @@ const getConfig = async () => {
|
||||
try {
|
||||
const data = await ConfigApi.getTradeConfig()
|
||||
if (data != null) {
|
||||
data.brokeragePostUrls = data.brokeragePostUrls.map((url) => ({ url }))
|
||||
data.brokeragePosterUrls = data.brokeragePosterUrls.map((url) => ({ url }))
|
||||
formData.value = data
|
||||
// 金额缩小
|
||||
formData.value.deliveryExpressFreePrice = data.deliveryExpressFreePrice / 100
|
||||
formData.value.brokerageWithdrawMinPrice = data.brokerageWithdrawMinPrice / 100
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
|
@ -7,17 +7,17 @@
|
||||
label-width="120px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="快递公司编码" prop="code">
|
||||
<el-form-item label="公司编码" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入快递编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="快递公司名称" prop="name">
|
||||
<el-form-item label="公司名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入快递名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="快递公司 logo" prop="logo">
|
||||
<el-form-item label="公司 logo" prop="logo">
|
||||
<UploadImg v-model="formData.logo" :limit="1" :is-show-tip="false" />
|
||||
<div style="font-size: 10px" class="pl-10px">推荐 180x180 图片分辨率</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类排序" prop="sort">
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
|
@ -53,11 +53,11 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="快递公司编号" prop="code" />
|
||||
<el-table-column label="快递公司名称" prop="name" />
|
||||
<el-table-column label="快递公司 logo " prop="logo">
|
||||
<el-table-column label="公司编码" prop="code" />
|
||||
<el-table-column label="公司名称" prop="name" />
|
||||
<el-table-column label="公司 logo " prop="logo">
|
||||
<template #default="scope">
|
||||
<img v-if="scope.row.logo" :src="scope.row.logo" alt="快递公司logo" class="h-100px" />
|
||||
<img v-if="scope.row.logo" :src="scope.row.logo" alt="公司logo" class="h-40px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="sort" />
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="80%">
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1300px">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
@ -21,23 +21,19 @@
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费" prop="templateCharge">
|
||||
<el-table border style="width: 100%" :data="formData.templateCharge">
|
||||
<el-table-column align="center" label="区域" width="180">
|
||||
<el-form-item label="运费" prop="charges">
|
||||
<el-table border style="width: 100%" :data="formData.charges">
|
||||
<el-table-column align="center" label="区域" width="360">
|
||||
<template #default="{ row }">
|
||||
<!-- 区域数据太多,用赖加载方式,要不然性能有问题 -->
|
||||
<el-tree-select
|
||||
<el-cascader
|
||||
v-model="row.areaIds"
|
||||
lazy
|
||||
:load="loadChargeArea"
|
||||
:props="defaultProps"
|
||||
multiple
|
||||
node-key="id"
|
||||
check-strictly
|
||||
show-checkbox
|
||||
check-on-click-node
|
||||
:render-after-expand="false"
|
||||
:cache-data="areaCache"
|
||||
:options="areaTree"
|
||||
:props="defaultProps2"
|
||||
class="w-1/1"
|
||||
clearable
|
||||
placeholder="请选择商品分类"
|
||||
filterable
|
||||
collapse-tags
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -85,23 +81,19 @@
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 添加区域
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="包邮区域" prop="templateFree">
|
||||
<el-table border style="width: 100%" :data="formData.templateFree">
|
||||
<el-table-column align="center" label="区域">
|
||||
<el-form-item label="包邮区域" prop="frees">
|
||||
<el-table border style="width: 100%" :data="formData.frees">
|
||||
<el-table-column align="center" label="区域" width="360">
|
||||
<template #default="{ row }">
|
||||
<!-- 区域数据太多,用赖加载方式,要不然性能有问题 -->
|
||||
<el-tree-select
|
||||
<el-cascader
|
||||
v-model="row.areaIds"
|
||||
multiple
|
||||
lazy
|
||||
:load="loadFreeArea"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
show-checkbox
|
||||
check-on-click-node
|
||||
:render-after-expand="true"
|
||||
:cache-data="areaCache"
|
||||
:options="areaTree"
|
||||
:props="defaultProps2"
|
||||
class="w-1/1"
|
||||
clearable
|
||||
placeholder="请选择商品分类"
|
||||
filterable
|
||||
collapse-tags
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -140,13 +132,18 @@
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import * as DeliveryExpressTemplateApi from '@/api/mall/trade/delivery/expressTemplate'
|
||||
import * as AreaApi from '@/api/system/area'
|
||||
import { defaultProps } from '@/utils/tree'
|
||||
import { yuanToFen, fenToYuan } from '@/utils'
|
||||
import { getChildrenArea, getAreaListByIds } from '@/api/system/area'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const defaultProps2 = {
|
||||
...defaultProps,
|
||||
multiple: true
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
@ -156,8 +153,8 @@ const formData = ref({
|
||||
name: '',
|
||||
chargeMode: 1,
|
||||
sort: 0,
|
||||
templateCharge: [],
|
||||
templateFree: []
|
||||
charges: [],
|
||||
frees: []
|
||||
})
|
||||
const columnTitleMap = new Map()
|
||||
const columnTitle = ref({
|
||||
@ -171,9 +168,6 @@ const formRules = reactive({
|
||||
sort: [{ required: true, message: '分类排序不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const areaCache = ref([]) // 由于区域节点懒加载,已选区域节点需要缓存展示
|
||||
// TODO @jason:配送的时候,只允许选择省市级别,不允许选择区;如果这样的话,是不是打开弹窗,直接把城市都请求过来;
|
||||
// TODO @jaosn:因为只有省市两级,感觉就不用特殊做全国逻辑;选择全国,就默认把子节点都选择上;另外,选择父节点,要把子节点选中哈;
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
@ -187,30 +181,14 @@ const open = async (type: string, id?: number) => {
|
||||
formLoading.value = true
|
||||
formData.value = await DeliveryExpressTemplateApi.getDeliveryExpressTemplate(id)
|
||||
columnTitle.value = columnTitleMap.get(formData.value.chargeMode)
|
||||
const chargeAreaIds = []
|
||||
const freeAreaIds = []
|
||||
formData.value.templateCharge.forEach((item) => {
|
||||
for (let i = 0; i < item.areaIds.length; i++) {
|
||||
if (!chargeAreaIds.includes(item.areaIds[i])) {
|
||||
chargeAreaIds.push(item.areaIds[i])
|
||||
}
|
||||
}
|
||||
//前端价格以元展示
|
||||
formData.value.charges.forEach((item) => {
|
||||
// 前端价格以元展示
|
||||
item.startPrice = fenToYuan(item.startPrice)
|
||||
item.extraPrice = fenToYuan(item.extraPrice)
|
||||
})
|
||||
formData.value.templateFree.forEach((item) => {
|
||||
for (let i = 0; i < item.areaIds.length; i++) {
|
||||
if (!chargeAreaIds.includes(item.areaIds[i]) && !freeAreaIds.includes(item.areaIds[i])) {
|
||||
freeAreaIds.push(item.areaIds[i])
|
||||
}
|
||||
}
|
||||
formData.value.frees.forEach((item) => {
|
||||
item.freePrice = fenToYuan(item.freePrice)
|
||||
})
|
||||
// 已选的区域节点
|
||||
const areaIds = chargeAreaIds.concat(freeAreaIds)
|
||||
// 区域节点,懒加载方式。已选节点需要缓存展示
|
||||
areaCache.value = await getAreaListByIds(areaIds.join(','))
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
@ -228,14 +206,13 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as DeliveryExpressTemplateApi.DeliveryExpressTemplateVO
|
||||
const data = cloneDeep(formData.value) as DeliveryExpressTemplateApi.DeliveryExpressTemplateVO
|
||||
// 前端价格以元展示,提交到后端。用分计算
|
||||
// TODO @jason:不能直接这样改,要复制出来改。不然后端操作失败,数据已经被改了
|
||||
data.templateCharge.forEach((item) => {
|
||||
data.charges.forEach((item) => {
|
||||
item.startPrice = yuanToFen(item.startPrice)
|
||||
item.extraPrice = yuanToFen(item.extraPrice)
|
||||
})
|
||||
data.templateFree.forEach((item) => {
|
||||
data.frees.forEach((item) => {
|
||||
item.freePrice = yuanToFen(item.freePrice)
|
||||
})
|
||||
if (formType.value === 'create') {
|
||||
@ -259,7 +236,7 @@ const resetForm = () => {
|
||||
id: undefined,
|
||||
name: '',
|
||||
chargeMode: 1,
|
||||
templateCharge: [
|
||||
charges: [
|
||||
{
|
||||
areaIds: [1],
|
||||
startCount: 2,
|
||||
@ -268,7 +245,7 @@ const resetForm = () => {
|
||||
extraPrice: 10
|
||||
}
|
||||
],
|
||||
templateFree: [],
|
||||
frees: [],
|
||||
sort: 0
|
||||
}
|
||||
columnTitle.value = columnTitleMap.get(1)
|
||||
@ -279,37 +256,10 @@ const resetForm = () => {
|
||||
const changeChargeMode = (chargeMode: number) => {
|
||||
columnTitle.value = columnTitleMap.get(chargeMode)
|
||||
}
|
||||
const defaultArea = [{ id: 1, name: '全国', disabled: false }]
|
||||
|
||||
/** 初始化数据 */
|
||||
// TODO @jason:是不是不用写这样一个初始化方法,columnTitleMap 直接就可以了呀
|
||||
// const columnTitleMap = {
|
||||
// '1': {
|
||||
// startCountTitle: '首件',
|
||||
// extraCountTitle: '续件',
|
||||
// freeCountTitle: '包邮件数'
|
||||
// },
|
||||
// '2': {
|
||||
// startCountTitle: '首件重量(kg)',
|
||||
// extraCountTitle: '续件重量(kg)',
|
||||
// freeCountTitle: '包邮重量(kg)'
|
||||
// },
|
||||
// '3': {
|
||||
// startCountTitle: '首件体积(m³)',
|
||||
// extraCountTitle: '续件体积(m³)',
|
||||
// freeCountTitle: '包邮体积(m³)'
|
||||
// }
|
||||
// }
|
||||
const areaTree = ref([])
|
||||
const initData = async () => {
|
||||
// TODO 从服务端全量加载数据, 后面看懒加载是不是可以从前端获取数据。 目前从后端获取数据
|
||||
// formLoading.value = true
|
||||
// try {
|
||||
// const data = await getAreaTree()
|
||||
// areaTree = data
|
||||
// console.log('areaTree', areaTree)
|
||||
// } finally {
|
||||
// formLoading.value = false
|
||||
// }
|
||||
// 表头标题和计费方式的映射
|
||||
columnTitleMap.set(1, {
|
||||
startCountTitle: '首件',
|
||||
@ -326,77 +276,14 @@ const initData = async () => {
|
||||
extraCountTitle: '续件体积(m³)',
|
||||
freeCountTitle: '包邮体积(m³)'
|
||||
})
|
||||
// 加载区域数据
|
||||
areaTree.value = await AreaApi.getAreaTree()
|
||||
}
|
||||
|
||||
/** 懒加载运费区域树 */
|
||||
const loadChargeArea = async (node, resolve) => {
|
||||
//已选区域需要禁止再次选择
|
||||
const areaIds = []
|
||||
formData.value.templateCharge.forEach((item) => {
|
||||
if (item.areaIds.length > 0) {
|
||||
item.areaIds.forEach((areaId) => areaIds.push(areaId))
|
||||
}
|
||||
})
|
||||
if (node.isLeaf) return resolve([])
|
||||
const length = node.data.length
|
||||
if (length === 0) {
|
||||
const data = cloneDeep(defaultArea)
|
||||
const item = data[0]
|
||||
if (areaIds.includes(item.id)) {
|
||||
// TODO 禁止选中的区域有些问题, 导致修改时候不能重新选择 不知道如何处理。 暂时注释掉 @芋艿 有空瞅瞅
|
||||
// TODO @jason:先不做这个功能哈。
|
||||
//item.disabled = true
|
||||
}
|
||||
resolve(data)
|
||||
} else {
|
||||
const id = node.data.id
|
||||
const data = await getChildrenArea(id)
|
||||
data.forEach((item) => {
|
||||
if (areaIds.includes(item.id)) {
|
||||
//item.disabled = true
|
||||
}
|
||||
})
|
||||
resolve(data)
|
||||
}
|
||||
}
|
||||
|
||||
/** 懒加载包邮区域树 */
|
||||
const loadFreeArea = async (node, resolve) => {
|
||||
if (node.isLeaf) return resolve([])
|
||||
//已选区域需要禁止再次选择
|
||||
const areaIds = []
|
||||
formData.value.templateFree.forEach((item) => {
|
||||
if (item.areaIds.length > 0) {
|
||||
item.areaIds.forEach((areaId) => areaIds.push(areaId))
|
||||
}
|
||||
})
|
||||
const length = node.data.length
|
||||
if (length === 0) {
|
||||
// 为空,从全国开始选择。全国 id == 1
|
||||
const data = cloneDeep(defaultArea)
|
||||
const item = data[0]
|
||||
if (areaIds.includes(item.id)) {
|
||||
//item.disabled = true
|
||||
}
|
||||
resolve(data)
|
||||
} else {
|
||||
const id = node.data.id
|
||||
const data = await getChildrenArea(id)
|
||||
// 已选区域需要禁止再次选择
|
||||
data.forEach((item) => {
|
||||
if (areaIds.includes(item.id)) {
|
||||
// TODO 禁止选中的区域有些问题, 导致修改时候不能重新选择 不知道如何处理。 暂时注释掉 @芋艿 有空瞅瞅
|
||||
// TODO @jason:先不做这个功能哈。
|
||||
//item.disabled = true
|
||||
}
|
||||
})
|
||||
resolve(data)
|
||||
}
|
||||
}
|
||||
/** 添加计费区域 */
|
||||
const addChargeArea = () => {
|
||||
const data = formData.value
|
||||
data.templateCharge.push({
|
||||
data.charges.push({
|
||||
areaIds: [],
|
||||
startCount: 1,
|
||||
startPrice: 1,
|
||||
@ -408,13 +295,13 @@ const addChargeArea = () => {
|
||||
/** 删除计费区域 */
|
||||
const deleteChargeArea = (index) => {
|
||||
const data = formData.value
|
||||
data.templateCharge.splice(index, 1)
|
||||
data.charges.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 添加包邮区域 */
|
||||
const addFreeArea = () => {
|
||||
const data = formData.value
|
||||
data.templateFree.push({
|
||||
data.frees.push({
|
||||
areaIds: [],
|
||||
freeCount: 1,
|
||||
freePrice: 1
|
||||
@ -424,7 +311,7 @@ const addFreeArea = () => {
|
||||
/** 删除包邮区域 */
|
||||
const deleteFreeArea = (index) => {
|
||||
const data = formData.value
|
||||
data.templateFree.splice(index, 1)
|
||||
data.frees.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
|
@ -51,14 +51,14 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" prop="id" />
|
||||
<el-table-column label="模板名称" prop="name" />
|
||||
<el-table-column label="计费方式" prop="chargeMode" align="center">
|
||||
<el-table-column label="编号" min-width="60" prop="id" />
|
||||
<el-table-column label="模板名称" min-width="100" prop="name" />
|
||||
<el-table-column label="计费方式" prop="chargeMode" min-width="100" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.EXPRESS_CHARGE_MODE" :value="scope.row.chargeMode" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sort" />
|
||||
<el-table-column label="排序" min-width="100" prop="sort" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
|
@ -51,7 +51,7 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="门店所在地区" prop="areaId">
|
||||
<el-cascader v-model="formData.areaId" :options="areaList" :props="areaTreeProps" />
|
||||
<el-cascader v-model="formData.areaId" :options="areaList" :props="defaultProps" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -99,7 +99,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="获取经纬度">
|
||||
<el-button type="primary" @click="mapDialogVisible.value = true">获取</el-button>
|
||||
<el-button type="primary" @click="mapDialogVisible = true">获取</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@ -121,8 +121,9 @@
|
||||
import * as DeliveryPickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import { defaultProps } from '@/utils/tree'
|
||||
import { getAreaTree } from '@/api/system/area'
|
||||
import * as ConfigApi from '@/api/infra/config'
|
||||
import * as ConfigApi from '@/api/mall/trade/config'
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
@ -161,12 +162,6 @@ const formRules = reactive({
|
||||
status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const areaTreeProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
emitPath: false
|
||||
}
|
||||
const areaList = ref() // 区域树
|
||||
const tencentLbsUrl = ref('') // 腾讯位置服务 url
|
||||
|
||||
@ -244,16 +239,8 @@ const selectAddress = function (loc: any): void {
|
||||
mapDialogVisible.value = false
|
||||
}
|
||||
|
||||
/** 初始化数据 */
|
||||
const initData = async () => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = await getAreaTree()
|
||||
areaList.value = data
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
// TODO @jason:要不创建一个 initTencentLbsMap
|
||||
/** 初始化腾讯地图 */
|
||||
const initTencentLbsMap = async () => {
|
||||
window.selectAddress = selectAddress
|
||||
window.addEventListener(
|
||||
'message',
|
||||
@ -267,17 +254,16 @@ const initData = async () => {
|
||||
},
|
||||
false
|
||||
)
|
||||
const data = await ConfigApi.getConfigKey('tencent.lbs.key')
|
||||
let key = ''
|
||||
if (data && data.length > 0) {
|
||||
key = data
|
||||
}
|
||||
const data = await ConfigApi.getTradeConfig()
|
||||
const key = data.tencentLbsKey
|
||||
tencentLbsUrl.value = `https://apis.map.qq.com/tools/locpicker?type=1&key=${key}&referer=myapp`
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
initData()
|
||||
onMounted(async () => {
|
||||
areaList.value = await getAreaTree()
|
||||
// 加载地图
|
||||
await initTencentLbsMap()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
@ -65,16 +65,21 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="编号" prop="id" />
|
||||
<el-table-column label="门店 logo" prop="logo">
|
||||
<el-table-column label="编号" min-width="80" prop="id" />
|
||||
<el-table-column label="门店 logo" min-width="100" prop="logo">
|
||||
<template #default="scope">
|
||||
<img v-if="scope.row.logo" :src="scope.row.logo" alt="门店 logo" class="h-100px" />
|
||||
<img v-if="scope.row.logo" :src="scope.row.logo" alt="门店 logo" class="h-50px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="门店名称" prop="name" />
|
||||
<el-table-column label="门店手机" prop="phone" />
|
||||
<el-table-column align="center" label="门店详细地址" prop="detailAddress" />
|
||||
<el-table-column align="center" label="开启状态" prop="status">
|
||||
<el-table-column label="门店名称" min-width="150" prop="name" />
|
||||
<el-table-column label="门店手机" min-width="100" prop="phone" />
|
||||
<el-table-column label="地址" min-width="100" prop="detailAddress" />
|
||||
<el-table-column label="营业时间" min-width="180">
|
||||
<template #default="scope">
|
||||
{{ scope.row.openingTime }} ~ {{ scope.row.closingTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="开启状态" min-width="100" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
|
@ -3,35 +3,21 @@
|
||||
<!-- 订单信息 -->
|
||||
<el-descriptions title="订单信息">
|
||||
<el-descriptions-item label="订单号: ">{{ formData.no }}</el-descriptions-item>
|
||||
<el-descriptions-item label="配送方式: ">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_DELIVERY_TYPE" :value="formData.deliveryType!" />
|
||||
</el-descriptions-item>
|
||||
<!-- TODO 营销活动待实现 -->
|
||||
<el-descriptions-item label="营销活动: ">秒杀活动</el-descriptions-item>
|
||||
<el-descriptions-item label="买家: ">{{ formData?.user?.nickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="订单类型: ">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="formData.type!" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收货人: ">{{ formData.receiverName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="买家留言: ">{{ formData.userRemark }}</el-descriptions-item>
|
||||
<el-descriptions-item label="订单来源: ">
|
||||
<dict-tag :type="DICT_TYPE.TERMINAL" :value="formData.terminal!" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话: ">{{ formData.receiverMobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="买家留言: ">{{ formData.userRemark }}</el-descriptions-item>
|
||||
<el-descriptions-item label="商家备注: ">{{ formData.remark }}</el-descriptions-item>
|
||||
<el-descriptions-item label="支付单号: ">{{ formData.payOrderId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="付款方式: ">
|
||||
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="formData.payChannelCode!" />
|
||||
</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="买家: ">{{ formData.user.nickname }}</el-descriptions-item> -->
|
||||
<!-- TODO @puhui999:待实现:跳转会员 -->
|
||||
<el-descriptions-item label="收货地址: ">
|
||||
{{ formData.receiverAreaName }} {{ formData.receiverDetailAddress }}
|
||||
<el-link
|
||||
v-clipboard:copy="formData.receiverAreaName + ' ' + formData.receiverDetailAddress"
|
||||
v-clipboard:success="clipboardSuccess"
|
||||
icon="ep:document-copy"
|
||||
type="primary"
|
||||
/>
|
||||
<el-descriptions-item label="推广用户: " v-if="formData.brokerageUser">
|
||||
{{ formData.brokerageUser?.nickname }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
@ -41,16 +27,40 @@
|
||||
<dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="formData.status!" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-class-name="no-colon">
|
||||
<el-button v-if="formData.status! === 0" type="primary" @click="updatePrice">
|
||||
<el-button
|
||||
v-if="formData.status! === TradeOrderStatusEnum.UNPAID.status"
|
||||
type="primary"
|
||||
@click="updatePrice"
|
||||
>
|
||||
调整价格
|
||||
</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>
|
||||
<!-- 待发货 -->
|
||||
<template v-if="formData.status! === TradeOrderStatusEnum.UNDELIVERED.status">
|
||||
<!-- 快递发货 -->
|
||||
<el-button
|
||||
v-if="formData.deliveryType === DeliveryTypeEnum.EXPRESS.type"
|
||||
type="primary"
|
||||
@click="delivery"
|
||||
>
|
||||
发货
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="formData.deliveryType === DeliveryTypeEnum.EXPRESS.type"
|
||||
type="primary"
|
||||
@click="updateAddress"
|
||||
>
|
||||
修改地址
|
||||
</el-button>
|
||||
<!-- 到店自提 -->
|
||||
<el-button
|
||||
v-if="formData.deliveryType === DeliveryTypeEnum.PICK_UP.type"
|
||||
type="primary"
|
||||
@click="handlePickUp"
|
||||
>
|
||||
核销
|
||||
</el-button>
|
||||
</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label><span style="color: red">提醒: </span></template>
|
||||
@ -75,11 +85,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品原价" prop="price" width="150">
|
||||
<template #default="{ row }">{{ floatToFixed2(row.price) }}元</template>
|
||||
<template #default="{ row }">{{ fenToYuan(row.price) }}元</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" prop="count" width="100" />
|
||||
<el-table-column label="合计" prop="payPrice" width="150">
|
||||
<template #default="{ row }">{{ floatToFixed2(row.payPrice) }}元</template>
|
||||
<template #default="{ row }">{{ fenToYuan(row.payPrice) }}元</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="售后状态" prop="afterSaleStatus" width="120">
|
||||
<template #default="{ row }">
|
||||
@ -95,64 +105,91 @@
|
||||
</el-row>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions :column="6">
|
||||
<el-descriptions :column="4">
|
||||
<!-- 第一层 -->
|
||||
<el-descriptions-item label="商品总额: ">
|
||||
{{ floatToFixed2(formData.totalPrice!) }}元
|
||||
{{ fenToYuan(formData.totalPrice!) }} 元
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="运费金额: ">
|
||||
{{ floatToFixed2(formData.deliveryPrice!) }}元
|
||||
{{ fenToYuan(formData.deliveryPrice!) }} 元
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="订单调价: ">
|
||||
{{ floatToFixed2(formData.adjustPrice!) }}元
|
||||
{{ fenToYuan(formData.adjustPrice!) }} 元
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item v-for="item in 1" :key="item" label-class-name="no-colon" />
|
||||
<!-- 第二层 -->
|
||||
<el-descriptions-item>
|
||||
<template #label><span style="color: red">商品优惠: </span></template>
|
||||
{{ floatToFixed2(formData.couponPrice!) }}元
|
||||
<template #label><span style="color: red">优惠劵优惠: </span></template>
|
||||
{{ fenToYuan(formData.couponPrice!) }} 元
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label><span style="color: red">订单优惠: </span></template>
|
||||
{{ floatToFixed2(formData.discountPrice!) }}元
|
||||
<template #label><span style="color: red">VIP 优惠: </span></template>
|
||||
{{ fenToYuan(formData.vipPrice!) }} 元
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label><span style="color: red">活动优惠: </span></template>
|
||||
{{ fenToYuan(formData.discountPrice!) }} 元
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label><span style="color: red">积分抵扣: </span></template>
|
||||
{{ floatToFixed2(formData.pointPrice!) }}元
|
||||
{{ fenToYuan(formData.pointPrice!) }} 元
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item v-for="item in 5" :key="item" label-class-name="no-colon" />
|
||||
<!-- 占位 -->
|
||||
<!-- 第三层 -->
|
||||
<el-descriptions-item v-for="item in 3" :key="item" label-class-name="no-colon" />
|
||||
<el-descriptions-item label="应付金额: ">
|
||||
{{ floatToFixed2(formData.payPrice!) }}元
|
||||
{{ fenToYuan(formData.payPrice!) }} 元
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- TODO 芋艿:需要改改 -->
|
||||
<el-descriptions :column="4" title="物流信息">
|
||||
<el-descriptions-item label="物流公司: ">
|
||||
{{ deliveryExpressList.find((item) => item.id === formData.logisticsId)?.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="运单号: ">{{ formData.logisticsNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发货时间: ">
|
||||
{{ formatDate(formData.deliveryTime!) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="物流状态: ">
|
||||
<!-- TODO 物流状态怎么获取? -->
|
||||
<dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="formData.deliveryStatus!" />
|
||||
</el-descriptions-item>
|
||||
<!-- 占位 4 -->
|
||||
<el-descriptions-item v-for="item in 4" :key="item" label-class-name="no-colon" />
|
||||
<el-descriptions-item label="物流详情: ">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(express, index) in expressTrackList"
|
||||
:key="index"
|
||||
:timestamp="formatDate(express.time)"
|
||||
>
|
||||
{{ express.content }}
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<!-- 物流信息 -->
|
||||
<el-descriptions :column="4" title="收货信息">
|
||||
<el-descriptions-item label="配送方式: ">
|
||||
<dict-tag :type="DICT_TYPE.TRADE_DELIVERY_TYPE" :value="formData.deliveryType!" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收货人: ">{{ formData.receiverName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话: ">{{ formData.receiverMobile }}</el-descriptions-item>
|
||||
<!-- 快递配送 -->
|
||||
<div v-if="formData.deliveryType === DeliveryTypeEnum.EXPRESS.type">
|
||||
<el-descriptions-item label="收货地址: " v-if="formData.receiverDetailAddress">
|
||||
{{ formData.receiverAreaName }} {{ formData.receiverDetailAddress }}
|
||||
<el-link
|
||||
v-clipboard:copy="formData.receiverAreaName + ' ' + formData.receiverDetailAddress"
|
||||
v-clipboard:success="clipboardSuccess"
|
||||
icon="ep:document-copy"
|
||||
type="primary"
|
||||
/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="物流公司: " v-if="formData.logisticsId">
|
||||
{{ deliveryExpressList.find((item) => item.id === formData.logisticsId)?.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="运单号: " v-if="formData.logisticsId">
|
||||
{{ formData.logisticsNo }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="发货时间: " v-if="formatDate.deliveryTime">
|
||||
{{ formatDate(formData.deliveryTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-for="item in 2" :key="item" label-class-name="no-colon" />
|
||||
<el-descriptions-item label="物流详情: " v-if="expressTrackList.length > 0">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(express, index) in expressTrackList"
|
||||
:key="index"
|
||||
:timestamp="formatDate(express.time)"
|
||||
>
|
||||
{{ express.content }}
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-descriptions-item>
|
||||
</div>
|
||||
<!-- 自提门店 -->
|
||||
<div v-if="formData.deliveryType === DeliveryTypeEnum.PICK_UP.type">
|
||||
<el-descriptions-item label="自提门店: " v-if="formData.pickUpStoreId">
|
||||
{{ pickUpStore?.name }}
|
||||
</el-descriptions-item>
|
||||
</div>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 订单日志 -->
|
||||
<el-descriptions title="订单操作日志">
|
||||
<el-descriptions-item labelClassName="no-colon">
|
||||
<el-timeline>
|
||||
@ -187,7 +224,7 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as TradeOrderApi from '@/api/mall/trade/order'
|
||||
import { floatToFixed2 } from '@/utils'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
|
||||
import OrderUpdateRemarkForm from '@/views/mall/trade/order/form/OrderUpdateRemarkForm.vue'
|
||||
@ -196,6 +233,8 @@ import OrderUpdateAddressForm from '@/views/mall/trade/order/form/OrderUpdateAdd
|
||||
import OrderUpdatePriceForm from '@/views/mall/trade/order/form/OrderUpdatePriceForm.vue'
|
||||
import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { DeliveryTypeEnum, TradeOrderStatusEnum } from '@/utils/constants'
|
||||
import * as DeliveryPickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore'
|
||||
|
||||
defineOptions({ name: 'TradeOrderDetail' })
|
||||
|
||||
@ -240,14 +279,27 @@ const updatePrice = () => {
|
||||
updatePriceFormRef.value?.open(formData.value)
|
||||
}
|
||||
|
||||
/** 核销 */
|
||||
const handlePickUp = async () => {
|
||||
try {
|
||||
// 二次确认
|
||||
await message.confirm('确认核销订单吗?')
|
||||
// 提交
|
||||
await TradeOrderApi.pickUpOrder(formData.value.id!)
|
||||
message.success('核销成功')
|
||||
// 刷新列表
|
||||
await getDetail()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 获得详情 */
|
||||
const { params } = useRoute() // 查询参数
|
||||
const getDetail = async () => {
|
||||
const id = params.orderId as unknown as number
|
||||
const id = params.id as unknown as number
|
||||
if (id) {
|
||||
const res = (await TradeOrderApi.getOrder(id)) as TradeOrderApi.OrderVO
|
||||
// 没有表单信息则关闭页面返回
|
||||
if (res === null) {
|
||||
if (!res) {
|
||||
message.error('交易订单不存在')
|
||||
close()
|
||||
}
|
||||
@ -271,10 +323,20 @@ const clipboardSuccess = () => {
|
||||
/** 初始化 **/
|
||||
const deliveryExpressList = ref([]) // 物流公司
|
||||
const expressTrackList = ref([]) // 物流详情
|
||||
const pickUpStore = ref({}) // 自提门店
|
||||
onMounted(async () => {
|
||||
await getDetail()
|
||||
deliveryExpressList.value = await DeliveryExpressApi.getSimpleDeliveryExpressList()
|
||||
expressTrackList.value = await TradeOrderApi.getExpressTrackList(formData.value.id!)
|
||||
// 如果配送方式为快递,则查询物流公司
|
||||
if (formData.value.deliveryType === DeliveryTypeEnum.EXPRESS.type) {
|
||||
deliveryExpressList.value = await DeliveryExpressApi.getSimpleDeliveryExpressList()
|
||||
if (form.value.logisticsId) {
|
||||
expressTrackList.value = await TradeOrderApi.getExpressTrackList(formData.value.id!)
|
||||
}
|
||||
} else if (formData.value.deliveryType === DeliveryTypeEnum.PICK_UP.type) {
|
||||
pickUpStore.value = await DeliveryPickUpStoreApi.getDeliveryPickUpStore(
|
||||
formData.value.pickUpStoreId
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@ -312,7 +374,7 @@ onMounted(async () => {
|
||||
|
||||
// 时间线样式调整
|
||||
:deep(.el-timeline) {
|
||||
margin: 10px 0px 0px 160px;
|
||||
margin: 10px 0 0 160px;
|
||||
|
||||
.el-timeline-item__wrapper {
|
||||
position: relative;
|
||||
|
@ -54,7 +54,7 @@ const open = async (row: TradeOrderApi.OrderVO) => {
|
||||
resetForm()
|
||||
// 设置数据
|
||||
copyValueToTarget(formData.value, row)
|
||||
if (row.logisticsId === null || row.logisticsId === 0) {
|
||||
if (row.logisticsId === 0) {
|
||||
expressType.value = 'none'
|
||||
}
|
||||
dialogVisible.value = true
|
||||
@ -73,7 +73,7 @@ const submitForm = async () => {
|
||||
data.logisticsId = 0
|
||||
data.logisticsNo = ''
|
||||
}
|
||||
await TradeOrderApi.delivery(data)
|
||||
await TradeOrderApi.deliveryOrder(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
|
@ -69,7 +69,7 @@ const submitForm = async () => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = unref(formData)
|
||||
await TradeOrderApi.updateAddress(data)
|
||||
await TradeOrderApi.updateOrderAddress(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
|
@ -69,7 +69,7 @@ const submitForm = async () => {
|
||||
data.adjustPrice = convertToInteger(data.adjustPrice)
|
||||
delete data.payPrice
|
||||
delete data.newPayPrice
|
||||
await TradeOrderApi.updatePrice(data)
|
||||
await TradeOrderApi.updateOrderPrice(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
|
@ -49,7 +49,7 @@ const submitForm = async () => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = unref(formData)
|
||||
await TradeOrderApi.updateRemark(data)
|
||||
await TradeOrderApi.updateOrderRemark(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
|
@ -74,7 +74,11 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="queryParams.deliveryType === 1" label="快递公司">
|
||||
<el-form-item
|
||||
v-if="queryParams.deliveryType === DeliveryTypeEnum.EXPRESS.type"
|
||||
label="快递公司"
|
||||
prop="logisticsId"
|
||||
>
|
||||
<el-select v-model="queryParams.logisticsId" class="!w-280px" clearable placeholder="全部">
|
||||
<el-option
|
||||
v-for="item in deliveryExpressList"
|
||||
@ -84,7 +88,11 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="queryParams.deliveryType === 2" label="自提门店">
|
||||
<el-form-item
|
||||
v-if="queryParams.deliveryType === DeliveryTypeEnum.PICK_UP.type"
|
||||
label="自提门店"
|
||||
prop="pickUpStoreId"
|
||||
>
|
||||
<el-select
|
||||
v-model="queryParams.pickUpStoreId"
|
||||
class="!w-280px"
|
||||
@ -100,6 +108,19 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="queryParams.deliveryType === DeliveryTypeEnum.PICK_UP.type"
|
||||
label="核销码"
|
||||
prop="pickUpVerifyCode"
|
||||
>
|
||||
<el-input
|
||||
v-model="queryParams.pickUpVerifyCode"
|
||||
class="!w-280px"
|
||||
clearable
|
||||
placeholder="请输入自提核销码"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- TODO puhui 聚合搜索等售后结束后实现-->
|
||||
<!-- TODO puhui999:尽量不要用 .k 这样的参数,完整拼写,有完整的业务含义 -->
|
||||
<el-form-item label="聚合搜索">
|
||||
@ -199,7 +220,7 @@
|
||||
<div class="flex items-center">
|
||||
<el-image
|
||||
:src="row.picUrl"
|
||||
class="w-30px h-30px mr-10px"
|
||||
class="mr-10px h-30px w-30px"
|
||||
@click="imagePreview(row.picUrl)"
|
||||
/>
|
||||
<span class="mr-10px">{{ row.spuName }}</span>
|
||||
@ -234,7 +255,10 @@
|
||||
<el-table-column label="买家/收货人" min-width="160">
|
||||
<template #default>
|
||||
<!-- 快递发货 -->
|
||||
<div v-if="scope.row.deliveryType === 1" class="flex flex-col">
|
||||
<div
|
||||
v-if="scope.row.deliveryType === DeliveryTypeEnum.EXPRESS.type"
|
||||
class="flex flex-col"
|
||||
>
|
||||
<span>买家:{{ scope.row.user.nickname }}</span>
|
||||
<span>
|
||||
收货人:{{ scope.row.receiverName }} {{ scope.row.receiverMobile }}
|
||||
@ -242,7 +266,10 @@
|
||||
</span>
|
||||
</div>
|
||||
<!-- 自提 -->
|
||||
<div v-if="scope.row.deliveryType === 2" class="flex flex-col">
|
||||
<div
|
||||
v-if="scope.row.deliveryType === DeliveryTypeEnum.PICK_UP.type"
|
||||
class="flex flex-col"
|
||||
>
|
||||
<span>
|
||||
门店名称:
|
||||
{{ pickUpStoreList.find((p) => p.id === scope.row.pickUpStoreId)?.name }}
|
||||
@ -273,7 +300,7 @@
|
||||
<el-table-column align="center" fixed="right" label="操作" width="160">
|
||||
<template #default>
|
||||
<!-- TODO 权限后续补齐 -->
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="flex items-center justify-center">
|
||||
<el-button link type="primary" @click="openDetail(scope.row.id)">
|
||||
<Icon icon="ep:notification" />
|
||||
详情
|
||||
@ -287,7 +314,10 @@
|
||||
<el-dropdown-menu>
|
||||
<!-- 如果是【快递】,并且【未发货】,则展示【发货】按钮 -->
|
||||
<el-dropdown-item
|
||||
v-if="scope.row.deliveryType === 1 && scope.row.status === 10"
|
||||
v-if="
|
||||
scope.row.deliveryType === DeliveryTypeEnum.EXPRESS.type &&
|
||||
scope.row.status === TradeOrderStatusEnum.UNDELIVERED.status
|
||||
"
|
||||
command="delivery"
|
||||
>
|
||||
<Icon icon="ep:takeaway-box" />
|
||||
@ -332,6 +362,7 @@ import { formatDate } from '@/utils/formatTime'
|
||||
import { floatToFixed2 } from '@/utils'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express'
|
||||
import { DeliveryTypeEnum, TradeOrderStatusEnum } from '@/utils/constants'
|
||||
|
||||
defineOptions({ name: 'TradeOrder' })
|
||||
|
||||
@ -352,7 +383,8 @@ const queryParams = ref({
|
||||
type: null, // 订单类型
|
||||
deliveryType: null, // 配送方式
|
||||
logisticsId: null, // 快递公司
|
||||
pickUpStoreId: null // 自提门店
|
||||
pickUpStoreId: null, // 自提门店
|
||||
pickUpVerifyCode: null // 自提核销码
|
||||
})
|
||||
const queryType = reactive({ k: '' }) // 订单搜索类型 k
|
||||
|
||||
@ -466,7 +498,7 @@ const imagePreview = (imgUrl: string) => {
|
||||
|
||||
/** 查看订单详情 */
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'TradeOrderDetail', params: { orderId: id } })
|
||||
push({ name: 'TradeOrderDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 操作分发 */
|
||||
|
@ -13,13 +13,13 @@
|
||||
|
||||
<el-tabs>
|
||||
<el-tab-pane label="积分">
|
||||
<el-form-item label="积分抵扣" prop="tradeDeductEnable">
|
||||
<el-switch v-model="formData.tradeDeductEnable" style="user-select: none" />
|
||||
<el-form-item label="积分抵扣" prop="pointTradeDeductEnable">
|
||||
<el-switch v-model="formData.pointTradeDeductEnable" style="user-select: none" />
|
||||
<el-text class="w-full" size="small" type="info">下单积分是否抵用订单金额</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="积分抵扣" prop="tradeDeductUnitPrice">
|
||||
<el-form-item label="积分抵扣" prop="pointTradeDeductUnitPrice">
|
||||
<el-input-number
|
||||
v-model="computedTradeDeductUnitPrice"
|
||||
v-model="computedPointTradeDeductUnitPrice"
|
||||
placeholder="请输入积分抵扣金额"
|
||||
:precision="2"
|
||||
/>
|
||||
@ -27,18 +27,18 @@
|
||||
积分抵用比例(1 积分抵多少金额),单位:元
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="积分抵扣最大值" prop="tradeDeductMaxPrice">
|
||||
<el-form-item label="积分抵扣最大值" prop="pointTradeDeductMaxPrice">
|
||||
<el-input-number
|
||||
v-model="formData.tradeDeductMaxPrice"
|
||||
v-model="formData.pointTradeDeductMaxPrice"
|
||||
placeholder="请输入积分抵扣最大值"
|
||||
/>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
单次下单积分使用上限,0 不限制
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="1 元赠送多少分" prop="tradeGivePoint">
|
||||
<el-form-item label="1 元赠送多少分" prop="pointTradeGivePoint">
|
||||
<el-input-number
|
||||
v-model="formData.tradeGivePoint"
|
||||
v-model="formData.pointTradeGivePoint"
|
||||
placeholder="请输入 1 元赠送多少积分"
|
||||
/>
|
||||
<el-text class="w-full" size="small" type="info">
|
||||
@ -55,9 +55,9 @@
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as ConfigApi from '@/api/member/point/config'
|
||||
import * as ConfigApi from '@/api/member/config'
|
||||
|
||||
defineOptions({ name: 'MemberPointConfig' })
|
||||
defineOptions({ name: 'MemberConfig' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
@ -66,17 +66,17 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
tradeDeductEnable: true,
|
||||
tradeDeductUnitPrice: 0,
|
||||
tradeDeductMaxPrice: 0,
|
||||
tradeGivePoint: 0
|
||||
pointTradeDeductEnable: true,
|
||||
pointTradeDeductUnitPrice: 0,
|
||||
pointTradeDeductMaxPrice: 0,
|
||||
pointTradeGivePoint: 0
|
||||
})
|
||||
|
||||
// 创建一个计算属性,用于将 tradeDeductUnitPrice 显示为带两位小数的形式
|
||||
const computedTradeDeductUnitPrice = computed({
|
||||
get: () => (formData.value.tradeDeductUnitPrice / 100).toFixed(2),
|
||||
set: (newValue) => {
|
||||
formData.value.tradeDeductUnitPrice = Math.round(newValue * 100)
|
||||
// 创建一个计算属性,用于将 pointTradeDeductUnitPrice 显示为带两位小数的形式
|
||||
const computedPointTradeDeductUnitPrice = computed({
|
||||
get: () => (formData.value.pointTradeDeductUnitPrice / 100).toFixed(2),
|
||||
set: (newValue: number) => {
|
||||
formData.value.pointTradeDeductUnitPrice = Math.round(newValue * 100)
|
||||
}
|
||||
})
|
||||
|
@ -45,7 +45,7 @@
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
:src="scope.row.icon"
|
||||
class="w-30px h-30px"
|
||||
class="h-30px w-30px"
|
||||
:preview-src-list="[scope.row.icon]"
|
||||
/>
|
||||
</template>
|
||||
@ -54,7 +54,7 @@
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
:src="scope.row.backgroundUrl"
|
||||
class="w-30px h-30px"
|
||||
class="h-30px w-30px"
|
||||
:preview-src-list="[scope.row.backgroundUrl]"
|
||||
/>
|
||||
</template>
|
||||
|
@ -13,8 +13,11 @@
|
||||
只允许设置 1-7,默认签到 7 天为一个周期
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item label="签到分数" prop="point">
|
||||
<el-input-number v-model="formData.point" :precision="0" />
|
||||
<el-form-item label="奖励积分" prop="point">
|
||||
<el-input-number v-model="formData.point" :min="0" :precision="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="奖励经验" prop="experience">
|
||||
<el-input-number v-model="formData.experience" :min="0" :precision="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
@ -46,12 +49,30 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
day: undefined,
|
||||
point: undefined
|
||||
const formData = ref<SignInConfigApi.SignInConfigVO>({} as SignInConfigApi.SignInConfigVO)
|
||||
// 奖励校验规则
|
||||
const awardValidator = (rule: any, _value: any, callback: any) => {
|
||||
if (!formData.value.point && !formData.value.experience) {
|
||||
callback(new Error('奖励积分与奖励经验至少配置一个'))
|
||||
return
|
||||
}
|
||||
|
||||
// 清除另一个字段的错误提示
|
||||
const otherAwardField = rule?.field === 'point' ? 'experience' : 'point'
|
||||
formRef.value.validateField(otherAwardField, () => null)
|
||||
callback()
|
||||
}
|
||||
const formRules = reactive({
|
||||
day: [{ required: true, message: '签到天数不能空', trigger: 'blur' }],
|
||||
point: [
|
||||
{ required: true, message: '奖励积分不能空', trigger: 'blur' },
|
||||
{ validator: awardValidator, trigger: 'blur' }
|
||||
],
|
||||
experience: [
|
||||
{ required: true, message: '奖励经验不能空', trigger: 'blur' },
|
||||
{ validator: awardValidator, trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
const formRules = reactive({})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
@ -82,14 +103,11 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as SignInConfigApi.SignInConfigVO
|
||||
if (formType.value === 'create') {
|
||||
//默认新创建的自动启动
|
||||
data.enable = true
|
||||
await SignInConfigApi.createSignInConfig(data)
|
||||
await SignInConfigApi.createSignInConfig(formData.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await SignInConfigApi.updateSignInConfig(data)
|
||||
await SignInConfigApi.updateSignInConfig(formData.value)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
@ -105,7 +123,8 @@ const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
day: undefined,
|
||||
point: undefined,
|
||||
point: 0,
|
||||
experience: 0,
|
||||
status: CommonStatusEnum.ENABLE
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
|
@ -20,7 +20,8 @@
|
||||
prop="day"
|
||||
:formatter="(_, __, cellValue) => ['第', cellValue, '天'].join(' ')"
|
||||
/>
|
||||
<el-table-column label="获得积分" align="center" prop="point" />
|
||||
<el-table-column label="奖励积分" align="center" prop="point" />
|
||||
<el-table-column label="奖励经验" align="center" prop="experience" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
|
128
src/views/member/user/UserPointUpdateForm.vue
Normal file
128
src/views/member/user/UserPointUpdateForm.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<Dialog title="修改用户积分" v-model="dialogVisible" width="600">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="用户编号" prop="id">
|
||||
<el-input v-model="formData.id" class="!w-240px" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户昵称" prop="nickname">
|
||||
<el-input v-model="formData.nickname" class="!w-240px" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="变动前积分" prop="point">
|
||||
<el-input-number v-model="formData.point" class="!w-240px" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="变动类型" prop="changeType">
|
||||
<el-radio-group v-model="formData.changeType">
|
||||
<el-radio :label="1">增加</el-radio>
|
||||
<el-radio :label="-1">减少</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="变动积分" prop="changePoint">
|
||||
<el-input-number v-model="formData.changePoint" class="!w-240px" :min="0" :precision="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="变动后积分">
|
||||
<el-input-number v-model="pointResult" class="!w-240px" disabled />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as UserApi from '@/api/member/user'
|
||||
|
||||
/** 修改用户积分表单 */
|
||||
defineOptions({ name: 'UpdatePointForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
nickname: undefined,
|
||||
point: 0,
|
||||
changePoint: 0,
|
||||
changeType: 1
|
||||
})
|
||||
const formRules = reactive({
|
||||
changePoint: [{ required: true, message: '变动积分不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (id?: number) => {
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await UserApi.getUser(id)
|
||||
formData.value.changeType = 1 // 默认增加积分
|
||||
formData.value.changePoint = 0 // 变动积分默认0
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
if (formData.value.changePoint < 1) {
|
||||
message.error('变动积分不能小于 1')
|
||||
return
|
||||
}
|
||||
if (pointResult.value < 0) {
|
||||
message.error('变动后的积分不能小于 0')
|
||||
return
|
||||
}
|
||||
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
await UserApi.updateUserPoint({
|
||||
id: formData.value.id,
|
||||
point: formData.value.changePoint * formData.value.changeType
|
||||
})
|
||||
|
||||
message.success(t('common.updateSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
nickname: undefined,
|
||||
levelId: undefined,
|
||||
reason: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 变动后的积分 */
|
||||
const pointResult = computed(
|
||||
() => formData.value.point + formData.value.changePoint * formData.value.changeType
|
||||
)
|
||||
</script>
|
@ -1,14 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'GrowthList'
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- TODO @梦:可以读取 member_experience_log 表 -->
|
||||
<template>
|
||||
<div>成长值列表</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
@ -24,36 +24,63 @@
|
||||
</template>
|
||||
{{ user.totalPoint || 0 }}
|
||||
</el-descriptions-item>
|
||||
<!-- TODO 芋艿:后续接入余额、支付金额 -->
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<descriptions-item-label label=" 当前余额 " icon="svg-icon:member_balance" />
|
||||
</template>
|
||||
{{ 0 }}
|
||||
{{ fenToYuan(wallet.balance || 0) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<descriptions-item-label label=" 支出金额 " icon="svg-icon:member_expenditure_balance" />
|
||||
</template>
|
||||
{{ 0 }}
|
||||
{{ fenToYuan(wallet.totalExpense || 0) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<descriptions-item-label label=" 充值金额 " icon="svg-icon:member_recharge_balance" />
|
||||
</template>
|
||||
{{ 0 }}
|
||||
{{ fenToYuan(wallet.totalRecharge || 0) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { DescriptionsItemLabel } from '@/components/Descriptions'
|
||||
import * as UserApi from '@/api/member/user'
|
||||
const { user } = defineProps<{ user: UserApi.UserVO }>()
|
||||
import * as WalletApi from '@/api/pay/wallet'
|
||||
import { UserTypeEnum } from '@/utils/constants'
|
||||
import { fenToYuan } from '@/utils'
|
||||
|
||||
const props = defineProps<{ user: UserApi.UserVO }>() // 用户信息
|
||||
const WALLET_INIT_DATA = {
|
||||
balance: 0,
|
||||
totalExpense: 0,
|
||||
totalRecharge: 0
|
||||
} as WalletApi.WalletVO // 钱包初始化数据
|
||||
const wallet = ref<WalletApi.WalletVO>(WALLET_INIT_DATA) // 钱包信息
|
||||
|
||||
/** 查询用户钱包信息 */
|
||||
const getUserWallet = async () => {
|
||||
if (!props.user.id) {
|
||||
wallet.value = WALLET_INIT_DATA
|
||||
return
|
||||
}
|
||||
const params = { userId: props.user.id, userType: UserTypeEnum.MEMBER }
|
||||
wallet.value = (await WalletApi.getWallet(params)) || WALLET_INIT_DATA
|
||||
}
|
||||
|
||||
/** 监听用户编号变化 */
|
||||
watch(
|
||||
() => props.user.id,
|
||||
() => getUserWallet(),
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.cell-item {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.cell-item::after {
|
||||
content: ':';
|
||||
}
|
||||
|
125
src/views/member/user/detail/UserBrokerageList.vue
Normal file
125
src/views/member/user/detail/UserBrokerageList.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="85px"
|
||||
>
|
||||
<el-form-item label="用户类型" prop="level">
|
||||
<el-radio-group v-model="queryParams.level" @change="handleQuery">
|
||||
<el-radio-button checked>全部</el-radio-button>
|
||||
<el-radio-button label="1">一级推广人</el-radio-button>
|
||||
<el-radio-button label="2">二级推广人</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定时间" prop="bindUserTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.bindUserTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="用户编号" align="center" prop="id" min-width="80px" />
|
||||
<el-table-column label="头像" align="center" prop="avatar" width="70px">
|
||||
<template #default="scope">
|
||||
<el-avatar :src="scope.row.avatar" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="昵称" align="center" prop="nickname" min-width="80px" />
|
||||
<el-table-column label="等级" align="center" prop="level" min-width="80px">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.bindUserId === bindUserId">一级</el-tag>
|
||||
<el-tag v-else>二级</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="绑定时间"
|
||||
align="center"
|
||||
prop="bindUserTime"
|
||||
:formatter="dateFormatter"
|
||||
width="170px"
|
||||
/>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as BrokerageUserApi from '@/api/mall/trade/brokerage/user'
|
||||
|
||||
/** 推广人列表 */
|
||||
defineOptions({ name: 'UserBrokerageList' })
|
||||
|
||||
const { bindUserId }: { bindUserId: number } = defineProps({
|
||||
bindUserId: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
}) //用户编号
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
bindUserId: null,
|
||||
level: '',
|
||||
bindUserTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
queryParams.bindUserId = bindUserId
|
||||
const data = await BrokerageUserApi.getBrokerageUserPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
190
src/views/member/user/detail/UserCouponList.vue
Normal file
190
src/views/member/user/detail/UserCouponList.vue
Normal file
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<!-- 搜索工作栏 -->
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"> <Icon icon="ep:search" class="mr-5px" />搜索 </el-button>
|
||||
<el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" />重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap>
|
||||
<!-- Tab 选项:真正的内容在 Lab -->
|
||||
<el-tabs v-model="activeTab" type="card" @tab-change="onTabChange">
|
||||
<el-tab-pane
|
||||
v-for="tab in statusTabs"
|
||||
:key="tab.value"
|
||||
:label="tab.label"
|
||||
:name="tab.value"
|
||||
/>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="优惠劵" align="center" prop="name" />
|
||||
<el-table-column label="优惠券类型" align="center" prop="discountType">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="领取方式" align="center" prop="takeType">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE" :value="scope.row.takeType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="领取时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
label="使用时间"
|
||||
align="center"
|
||||
prop="useTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['promotion:coupon:delete']"
|
||||
type="danger"
|
||||
link
|
||||
@click="handleDelete(scope.row.id)"
|
||||
>
|
||||
回收
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserCouponList">
|
||||
import { deleteCoupon, getCouponPage } from '@/api/mall/promotion/coupon/coupon'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
|
||||
defineOptions({ name: 'UserCouponList' })
|
||||
|
||||
const { userId }: { userId: number } = defineProps({
|
||||
userId: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
}) //用户编号
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 字典表格数据
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
createTime: [],
|
||||
status: undefined,
|
||||
userIds: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
const activeTab = ref('all') // Tab 筛选
|
||||
const statusTabs = reactive([
|
||||
{
|
||||
label: '全部',
|
||||
value: 'all'
|
||||
}
|
||||
])
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
// 执行查询
|
||||
try {
|
||||
queryParams.userIds = userId
|
||||
const data = await getCouponPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 二次确认
|
||||
await message.confirm(
|
||||
'回收将会收回会员领取的待使用的优惠券,已使用的将无法回收,确定要回收所选优惠券吗?'
|
||||
)
|
||||
// 发起删除
|
||||
await deleteCoupon(id)
|
||||
message.notifySuccess('回收成功')
|
||||
// 重新加载列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** tab 切换 */
|
||||
const onTabChange = (tabName) => {
|
||||
queryParams.status = tabName === 'all' ? undefined : tabName
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
// 设置 statuses 过滤
|
||||
for (const dict of getIntDictOptions(DICT_TYPE.PROMOTION_COUPON_STATUS)) {
|
||||
statusTabs.push({
|
||||
label: dict.label,
|
||||
value: dict.value as string
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
@ -16,7 +16,7 @@
|
||||
</el-col>
|
||||
<!-- 右上角:账户信息 -->
|
||||
<el-col :span="10" class="detail-info-item">
|
||||
<el-card shadow="never">
|
||||
<el-card shadow="never" class="h-full">
|
||||
<template #header>
|
||||
<CardTitle title="账户信息" />
|
||||
</template>
|
||||
@ -24,31 +24,37 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 下边:账户明细 -->
|
||||
<!-- TODO 芋艿:【订单管理】【售后管理】【收藏记录】【优惠劵】 -->
|
||||
<!-- TODO 芋艿:【订单管理】【售后管理】【收藏记录】-->
|
||||
<el-card header="账户明细" style="width: 100%; margin-top: 20px" shadow="never">
|
||||
<template #header>
|
||||
<CardTitle title="账户明细" />
|
||||
</template>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="积分" name="point">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="积分">
|
||||
<UserPointList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="签到" name="sign" lazy>
|
||||
<el-tab-pane label="签到" lazy>
|
||||
<UserSignList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="成长值" name="experience" lazy>
|
||||
<UserExperienceRecordList :user-id="id"
|
||||
/></el-tab-pane>
|
||||
<el-tab-pane label="余额" name="fourth">余额(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="收货地址" name="address" lazy>
|
||||
<el-tab-pane label="成长值" lazy>
|
||||
<UserExperienceRecordList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
<!-- TODO @jason:增加一个余额变化; -->
|
||||
<el-tab-pane label="余额" lazy>余额(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="收货地址" lazy>
|
||||
<UserAddressList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="订单管理" name="order" lazy>
|
||||
<el-tab-pane label="订单管理" lazy>
|
||||
<UserOrderList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="售后管理" name="fourth">售后管理(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="收藏记录" name="fourth">收藏记录(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="优惠劵" name="fourth">优惠劵(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="售后管理" lazy>售后管理(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="收藏记录" lazy>收藏记录(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="优惠劵" lazy>
|
||||
<UserCouponList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="推广用户" lazy>
|
||||
<UserBrokerageList :bind-user-id="id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-row>
|
||||
@ -60,21 +66,23 @@
|
||||
<script setup lang="ts">
|
||||
import * as UserApi from '@/api/member/user'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import UserBasicInfo from './UserBasicInfo.vue'
|
||||
import UserForm from '@/views/member/user/UserForm.vue'
|
||||
import UserAccountInfo from './UserAccountInfo.vue'
|
||||
import UserAddressList from './UserAddressList.vue'
|
||||
import UserBasicInfo from './UserBasicInfo.vue'
|
||||
import UserBrokerageList from './UserBrokerageList.vue'
|
||||
import UserCouponList from './UserCouponList.vue'
|
||||
import UserExperienceRecordList from './UserExperienceRecordList.vue'
|
||||
import UserOrderList from './UserOrderList.vue'
|
||||
import UserPointList from './UserPointList.vue'
|
||||
import UserSignList from './UserSignList.vue'
|
||||
import UserExperienceRecordList from './UserExperienceRecordList.vue'
|
||||
import { CardTitle } from '@/components/Card/index'
|
||||
import UserOrderList from '@/views/member/user/detail/UserOrderList.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'MemberDetail' })
|
||||
|
||||
const activeName = ref('point') // 账户明细 选中的 tabs
|
||||
const loading = ref(true) // 加载中
|
||||
let user = ref<UserApi.UserVO>({})
|
||||
const user = ref<UserApi.UserVO>({} as UserApi.UserVO)
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
@ -110,10 +118,12 @@ onMounted(() => {
|
||||
.detail-info-item:first-child {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
/* first-child 不生效有没有大佬给看下q.q */
|
||||
.detail-info-item:nth-child(2) {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -117,26 +117,56 @@
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="180px" fixed="right">
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
width="100px"
|
||||
fixed="right"
|
||||
:show-overflow-tooltip="false"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openDetail(scope.row.id)">详情</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['member:user:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<!-- todo 放到更多菜单中 -->
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="updateLevelFormRef.open(scope.row.id)"
|
||||
v-hasPermi="['member:user:update-level']"
|
||||
>
|
||||
修改等级
|
||||
</el-button>
|
||||
<div class="flex items-center justify-center">
|
||||
<el-button link type="primary" @click="openDetail(scope.row.id)">详情</el-button>
|
||||
<el-dropdown
|
||||
@command="(command) => handleCommand(command, scope.row)"
|
||||
v-hasPermi="[
|
||||
'member:user:update',
|
||||
'member:user:update-level',
|
||||
'member:user:update-point',
|
||||
'member:user:update-balance'
|
||||
]"
|
||||
>
|
||||
<el-button type="primary" link><Icon icon="ep:d-arrow-right" /> 更多</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
command="handleUpdate"
|
||||
v-if="checkPermi(['member:user:update'])"
|
||||
>
|
||||
编辑
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
command="handleUpdateLevel"
|
||||
v-if="checkPermi(['member:user:update-level'])"
|
||||
>
|
||||
修改等级
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
command="handleUpdatePoint"
|
||||
v-if="checkPermi(['member:user:update-point'])"
|
||||
>
|
||||
修改积分
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
command="handleUpdateBlance"
|
||||
v-if="checkPermi(['member:user:update-balance'])"
|
||||
>
|
||||
修改余额(WIP)
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -152,7 +182,9 @@
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<UserForm ref="formRef" @success="getList" />
|
||||
<!-- 修改用户等级弹窗 -->
|
||||
<UpdateLevelForm ref="updateLevelFormRef" @success="getList" />
|
||||
<UserLevelUpdateForm ref="updateLevelFormRef" @success="getList" />
|
||||
<!-- 修改用户积分弹窗 -->
|
||||
<UserPointUpdateForm ref="updatePointFormRef" @success="getList" />
|
||||
<!-- 发送优惠券弹窗 -->
|
||||
<CouponSendForm ref="couponSendFormRef" />
|
||||
</template>
|
||||
@ -160,11 +192,14 @@
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as UserApi from '@/api/member/user'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import UserForm from './UserForm.vue'
|
||||
import MemberTagSelect from '@/views/member/tag/components/MemberTagSelect.vue'
|
||||
import MemberLevelSelect from '@/views/member/level/components/MemberLevelSelect.vue'
|
||||
import MemberGroupSelect from '@/views/member/group/components/MemberGroupSelect.vue'
|
||||
import UpdateLevelForm from '@/views/member/user/UpdateLevelForm.vue'
|
||||
import UserLevelUpdateForm from './UserLevelUpdateForm.vue'
|
||||
import UserPointUpdateForm from './UserPointUpdateForm.vue'
|
||||
import CouponSendForm from '@/views/mall/promotion/coupon/components/CouponSendForm.vue'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
|
||||
defineOptions({ name: 'MemberUser' })
|
||||
|
||||
@ -186,6 +221,7 @@ const queryParams = reactive({
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const updateLevelFormRef = ref() // 修改会员等级表单
|
||||
const updatePointFormRef = ref() // 修改会员积分表单
|
||||
const selectedIds = ref<number[]>([]) // 表格的选中 ID 数组
|
||||
|
||||
/** 查询列表 */
|
||||
@ -218,6 +254,12 @@ const openDetail = (id: number) => {
|
||||
push({ name: 'MemberUserDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 表格选中事件 */
|
||||
const handleSelectionChange = (rows: UserApi.UserVO[]) => {
|
||||
selectedIds.value = rows.map((row) => row.id)
|
||||
@ -233,6 +275,26 @@ const openCoupon = () => {
|
||||
couponSendFormRef.value.open(selectedIds.value)
|
||||
}
|
||||
|
||||
/** 操作分发 */
|
||||
const handleCommand = (command: string, row: UserApi.UserVO) => {
|
||||
switch (command) {
|
||||
case 'handleUpdate':
|
||||
openForm('update', row.id)
|
||||
break
|
||||
case 'handleUpdateLevel':
|
||||
updateLevelFormRef.value.open(row.id)
|
||||
break
|
||||
case 'handleUpdatePoint':
|
||||
updatePointFormRef.value.open(row.id)
|
||||
break
|
||||
case 'handleUpdateBlance':
|
||||
// todo @jason:增加一个【修改余额】
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
|
@ -26,8 +26,8 @@
|
||||
:src="props.url"
|
||||
poster=""
|
||||
crossorigin="anonymous"
|
||||
playsinline
|
||||
controls
|
||||
playsinline
|
||||
:volume="0.6"
|
||||
:width="800"
|
||||
:playback-rates="[0.7, 1.0, 1.5, 2.0]"
|
||||
|
@ -26,7 +26,7 @@
|
||||
@end="onChildDragEnd"
|
||||
>
|
||||
<template #item="{ element: child, index: y }">
|
||||
<div class="subtitle menu_bottom">
|
||||
<div class="menu_bottom subtitle">
|
||||
<div
|
||||
class="menu_subItem"
|
||||
v-if="parent.children"
|
||||
|
@ -10,13 +10,13 @@
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap>
|
||||
<div class="public-account-management clearfix" v-loading="loading">
|
||||
<div class="clearfix public-account-management" v-loading="loading">
|
||||
<!--左边配置菜单-->
|
||||
<div class="left">
|
||||
<div class="weixin-hd">
|
||||
<div class="weixin-title">{{ accountName }}</div>
|
||||
</div>
|
||||
<div class="weixin-menu clearfix">
|
||||
<div class="clearfix weixin-menu">
|
||||
<MenuPreviewer
|
||||
v-model="menuList"
|
||||
:account-id="accountId"
|
||||
|
@ -267,7 +267,7 @@ onMounted(async () => {
|
||||
</script>
|
||||
<style>
|
||||
.order-font {
|
||||
font-size: 12px;
|
||||
padding: 2px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="bg flex flex-col gap-2 p-6">
|
||||
<div class="flex items-center justify-between text-gray-500">
|
||||
<span>{{ title }}</span>
|
||||
<el-tooltip :content="tooltip" placement="top-start" v-if="tooltip">
|
||||
<Icon icon="ep:warning" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="mb-4 text-3xl">
|
||||
<CountTo :prefix="prefix" :end-val="value" :decimals="decimals" />
|
||||
</div>
|
||||
<div class="flex flex-row gap-1 text-sm">
|
||||
<span class="text-gray-500">环比</span>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { toNumber } from 'lodash-es'
|
||||
|
||||
/** 交易统计值组件 */
|
||||
defineOptions({ name: 'TradeStatisticValue' })
|
||||
|
||||
defineProps({
|
||||
tooltip: propTypes.string.def(''),
|
||||
title: propTypes.string.def(''),
|
||||
prefix: propTypes.string.def(''),
|
||||
value: propTypes.number.def(0),
|
||||
decimals: propTypes.number.def(0),
|
||||
percent: propTypes.oneOfType([Number, String]).def(0)
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.bg {
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
}
|
||||
</style>
|
54
src/views/statistics/trade/components/TradeTrendValue.vue
Normal file
54
src/views/statistics/trade/components/TradeTrendValue.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="mb-8 flex flex-row items-center gap-3">
|
||||
<div
|
||||
class="h-12 w-12 flex flex-shrink-0 items-center justify-center rounded-1"
|
||||
:class="`${iconColor} ${iconBgColor}`"
|
||||
>
|
||||
<Icon :icon="icon" class="!text-6" />
|
||||
</div>
|
||||
<div class="bg flex flex-col gap-1">
|
||||
<div class="flex items-center gap-1 text-gray-500">
|
||||
<span class="text-3.5">{{ title }}</span>
|
||||
<el-tooltip :content="tooltip" placement="top-start" v-if="tooltip">
|
||||
<Icon icon="ep:warning" class="item-center flex !text-3" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="flex flex-row items-baseline gap-2">
|
||||
<div class="text-7">
|
||||
<CountTo :prefix="prefix" :end-val="value" :decimals="decimals" />
|
||||
</div>
|
||||
<span :class="toNumber(percent) > 0 ? 'text-red-500' : 'text-green-500'">
|
||||
<span class="text-sm">{{ Math.abs(toNumber(percent)) }}%</span>
|
||||
<Icon
|
||||
:icon="toNumber(percent) > 0 ? 'ep:caret-top' : 'ep:caret-bottom'"
|
||||
class="ml-0.5 !text-3"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { toNumber } from 'lodash-es'
|
||||
|
||||
/** 交易状况统计值组件 */
|
||||
defineOptions({ name: 'TradeTrendValue' })
|
||||
|
||||
defineProps({
|
||||
title: propTypes.string.def(''),
|
||||
tooltip: propTypes.string.def(''),
|
||||
icon: propTypes.string.def(''),
|
||||
iconColor: propTypes.string.def(''),
|
||||
iconBgColor: propTypes.string.def(''),
|
||||
prefix: propTypes.string.def(''),
|
||||
value: propTypes.number.def(0),
|
||||
decimals: propTypes.number.def(0),
|
||||
percent: propTypes.oneOfType([Number, String]).def(0)
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.bg {
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
}
|
||||
</style>
|
428
src/views/statistics/trade/index.vue
Normal file
428
src/views/statistics/trade/index.vue
Normal file
@ -0,0 +1,428 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<el-row :gutter="16" class="summary">
|
||||
<el-col :sm="6" :xs="12">
|
||||
<TradeStatisticValue
|
||||
tooltip="昨日订单数量"
|
||||
title="昨日订单数量"
|
||||
:value="summary?.value?.yesterdayOrderCount || 0"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
summary?.value?.yesterdayOrderCount,
|
||||
summary?.reference?.yesterdayOrderCount
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :sm="6" :xs="12">
|
||||
<TradeStatisticValue
|
||||
tooltip="本月订单数量"
|
||||
title="本月订单数量"
|
||||
:value="summary?.value?.monthOrderCount || 0"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
summary?.value?.monthOrderCount,
|
||||
summary?.reference?.monthOrderCount
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :sm="6" :xs="12">
|
||||
<TradeStatisticValue
|
||||
tooltip="昨日支付金额"
|
||||
title="昨日支付金额"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(summary?.value?.yesterdayPayPrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
summary?.value?.yesterdayPayPrice,
|
||||
summary?.reference?.yesterdayPayPrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :sm="6" :xs="12">
|
||||
<TradeStatisticValue
|
||||
tooltip="本月支付金额"
|
||||
title="本月支付金额"
|
||||
prefix="¥"
|
||||
::decimals="2"
|
||||
:value="fenToYuan(summary?.value?.monthPayPrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(summary?.value?.monthPayPrice, summary?.reference?.monthPayPrice)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<!-- 标题 -->
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<span>交易状况</span>
|
||||
<!-- 查询条件 -->
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<el-radio-group v-model="shortcutDays" @change="handleDateTypeChange">
|
||||
<el-radio-button :label="1">昨天</el-radio-button>
|
||||
<el-radio-button :label="7">最近7天</el-radio-button>
|
||||
<el-radio-button :label="30">最近30天</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-date-picker
|
||||
v-model="queryParams.times"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
:shortcuts="shortcuts"
|
||||
class="!w-240px"
|
||||
@change="getTradeTrendData"
|
||||
/>
|
||||
<el-button
|
||||
class="ml-4"
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['statistics:trade:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-1" />导出
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 统计值 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :md="6" :sm="12" :xs="24">
|
||||
<TradeTrendValue
|
||||
title="营业额"
|
||||
tooltip="商品支付金额、充值金额"
|
||||
icon="fa-solid:yen-sign"
|
||||
icon-color="bg-blue-100"
|
||||
icon-bg-color="text-blue-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.turnover || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.turnover,
|
||||
trendSummary?.reference?.turnover
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :md="6" :sm="12" :xs="24">
|
||||
<TradeTrendValue
|
||||
title="商品支付金额"
|
||||
tooltip="用户购买商品的实际支付金额,包括微信支付、余额支付、支付宝支付、线下支付金额(拼团商品在成团之后计入,线下支付订单在后台确认支付后计入)"
|
||||
icon="fa-solid:shopping-cart"
|
||||
icon-color="bg-purple-100"
|
||||
icon-bg-color="text-purple-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.orderPayPrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.orderPayPrice,
|
||||
trendSummary?.reference?.orderPayPrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :md="6" :sm="12" :xs="24">
|
||||
<TradeTrendValue
|
||||
title="充值金额"
|
||||
tooltip="用户成功充值的金额"
|
||||
icon="fa-solid:money-check-alt"
|
||||
icon-color="bg-yellow-100"
|
||||
icon-bg-color="text-yellow-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.rechargePrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.rechargePrice,
|
||||
trendSummary?.reference?.rechargePrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :md="6" :sm="12" :xs="24">
|
||||
<TradeTrendValue
|
||||
title="支出金额"
|
||||
tooltip="余额支付金额、支付佣金金额、商品退款金额"
|
||||
icon="ep:warning-filled"
|
||||
icon-color="bg-green-100"
|
||||
icon-bg-color="text-green-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.expensePrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.expensePrice,
|
||||
trendSummary?.reference?.expensePrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :md="6" :sm="12" :xs="24">
|
||||
<TradeTrendValue
|
||||
title="余额支付金额"
|
||||
tooltip="用户下单时使用余额实际支付的金额"
|
||||
icon="fa-solid:wallet"
|
||||
icon-color="bg-cyan-100"
|
||||
icon-bg-color="text-cyan-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.balancePrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.balancePrice,
|
||||
trendSummary?.reference?.balancePrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :md="6" :sm="12" :xs="24">
|
||||
<TradeTrendValue
|
||||
title="支付佣金金额"
|
||||
tooltip="后台给推广员支付的推广佣金,以实际支付为准"
|
||||
icon="fa-solid:award"
|
||||
icon-color="bg-yellow-100"
|
||||
icon-bg-color="text-yellow-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.brokerageSettlementPrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.brokerageSettlementPrice,
|
||||
trendSummary?.reference?.brokerageSettlementPrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :md="6" :sm="12" :xs="24">
|
||||
<TradeTrendValue
|
||||
title="商品退款金额"
|
||||
tooltip="用户成功退款的商品金额"
|
||||
icon="fa-solid:times-circle"
|
||||
icon-color="bg-blue-100"
|
||||
icon-bg-color="text-blue-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.orderRefundPrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.orderRefundPrice,
|
||||
trendSummary?.reference?.orderRefundPrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 拆线图 -->
|
||||
<el-skeleton :loading="trendLoading" animated>
|
||||
<Echart :height="500" :options="lineChartOptions" />
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as TradeStatisticsApi from '@/api/statistics/trade'
|
||||
import TradeStatisticValue from './components/TradeStatisticValue.vue'
|
||||
import TradeTrendValue from './components/TradeTrendValue.vue'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import {
|
||||
TradeStatisticsComparisonRespVO,
|
||||
TradeSummaryRespVO,
|
||||
TradeTrendReqVO,
|
||||
TradeTrendSummaryRespVO
|
||||
} from '@/api/statistics/trade'
|
||||
import dayjs from 'dayjs'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import * as DateUtil from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
|
||||
/** 交易统计 */
|
||||
defineOptions({ name: 'TradeStatistics' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const loading = ref(true) // 加载中
|
||||
const trendLoading = ref(true) // 交易状态加载中
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const queryParams = reactive<TradeTrendReqVO>({ times: ['', ''] }) // 交易状况查询参数
|
||||
const shortcutDays = ref(7) // 日期快捷天数(单选按钮组), 默认7天
|
||||
const summary = ref<TradeStatisticsComparisonRespVO<TradeSummaryRespVO>>() // 交易统计数据
|
||||
const trendSummary = ref<TradeStatisticsComparisonRespVO<TradeTrendSummaryRespVO>>() // 交易状况统计数据
|
||||
|
||||
/** 日期快捷选择 */
|
||||
const shortcuts = [
|
||||
{
|
||||
text: '昨天',
|
||||
value: () => DateUtil.getDayRange(new Date(), -1)
|
||||
},
|
||||
{
|
||||
text: '最近7天',
|
||||
value: () => DateUtil.getLast7Days()
|
||||
},
|
||||
{
|
||||
text: '本月',
|
||||
value: () => [dayjs().startOf('M'), dayjs().subtract(1, 'd')]
|
||||
},
|
||||
{
|
||||
text: '最近30天',
|
||||
value: () => DateUtil.getLast30Days()
|
||||
},
|
||||
{
|
||||
text: '最近1年',
|
||||
value: () => DateUtil.getLast1Year()
|
||||
}
|
||||
]
|
||||
|
||||
/** 折线图配置 */
|
||||
const lineChartOptions = reactive<EChartsOption>({
|
||||
dataset: {
|
||||
dimensions: ['date', 'turnover', 'orderPayPrice', 'rechargePrice', 'expensePrice'],
|
||||
source: []
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
top: 80,
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
top: 50
|
||||
},
|
||||
series: [
|
||||
{ name: '营业额', type: 'line', smooth: true },
|
||||
{ name: '商品支付金额', type: 'line', smooth: true },
|
||||
{ name: '充值金额', type: 'line', smooth: true },
|
||||
{ name: '支出金额', type: 'line', smooth: true }
|
||||
],
|
||||
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',
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}) as EChartsOption
|
||||
|
||||
/** 计算环比 */
|
||||
const calculateRelativeRate = (value?: number, reference?: number) => {
|
||||
// 防止除0
|
||||
if (!reference) return 0
|
||||
|
||||
return ((100 * ((value || 0) - reference)) / reference).toFixed(0)
|
||||
}
|
||||
|
||||
/** 设置时间范围 */
|
||||
function setTimes() {
|
||||
const beginDate = dayjs().subtract(shortcutDays.value, 'd')
|
||||
const yesterday = dayjs().subtract(1, 'd')
|
||||
queryParams.times = DateUtil.getDateRange(beginDate, yesterday)
|
||||
}
|
||||
|
||||
/** 处理交易状况查询(日期单选按钮组选择后) */
|
||||
const handleDateTypeChange = async () => {
|
||||
// 设置时间范围
|
||||
setTimes()
|
||||
// 查询数据
|
||||
await getTradeTrendData()
|
||||
}
|
||||
|
||||
/** 处理交易状况查询 */
|
||||
const getTradeTrendData = async () => {
|
||||
trendLoading.value = true
|
||||
await Promise.all([getTradeTrendSummary(), getTradeTrendList()])
|
||||
trendLoading.value = false
|
||||
}
|
||||
|
||||
/** 查询交易统计 */
|
||||
const getTradeStatisticsSummary = async () => {
|
||||
summary.value = await TradeStatisticsApi.getTradeStatisticsSummary()
|
||||
}
|
||||
|
||||
/** 查询交易状况数据统计 */
|
||||
const getTradeTrendSummary = async () => {
|
||||
loading.value = true
|
||||
trendSummary.value = await TradeStatisticsApi.getTradeTrendSummary(queryParams)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
/** 查询交易状况数据列表 */
|
||||
const getTradeTrendList = async () => {
|
||||
const times = queryParams.times
|
||||
// 开始与截止在同一天的, 折线图出不来, 需要延长一天
|
||||
if (DateUtil.isSameDay(times[0], times[1])) {
|
||||
// 前天
|
||||
times[0] = DateUtil.formatDate(dayjs(times[0]).subtract(1, 'd'))
|
||||
}
|
||||
// 查询数据
|
||||
const list = await TradeStatisticsApi.getTradeTrendList({ times })
|
||||
// 处理数据
|
||||
for (let item of list) {
|
||||
item.turnover = fenToYuan(item.turnover)
|
||||
item.orderPayPrice = fenToYuan(item.orderPayPrice)
|
||||
item.rechargePrice = fenToYuan(item.rechargePrice)
|
||||
item.expensePrice = fenToYuan(item.expensePrice)
|
||||
}
|
||||
// 更新 Echarts 数据
|
||||
if (lineChartOptions.dataset && lineChartOptions.dataset['source']) {
|
||||
lineChartOptions.dataset['source'] = list
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await TradeStatisticsApi.exportTradeTrend(queryParams)
|
||||
download.excel(data, '交易状况.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getTradeStatisticsSummary()
|
||||
await handleDateTypeChange()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.summary {
|
||||
.el-col {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -58,9 +58,9 @@
|
||||
<el-form-item label="授权范围" prop="scopes">
|
||||
<el-select
|
||||
v-model="formData.scopes"
|
||||
allow-create
|
||||
filterable
|
||||
multiple
|
||||
allow-create
|
||||
placeholder="请输入授权范围"
|
||||
style="width: 500px"
|
||||
>
|
||||
|
@ -51,6 +51,11 @@ const handleNodeClick = async (row: { [key: string]: any }) => {
|
||||
}
|
||||
const emits = defineEmits(['node-click'])
|
||||
|
||||
/** 监听deptName */
|
||||
watch(deptName, (val) => {
|
||||
treeRef.value!.filter(val)
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
await getTree()
|
||||
|
@ -136,7 +136,7 @@
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="flex items-center justify-center">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
|
Reference in New Issue
Block a user