Bläddra i källkod

交易管理修改

“陈博论” 10 månader sedan
förälder
incheckning
26bda0f854

+ 26 - 0
web/src/api/backend/order/distribution.ts

@@ -0,0 +1,26 @@
+import createAxios from '/@/utils/axios'
+
+// 获取分销设置
+export function getSetting(data: anyObj) {
+    return createAxios(
+        {
+            url: '/admin/order.Distribution/getSetting',
+            method: 'get',
+            params: data,
+        },
+    )
+}
+
+// 更新分销设置
+export function updateSetting(data: anyObj) {
+    return createAxios(
+        {
+            url: '/admin/order.Distribution/updateSetting',
+            method: 'post',
+            data: data,
+        },
+        {
+            showSuccessMessage: true
+        }
+    )
+}

+ 40 - 0
web/src/api/backend/order/withdrawal.ts

@@ -0,0 +1,40 @@
+import createAxios from '/@/utils/axios'
+
+// 提现审核通过
+export function approve(data: anyObj) {
+    return createAxios(
+        {
+            url: '/admin/order.Withdrawal/approve',
+            method: 'post',
+            data: data,
+        },
+        {
+            showSuccessMessage: true
+        }
+    )
+}
+
+// 审核拒绝
+export function reject(data: anyObj) {
+    return createAxios(
+        {
+            url: '/admin/order.Withdrawal/reject',
+            method: 'post',
+            data: data,
+        },
+        {
+            showSuccessMessage: true
+        }
+    )
+}
+
+// 提现统计
+export function statistics(data: anyObj) {
+    return createAxios(
+        {
+            url: '/admin/order.Withdrawal/statistics',
+            method: 'get',
+            params: data,
+        },
+    )
+}

+ 23 - 2
web/src/views/backend/order/distribution/index.vue

@@ -1,21 +1,26 @@
 <template>
 	<div class="default-main ba-table-box">
 		<!-- 表格顶部菜单 -->
-		<TableHeader :buttons="['refresh', 'columnDisplay',]"></TableHeader>
+		<TableHeader :buttons="['refresh', 'columnDisplay',]">
+			<el-button type="primary" @click="handleGetSetting" plain v-auth="'getSetting'">分销配置</el-button>
+		</TableHeader>
 		<!-- 表格 -->
 		<!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
 		<Table ref="tableRef"></Table>
 
+		<!-- 分销配置弹框 -->
+		<SettingDialog ref="settingDialogRef" @success="handleSettingSuccess" />
 	</div>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, provide, } from 'vue'
+import { ref, reactive, provide, useTemplateRef } from 'vue'
 import baTableClass from '/@/utils/baTable'
 import Table from '/@/components/table/index.vue'
 import TableHeader from '/@/components/table/header/index.vue'
 import { baTableApi } from '/@/api/common'
 import { useI18n } from 'vue-i18n'
