mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-04 20:28:45 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
		@@ -41,3 +41,20 @@ export const getTaskListByProcessInstanceId = async (processInstanceId) => {
 | 
			
		||||
export const exportTask = async (params) => {
 | 
			
		||||
  return await request.download({ url: '/bpm/task/export', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取所有可回退的节点
 | 
			
		||||
export const getReturnList = async (params) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task/get-return-list', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 回退
 | 
			
		||||
export const returnTask = async (data) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/return', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 委派
 | 
			
		||||
 */
 | 
			
		||||
export const delegateTask = async (data) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/delegate', data })
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import BpmnViewer from 'bpmn-js/lib/Viewer'
 | 
			
		||||
import DefaultEmptyXML from './plugins/defaultEmpty'
 | 
			
		||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 | 
			
		||||
import { formatDate } from '@/utils/formatTime'
 | 
			
		||||
import { isEmpty } from '@/utils/is'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'MyProcessViewer' })
 | 
			
		||||
 | 
			
		||||
@@ -85,6 +86,7 @@ const createNewDiagram = async (xml) => {
 | 
			
		||||
    // console.error(`[Process Designer Warn]: ${e?.message || e}`);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 高亮流程图 */
 | 
			
		||||
// TODO 芋艿:如果多个 endActivity 的话,目前的逻辑可能有一定的问题。https://www.jdon.com/workflow/multi-events.html
 | 
			
		||||
const highlightDiagram = async () => {
 | 
			
		||||
@@ -97,6 +99,9 @@ const highlightDiagram = async () => {
 | 
			
		||||
  let canvas = bpmnModeler.get('canvas')
 | 
			
		||||
  let todoActivity: any = activityList.find((m: any) => !m.endTime) // 找到待办的任务
 | 
			
		||||
  let endActivity: any = activityList[activityList.length - 1] // 获得最后一个任务
 | 
			
		||||
  let findProcessTask = false //是否已经高亮了进行中的任务
 | 
			
		||||
  //进行中高亮之后的任务 key 集合,用于过滤掉 taskList 进行中后面的任务,避免进行中后面的数据 Hover 还有数据
 | 
			
		||||
  let removeTaskDefinitionKeyList = []
 | 
			
		||||
  // debugger
 | 
			
		||||
  bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach((n: any) => {
 | 
			
		||||
    let activity: any = activityList.find((m: any) => m.key === n.id) // 找到对应的活动
 | 
			
		||||
@@ -110,9 +115,17 @@ const highlightDiagram = async () => {
 | 
			
		||||
      if (!task) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      //进行中的任务已经高亮过了,则不高亮后面的任务了
 | 
			
		||||
      if (findProcessTask) {
 | 
			
		||||
        removeTaskDefinitionKeyList.push(n.id)
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      // 高亮任务
 | 
			
		||||
      canvas.addMarker(n.id, getResultCss(task.result))
 | 
			
		||||
 | 
			
		||||
      //标记是否高亮了进行中任务
 | 
			
		||||
      if (task.result === 1) {
 | 
			
		||||
        findProcessTask = true
 | 
			
		||||
      }
 | 
			
		||||
      // 如果非通过,就不走后面的线条了
 | 
			
		||||
      if (task.result !== 2) {
 | 
			
		||||
        return
 | 
			
		||||
@@ -212,6 +225,11 @@ const highlightDiagram = async () => {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  if (!isEmpty(removeTaskDefinitionKeyList)) {
 | 
			
		||||
    taskList.value = taskList.value.filter(
 | 
			
		||||
      (item) => !removeTaskDefinitionKeyList.includes(item.definitionKey)
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const getActivityHighlightCss = (activity) => {
 | 
			
		||||
  return activity.endTime ? 'highlight' : 'highlight-todo'
 | 
			
		||||
@@ -229,6 +247,9 @@ const getResultCss = (result) => {
 | 
			
		||||
  } else if (result === 4) {
 | 
			
		||||
    // 已取消
 | 
			
		||||
    return 'highlight-cancel'
 | 
			
		||||
  } else if (result === 5) {
 | 
			
		||||
    // 退回
 | 
			
		||||
    return 'highlight-return'
 | 
			
		||||
  }
 | 
			
		||||
  return ''
 | 
			
		||||
}
 | 
			
		||||
@@ -273,9 +294,9 @@ const elementHover = (element) => {
 | 
			
		||||
  console.log(element.value, 'element.value')
 | 
			
		||||
  const activity = activityLists.value.find((m) => m.key === element.value.id)
 | 
			
		||||
  console.log(activity, 'activityactivityactivityactivity')
 | 
			
		||||
  // if (!activity) {
 | 
			
		||||
  //   return
 | 
			
		||||
  // }
 | 
			
		||||
  if (!activity) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  if (!elementOverlayIds.value[element.value.id] && element.value.type !== 'bpmn:Process') {
 | 
			
		||||
    let html = `<div class="element-overlays">
 | 
			
		||||
            <p>Elemet id: ${element.value.id}</p>
 | 
			
		||||
@@ -564,6 +585,45 @@ watch(
 | 
			
		||||
  stroke: grey !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 回退 */
 | 
			
		||||
.highlight-return.djs-shape .djs-visual > :nth-child(1) {
 | 
			
		||||
  fill: #e6a23c !important;
 | 
			
		||||
  stroke: #e6a23c !important;
 | 
			
		||||
  fill-opacity: 0.2 !important;
 | 
			
		||||
}
 | 
			
		||||
.highlight-return.djs-shape .djs-visual > :nth-child(2) {
 | 
			
		||||
  fill: #e6a23c !important;
 | 
			
		||||
}
 | 
			
		||||
.highlight-return.djs-shape .djs-visual > path {
 | 
			
		||||
  fill: #e6a23c !important;
 | 
			
		||||
  fill-opacity: 0.2 !important;
 | 
			
		||||
  stroke: #e6a23c !important;
 | 
			
		||||
}
 | 
			
		||||
.highlight-return.djs-connection > .djs-visual > path {
 | 
			
		||||
  stroke: #e6a23c !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.highlight-return:not(.djs-connection) .djs-visual > :nth-child(1) {
 | 
			
		||||
  fill: #e6a23c !important; /* color elements as green */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:deep(.highlight-return.djs-shape .djs-visual > :nth-child(1)) {
 | 
			
		||||
  fill: #e6a23c !important;
 | 
			
		||||
  stroke: #e6a23c !important;
 | 
			
		||||
  fill-opacity: 0.2 !important;
 | 
			
		||||
}
 | 
			
		||||
:deep(.highlight-return.djs-shape .djs-visual > :nth-child(2)) {
 | 
			
		||||
  fill: #e6a23c !important;
 | 
			
		||||
}
 | 
			
		||||
:deep(.highlight-return.djs-shape .djs-visual > path) {
 | 
			
		||||
  fill: #e6a23c !important;
 | 
			
		||||
  fill-opacity: 0.2 !important;
 | 
			
		||||
  stroke: #e6a23c !important;
 | 
			
		||||
}
 | 
			
		||||
:deep(.highlight-return.djs-connection > .djs-visual > path) {
 | 
			
		||||
  stroke: #e6a23c !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.element-overlays {
 | 
			
		||||
  width: 200px;
 | 
			
		||||
  padding: 8px;
 | 
			
		||||
 
 | 
			
		||||
@@ -278,12 +278,16 @@ export const PromotionDiscountTypeEnum = {
 | 
			
		||||
 */
 | 
			
		||||
export const BrokerageBindModeEnum = {
 | 
			
		||||
  ANYTIME: {
 | 
			
		||||
    mode: 0,
 | 
			
		||||
    name: '没有推广人'
 | 
			
		||||
    mode: 1,
 | 
			
		||||
    name: '首次绑定'
 | 
			
		||||
  },
 | 
			
		||||
  REGISTER: {
 | 
			
		||||
    mode: 1,
 | 
			
		||||
    name: '新用户'
 | 
			
		||||
    mode: 2,
 | 
			
		||||
    name: '注册绑定'
 | 
			
		||||
  },
 | 
			
		||||
  OVERRIDE: {
 | 
			
		||||
    mode: 3,
 | 
			
		||||
    name: '覆盖绑定'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
@@ -291,11 +295,11 @@ export const BrokerageBindModeEnum = {
 | 
			
		||||
 */
 | 
			
		||||
export const BrokerageEnabledConditionEnum = {
 | 
			
		||||
  ALL: {
 | 
			
		||||
    condition: 0,
 | 
			
		||||
    condition: 1,
 | 
			
		||||
    name: '人人分销'
 | 
			
		||||
  },
 | 
			
		||||
  ADMIN: {
 | 
			
		||||
    condition: 1,
 | 
			
		||||
    condition: 2,
 | 
			
		||||
    name: '指定分销'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 自己不是处理人
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
    // 二次确认
 | 
			
		||||
 
 | 
			
		||||
@@ -132,7 +132,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,43 @@
 | 
			
		||||
      <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" />
 | 
			
		||||
@@ -22,10 +57,15 @@
 | 
			
		||||
              v-model="formData.deliveryExpressFreePrice"
 | 
			
		||||
              placeholder="请输入满额包邮"
 | 
			
		||||
              class="!w-xs"
 | 
			
		||||
              :precision="2"
 | 
			
		||||
              :min="0"
 | 
			
		||||
            />
 | 
			
		||||
            <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-tab-pane>
 | 
			
		||||
        <!-- 分销 -->
 | 
			
		||||
        <el-tab-pane label="分销">
 | 
			
		||||
          <el-form-item label="分佣启用" prop="brokerageEnabled">
 | 
			
		||||
            <el-switch v-model="formData.brokerageEnabled" style="user-select: none" />
 | 
			
		||||
@@ -59,16 +99,16 @@
 | 
			
		||||
              </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.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">
 | 
			
		||||
@@ -76,6 +116,8 @@
 | 
			
		||||
              v-model="formData.brokerageFirstPercent"
 | 
			
		||||
              placeholder="请输入一级返佣比例"
 | 
			
		||||
              class="!w-xs"
 | 
			
		||||
              :min="0"
 | 
			
		||||
              :max="100"
 | 
			
		||||
            />
 | 
			
		||||
            <el-text class="w-full" size="small" type="info">
 | 
			
		||||
              订单交易成功后给推广人返佣的百分比
 | 
			
		||||
@@ -86,6 +128,8 @@
 | 
			
		||||
              v-model="formData.brokerageSecondPercent"
 | 
			
		||||
              placeholder="请输入二级返佣比例"
 | 
			
		||||
              class="!w-xs"
 | 
			
		||||
              :min="0"
 | 
			
		||||
              :max="100"
 | 
			
		||||
            />
 | 
			
		||||
            <el-text class="w-full" size="small" type="info">
 | 
			
		||||
              订单交易成功后给推广人的推荐人返佣的百分比
 | 
			
		||||
@@ -96,6 +140,7 @@
 | 
			
		||||
              v-model="formData.brokerageFrozenDays"
 | 
			
		||||
              placeholder="请输入佣金冻结天数"
 | 
			
		||||
              class="!w-xs"
 | 
			
		||||
              :min="0"
 | 
			
		||||
            />
 | 
			
		||||
            <el-text class="w-full" size="small" type="info">
 | 
			
		||||
              防止用户退款,佣金被提现了,所以需要设置佣金冻结时间,单位:天
 | 
			
		||||
@@ -106,6 +151,8 @@
 | 
			
		||||
              v-model="formData.brokerageWithdrawMinPrice"
 | 
			
		||||
              placeholder="请输入提现最低金额"
 | 
			
		||||
              class="!w-xs"
 | 
			
		||||
              :precision="2"
 | 
			
		||||
              :min="0"
 | 
			
		||||
            />
 | 
			
		||||
            <el-text class="w-full" size="small" type="info">
 | 
			
		||||
              用户提现最低金额限制,单位:元
 | 
			
		||||
@@ -116,9 +163,12 @@
 | 
			
		||||
              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%手续费,提现100元,到账90元,10元手续费
 | 
			
		||||
              提现手续费百分比,范围 0-100,0 为无提现手续费。例:设置 10,即收取 10% 手续费,提现
 | 
			
		||||
              10 元,到账 9 元,1 元手续费
 | 
			
		||||
            </el-text>
 | 
			
		||||
          </el-form-item>
 | 
			
		||||
          <el-form-item label="提现方式" prop="brokerageWithdrawTypes">
 | 
			
		||||
@@ -146,7 +196,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>
 | 
			
		||||
@@ -167,11 +217,13 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
 | 
			
		||||
const formRef = ref()
 | 
			
		||||
const formData = ref({
 | 
			
		||||
  id: null,
 | 
			
		||||
  deliveryExpressFreeEnabled: true,
 | 
			
		||||
  afterSaleRefundReasons: [],
 | 
			
		||||
  afterSaleReturnReasons: [],
 | 
			
		||||
  deliveryExpressFreeEnabled: false,
 | 
			
		||||
  deliveryExpressFreePrice: 0,
 | 
			
		||||
  brokerageEnabled: true,
 | 
			
		||||
  brokerageEnabledCondition: BrokerageEnabledConditionEnum.ALL.condition,
 | 
			
		||||
  brokerageBindMode: BrokerageBindModeEnum.ANYTIME.mode,
 | 
			
		||||
  brokerageEnabled: false,
 | 
			
		||||
  brokerageEnabledCondition: undefined,
 | 
			
		||||
  brokerageBindMode: undefined,
 | 
			
		||||
  brokeragePosterUrls: [],
 | 
			
		||||
  brokerageFirstPercent: 0,
 | 
			
		||||
  brokerageSecondPercent: 0,
 | 
			
		||||
@@ -211,10 +263,15 @@ const submitForm = async () => {
 | 
			
		||||
  // 提交请求
 | 
			
		||||
  formLoading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    const data = formData.value as unknown as ConfigApi.ConfigVO
 | 
			
		||||
    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 {
 | 
			
		||||
@@ -230,6 +287,9 @@ const getConfig = async () => {
 | 
			
		||||
    if (data != null) {
 | 
			
		||||
      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
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
      </template>
 | 
			
		||||
      {{ user.totalPoint || 0 }}
 | 
			
		||||
    </el-descriptions-item>
 | 
			
		||||
    <!-- TODO 芋艿:后续接入余额、支付金额 -->
 | 
			
		||||
    <!-- TODO @疯狂:从 wallet 读取下对应字段 -->
 | 
			
		||||
    <el-descriptions-item>
 | 
			
		||||
      <template #label>
 | 
			
		||||
        <descriptions-item-label label=" 当前余额 " icon="svg-icon:member_balance" />
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,7 @@
 | 
			
		||||
          <el-tab-pane label="成长值" name="experience" lazy>
 | 
			
		||||
            <UserExperienceRecordList :user-id="id"
 | 
			
		||||
          /></el-tab-pane>
 | 
			
		||||
          <!-- TODO @jason:增加一个余额变化; -->
 | 
			
		||||
          <el-tab-pane label="余额" name="fourth">余额(WIP)</el-tab-pane>
 | 
			
		||||
          <el-tab-pane label="收货地址" name="address" lazy>
 | 
			
		||||
            <UserAddressList :user-id="id" />
 | 
			
		||||
@@ -48,7 +49,9 @@
 | 
			
		||||
          </el-tab-pane>
 | 
			
		||||
          <el-tab-pane label="售后管理" name="fourth">售后管理(WIP)</el-tab-pane>
 | 
			
		||||
          <el-tab-pane label="收藏记录" name="fourth">收藏记录(WIP)</el-tab-pane>
 | 
			
		||||
          <!-- TODO @疯狂:优惠劵的读取 -->
 | 
			
		||||
          <el-tab-pane label="优惠劵" name="fourth">优惠劵(WIP)</el-tab-pane>
 | 
			
		||||
          <!-- TODO @疯狂:增加获得分校用户;直接查询出所有;需要体现出是一级还是二级;用户编号、昵称、级别、绑定时间 -->
 | 
			
		||||
        </el-tabs>
 | 
			
		||||
      </el-card>
 | 
			
		||||
    </el-row>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,6 +128,8 @@
 | 
			
		||||
          >
 | 
			
		||||
            编辑
 | 
			
		||||
          </el-button>
 | 
			
		||||
          <!-- todo @jason:增加一个【修改余额】 -->
 | 
			
		||||
          <!-- todo @疯狂:增加一个【修改积分】,表单是:radio 增加/减少;input 具体的变化积分 -->
 | 
			
		||||
          <!-- todo 放到更多菜单中 -->
 | 
			
		||||
          <el-button
 | 
			
		||||
            link
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user