|
@@ -67,6 +67,28 @@ interface DownLoadOption {
|
|
|
updatedAt: string
|
|
|
}
|
|
|
|
|
|
+interface EventOptionsItem {
|
|
|
+ keyId: string
|
|
|
+ optionId: string
|
|
|
+ optionName: string
|
|
|
+ optionType: string // 比如 "int", "string" 等
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 默认事件配置数据结构说明:
|
|
|
+ * - keyId: 唯一标识符(用于el-table的row-key)
|
|
|
+ * - actionId/actionName: 事件ID和名称(必填字段)
|
|
|
+ * - options: 关联选项数组,包含optionId/optionName/optionType
|
|
|
+ */
|
|
|
+interface DefaultEventData {
|
|
|
+ keyId: string
|
|
|
+ actionId: string
|
|
|
+ actionName: string
|
|
|
+ options: EventOptionsItem[] // 注意这里是数组类型
|
|
|
+ remark: string
|
|
|
+ status: number // 1 表示启用,其他值可能表示不同状态
|
|
|
+}
|
|
|
+
|
|
|
type GetTableReturn<T> = T extends 'all'
|
|
|
? {
|
|
|
allEventTable: Array<DownLoadEvent>
|
|
@@ -152,7 +174,7 @@ const configCopyInfo = ref<DialogConfig>({
|
|
|
name: 'gid',
|
|
|
cnName: '原游戏',
|
|
|
type: FormFieldType.SELECT,
|
|
|
- defaultValue: selectInfo.gid,
|
|
|
+
|
|
|
otherOptions: {
|
|
|
options: gameOptions,
|
|
|
searchSelected: true
|
|
@@ -162,7 +184,7 @@ const configCopyInfo = ref<DialogConfig>({
|
|
|
name: 'newGid',
|
|
|
cnName: '目标游戏',
|
|
|
type: FormFieldType.SELECT,
|
|
|
-
|
|
|
+ defaultValue: selectInfo.gid,
|
|
|
otherOptions: {
|
|
|
options: gameOptions,
|
|
|
searchSelected: true
|
|
@@ -171,6 +193,17 @@ const configCopyInfo = ref<DialogConfig>({
|
|
|
]
|
|
|
})
|
|
|
|
|
|
+// 控制默认事件配置对话框的显示状态
|
|
|
+const defaultEventDialogVisible = ref(false)
|
|
|
+// 存储默认事件配置数据
|
|
|
+const defaultEventsData = ref<DefaultEventData[]>([])
|
|
|
+// 选项类型映射表(字符串/整数/数组)
|
|
|
+const optionTypes = [
|
|
|
+ { name: 'string', label: '字符串', value: 'string' },
|
|
|
+ { name: 'int', label: '整数', value: 'int' },
|
|
|
+ { name: 'array', label: '数组', value: 'array' }
|
|
|
+]
|
|
|
+
|
|
|
/**
|
|
|
* 进入详情页,触发headerCard的添加事件,增加一个面包屑导航
|
|
|
* @param {*} info 传入的信息
|
|
@@ -279,9 +312,10 @@ const getTableData = async (
|
|
|
|
|
|
/**
|
|
|
* 批量请求选项数据
|
|
|
- * @param {*} url 请求的地址
|
|
|
- * @param {*} reqParams 请求的参数列表,主要是actionId
|
|
|
- * @param {*} eventTable 事件列表
|
|
|
+ * 通过事件列表的actionId生成请求参数,批量获取选项数据
|
|
|
+ * @param url 请求地址
|
|
|
+ * @param reqParams 请求参数列表(包含actionId)
|
|
|
+ * @param eventTable 事件列表数据
|
|
|
*/
|
|
|
const batReqOptionsData = async (
|
|
|
url: string,
|
|
@@ -473,16 +507,11 @@ const uploadOption = async (uploadOptionsInfo: { [key: string]: Array<UploadOpti
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 当文件添加后,开始进行上传前的处理
|
|
|
- * 首先需要上传事件,如果现有上传的列表中的actionId已经包含在了现有列表中,那么判定为更新,否则为新增
|
|
|
- *
|
|
|
- * 上传完成后,需要首先获取一次新的数据,然后再开始上传选项。
|
|
|
- *
|
|
|
- * 分别在上传和已有的选项列表中,找到对应的actionID的那一组选项,对这一组数据,如果已经存在对应的id,那么就是更新,否则为新增
|
|
|
- *
|
|
|
- * 然后上传,上传完毕后,整体列表刷新,关闭弹框
|
|
|
- *
|
|
|
- * @param data 上传的文件数据,里面包含allEventTable:所有的事件列表,allOptionsInfo:所有选项列表
|
|
|
+ * 处理文件上传成功后的逻辑
|
|
|
+ * 1. 区分新事件和旧事件分别执行新增/更新操作
|
|
|
+ * 2. 事件上传成功后继续上传关联选项
|
|
|
+ * 3. 最终刷新表格并关闭弹窗
|
|
|
+ * @param data 上传的文件数据,包含事件和选项列表
|
|
|
*/
|
|
|
const uploadSuccess = async (data: {
|
|
|
allEventTable: Array<uploadEvent>
|
|
@@ -519,28 +548,113 @@ const uploadSuccess = async (data: {
|
|
|
uploadRef.value?.uploadCallback()
|
|
|
eventTableRef.value?.updateData()
|
|
|
}
|
|
|
-
|
|
|
+/**
|
|
|
+ * 处理复制配置
|
|
|
+ */
|
|
|
const handleCopyConfig = () => {
|
|
|
copyConfigDialogRef.value?.addForm()
|
|
|
}
|
|
|
|
|
|
-const defaultEventDialogVisible = ref(false)
|
|
|
+/**
|
|
|
+ * 提交单个事件配置到后端接口
|
|
|
+ * 1. 添加游戏ID(gid)并清理冗余字段
|
|
|
+ * 2. 通过 Axios 发送 POST 请求
|
|
|
+ * @param data 事件配置数据(包含事件基础信息和关联选项)
|
|
|
+ * @returns boolean 表示请求是否成功
|
|
|
+ */
|
|
|
+const submitEventConfig = async (data: DefaultEventData) => {
|
|
|
+ const url = AllApi.setGameAction
|
|
|
+ const result = { ...data }
|
|
|
+ delete (result as any).keyIdl
|
|
|
+ ;(result as any).gid = selectInfo.gid
|
|
|
+ const res = (await axiosInstance.post(url, result)) as ResponseInfo
|
|
|
+ return res.code === 0
|
|
|
+}
|
|
|
|
|
|
-const events = [
|
|
|
- { id: '1', name: '登录事件', enabled: true },
|
|
|
- { id: '2', name: '注册事件', enabled: false }
|
|
|
-]
|
|
|
+/**
|
|
|
+ * 表单提交验证逻辑
|
|
|
+ * 1. 检查启用状态下的事件是否填写完整信息
|
|
|
+ * 2. 并行提交所有事件配置
|
|
|
+ * 3. 统计失败数量并反馈结果
|
|
|
+ */
|
|
|
+const submitEventInfo = async () => {
|
|
|
+ try {
|
|
|
+ const pickedEvents = defaultEventsData.value.filter((item) => item.status === 1)
|
|
|
+
|
|
|
+ const hasEmpty = pickedEvents.some((item) => {
|
|
|
+ const eventConfigHasEmpty = item.actionId === '' || item.actionName === ''
|
|
|
+ const optionHasEmpty = item.options.some(
|
|
|
+ (child) => child.optionName === '' || child.optionId === ''
|
|
|
+ )
|
|
|
+ return eventConfigHasEmpty || optionHasEmpty
|
|
|
+ })
|
|
|
+ if (hasEmpty) {
|
|
|
+ ElMessage.warning('请为所有启用事件填写完整信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const submitQueue: Promise<boolean>[] = []
|
|
|
+ pickedEvents.forEach((item) => {
|
|
|
+ submitQueue.push(submitEventConfig(item))
|
|
|
+ })
|
|
|
+ const res = await Promise.allSettled(submitQueue)
|
|
|
+ // 统计失败数量
|
|
|
+ const failedCount = res.filter((result) => result.status === 'rejected').length
|
|
|
+ if (failedCount > 0) {
|
|
|
+ ElMessage.error(`有 ${failedCount} 个事件配置失败`)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ElMessage.success('事件配置成功')
|
|
|
+ defaultEventDialogVisible.value = false
|
|
|
+ eventTableRef.value?.updateData()
|
|
|
+ } catch (err) {
|
|
|
+ ElMessage.error('部分事件配置失败')
|
|
|
+ console.error(err)
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-const submitEventInfo = () => {
|
|
|
- console.log(events.filter((item) => item.enabled))
|
|
|
- const hasEmpty = events
|
|
|
- .filter((item) => item.enabled)
|
|
|
- .some((item) => item.id === '' || item.name === '')
|
|
|
- if (hasEmpty) {
|
|
|
- ElMessage.warning('请为所有启用事件填写完整信息')
|
|
|
- return
|
|
|
+/**
|
|
|
+ * 导入默认事件配置
|
|
|
+ * 1. 调用接口获取预设事件模板数据
|
|
|
+ * 2. 为事件及选项生成唯一标识符 keyId
|
|
|
+ * 3. 展示事件配置对话框
|
|
|
+ */
|
|
|
+const handleImportDefaultEvent = async () => {
|
|
|
+ try {
|
|
|
+ const res = (await axiosInstance.post(AllApi.getDefaultEventConfig, {
|
|
|
+ gid: selectInfo.gid
|
|
|
+ })) as ResponseInfo
|
|
|
+ if (res.code !== 0) return false
|
|
|
+ defaultEventsData.value = res.data as DefaultEventData[]
|
|
|
+ // 给每行添加keyId
|
|
|
+ defaultEventsData.value.map((item) => {
|
|
|
+ item.keyId = createRowKey(item)
|
|
|
+ item.options.map((child) => {
|
|
|
+ child.keyId = createRowKey(child)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ defaultEventDialogVisible.value = true
|
|
|
+ console.log(defaultEventsData.value)
|
|
|
+ } catch (err) {
|
|
|
+ console.error(err)
|
|
|
+ ElMessage.error('获取默认配置失败,请稍后重试')
|
|
|
+ defaultEventDialogVisible.value = false
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建row-key
|
|
|
+ */
|
|
|
+const createRowKey = (row: any) => {
|
|
|
+ return row.actionId || row.optionId || `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取rowKey
|
|
|
+ * @param row
|
|
|
+ */
|
|
|
+const getRowKey = (row: any) => {
|
|
|
+ return row.keyId
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
@@ -556,11 +670,9 @@ const submitEventInfo = () => {
|
|
|
|
|
|
<div class="handleEvent" v-if="nowRouteName === 'EventTable'">
|
|
|
<div class="fileGroup">
|
|
|
- <el-button class="fileBtn" @click="handleCopyConfig">复制</el-button>
|
|
|
<!-- TODO 完善-->
|
|
|
- <el-button class="fileBtn" @click="defaultEventDialogVisible = true" v-if="false"
|
|
|
- >导入默认配置
|
|
|
- </el-button>
|
|
|
+ <el-button class="fileBtn" color="#626aef" @click="handleCopyConfig">复制</el-button>
|
|
|
+ <el-button class="fileBtn" @click="handleImportDefaultEvent">导入默认配置 </el-button>
|
|
|
<el-button class="fileBtn" color="#626aef" @click="startUpload">上传</el-button>
|
|
|
<el-button class="fileBtn" @click="throttleStartDownload">下载</el-button>
|
|
|
</div>
|
|
@@ -595,25 +707,59 @@ const submitEventInfo = () => {
|
|
|
</div>
|
|
|
<div>
|
|
|
<el-dialog v-model="defaultEventDialogVisible" title="事件配置">
|
|
|
- <el-table :data="events" style="width: 100%" table-layout="auto">
|
|
|
+ <el-table
|
|
|
+ :data="defaultEventsData"
|
|
|
+ style="width: 100%"
|
|
|
+ table-layout="auto"
|
|
|
+ :row-key="getRowKey"
|
|
|
+ >
|
|
|
+ <el-table-column type="expand">
|
|
|
+ <template #default="{ row: eventsRows }">
|
|
|
+ <el-table :data="eventsRows.options" :row-key="getRowKey">
|
|
|
+ <el-table-column prop="optionId" label="选项ID">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input v-model="row.optionId" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="optionName" label="选项名">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input v-model="row.optionName" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="optionType" label="选项类型">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-select v-model="row.optionType" placeholder="Select" size="large">
|
|
|
+ <el-option
|
|
|
+ v-for="item in optionTypes"
|
|
|
+ :key="item.name"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
<!-- 是否启用列 -->
|
|
|
- <el-table-column prop="enabled" label="是否启用" align="center">
|
|
|
+ <el-table-column prop="status" label="是否启用" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <el-checkbox v-model="row.enabled" />
|
|
|
+ <el-checkbox v-model="row.status" :true-value="1" :false-value="0" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
<!-- 事件ID列 -->
|
|
|
- <el-table-column align="center" prop="id" label="事件ID">
|
|
|
+ <el-table-column align="center" prop="actionId" label="事件ID">
|
|
|
<template #default="{ row }">
|
|
|
- <el-input v-model="row.id" />
|
|
|
+ <el-input v-model="row.actionId" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
<!-- 事件名称列 -->
|
|
|
- <el-table-column align="center" prop="name" label="事件名称">
|
|
|
+ <el-table-column align="center" prop="actionName" label="事件名称">
|
|
|
<template #default="{ row }">
|
|
|
- <el-input v-model="row.name" placeholder="事件名称" />
|
|
|
+ <el-input v-model="row.actionName" placeholder="事件名称" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|