+import SettingDialog from './settingDialog.vue'
 
 defineOptions({
     name: 'order/distribution',
@@ -120,6 +125,22 @@ const seachList = () => {
 	baTable.table.filter!.page = 1
 	baTable.table.loading = true
 }
+
+// 分销配置弹框引用
+const settingDialogRef = useTemplateRef('settingDialogRef')
+
+// 打开分销设置弹框
+const handleGetSetting = () => {
+	if (settingDialogRef.value) {
+		settingDialogRef.value.open()
+	}
+}
+
+// 配置成功回调
+const handleSettingSuccess = () => {
+	// 配置更新后可以刷新表格,如果需要的话
+	// baTable.getIndex()
+}
 </script>
 <style scoped lang="scss">
 

+ 224 - 0
web/src/views/backend/order/distribution/settingDialog.vue

@@ -0,0 +1,224 @@
+<template>
+    <!-- 分销配置对话框 -->
+    <el-dialog class="ba-operate-dialog" :close-on-click-modal="false" :destroy-on-close="true" v-model="dialogVisible"
+        @close="handleClose" width="600px" style="height: 400px;">
+        <template #header>
+            <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
+                分销配置
+            </div>
+        </template>
+        <el-scrollbar v-loading="loading" class="ba-table-form-scrollbar">
+            <div class="ba-operate-form">
+                <el-form ref="formRef" :model="formData" :rules="rules" :label-width="140">
+                    <el-form-item label="默认分销佣金比例" prop="default_rate">
+                        <div class="input-with-unit">
+                            <el-input-number v-model="formData.default_rate" :min="0" :max="100" :precision="2"
+                                placeholder="请输入默认分销佣金比例" style="width: 100%" controls-position="right" />
+                            <span class="form-unit">%</span>
+                        </div>
+                    </el-form-item>
+
+                    <el-form-item label="最低提现金额" prop="min_withdrawal">
+                        <div class="input-with-unit">
+                            <el-input-number v-model="formData.min_withdrawal" :min="0" :precision="2"
+                                placeholder="请输入最低提现金额" style="width: 100%" controls-position="right"
+                                @change="handleMinWithdrawalChange" />
+                            <span class="form-unit">元</span>
+                        </div>
+                    </el-form-item>
+
+                    <el-form-item label="最高提现金额" prop="max_withdrawal">
+                        <div class="input-with-unit">
+                            <el-input-number v-model="formData.max_withdrawal" :min="0" :precision="2"
+                                placeholder="请输入最高提现金额" style="width: 100%" controls-position="right" />
+                            <span class="form-unit">元</span>
+                        </div>
+                    </el-form-item>
+
+                    <el-form-item label="提现手续费比例" prop="withdrawal_fee_rate">
+                        <div class="input-with-unit">
+                            <el-input-number v-model="formData.withdrawal_fee_rate" :min="0" :max="100" :precision="2"
+                                placeholder="请输入提现手续费比例" style="width: 100%" controls-position="right" />
+                            <span class="form-unit">%</span>
+                        </div>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </el-scrollbar>
+        <template #footer>
+            <div class="dialog-footer">
+                <el-button @click="handleClose" size="default">取消</el-button>
+                <el-button v-blur :loading="loading" @click="handleSubmit" type="primary" size="default">
+                    保存
+                </el-button>
+            </div>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type { FormInstance, FormRules } from 'element-plus'
+import { getSetting, updateSetting } from '/@/api/backend/order/distribution'
+
+const { t } = useI18n()
+
+const dialogVisible = ref(false)
+const loading = ref(false)
+const formRef = ref<FormInstance>()
+
+const formData = reactive({
+    default_rate: 0,
+    min_withdrawal: 0,
+    max_withdrawal: 0,
+    withdrawal_fee_rate: 0,
+})
+
+// 表单验证规则
+const rules = reactive<FormRules>({
+    default_rate: [
+        { required: true, message: '请输入默认分销佣金比例', trigger: 'blur' },
+        {
+            validator: (rule: any, value: number, callback: Function) => {
+                if (value < 0 || value > 100) {
+                    callback(new Error('默认分销佣金比例必须在0-100之间'))
+                } else {
+                    callback()
+                }
+            },
+            trigger: 'blur',
+        },
+    ],
+    min_withdrawal: [
+        { required: true, message: '请输入最低提现金额', trigger: 'blur' },
+        {
+            validator: (rule: any, value: number, callback: Function) => {
+                if (value < 0) {
+                    callback(new Error('最低提现金额必须大于等于0'))
+                } else {
+                    callback()
+                }
+            },
+            trigger: 'blur',
+        },
+    ],
+    max_withdrawal: [
+        { required: true, message: '请输入最高提现金额', trigger: 'blur' },
+        {
+            validator: (rule: any, value: number, callback: Function) => {
+                if (value < 0) {
+                    callback(new Error('最高提现金额必须大于等于0'))
+                } else if (value < formData.min_withdrawal) {
+                    callback(new Error('最高提现金额必须大于等于最低提现金额'))
+                } else {
+                    callback()
+                }
+            },
+            trigger: 'blur',
+        },
+    ],
+    withdrawal_fee_rate: [
+        { required: true, message: '请输入提现手续费比例', trigger: 'blur' },
+        {
+            validator: (rule: any, value: number, callback: Function) => {
+                if (value < 0 || value > 100) {
+                    callback(new Error('提现手续费比例必须在0-100之间'))
+                } else {
+                    callback()
+                }
+            },
+            trigger: 'blur',
+        },
+    ],
+})
+
+// 打开对话框
+const open = () => {
+    dialogVisible.value = true
+    loadSetting()
+}
+
+// 加载分销设置
+const loadSetting = () => {
+    loading.value = true
+    getSetting({}).then((res: any) => {
+        formData.default_rate = res.data.default_rate || 0
+        formData.min_withdrawal = res.data.min_withdrawal || 0
+        formData.max_withdrawal = res.data.max_withdrawal || 0
+        formData.withdrawal_fee_rate = res.data.withdrawal_fee_rate || 0
+    }).finally(() => {
+        loading.value = false
+    })
+}
+
+// 提交表单
+const handleSubmit = () => {
+    if (!formRef.value) return
+
+    formRef.value.validate((valid: boolean) => {
+        if (valid) {
+            loading.value = true
+
+            const params = {
+                default_rate: formData.default_rate,
+                min_withdrawal: formData.min_withdrawal,
+                max_withdrawal: formData.max_withdrawal,
+                withdrawal_fee_rate: formData.withdrawal_fee_rate,
+            }
+
+            updateSetting(params).then(() => {
+                dialogVisible.value = false
+                emit('success')
+            }).finally(() => {
+                loading.value = false
+            })
+        }
+    })
+}
+
+// 最低提现金额改变时,重新验证最高提现金额
+const handleMinWithdrawalChange = () => {
+    if (formRef.value) {
+        formRef.value.validateField('max_withdrawal', () => { })
+    }
+}
+
+// 关闭对话框
+const handleClose = () => {
+    dialogVisible.value = false
+    formRef.value?.clearValidate()
+}
+
+const emit = defineEmits(['success'])
+
+// 导出方法供父组件调用
+defineExpose({
+    open,
+})
+</script>
+
+<style scoped lang="scss">
+.dialog-footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: 10px;
+}
+
+.input-with-unit {
+    display: flex;
+    align-items: center;
+    width: 100%;
+
+    :deep(.el-input-number) {
+        flex: 1;
+    }
+
+    .form-unit {
+        margin-left: 8px;
+        color: var(--el-text-color-regular);
+        font-size: 14px;
+        white-space: nowrap;
+    }
+}
+</style>

+ 38 - 38
web/src/views/backend/order/withdrawal/index.vue

@@ -4,18 +4,30 @@
 		<TableHeader :buttons="['refresh', 'columnDisplay', 'comSearch']"></TableHeader>
 		<!-- 表格 -->
 		<!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
-		<Table ref="tableRef"></Table>
+		<Table ref="tableRef">
+			<template #operate>
+                <el-table-column prop="operate" align="center" label="操作" min-width="150">
+                    <template #default="scope">
+                        <el-button type="primary" size="small" @click="handleApprove(scope.row)" v-auth="'approve'">同意</el-button>
+                        <el-button type="danger" size="small" @click="handleReject(scope.row)" v-auth="'reject'">拒绝</el-button>
+                    </template>
+                </el-table-column>
+            </template>
+		</Table>
 
+		<!-- 审核弹框 -->
+		<ReviewDialog ref="reviewDialogRef" @success="handleReviewSuccess" />
 	</div>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, provide, computed, onMounted } from 'vue'
