|
|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <Dialog :title="dialogTitle" v-model="dialogVisible">
|
|
|
+ <Dialog :title="dialogTitle" v-model="dialogVisible" width="900px">
|
|
|
<el-form
|
|
|
ref="formRef"
|
|
|
:model="formData"
|
|
|
@@ -10,71 +10,115 @@
|
|
|
<el-form-item label="名称" prop="name">
|
|
|
<el-input v-model="formData.name" placeholder="请输入名称" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="简介" prop="info">
|
|
|
+ <el-form-item label="简介" prop="info" width="100%">
|
|
|
<el-input v-model="formData.info" placeholder="请输入简介" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="人物图标" prop="userImage">
|
|
|
- <UploadImg v-model="formData.userImage" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="地图背景图" prop="mapBackground">
|
|
|
- <UploadImg v-model="formData.mapBackground" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="地图方格尺寸" prop="mapTileSize">
|
|
|
- <el-input-number v-model="formData.mapTileSize" :min="10" :max="200">
|
|
|
- <template #suffix>
|
|
|
- <span>px</span>
|
|
|
- </template>
|
|
|
- </el-input-number>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="人物朝向" prop="userDirection">
|
|
|
- <el-radio-group v-model="formData.userDirection" size="large" fill="#6cf">
|
|
|
- <el-radio-button label="上" value=0 />
|
|
|
- <el-radio-button label="右" value=1 />
|
|
|
- <el-radio-button label="下" value=2 />
|
|
|
- <el-radio-button label="左" value=3 />
|
|
|
- </el-radio-group>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="地图开始坐标" prop="mapStartPoint">
|
|
|
- <el-input v-model="formData.mapStartPoint" placeholder="请输入地图开始坐标" />
|
|
|
+
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="人物图标" prop="userImage">
|
|
|
+ <UploadImg v-model="formData.userImage" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="地图背景图" prop="mapBackground">
|
|
|
+ <UploadImg v-model="formData.mapBackground" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="人物朝向" prop="userDirection">
|
|
|
+ <el-radio-group v-model="formData.userDirection" size="large" >
|
|
|
+ <el-radio-button label="上" :value="0" />
|
|
|
+ <el-radio-button label="右" :value="1" />
|
|
|
+ <el-radio-button label="下" :value="2" />
|
|
|
+ <el-radio-button label="左" :value="3" />
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="地图方格尺寸" prop="mapTileSize">
|
|
|
+ <el-input-number v-model="formData.mapTileSize" :min="10" :max="200">
|
|
|
+ <template #suffix>
|
|
|
+ <span>px</span>
|
|
|
+ </template>
|
|
|
+ </el-input-number>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 地图开始坐标 - 拆分为X轴和Y轴 -->
|
|
|
+ <el-form-item label="地图开始坐标" required>
|
|
|
+ <div class="coordinate-group">
|
|
|
+ X:
|
|
|
+ <el-input-number v-model="mapStartPointX" placeholder="X轴" :min="1" style="width: 120px;" />
|
|
|
+ Y:
|
|
|
+ <el-input-number v-model="mapStartPointY" placeholder="Y轴" :min="1" style="width: 120px;" />
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="地图结束坐标" prop="mapEndPoint">
|
|
|
- <el-input v-model="formData.mapEndPoint" placeholder="请输入地图结束坐标" />
|
|
|
+
|
|
|
+ <!-- 地图结束坐标 - 拆分为X轴和Y轴 -->
|
|
|
+ <el-form-item label="地图结束坐标" required>
|
|
|
+ <div class="coordinate-group">
|
|
|
+ X:
|
|
|
+ <el-input-number v-model="mapEndPointX" placeholder="X轴" :min="1" style="width: 120px;" />
|
|
|
+ Y:
|
|
|
+ <el-input-number v-model="mapEndPointY" placeholder="Y轴" :min="1" style="width: 120px;" />
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="地图可行走坐标" prop="mapWalkablePoints">
|
|
|
- <el-input
|
|
|
- v-model="formData.mapWalkablePoints"
|
|
|
- type="textarea"
|
|
|
- placeholder="请输入地图可行走坐标"
|
|
|
- :rows="6"
|
|
|
- show-word-limit
|
|
|
- />
|
|
|
+
|
|
|
+ <!-- 地图可行走坐标 - 动态添加 -->
|
|
|
+ <el-form-item label="地图可行走坐标" required>
|
|
|
+ <div class="walkable-points-container">
|
|
|
+ <div v-for="(point, index) in walkablePoints" :key="index" class="walkable-point-item">
|
|
|
+ X:
|
|
|
+ <el-input-number v-model="point.x" placeholder="X轴" :min="1" style="width: 100px;" />
|
|
|
+ Y:
|
|
|
+ <el-input-number v-model="point.y" placeholder="Y轴" :min="1" style="width: 100px;" />
|
|
|
+ <el-select v-model="point.type" placeholder="类型" style="width: 120px;" clearable>
|
|
|
+ <el-option v-for="dict in getIntDictOptions(DICT_TYPE.AI_BLOCKLY_MAP_TYPE)" :key="String(dict.value)" :label="dict.label" :value="dict.value" />
|
|
|
+ </el-select>
|
|
|
+ <el-input v-model="point.tip" placeholder="提示语" style="flex: 1; min-width: 200px;" />
|
|
|
+ <el-button @click="removeWalkablePoint(index)" type="danger" circle :disabled="walkablePoints.length <= 1" >
|
|
|
+ <Icon icon="ep:delete" /></el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-button @click="addWalkablePoint" type="primary" style="margin-top: 10px;">+ 添加坐标</el-button>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="json数据" prop="jsonData">
|
|
|
+
|
|
|
+<!-- <el-form-item label="json数据" prop="jsonData">
|
|
|
<el-input
|
|
|
v-model="formData.jsonData"
|
|
|
type="textarea"
|
|
|
placeholder="请输入JSON格式数据"
|
|
|
- :rows="6"
|
|
|
+ :rows="3"
|
|
|
show-word-limit
|
|
|
/>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="排序" prop="sort">
|
|
|
- <el-input-number v-model="formData.sort" :step="1" :min="1" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="状态" prop="status">
|
|
|
- <el-select
|
|
|
- v-model="formData.status"
|
|
|
- placeholder="请选择状态"
|
|
|
- clearable
|
|
|
- >
|
|
|
- <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-row>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="排序" prop="sort">
|
|
|
+ <el-input-number v-model="formData.sort" :step="1" :min="1" style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="状态" prop="status">
|
|
|
+ <el-select
|
|
|
+ v-model="formData.status"
|
|
|
+ placeholder="请选择状态"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <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-col>
|
|
|
+ </el-row>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
|
|
@@ -101,7 +145,7 @@ const formData = ref({
|
|
|
name: undefined,
|
|
|
info: undefined,
|
|
|
userImage: "https://learn-ai.com.cn/admin-api/infra/file/29/get/20251107/user_1762504554550.png",
|
|
|
- userDirection: undefined,
|
|
|
+ userDirection: 0,
|
|
|
mapTileSize: undefined,
|
|
|
mapStartPoint: undefined,
|
|
|
mapBackground: undefined,
|
|
|
@@ -111,15 +155,32 @@ const formData = ref({
|
|
|
sort: undefined,
|
|
|
status: undefined
|
|
|
})
|
|
|
+
|
|
|
+// 新增的响应式数据
|
|
|
+const mapStartPointX = ref<number>()
|
|
|
+const mapStartPointY = ref<number>()
|
|
|
+const mapEndPointX = ref<number>()
|
|
|
+const mapEndPointY = ref<number>()
|
|
|
+
|
|
|
+// 可行走点接口定义
|
|
|
+interface WalkablePoint {
|
|
|
+ x: number
|
|
|
+ y: number
|
|
|
+ type: string | number
|
|
|
+ tip: string
|
|
|
+}
|
|
|
+
|
|
|
+// 可行走点数组
|
|
|
+const walkablePoints = ref<WalkablePoint[]>([{ x: 0, y: 0, type: "", tip: "" }])
|
|
|
+
|
|
|
const formRules = reactive({
|
|
|
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
|
|
|
userImage: [{ required: true, message: '人物图标不能为空', trigger: 'blur' }],
|
|
|
mapBackground: [{ required: true, message: '地图背景图不能为空', trigger: 'blur' }],
|
|
|
mapTileSize: [{ required: true, message: '地图方格尺寸不能为空', trigger: 'blur' }],
|
|
|
userDirection: [{ required: true, message: '人物朝向不能为空', trigger: 'blur' }],
|
|
|
- mapStartPoint: [{ required: true, message: '地图开始坐标不能为空', trigger: 'blur' }],
|
|
|
- mapEndPoint: [{ required: true, message: '地图结束坐标不能为空', trigger: 'blur' }],
|
|
|
- mapWalkablePoints: [{ required: true, message: '地图可行走坐标不能为空', trigger: 'blur' }],
|
|
|
+ // 注意:这里移除了对mapStartPoint、mapEndPoint和mapWalkablePoints的直接验证
|
|
|
+ // 因为这些值现在是通过计算得到的
|
|
|
})
|
|
|
const formRef = ref() // 表单 Ref
|
|
|
|
|
|
@@ -133,7 +194,38 @@ const open = async (type: string, id?: number) => {
|
|
|
if (id) {
|
|
|
formLoading.value = true
|
|
|
try {
|
|
|
- formData.value = await MapGameApi.getMapGame(id)
|
|
|
+ const data = await MapGameApi.getMapGame(id)
|
|
|
+ formData.value = data
|
|
|
+
|
|
|
+ // 处理回显数据 - 解析坐标JSON
|
|
|
+ if (data.mapStartPoint) {
|
|
|
+ try {
|
|
|
+ const point = JSON.parse(data.mapStartPoint)
|
|
|
+ mapStartPointX.value = point.x
|
|
|
+ mapStartPointY.value = point.y
|
|
|
+ } catch (e) {
|
|
|
+ console.error('解析地图开始坐标失败:', e)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data.mapEndPoint) {
|
|
|
+ try {
|
|
|
+ const point = JSON.parse(data.mapEndPoint)
|
|
|
+ mapEndPointX.value = point.x
|
|
|
+ mapEndPointY.value = point.y
|
|
|
+ } catch (e) {
|
|
|
+ console.error('解析地图结束坐标失败:', e)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data.mapWalkablePoints) {
|
|
|
+ try {
|
|
|
+ walkablePoints.value = JSON.parse(data.mapWalkablePoints)
|
|
|
+ } catch (e) {
|
|
|
+ console.error('解析地图可行走坐标失败:', e)
|
|
|
+ walkablePoints.value = [{ x: 0, y: 0, type: "", tip: "" }]
|
|
|
+ }
|
|
|
+ }
|
|
|
} finally {
|
|
|
formLoading.value = false
|
|
|
}
|
|
|
@@ -141,20 +233,61 @@ const open = async (type: string, id?: number) => {
|
|
|
}
|
|
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
|
|
|
|
+/** 添加可行走点 */
|
|
|
+const addWalkablePoint = () => {
|
|
|
+ walkablePoints.value.push({ x: 0, y: 0, type: "", tip: "" })
|
|
|
+}
|
|
|
+
|
|
|
+/** 移除可行走点 */
|
|
|
+const removeWalkablePoint = (index: number) => {
|
|
|
+ if (walkablePoints.value.length > 1) {
|
|
|
+ walkablePoints.value.splice(index, 1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/** 提交表单 */
|
|
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
|
const submitForm = async () => {
|
|
|
- // 校验表单
|
|
|
+ // 坐标数据验证
|
|
|
+ if (mapStartPointX.value === undefined || mapStartPointY.value === undefined) {
|
|
|
+ message.error('地图开始坐标不能为空')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mapEndPointX.value === undefined || mapEndPointY.value === undefined) {
|
|
|
+ message.error('地图结束坐标不能为空')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查可行走点数据
|
|
|
+ const hasInvalidPoint = walkablePoints.value.some(point =>
|
|
|
+ point.x === undefined || point.y === undefined
|
|
|
+ )
|
|
|
+
|
|
|
+ if (hasInvalidPoint) {
|
|
|
+ message.error('请完善所有可行走点的数据')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 表单基础验证
|
|
|
await formRef.value.validate()
|
|
|
+
|
|
|
+ // 准备提交数据 - 转换坐标为JSON格式
|
|
|
+ const submitData = { ...formData.value } as MapGameVO
|
|
|
+
|
|
|
+ // 设置坐标JSON
|
|
|
+ submitData.mapStartPoint = JSON.stringify({ x: mapStartPointX.value, y: mapStartPointY.value })
|
|
|
+ submitData.mapEndPoint = JSON.stringify({ x: mapEndPointX.value, y: mapEndPointY.value })
|
|
|
+ submitData.mapWalkablePoints = JSON.stringify(walkablePoints.value)
|
|
|
+
|
|
|
// 提交请求
|
|
|
formLoading.value = true
|
|
|
try {
|
|
|
- const data = formData.value as unknown as MapGameVO
|
|
|
if (formType.value === 'create') {
|
|
|
- await MapGameApi.createMapGame(data)
|
|
|
+ await MapGameApi.createMapGame(submitData)
|
|
|
message.success(t('common.createSuccess'))
|
|
|
} else {
|
|
|
- await MapGameApi.updateMapGame(data)
|
|
|
+ await MapGameApi.updateMapGame(submitData)
|
|
|
message.success(t('common.updateSuccess'))
|
|
|
}
|
|
|
dialogVisible.value = false
|
|
|
@@ -172,7 +305,7 @@ const resetForm = () => {
|
|
|
name: undefined,
|
|
|
info: undefined,
|
|
|
userImage: "https://learn-ai.com.cn/admin-api/infra/file/29/get/20251107/user_1762504554550.png",
|
|
|
- userDirection: undefined,
|
|
|
+ userDirection: 0,
|
|
|
mapTileSize: undefined,
|
|
|
mapStartPoint: undefined,
|
|
|
mapBackground: undefined,
|
|
|
@@ -182,6 +315,53 @@ const resetForm = () => {
|
|
|
sort: undefined,
|
|
|
status: undefined
|
|
|
}
|
|
|
+
|
|
|
+ // 重置新增的响应式数据
|
|
|
+ mapStartPointX.value = undefined
|
|
|
+ mapStartPointY.value = undefined
|
|
|
+ mapEndPointX.value = undefined
|
|
|
+ mapEndPointY.value = undefined
|
|
|
+ walkablePoints.value = [{ x: 0, y: 0, type: "", tip: "" }]
|
|
|
+
|
|
|
formRef.value?.resetFields()
|
|
|
}
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 坐标输入框样式 */
|
|
|
+.coordinate-group {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.coordinate-separator {
|
|
|
+ font-size: 16px;
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+
|
|
|
+/* 可行走点样式 */
|
|
|
+.walkable-points-container {
|
|
|
+ max-height: 400px;
|
|
|
+ overflow-y: auto;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 15px;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+}
|
|
|
+
|
|
|
+.walkable-point-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+}
|
|
|
+
|
|
|
+.walkable-point-item:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+</style>
|