Browse Source

星球农场的处理

lalalashen 2 months ago
parent
commit
48a1161957

+ 147 - 0
components/CustomDialog/CustomDialog.vue

@@ -0,0 +1,147 @@
+<template>
+  <view class="custom-dialog" v-if="visible">
+    <view class="dialog-mask" @click="closeDialog"></view>
+    <view class="dialog-content" :class="{'dialog-show': visible}" :animation="animationData">
+      <view class="dialog-header">
+        <text class="dialog-title">{{title}}</text>
+        <view class="dialog-close" @click="closeDialog">×</view>
+      </view>
+      <view class="dialog-body">
+        <slot></slot>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'CustomDialog',
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    title: {
+      type: String,
+      default: '提示'
+    }
+  },
+  data() {
+    return {
+      animationData: {}
+    }
+  },
+  watch: {
+    visible(val) {
+      if (val) {
+        this.playShowAnimation()
+      } else {
+        this.playHideAnimation()
+      }
+    }
+  },
+  methods: {
+    playShowAnimation() {
+      const animation = uni.createAnimation({
+        duration: 300,
+        timingFunction: 'ease'
+      })
+      
+      animation.scale(0.8).opacity(0).step({ duration: 0 })
+      animation.scale(1.1).opacity(1).step({ duration: 150 })
+      animation.scale(0.95).step({ duration: 75 })
+      animation.scale(1).step({ duration: 75 })
+      
+      this.animationData = animation.export()
+    },
+    playHideAnimation() {
+      const animation = uni.createAnimation({
+        duration: 200,
+        timingFunction: 'ease'
+      })
+      
+      animation.scale(0.95).opacity(0.8).step({ duration: 100 })
+      animation.scale(0.8).opacity(0).step({ duration: 100 })
+      
+      this.animationData = animation.export()
+    },
+    closeDialog() {
+      this.$emit('update:visible', false)
+      this.$emit('close')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.custom-dialog {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 9999;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  
+  .dialog-mask {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: rgba(0, 0, 0, 0.6);
+  }
+  
+  .dialog-content {
+    position: relative;
+    width: 600rpx;
+    background: #fff;
+    border-radius: 16rpx;
+    overflow: hidden;
+    opacity: 0;
+    transform: scale(0.8);
+    
+    &.dialog-show {
+      opacity: 1;
+      transform: scale(1);
+    }
+  }
+  
+  .dialog-header {
+    position: relative;
+    padding: 30rpx;
+    text-align: center;
+    border-bottom: 1rpx solid #eee;
+    
+    .dialog-title {
+      font-size: 32rpx;
+      font-weight: 500;
+      color: #333;
+    }
+    
+    .dialog-close {
+      position: absolute;
+      right: 20rpx;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 60rpx;
+      height: 60rpx;
+      line-height: 60rpx;
+      text-align: center;
+      font-size: 40rpx;
+      color: #999;
+      cursor: pointer;
+      
+      &:active {
+        opacity: 0.7;
+      }
+    }
+  }
+  
+  .dialog-body {
+    padding: 30rpx;
+  }
+}
+</style> 

+ 174 - 0
components/dialogs/CharacterDialog.vue

@@ -0,0 +1,174 @@
+<template>
+  <custom-dialog :visible.sync="dialogVisible" title="角色信息" @close="onClose">
+    <view class="character-content">
+      <view class="character-info">
+        <view class="avatar-section">
+          <image class="avatar" src="/static/avatar/default.png" mode="aspectFit"></image>
+          <text class="level">Lv.{{level}}</text>
+        </view>
+        <view class="info-section">
+          <view class="info-item">
+            <text class="label">名称:</text>
+            <text class="value">{{name}}</text>
+          </view>
+          <view class="info-item">
+            <text class="label">经验:</text>
+            <text class="value">{{exp}}/{{nextLevelExp}}</text>
+          </view>
+          <view class="progress-bar">
+            <view class="progress" :style="{width: expProgress + '%'}"></view>
+          </view>
+        </view>
+      </view>
+      <view class="stats-section">
+        <view class="stat-item" v-for="(stat, index) in stats" :key="index">
+          <text class="stat-label">{{stat.label}}</text>
+          <text class="stat-value">{{stat.value}}</text>
+        </view>
+      </view>
+    </view>
+  </custom-dialog>
+</template>
+
+<script>
+import CustomDialog from '../CustomDialog/CustomDialog.vue'
+
+export default {
+  name: 'CharacterDialog',
+  components: {
+    CustomDialog
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      name: '玩家名称',
+      level: 1,
+      exp: 100,
+      nextLevelExp: 200,
+      stats: [
+        { label: '力量', value: 10 },
+        { label: '敏捷', value: 8 },
+        { label: '智力', value: 12 },
+        { label: '体力', value: 15 }
+      ]
+    }
+  },
+  computed: {
+    expProgress() {
+      return (this.exp / this.nextLevelExp) * 100
+    }
+  },
+  watch: {
+    visible(val) {
+      this.dialogVisible = val
+    },
+    dialogVisible(val) {
+      this.$emit('update:visible', val)
+    }
+  },
+  methods: {
+    onClose() {
+      this.dialogVisible = false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.character-content {
+  .character-info {
+    display: flex;
+    padding: 20rpx;
+    gap: 30rpx;
+
+    .avatar-section {
+      position: relative;
+      
+      .avatar {
+        width: 160rpx;
+        height: 160rpx;
+        border-radius: 80rpx;
+        border: 4rpx solid #eee;
+      }
+
+      .level {
+        position: absolute;
+        bottom: -10rpx;
+        left: 50%;
+        transform: translateX(-50%);
+        background: #007AFF;
+        color: white;
+        padding: 4rpx 16rpx;
+        border-radius: 20rpx;
+        font-size: 24rpx;
+      }
+    }
+
+    .info-section {
+      flex: 1;
+
+      .info-item {
+        margin-bottom: 16rpx;
+        
+        .label {
+          color: #666;
+          font-size: 28rpx;
+        }
+        
+        .value {
+          color: #333;
+          font-size: 28rpx;
+        }
+      }
+
+      .progress-bar {
+        height: 16rpx;
+        background: #eee;
+        border-radius: 8rpx;
+        overflow: hidden;
+        margin-top: 10rpx;
+
+        .progress {
+          height: 100%;
+          background: #007AFF;
+          transition: width 0.3s ease;
+        }
+      }
+    }
+  }
+
+  .stats-section {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 20rpx;
+    padding: 20rpx;
+    background: #f8f8f8;
+    border-radius: 12rpx;
+    margin: 20rpx;
+
+    .stat-item {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 10rpx 20rpx;
+
+      .stat-label {
+        color: #666;
+        font-size: 28rpx;
+      }
+
+      .stat-value {
+        color: #333;
+        font-size: 28rpx;
+        font-weight: 500;
+      }
+    }
+  }
+}
+</style> 

+ 100 - 0
components/dialogs/InventoryDialog.vue

@@ -0,0 +1,100 @@
+<template>
+  <custom-dialog :visible.sync="dialogVisible" title="背包" @close="onClose">
+    <view class="inventory-content">
+      <view class="inventory-grid">
+        <view class="inventory-slot" v-for="(item, index) in items" :key="index">
+          <image v-if="item" :src="item.icon" mode="aspectFit" class="item-icon"></image>
+          <text v-if="item && item.count > 1" class="item-count">{{item.count}}</text>
+        </view>
+      </view>
+    </view>
+  </custom-dialog>
+</template>
+
+<script>
+import CustomDialog from '../CustomDialog/CustomDialog.vue'
+
+export default {
+  name: 'InventoryDialog',
+  components: {
+    CustomDialog
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      items: [
+        { icon: '/static/items/item1.png', count: 1 },
+        { icon: '/static/items/item2.png', count: 5 },
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null
+      ]
+    }
+  },
+  watch: {
+    visible(val) {
+      this.dialogVisible = val
+    },
+    dialogVisible(val) {
+      this.$emit('update:visible', val)
+    }
+  },
+  methods: {
+    onClose() {
+      this.dialogVisible = false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.inventory-content {
+  .inventory-grid {
+    display: grid;
+    grid-template-columns: repeat(4, 1fr);
+    gap: 20rpx;
+    padding: 20rpx;
+  }
+
+  .inventory-slot {
+    position: relative;
+    width: 120rpx;
+    height: 120rpx;
+    background: #f5f5f5;
+    border-radius: 8rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border: 2rpx solid #eee;
+
+    .item-icon {
+      width: 80rpx;
+      height: 80rpx;
+    }
+
+    .item-count {
+      position: absolute;
+      right: 4rpx;
+      bottom: 4rpx;
+      font-size: 24rpx;
+      color: #666;
+      background: rgba(255, 255, 255, 0.8);
+      padding: 2rpx 6rpx;
+      border-radius: 4rpx;
+    }
+  }
+}
+</style> 

+ 174 - 0
components/dialogs/ShopDialog.vue

@@ -0,0 +1,174 @@
+<template>
+  <custom-dialog :visible.sync="dialogVisible" title="商店" @close="onClose">
+    <view class="shop-content">
+      <view class="shop-tabs">
+        <view 
+          v-for="(tab, index) in tabs" 
+          :key="index"
+          class="tab-item"
+          :class="{'active': currentTab === index}"
+          @click="currentTab = index"
+        >
+          {{tab}}
+        </view>
+      </view>
+      <view class="shop-items">
+        <view class="shop-item" v-for="(item, index) in currentItems" :key="index" @click="onItemClick(item)">
+          <image class="item-icon" :src="item.icon" mode="aspectFit"></image>
+          <view class="item-info">
+            <text class="item-name">{{item.name}}</text>
+            <text class="item-desc">{{item.description}}</text>
+          </view>
+          <view class="item-price">
+            <image class="currency-icon" src="/static/currency/coin.png" mode="aspectFit"></image>
+            <text>{{item.price}}</text>
+          </view>
+        </view>
+      </view>
+    </view>
+  </custom-dialog>
+</template>
+
+<script>
+import CustomDialog from '../CustomDialog/CustomDialog.vue'
+
+export default {
+  name: 'ShopDialog',
+  components: {
+    CustomDialog
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      currentTab: 0,
+      tabs: ['道具', '装备', '材料'],
+      items: {
+        0: [
+          { icon: '/static/items/potion1.png', name: '生命药水', description: '恢复100点生命值', price: 100 },
+          { icon: '/static/items/potion2.png', name: '魔法药水', description: '恢复50点魔法值', price: 150 },
+          { icon: '/static/items/potion3.png', name: '体力药水', description: '恢复50点体力值', price: 120 }
+        ],
+        1: [
+          { icon: '/static/items/weapon1.png', name: '铁剑', description: '攻击力+10', price: 500 },
+          { icon: '/static/items/armor1.png', name: '皮甲', description: '防御力+5', price: 300 }
+        ],
+        2: [
+          { icon: '/static/items/material1.png', name: '木材', description: '基础材料', price: 50 },
+          { icon: '/static/items/material2.png', name: '铁矿', description: '基础材料', price: 80 }
+        ]
+      }
+    }
+  },
+  computed: {
+    currentItems() {
+      return this.items[this.currentTab]
+    }
+  },
+  watch: {
+    visible(val) {
+      this.dialogVisible = val
+    },
+    dialogVisible(val) {
+      this.$emit('update:visible', val)
+    }
+  },
+  methods: {
+    onClose() {
+      this.dialogVisible = false
+    },
+    onItemClick(item) {
+      // 处理购买逻辑
+      this.$emit('buy', item)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.shop-content {
+  .shop-tabs {
+    display: flex;
+    border-bottom: 2rpx solid #eee;
+    margin-bottom: 20rpx;
+
+    .tab-item {
+      flex: 1;
+      text-align: center;
+      padding: 20rpx 0;
+      font-size: 28rpx;
+      color: #666;
+      position: relative;
+
+      &.active {
+        color: #007AFF;
+        font-weight: 500;
+
+        &::after {
+          content: '';
+          position: absolute;
+          bottom: 0;
+          left: 50%;
+          transform: translateX(-50%);
+          width: 40rpx;
+          height: 4rpx;
+          background: #007AFF;
+          border-radius: 2rpx;
+        }
+      }
+    }
+  }
+
+  .shop-items {
+    .shop-item {
+      display: flex;
+      align-items: center;
+      padding: 20rpx;
+      border-bottom: 2rpx solid #f5f5f5;
+
+      .item-icon {
+        width: 80rpx;
+        height: 80rpx;
+        margin-right: 20rpx;
+      }
+
+      .item-info {
+        flex: 1;
+
+        .item-name {
+          font-size: 28rpx;
+          color: #333;
+          margin-bottom: 4rpx;
+        }
+
+        .item-desc {
+          font-size: 24rpx;
+          color: #999;
+        }
+      }
+
+      .item-price {
+        display: flex;
+        align-items: center;
+        gap: 8rpx;
+
+        .currency-icon {
+          width: 32rpx;
+          height: 32rpx;
+        }
+
+        text {
+          font-size: 28rpx;
+          color: #FF9500;
+          font-weight: 500;
+        }
+      }
+    }
+  }
+}
+</style> 

+ 5 - 0
pages.json

@@ -319,6 +319,11 @@
 			"style": {
 				"navigationBarTitleText": "主大陆"
 			}
+		}, {
+			"path": "pages/isLand/homeLand",
+			"style": {
+				"navigationBarTitleText": "主岛"
+			}
 		}
 	],
 	"globalStyle": {

+ 125 - 0
pages/isLand/FarmDialog.scss

@@ -0,0 +1,125 @@
+.farm-container {
+  padding: 20rpx;
+  display: flex;
+  flex-direction: column;
+  gap: 20rpx;
+}
+
+.farm-grid {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  grid-template-rows: repeat(3, 1fr);
+  gap: 10rpx;
+  aspect-ratio: 4/3;
+  background-color: #8B4513;
+  padding: 20rpx;
+  border-radius: 10rpx;
+}
+
+.farm-plot {
+  aspect-ratio: 1;
+  background-color: #654321;
+  border-radius: 8rpx;
+  position: relative;
+  cursor: pointer;
+  transition: transform 0.2s;
+
+  &:hover {
+    transform: scale(1.05);
+  }
+
+  .plot-content {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: relative;
+
+    &.needs-water::after {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      background: rgba(0, 0, 255, 0.2);
+      border-radius: 8rpx;
+    }
+
+    &.has-weeds::before {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      background: url('/static/weeds.png') center/cover;
+      opacity: 0.5;
+      border-radius: 8rpx;
+    }
+  }
+
+  .crop-stage {
+    width: 80%;
+    height: 80%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .crop-image {
+      width: 100%;
+      height: 100%;
+      object-fit: contain;
+    }
+  }
+}
+
+.action-panel {
+  display: flex;
+  flex-direction: column;
+  gap: 20rpx;
+  padding: 20rpx;
+  background-color: rgba(255, 255, 255, 0.9);
+  border-radius: 10rpx;
+
+  .action-buttons {
+    display: flex;
+    gap: 10rpx;
+    flex-wrap: wrap;
+
+    button {
+      flex: 1;
+      min-width: 120rpx;
+      padding: 10rpx 20rpx;
+      border: none;
+      border-radius: 5rpx;
+      background-color: #4CAF50;
+      color: white;
+      font-size: 28rpx;
+
+      &:disabled {
+        background-color: #cccccc;
+        cursor: not-allowed;
+      }
+
+      &:active {
+        transform: scale(0.95);
+      }
+    }
+  }
+
+  .selected-plot-info {
+    display: flex;
+    flex-direction: column;
+    gap: 10rpx;
+    padding: 10rpx;
+    background-color: rgba(0, 0, 0, 0.05);
+    border-radius: 5rpx;
+
+    text {
+      font-size: 24rpx;
+      color: #333;
+    }
+  }
+} 

+ 233 - 0
pages/isLand/FarmDialog.vue

@@ -0,0 +1,233 @@
+<template>
+  <custom-dialog :visible.sync="dialogVisible" title="我的农场" @close="onClose">
+    <view class="farm-container">
+      <view class="farm-grid">
+        <view v-for="(plot, index) in plots" :key="index" class="farm-plot" @click="onPlotClick(index)">
+          <view class="plot-content" :class="getPlotClass(plot)">
+            <view v-if="plot.crop" class="crop-stage">
+              <image :src="getCropImage(plot)" mode="aspectFit" class="crop-image"></image>
+            </view>
+            <view v-if="plot.weeds" class="weeds"></view>
+            <view v-if="plot.needsWater" class="water-indicator"></view>
+          </view>
+        </view>
+      </view>
+      
+      <view class="action-panel">
+        <view class="action-buttons">
+          <button @click="selectAction('plant')" :disabled="!canPlant">播种</button>
+          <button @click="selectAction('water')" :disabled="!canWater">浇水</button>
+          <button @click="selectAction('fertilize')" :disabled="!canFertilize">施肥</button>
+          <button @click="selectAction('weed')" :disabled="!canWeed">除草</button>
+          <button @click="selectAction('harvest')" :disabled="!canHarvest">收获</button>
+        </view>
+        <view class="selected-plot-info" v-if="selectedPlot !== null">
+          <text>状态: {{ getPlotStatus(plots[selectedPlot]) }}</text>
+          <text v-if="plots[selectedPlot].crop">作物: {{ plots[selectedPlot].crop.name }}</text>
+          <text v-if="plots[selectedPlot].crop">生长进度: {{ getGrowthProgress(plots[selectedPlot]) }}%</text>
+        </view>
+      </view>
+    </view>
+  </custom-dialog>
+</template>
+
+<script>
+import CustomDialog from '@/components/CustomDialog/CustomDialog.vue'
+
+const CROPS = {
+  carrot: {
+    name: '胡萝卜',
+    growthTime: 30, // minutes
+    stages: ['/static/crops/carrot_stage1.png', '/static/crops/carrot_stage2.png', '/static/crops/carrot_stage3.png'],
+    value: 100
+  },
+  tomato: {
+    name: '番茄',
+    growthTime: 60,
+    stages: ['/static/crops/tomato_stage1.png', '/static/crops/tomato_stage2.png', '/static/crops/tomato_stage3.png'],
+    value: 200
+  }
+}
+
+export default {
+  name: 'FarmDialog',
+  components: {
+    CustomDialog
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      plots: Array(12).fill().map(() => ({
+        crop: null,
+        plantedAt: null,
+        watered: false,
+        fertilized: false,
+        weeds: false,
+        needsWater: false
+      })),
+      selectedPlot: null,
+      selectedAction: null,
+      growthTimer: null
+    }
+  },
+  computed: {
+    canPlant() {
+      return this.selectedPlot !== null && !this.plots[this.selectedPlot].crop
+    },
+    canWater() {
+      return this.selectedPlot !== null && 
+             this.plots[this.selectedPlot].crop && 
+             !this.plots[this.selectedPlot].watered
+    },
+    canFertilize() {
+      return this.selectedPlot !== null && 
+             this.plots[this.selectedPlot].crop && 
+             !this.plots[this.selectedPlot].fertilized
+    },
+    canWeed() {
+      return this.selectedPlot !== null && 
+             this.plots[this.selectedPlot].weeds
+    },
+    canHarvest() {
+      return this.selectedPlot !== null && 
+             this.plots[this.selectedPlot].crop && 
+             this.isCropReady(this.plots[this.selectedPlot])
+    }
+  },
+  watch: {
+    visible(newVal) {
+      this.dialogVisible = newVal
+      if (newVal) {
+        this.startGrowthTimer()
+      } else {
+        this.stopGrowthTimer()
+      }
+    }
+  },
+  methods: {
+    onClose() {
+      this.$emit('update:visible', false)
+    },
+    onPlotClick(index) {
+      this.selectedPlot = index
+      if (this.selectedAction) {
+        this.performAction(index)
+      }
+    },
+    selectAction(action) {
+      this.selectedAction = action
+    },
+    performAction(plotIndex) {
+      const plot = this.plots[plotIndex]
+      
+      switch (this.selectedAction) {
+        case 'plant':
+          if (this.canPlant) {
+            plot.crop = { ...CROPS.carrot }
+            plot.plantedAt = Date.now()
+            plot.watered = false
+            plot.fertilized = false
+            plot.weeds = false
+            plot.needsWater = true
+          }
+          break
+        case 'water':
+          if (this.canWater) {
+            plot.watered = true
+            plot.needsWater = false
+          }
+          break
+        case 'fertilize':
+          if (this.canFertilize) {
+            plot.fertilized = true
+          }
+          break
+        case 'weed':
+          if (this.canWeed) {
+            plot.weeds = false
+          }
+          break
+        case 'harvest':
+          if (this.canHarvest) {
+            // TODO: Add harvested items to inventory
+            plot.crop = null
+            plot.plantedAt = null
+            plot.watered = false
+            plot.fertilized = false
+            plot.weeds = false
+            plot.needsWater = false
+          }
+          break
+      }
+      
+      this.selectedAction = null
+    },
+    getPlotClass(plot) {
+      return {
+        'has-crop': plot.crop,
+        'needs-water': plot.needsWater,
+        'has-weeds': plot.weeds
+      }
+    },
+    getCropImage(plot) {
+      if (!plot.crop) return ''
+      const growthStage = this.getGrowthStage(plot)
+      return plot.crop.stages[growthStage]
+    },
+    getGrowthStage(plot) {
+      if (!plot.crop || !plot.plantedAt) return 0
+      const growthProgress = this.getGrowthProgress(plot)
+      if (growthProgress >= 100) return 2
+      if (growthProgress >= 50) return 1
+      return 0
+    },
+    getGrowthProgress(plot) {
+      if (!plot.crop || !plot.plantedAt) return 0
+      const elapsed = (Date.now() - plot.plantedAt) / 1000 / 60 // minutes
+      const progress = (elapsed / plot.crop.growthTime) * 100
+      return Math.min(100, Math.floor(progress))
+    },
+    isCropReady(plot) {
+      return this.getGrowthProgress(plot) >= 100
+    },
+    getPlotStatus(plot) {
+      if (!plot.crop) return '空地'
+      if (plot.weeds) return '需要除草'
+      if (plot.needsWater) return '需要浇水'
+      if (this.isCropReady(plot)) return '可以收获'
+      return '生长中'
+    },
+    startGrowthTimer() {
+      this.growthTimer = setInterval(() => {
+        this.plots.forEach(plot => {
+          if (plot.crop && !plot.watered) {
+            plot.needsWater = true
+          }
+          if (plot.crop && Math.random() < 0.1) { // 10% chance to spawn weeds
+            plot.weeds = true
+          }
+        })
+      }, 60000) // Check every minute
+    },
+    stopGrowthTimer() {
+      if (this.growthTimer) {
+        clearInterval(this.growthTimer)
+        this.growthTimer = null
+      }
+    }
+  },
+  beforeDestroy() {
+    this.stopGrowthTimer()
+  }
+}
+</script>
+
+<style lang="scss">
+@import './FarmDialog.scss';
+</style> 

+ 71 - 0
pages/isLand/homeLand.scss

@@ -0,0 +1,71 @@
+.home-land-container {
+  width: 100%;
+  height: 100vh;
+  overflow: hidden;
+  position: relative;
+  
+  .scroll-container {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    overflow: hidden;
+    touch-action: pan-x;
+  }
+  
+  /* 第三层:背景层 */
+  .background-layer {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: linear-gradient(to bottom, #87CEEB, #4682B4); /* 渐变背景 */
+    z-index: 1; /* 最底层 */
+    user-select: none; /* 禁止选中 */
+    pointer-events: none; /* 禁止交互 */
+  }
+
+  /* 第二层:地图层(可拖动) */
+  .map-layer {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    width: 1536rpx;  /* 地图比屏幕大 */
+    height: 100%;
+    z-index: 2;   /* 中间层 */
+    cursor: grab; /* 拖动光标 */
+  }
+
+  /* 第一层:UI层(最上层) */
+  .ui-layer {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 3; /* 最上层 */
+    pointer-events: none; /* 允许穿透点击,但内部元素可交互 */
+  }
+  
+  /* UI层内部元素需要启用交互 */
+  .ui-content {
+    pointer-events: none; /* 重新启用交互 */
+    bottom: 150px;
+    padding-top: 50px;
+    height: 120px;
+  }
+
+  /* 示例按钮样式 */
+  .dialog-button {
+    pointer-events: auto; /* 重新启用交互 */
+    background: white;
+    padding: 10px 20px;
+    border-radius: 5px;
+    box-shadow: 0 2px 5px rgba(0,0,0,0.2);
+  }
+
+  text {
+    font-size: 16px;
+    color: #333;
+  }
+} 

+ 335 - 0
pages/isLand/homeLand.vue

@@ -0,0 +1,335 @@
+<template>
+ <view class="home-land-container">
+    <!-- 第三层:背景 -->
+    <view class="background-layer"></view>
+  
+    <!-- 第二层:地图 -->
+    <view class="map-layer" id="mapLayer" :style="{ transform: `translateX(${translateX}px)` }" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @mousedown="onmousedown" @mousemove="onmousemove" @mouseup="onmouseup">
+      <image class="island-image" src="/static/island/island.png" mode="widthFix" style="width:1536rpx; bottom: 0rpx;left: 0rpx; position: absolute;"></image>
+      <view style="position: absolute;width: 670rpx;left: 680rpx; bottom:430rpx;align-items: center;">
+        <image class="house-image" src="/static/island/building/4.png" mode="widthFix" style="width:670rpx; position: static;" @click="onHouseClick" :animation="houseAnimationData">	</image>
+      </view>
+
+      <view style="position: absolute;width: 670rpx;left:180rpx; bottom:130rpx;align-items: center;">
+        <image class="hall-image" src="/static/island/building/1.png" mode="widthFix" style="width:670rpx; position: static;" @click="onHallClick" :animation="hallAnimationData">	</image>
+      </view>
+    </view>
+  
+    <!-- 第一层:UI -->
+    <view class="ui-layer">
+      <view class="ui-content">
+        <view class="ui-buttons">
+          <button class="ui-button" @click="showInventory">背包</button>
+          <button class="ui-button" @click="showCharacter">角色</button>
+          <button class="ui-button" @click="showShop">商店</button>
+        </view>
+      </view>
+    </view>
+
+    <!-- 对话框组件 -->
+    <inventory-dialog :visible.sync="inventoryVisible" @close="onInventoryClose"></inventory-dialog>
+    <character-dialog :visible.sync="characterVisible" @close="onCharacterClose"></character-dialog>
+    <shop-dialog :visible.sync="shopVisible" @close="onShopClose" @buy="onShopBuy"></shop-dialog>
+  </view> 
+</template>
+
+
+<script>
+import InventoryDialog from '@/components/dialogs/InventoryDialog.vue'
+import CharacterDialog from '@/components/dialogs/CharacterDialog.vue'
+import ShopDialog from '@/components/dialogs/ShopDialog.vue'
+
+export default {
+	components: {
+		InventoryDialog,
+		CharacterDialog,
+		ShopDialog
+	},
+	data() {
+		return {
+			// 背景位置控制
+			translateX: 0,
+			startX: 0,
+			currentX: 0,
+			isDragging : false,
+			
+			// 获取屏幕宽度和背景宽度
+			screenWidth: 0,
+			backgroundWidth: 0,
+			islandHeight: 0,
+			houseAnimationData: {},
+			hallAnimationData: {},
+			houseAnimating: false,
+			hallAnimating: false,
+			baseTranslate: { x: -50, y: 50 },
+			inventoryVisible: false,
+			characterVisible: false,
+			shopVisible: false,
+		}
+	},
+	onLoad() {
+		let self = this;
+		// 获取屏幕宽度
+		uni.getSystemInfo({
+			success: (res) => {
+				self.screenWidth = res.windowWidth;
+				console.log('屏幕宽度:', self.screenWidth);
+			}
+		});
+	},
+	onShow() {
+		// this.loadData();
+	},
+	onReady() {
+		// 在组件渲染完成后获取图片尺寸
+		setTimeout(() => {
+			this.getImageSize();
+		}, 300);
+	},
+	methods: {
+		loadData() {
+			// 可以在这里加载其他数据
+		},
+		getImageSize() {
+			const query = uni.createSelectorQuery().in(this);
+			query.select('.island-image').boundingClientRect(data => {
+				if (data) {
+					// 获取岛屿图片的宽度和高度
+					this.backgroundWidth = data.width;
+					this.islandHeight = data.height;
+					
+					// 计算最大可移动距离
+					this.maxTranslate = this.backgroundWidth - this.screenWidth;
+					
+					// 初始位置居中
+					this.translateX = -this.maxTranslate / 2;
+					
+					// 打印调试信息
+					console.log('屏幕宽度:', this.screenWidth);
+					console.log('背景宽度:', this.backgroundWidth);
+					console.log('岛屿高度:', this.islandHeight);
+					console.log('最大可移动距离:',this.maxTranslate);
+					
+					console.log('岛屿data:', data);
+					
+				} else {
+					console.error('未能获取岛屿图片的尺寸');
+				}
+			}).exec();
+		},
+		// 触摸开始
+		touchStart(e) {
+			this.startX = e.touches[0].clientX;
+			this.currentX = this.translateX;
+			console.log('this.startX =',this.startX);
+			console.log('this.currentX =',this.currentX);
+		},
+		// 触摸移动
+		touchMove(e) {
+			console.log('----------- touchMove');
+			const moveX = e.touches[0].clientX - this.startX;
+			let newTranslateX = this.currentX + moveX;
+			
+			// 限制移动范围,不让背景两侧露出
+			if (newTranslateX > 0) {
+				newTranslateX = 0;
+			} else if (newTranslateX < -this.maxTranslate) {
+				newTranslateX = -this.maxTranslate;
+			}
+			
+			this.translateX = newTranslateX;
+			console.log('moveX =',moveX);
+			console.log('this.translateX =',this.translateX);
+		},
+		// 触摸结束
+		touchEnd() {
+			console.log('----------- touchEnd');
+			this.currentX = this.translateX;
+			console.log('this.currentX =',this.currentX);
+		},
+		
+		onmousedown(e) {
+			console.log('----------- onmousedown');
+			console.log('----------- e',e);
+			this.isDragging = true;
+			this.startX = e.clientX;
+			this.currentX = this.translateX;
+			mapLayer.style.cursor = 'grabbing';
+		},
+		
+		onmousemove(e) {
+			if(this.isDragging){
+				console.log('----------- onmousemove');
+				const moveX = e.clientX - this.startX;
+				let newTranslateX = this.currentX + moveX;
+				
+				//限制移动范围,不让背景两侧露出
+				if (newTranslateX > 0) {
+					newTranslateX = 0;
+				} else if (newTranslateX < -this.maxTranslate) {
+					newTranslateX = -this.maxTranslate;
+				}
+				
+				this.translateX = newTranslateX;
+				console.log('moveX =',moveX);
+				console.log('this.translateX =',this.translateX);
+			}
+		},
+		
+		onmouseup(e) {
+			console.log('----------- onmouseup');
+			this.isDragging = false;
+			mapLayer.style.cursor = 'grab';
+		},
+
+		onHouseClick(event) {
+			if(this.houseAnimating) return;
+			// 处理点击事件
+			console.log('House clicked!');
+			
+			// 播放房子的点击动画
+			this.playAnimation('house');
+			
+			uni.showToast({
+				title: 'House clicked!',
+				icon: 'none'
+			});
+		},
+
+		onHallClick(event) {
+			if(this.hallAnimating) return;
+			// 处理点击事件
+			console.log('Hall clicked!');
+			
+			// 播放大厅的点击动画
+			this.playAnimation('hall');
+			
+			uni.showToast({
+				title: 'Hall clicked!',
+				icon: 'none'
+			});
+		},
+
+		playAnimation(type) {
+			let self = this;
+			
+			// 根据类型设置对应的动画状态
+			if (type === 'house') {
+				this.houseAnimating = true;
+			} else if (type === 'hall') {
+				this.hallAnimating = true;
+			}
+
+			// 创建动画实例
+			const animation = uni.createAnimation({
+				duration: 400,
+				timingFunction: 'ease',
+			});
+
+			// 定义果冻动画序列
+			animation.scale(0.95).step({ duration: 100 })
+					.scale(1.05).step({ duration: 100 })
+					.scale(0.98).step({ duration: 100 })
+					.scale(1).step({ duration: 100 });
+
+			// 根据类型应用动画到对应的元素
+			if (type === 'house') {
+				this.houseAnimationData = animation.export();
+			} else if (type === 'hall') {
+				this.hallAnimationData = animation.export();
+			}
+
+			// 动画结束后重置状态
+			setTimeout(() => {
+				if (type === 'house') {
+					self.houseAnimating = false;
+				} else if (type === 'hall') {
+					self.hallAnimating = false;
+				}
+			}, 800);
+		},
+
+		showInventory() {
+			console.log('Opening inventory...')
+			this.inventoryVisible = true
+		},
+		onInventoryClose() {
+			console.log('Closing inventory...')
+			this.inventoryVisible = false
+		},
+		showCharacter() {
+			console.log('Opening character...')
+			this.characterVisible = true
+		},
+		onCharacterClose() {
+			console.log('Closing character...')
+			this.characterVisible = false
+		},
+		showShop() {
+			console.log('Opening shop...')
+			this.shopVisible = true
+		},
+		onShopClose() {
+			console.log('Closing shop...')
+			this.shopVisible = false
+		},
+		onShopBuy(item) {
+			console.log('Buying item:', item)
+		}
+	}
+}
+</script>
+
+
+<style lang="scss" scoped>
+@import './homeLand.scss';
+.ui-layer {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  pointer-events: none;
+  z-index: 100;
+
+  .ui-content {
+    position: absolute;
+    top: 20rpx;
+    right: 20rpx;
+    pointer-events: auto;
+    z-index: 101;
+  }
+
+  .ui-buttons {
+    display: flex;
+    flex-direction: column;
+    gap: 20rpx;
+    
+    .ui-button {
+      background: rgba(255, 255, 255, 0.9);
+      border: none;
+      padding: 16rpx 32rpx;
+      border-radius: 8rpx;
+      font-size: 28rpx;
+      color: #333;
+      box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+      min-width: 120rpx;
+      
+      &:active {
+        opacity: 0.8;
+        transform: scale(0.95);
+      }
+    }
+  }
+}
+
+.map-layer {
+  position: relative;
+  z-index: 1;
+  cursor: grab;
+  
+  &:active {
+    cursor: grabbing;
+  }
+}
+</style> 

+ 5 - 4
pages/isLand/mainLand.scss

@@ -1,4 +1,3 @@
-
 .main-land-container {
   width: 100%;
   height: 100vh;
@@ -51,13 +50,15 @@
     }
     /* UI层内部元素需要启用交互 */
     .ui-content {
-      pointer-events: auto; /* 重新启用交互 */
-	  bottom: 150px;
-      padding: 100px;
+      pointer-events: none; /* 重新启用交互 */
+	    bottom: 150px;
+      padding-top: 50px;
+      height: 120px;
     }
 
     /* 示例按钮样式 */
     .dialog-button {
+      pointer-events: auto; /* 重新启用交互 */
       background: white;
 	  // bottom: 150px;
       padding: 10px 20px;

+ 313 - 127
pages/isLand/mainLand.vue

@@ -15,163 +15,349 @@
       <!-- 这里可以放置地图元素(示例:一个方块) -->
       <!-- <view style="position: absolute; top: 30%; left: 30%; width: 100px; height: 100px; background: green;"></view> -->
 	  <image class="island-image" src="/static/island/island.png" mode="widthFix" style="width:1536rpx; bottom: 0rpx;left: 0rpx; position: absolute;"></image>
-	  <image class="house-image" src="/static/island/building/4.png" mode="widthFix" style="width:670rpx; left: 980rpx; bottom:530rpx;position: absolute; transform:translate(-50%,50%);"></image>
-    </view>
+	 	<view style="position: absolute;width: 670rpx;left: 680rpx; bottom:430rpx;align-items: center;">
+			<image class="house-image" src="/static/island/building/4.png" mode="widthFix" style="width:670rpx; position: static;" @click="onHouseClick" :animation="houseAnimationData">	</image>
+		</view>
+
+		<view style="position: absolute;width: 670rpx;left:180rpx; bottom:130rpx;align-items: center;">
+			<image class="farm-image" src="/static/island/building/2.png" mode="widthFix" style="width:670rpx; position: static;" @click="onFarmClick" :animation="farmAnimationData">	</image>
+		</view>
+	</view>
   
     <!-- 第一层:UI -->
     <view class="ui-layer">
       <view class="ui-content">
-        <button class="dialog-button">打开对话框</button>
+        <view class="ui-buttons">
+          <button class="ui-button" @click="showInventory">背包</button>
+          <button class="ui-button" @click="showCharacter">角色</button>
+          <button class="ui-button" @click="showShop">商店</button>
+        </view>
       </view>
     </view>
+
+    <!-- 对话框组件 -->
+    <inventory-dialog :visible.sync="inventoryVisible" @close="onInventoryClose"></inventory-dialog>
+    <character-dialog :visible.sync="characterVisible" @close="onCharacterClose"></character-dialog>
+    <shop-dialog :visible.sync="shopVisible" @close="onShopClose" @buy="onShopBuy"></shop-dialog>
+    <farm-dialog :visible.sync="farmVisible" @close="onFarmClose"></farm-dialog>
   </view> 
 </template>
 
 
 <script>
-	export default {
-		components: {},
-		data() {
-			return {
-				// // 背景位置控制
-				translateX: 0,
-				startX: 0,
-				currentX: 0,
-				isDragging : false,
-				// maxTranslate: 0,
-				
-				// // 获取屏幕宽度和背景宽度
-				screenWidth: 0,
-				backgroundWidth: 0,
-				islandHeight: 0
+import InventoryDialog from '@/components/dialogs/InventoryDialog.vue'
+import CharacterDialog from '@/components/dialogs/CharacterDialog.vue'
+import ShopDialog from '@/components/dialogs/ShopDialog.vue'
+import FarmDialog from './FarmDialog.vue'
+
+export default {
+	components: {
+		InventoryDialog,
+		CharacterDialog,
+		ShopDialog,
+		FarmDialog
+	},
+	data() {
+		return {
+			// // 背景位置控制
+			translateX: 0,
+			startX: 0,
+			currentX: 0,
+			isDragging : false,
+			// maxTranslate: 0,
+			
+			// // 获取屏幕宽度和背景宽度
+			screenWidth: 0,
+			backgroundWidth: 0,
+			islandHeight: 0,
+			houseAnimationData: {},
+			farmAnimationData: {},
+			houseAnimating: false,
+			farmAnimating: false,
+			baseTranslate: { x: -50, y: 50 },
+			inventoryVisible: false,
+			characterVisible: false,
+			shopVisible: false,
+			farmVisible: false,
+		}
+	},
+	onLoad() {
+		let self = this;
+		// 获取屏幕宽度
+		uni.getSystemInfo({
+			success: (res) => {
+				self.screenWidth = res.windowWidth;
+				console.log('屏幕宽度:', self.screenWidth);
 			}
+		});
+	},
+	onShow() {
+		// this.loadData();
+	},
+	onReady() {
+		// 在组件渲染完成后获取图片尺寸
+		setTimeout(() => {
+			this.getImageSize();
+		}, 300);
+	},
+	methods: {
+		loadData() {
+			// 可以在这里加载其他数据
 		},
-		onLoad() {
-			let self = this;
-			// 获取屏幕宽度
-			uni.getSystemInfo({
-				success: (res) => {
-					self.screenWidth = res.windowWidth;
-					console.log('屏幕宽度:', self.screenWidth);
+		getImageSize() {
+			const query = uni.createSelectorQuery().in(this);
+			query.select('.island-image').boundingClientRect(data => {
+				if (data) {
+					// 获取岛屿图片的宽度和高度
+					this.backgroundWidth = data.width;
+					this.islandHeight = data.height;
+					// this.backgroundWidth = 1536;
+					// this.islandHeight = 1024;
+
+					
+					// 设置背景高度为岛屿高度的两倍(在CSS中实现)
+					
+					// 计算最大可移动距离
+					this.maxTranslate = this.backgroundWidth - this.screenWidth;
+					
+					// 初始位置居中
+					this.translateX = -this.maxTranslate / 2;
+					
+					// 打印调试信息
+					console.log('屏幕宽度:', this.screenWidth);
+					console.log('背景宽度:', this.backgroundWidth);
+					console.log('岛屿高度:', this.islandHeight);
+					console.log('屏幕宽度:',this.screenWidth);
+					console.log('最大可移动距离:',this.maxTranslate);
+					
+					console.log('岛屿data:', data);
+					
+				} else {
+					console.error('未能获取岛屿图片的尺寸');
 				}
-			});
+			}).exec();
 		},
-		onShow() {
-			// this.loadData();
+		// 触摸开始
+		touchStart(e) {
+			// console.log('----------- touchStart');
+			this.startX = e.touches[0].clientX;
+			this.currentX = this.translateX;
+			console.log('this.startX =',this.startX);
+			console.log('this.currentX =',this.currentX);
 		},
-		onReady() {
-			// 在组件渲染完成后获取图片尺寸
-			setTimeout(() => {
-				this.getImageSize();
-			}, 300);
-		},
-		methods: {
-			loadData() {
-				// 可以在这里加载其他数据
-			},
-			getImageSize() {
-				const query = uni.createSelectorQuery().in(this);
-				query.select('.island-image').boundingClientRect(data => {
-					if (data) {
-						// 获取岛屿图片的宽度和高度
-						this.backgroundWidth = data.width;
-						this.islandHeight = data.height;
-						// this.backgroundWidth = 1536;
-						// this.islandHeight = 1024;
-
-						
-						// 设置背景高度为岛屿高度的两倍(在CSS中实现)
-						
-						// 计算最大可移动距离
-						this.maxTranslate = this.backgroundWidth - this.screenWidth;
-						
-						// 初始位置居中
-						this.translateX = -this.maxTranslate / 2;
-						
-						// 打印调试信息
-						console.log('屏幕宽度:', this.screenWidth);
-						console.log('背景宽度:', this.backgroundWidth);
-						console.log('岛屿高度:', this.islandHeight);
-						console.log('屏幕宽度:',this.screenWidth);
-						console.log('最大可移动距离:',this.maxTranslate);
-						
-						console.log('岛屿data:', data);
-						
-					} else {
-						console.error('未能获取岛屿图片的尺寸');
-					}
-				}).exec();
-			},
-			// 触摸开始
-			touchStart(e) {
-				console.log('----------- touchStart');
-				this.startX = e.touches[0].clientX;
-				this.currentX = this.translateX;
-				console.log('this.startX =',this.startX);
-				console.log('this.currentX =',this.currentX);
-			},
-			// 触摸移动
-			touchMove(e) {
-				console.log('----------- touchMove');
-				const moveX = e.touches[0].clientX - this.startX;
+		// 触摸移动
+		touchMove(e) {
+			console.log('----------- touchMove');
+			const moveX = e.touches[0].clientX - this.startX;
+			let newTranslateX = this.currentX + moveX;
+			
+			// 限制移动范围,不让背景两侧露出
+			if (newTranslateX > 0) {
+				newTranslateX = 0;
+			} else if (newTranslateX < -this.maxTranslate) {
+				newTranslateX = -this.maxTranslate;
+			}
+			
+			this.translateX = newTranslateX;
+			console.log('moveX =',moveX);
+			console.log('this.translateX =',this.translateX);
+		},
+		// 触摸结束
+		touchEnd() {
+			console.log('----------- touchEnd');
+			this.currentX = this.translateX;
+			console.log('this.currentX =',this.currentX);
+		},
+		
+		onmousedown(e) {
+			console.log('----------- onmousedown');
+			console.log('----------- e',e);
+			this.isDragging = true;
+			this.startX = e.clientX;
+			this.currentX = this.translateX;
+			mapLayer.style.cursor = 'grabbing';
+		},
+		
+		onmousemove(e) {
+			if(this.isDragging){
+				console.log('----------- onmousemove');
+				const moveX = e.clientX - this.startX;
 				let newTranslateX = this.currentX + moveX;
 				
-				// 限制移动范围,不让背景两侧露出
-				// if (newTranslateX > 0) {
-				// 	newTranslateX = 0;
-				// } else if (newTranslateX < -this.maxTranslate) {
-				// 	newTranslateX = -this.maxTranslate;
-				// }
+				//限制移动范围,不让背景两侧露出
+				if (newTranslateX > 0) {
+					newTranslateX = 0;
+				} else if (newTranslateX < -this.maxTranslate) {
+					newTranslateX = -this.maxTranslate;
+				}
 				
 				this.translateX = newTranslateX;
 				console.log('moveX =',moveX);
 				console.log('this.translateX =',this.translateX);
-			},
-			// 触摸结束
-			touchEnd() {
-				console.log('----------- touchEnd');
-				this.currentX = this.translateX;
-				console.log('this.currentX =',this.currentX);
-			},
+			}
+		},
+		
+		onmouseup(e) {
+			console.log('----------- onmouseup');
+			this.isDragging = false;
+			mapLayer.style.cursor = 'grab';
+		},
+
+		onHouseClick(event) {
+			if(this.houseAnimating) return;
+			// 处理点击事件
+			console.log('House clicked!');
 			
-			onmousedown(e) {
-				console.log('----------- onmousedown');
-				console.log('----------- e',e);
-				this.isDragging = true;
-				this.startX = e.clientX;
-				this.currentX = this.translateX;
-				mapLayer.style.cursor = 'grabbing';
-			},
+			// 播放房子的点击动画
+			this.playAnimation('house');
 			
-			onmousemove(e) {
-				if(this.isDragging){
-					console.log('----------- onmousemove');
-					const moveX = e.clientX - this.startX;
-					let newTranslateX = this.currentX + moveX;
-					
-					//限制移动范围,不让背景两侧露出
-					if (newTranslateX > 0) {
-						newTranslateX = 0;
-					} else if (newTranslateX < -this.maxTranslate) {
-						newTranslateX = -this.maxTranslate;
-					}
-					
-					this.translateX = newTranslateX;
-					console.log('moveX =',moveX);
-					console.log('this.translateX =',this.translateX);
-				}
-			},
+			uni.showToast({
+				title: 'House clicked!',
+				icon: 'none'
+			});
+		},
+
+		onFarmClick(event) {
+			if(this.farmAnimating) return;
+			// 处理点击事件
+			console.log('farm clicked!111');
+			
+			// 播放大厅的点击动画
+			this.playAnimation('farm');
+			
+			// 打开农场对话框
+			this.farmVisible = true;
+			
+			uni.showToast({
+				title: 'farm clicked!111',
+				icon: 'none'
+			});
+		},
+
+		playAnimation(type) {
+			let self = this;
 			
-			onmouseup(e) {
-				console.log('----------- onmouseup');
-				this.isDragging = false;
-				mapLayer.style.cursor = 'grab';
-			},
+			// 根据类型设置对应的动画状态
+			if (type === 'house') {
+				this.houseAnimating = true;
+			} else if (type === 'farm') {
+				this.farmAnimating = true;
+			}
+
+			// 创建动画实例
+			const animation = uni.createAnimation({
+				duration: 400,
+				timingFunction: 'ease',
+			});
+
+			// 定义果冻动画序列
+			animation.scale(0.95).step({ duration: 100 })
+					.scale(1.05).step({ duration: 100 })
+					.scale(0.98).step({ duration: 100 })
+					.scale(1).step({ duration: 100 });
+
+			// 根据类型应用动画到对应的元素
+			if (type === 'house') {
+				this.houseAnimationData = animation.export();
+			} else if (type === 'farm') {
+				this.farmAnimationData = animation.export();
+			}
+
+			// 动画结束后重置状态
+			setTimeout(() => {
+				if (type === 'house') {
+					self.houseAnimating = false;
+				} else if (type === 'farm') {
+					self.farmAnimating = false;
+				}
+			}, 800);
+		},
+
+		showInventory() {
+			console.log('Opening inventory...')
+			this.inventoryVisible = true
+		},
+		onInventoryClose() {
+			console.log('Closing inventory...')
+			this.inventoryVisible = false
+		},
+		showCharacter() {
+			console.log('Opening character...')
+			this.characterVisible = true
+		},
+		onCharacterClose() {
+			console.log('Closing character...')
+			this.characterVisible = false
+		},
+		showShop() {
+			console.log('Opening shop...')
+			this.shopVisible = true
+		},
+		onShopClose() {
+			console.log('Closing shop...')
+			this.shopVisible = false
+		},
+		onShopBuy(item) {
+			console.log('Buying item:', item)
+		},
+		onFarmClose() {
+			console.log('Closing farm dialog...')
+			this.farmVisible = false
 		}
 	}
+}
 </script>
 
 
 <style lang="scss" scoped>
 @import './mainLand.scss';
+.ui-layer {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  pointer-events: none;
+  z-index: 100;
+
+  .ui-content {
+    position: absolute;
+    top: 20rpx;
+    right: 20rpx;
+    pointer-events: auto;
+    z-index: 101;
+  }
+
+  .ui-buttons {
+    display: flex;
+    flex-direction: column;
+    gap: 20rpx;
+    
+    .ui-button {
+      background: rgba(255, 255, 255, 0.9);
+      border: none;
+      padding: 16rpx 32rpx;
+      border-radius: 8rpx;
+      font-size: 28rpx;
+      color: #333;
+      box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+      min-width: 120rpx;
+      
+      &:active {
+        opacity: 0.8;
+        transform: scale(0.95);
+      }
+    }
+  }
+}
+
+.map-layer {
+  position: relative;
+  z-index: 1;
+  cursor: grab;
+  
+  &:active {
+    cursor: grabbing;
+  }
+}
 </style>