+import { ref, provide, useTemplateRef } from 'vue'
 import baTableClass from '/@/utils/baTable'
 import Table from '/@/components/table/index.vue'
 import TableHeader from '/@/components/table/header/index.vue'
 import { baTableApi } from '/@/api/common'
 import { useI18n } from 'vue-i18n'
+import ReviewDialog from './reviewDialog.vue'
 
 defineOptions({
     name: 'order/withdrawal',
@@ -126,42 +138,6 @@ const baTable = new baTableClass(
 	},
 )
 
-// 重写getComSearchData方法,将create_time的RANGE值转换为start_time和end_time
-const originalGetComSearchData = baTable.getComSearchData
-baTable.getComSearchData = () => {
-	const comSearchData = originalGetComSearchData.call(baTable)
-	
-	// 查找create_time的RANGE搜索
-	const createTimeIndex = comSearchData.findIndex((item: any) => item.field === 'create_time' && item.operator === 'RANGE')
-	if (createTimeIndex !== -1) {
-		const createTimeItem = comSearchData[createTimeIndex]
-		if (createTimeItem.val && typeof createTimeItem.val === 'string') {
-			const [startTime, endTime] = createTimeItem.val.split(',')
-			// 移除create_time的搜索项
-			comSearchData.splice(createTimeIndex, 1)
-			// 添加start_time和end_time的搜索项
-			if (startTime) {
-				comSearchData.push({
-					field: 'start_time',
-					val: startTime.trim(),
-					operator: '>=',
-					render: 'datetime',
-				})
-			}
-			if (endTime) {
-				comSearchData.push({
-					field: 'end_time',
-					val: endTime.trim(),
-					operator: '<=',
-					render: 'datetime',
-				})
-			}
-		}
-	}
-	
-	return comSearchData
-}
-
 baTable.mount()
 baTable.getIndex()
 provide('baTable', baTable)
@@ -171,6 +147,30 @@ const seachList = () => {
 	baTable.table.filter!.page = 1
 	baTable.table.loading = true
 }
+
+// 审核弹框引用
+const reviewDialogRef = useTemplateRef('reviewDialogRef')
+
+// 同意
+const handleApprove = (row: any) => {
+	if (reviewDialogRef.value) {
+		reviewDialogRef.value.openApprove(row)
+	}
+}
+
+// 拒绝
+const handleReject = (row: any) => {
+	if (reviewDialogRef.value) {
+		reviewDialogRef.value.openReject(row)
+	}
+}
+
+// 审核成功回调
+const handleReviewSuccess = () => {
+	baTable.getIndex()
+}
+
+
 </script>
 <style scoped lang="scss">
 

+ 163 - 0
web/src/views/backend/order/withdrawal/reviewDialog.vue

@@ -0,0 +1,163 @@
+<template>
+    <!-- 审核对话框 -->
+    <el-dialog
+        class="ba-operate-dialog"
+        :close-on-click-modal="false"
+        :destroy-on-close="true"
+        v-model="dialogVisible"
+        @close="handleClose"
+        width="500px"
+        style="height: 300px;"
+    >
+        <template #header>
+            <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
+                {{ dialogTitle }}
+            </div>
+        </template>
+        <el-scrollbar v-loading="loading" class="ba-table-form-scrollbar">
+            <div class="ba-operate-form">
+                <el-form
+                    ref="formRef"
+                    :model="formData"
+                    :rules="rules"
+                    :label-width="100"
+                >
+                    <el-form-item label="审核备注" prop="audit_remark">
+                        <el-input
+                            v-model="formData.audit_remark"
+                            type="textarea"
+                            :rows="5"
+                            :placeholder="dialogType === 'approve' ? '请输入审核通过备注(可选)' : '请输入审核拒绝原因'"
+                            maxlength="200"
+                            show-word-limit
+                        />
+                    </el-form-item>
+                </el-form>
+            </div>
+        </el-scrollbar>
+        <template #footer>
+            <div class="dialog-footer">
+                <el-button @click="handleClose" size="default">取消</el-button>
+                <el-button
+                    v-blur
+                    :loading="loading"
+                    @click="handleSubmit"
+                    :type="dialogType === 'approve' ? 'primary' : 'danger'"
+                    size="default"
+                >
+                    {{ dialogType === 'approve' ? '确认通过' : '确认拒绝' }}
+                </el-button>
+            </div>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type { FormInstance, FormRules } from 'element-plus'
+import { approve, reject } from '/@/api/backend/order/withdrawal'
+
+const { t } = useI18n()
+
+const dialogVisible = ref(false)
+const loading = ref(false)
+const dialogType = ref<'approve' | 'reject'>('approve')
+const currentId = ref<string | number>('')
+
+const formRef = ref<FormInstance>()
+
+const formData = reactive({
+    id: '',
+    audit_remark: '',
+})
+
+// 表单验证规则(动态规则)
+const rules = computed<FormRules>(() => {
+    return {
+        audit_remark: [
+            {
+                required: dialogType.value === 'reject',
+                message: dialogType.value === 'reject' ? '请输入审核拒绝原因' : '请输入审核备注',
+                trigger: 'blur',
+            },
+        ],
+    }
+})
+
+// 对话框标题
+const dialogTitle = computed(() => {
+    return dialogType.value === 'approve' ? '审核通过' : '审核拒绝'
+})
+
+// 打开审核对话框(同意)
+const openApprove = (row: { id: string | number }) => {
+    dialogType.value = 'approve'
+    currentId.value = row.id
+    formData.id = String(row.id)
+    formData.audit_remark = ''
+    dialogVisible.value = true
+    formRef.value?.clearValidate()
+}
+
+// 打开审核对话框(拒绝)
+const openReject = (row: { id: string | number }) => {
+    dialogType.value = 'reject'
+    currentId.value = row.id
+    formData.id = String(row.id)
+    formData.audit_remark = ''
+    dialogVisible.value = true
+    formRef.value?.clearValidate()
+}
+
+// 提交审核
+const handleSubmit = () => {
+    if (!formRef.value) return
+
+    formRef.value.validate((valid: boolean) => {
+        if (valid) {
+            loading.value = true
+
+            const params = {
+                id: formData.id,
+                audit_remark: formData.audit_remark || (dialogType.value === 'approve' ? '审核通过' : ''),
+            }
+
+            const apiCall = dialogType.value === 'approve' ? approve(params) : reject(params)
+
+            apiCall
+                .then(() => {
+                    dialogVisible.value = false
+                    emit('success')
+                })
+                .finally(() => {
+                    loading.value = false
+                })
+        }
+    })
+}
+
+// 关闭对话框
+const handleClose = () => {
+    dialogVisible.value = false
+    formData.audit_remark = ''
+    formRef.value?.clearValidate()
+}
+
+const emit = defineEmits(['success'])
+
+// 导出方法供父组件调用
+defineExpose({
+    openApprove,
+    openReject,
+})
+</script>
+
+<style scoped lang="scss">
+.dialog-footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: 10px;
+}
+</style>
+