160 lines
6.8 KiB
Vue
160 lines
6.8 KiB
Vue
<template>
|
||
<div>
|
||
<div style="display:flex;justify-content:space-between;margin-bottom:16px;">
|
||
<h3>战略地图</h3>
|
||
<div>
|
||
<button class="map-btn map-btn-danger" :disabled="selectedIds.length === 0" @click="batchDelete">批量删除({{ selectedIds.length }})</button>
|
||
<el-button type="primary" @click="showForm=true;form={}">新建地图</el-button>
|
||
</div>
|
||
</div>
|
||
<el-table :data="maps" style="width:100%" @selection-change="onSelectionChange">
|
||
<el-table-column type="selection" width="40" />
|
||
<el-table-column prop="title" label="地图名称" min-width="200" />
|
||
<el-table-column prop="version" label="版本" width="80" />
|
||
<el-table-column prop="status" label="状态" width="80">
|
||
<template #default="{ row }">{{ row.status === "published" ? "已发布" : "草稿" }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="created_at" label="创建时间" width="180" />
|
||
<el-table-column label="操作" width="250">
|
||
<template #default="{ row }">
|
||
<el-button size="small" @click='goCanvas(row.id)'>画布</el-button>
|
||
<el-button size="small" @click='editMap(row)'>编辑</el-button>
|
||
<el-button size="small" @click='goVersion(row.id)'>版本</el-button>
|
||
<el-button size="small" type="danger" @click="deleteMap(row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 新建/编辑弹窗(内联v-show) -->
|
||
<div v-show="showForm" class="map-form-overlay" @click.self="showForm=false">
|
||
<div class="map-form-dialog">
|
||
<div class="map-form-header">
|
||
<span>{{ editingMap ? '编辑地图' : '新建战略地图' }}</span>
|
||
<button class="map-form-close" @click="showForm=false">×</button>
|
||
</div>
|
||
<div class="map-form-body">
|
||
<div class="map-form-field">
|
||
<label>地图名称</label>
|
||
<input v-model="form.title" class="map-input" placeholder="请输入地图名称" />
|
||
</div>
|
||
<div class="map-form-field">
|
||
<label>版本号</label>
|
||
<input v-model="form.version" class="map-input" placeholder="v1.0" />
|
||
</div>
|
||
<div class="map-form-field" v-if="!editingMap">
|
||
<label>创建方式</label>
|
||
<div class="map-radio-group">
|
||
<label class="map-radio"><input type="radio" v-model="createMode" value="blank" /> 空白地图</label>
|
||
<label class="map-radio"><input type="radio" v-model="createMode" value="template" /> 快速模板(4维度+预设目标)</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="map-form-footer">
|
||
<button class="map-btn" @click="showForm=false">取消</button>
|
||
<button class="map-btn map-btn-primary" @click="saveMap">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted } from "vue"
|
||
import { useRouter } from "vue-router"
|
||
import { ElMessage, ElMessageBox } from "element-plus"
|
||
import { mapApi } from "../api/index"
|
||
|
||
const router = useRouter()
|
||
const maps = ref<any[]>([])
|
||
const showForm = ref(false)
|
||
const editingMap = ref<any>(null)
|
||
const form = ref<any>({})
|
||
const createMode = ref("blank")
|
||
const selectedIds = ref<number[]>([])
|
||
|
||
function onSelectionChange(rows: any[]) {
|
||
selectedIds.value = rows.map(r => r.id)
|
||
}
|
||
|
||
async function batchDelete() {
|
||
if (selectedIds.value.length === 0) return
|
||
try {
|
||
const r: any = await mapApi.batchDelete(selectedIds.value)
|
||
ElMessage.success(r?.message || `已删除 ${selectedIds.value.length} 个地图`)
|
||
selectedIds.value = []
|
||
load()
|
||
} catch (e: any) {
|
||
ElMessage.error("批量删除失败: " + (e?.response?.data?.detail || e?.message || "未知错误"))
|
||
}
|
||
}
|
||
|
||
async function load() {
|
||
try { const r: any = await mapApi.list(); maps.value = r.data || [] } catch (e) {}
|
||
}
|
||
|
||
function goCanvas(id: number) { router.push('/maps/canvas/' + id) }
|
||
function goVersion(id: number) { router.push('/maps/versions/' + id) }
|
||
|
||
async function saveMap() {
|
||
try {
|
||
if (editingMap.value) {
|
||
await mapApi.update(editingMap.value.id, form.value)
|
||
ElMessage.success("已更新")
|
||
} else if (createMode.value === "template") {
|
||
await mapApi.createWithTemplate ? await mapApi.createWithTemplate(form.value) : await mapApi.create(form.value)
|
||
ElMessage.success("模板地图已创建")
|
||
} else {
|
||
await mapApi.create(form.value)
|
||
ElMessage.success("创建成功")
|
||
}
|
||
showForm.value = false
|
||
editingMap.value = null
|
||
load()
|
||
} catch (e) {
|
||
ElMessage.error("操作失败")
|
||
}
|
||
}
|
||
|
||
function editMap(map: any) {
|
||
editingMap.value = map
|
||
form.value = { title: map.title, version: map.version }
|
||
showForm.value = true
|
||
}
|
||
|
||
function handleClick(path: string) { window.location.href = path }
|
||
async function deleteMap(map: any) {
|
||
try {
|
||
await ElMessageBox.confirm(`确定删除「${map.title}」?`)
|
||
await mapApi.delete ? await mapApi.delete(map.id) : ElMessage.warning("当前API不支持删除")
|
||
ElMessage.success("已删除")
|
||
load()
|
||
} catch (e: any) {
|
||
if (e !== "cancel") ElMessage.error("删除失败")
|
||
}
|
||
}
|
||
|
||
onMounted(load)
|
||
</script>
|
||
|
||
<style scoped>
|
||
.map-form-overlay { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.5); z-index:9999; display:flex; align-items:center; justify-content:center; }
|
||
.map-form-dialog { background:#fff; border-radius:12px; width:520px; box-shadow:0 8px 32px rgba(0,0,0,0.18); overflow:hidden; }
|
||
.map-form-header { display:flex; justify-content:space-between; align-items:center; padding:16px 20px; border-bottom:1px solid #f0f0f0; font-weight:600; font-size:16px; }
|
||
.map-form-close { background:none; border:none; font-size:22px; color:#999; cursor:pointer; padding:0 4px; }
|
||
.map-form-close:hover { color:#333; }
|
||
.map-form-body { padding:20px; }
|
||
.map-form-field { margin-bottom:16px; }
|
||
.map-form-field label { display:block; font-size:13px; color:#666; margin-bottom:6px; font-weight:500; }
|
||
.map-input { width:100%; padding:8px 12px; border:1px solid #dcdfe6; border-radius:6px; font-size:14px; outline:none; box-sizing:border-box; }
|
||
.map-input:focus { border-color:#409eff; }
|
||
.map-radio-group { display:flex; flex-direction:column; gap:8px; }
|
||
.map-radio { display:inline-flex; align-items:center; gap:4px; font-size:13px; color:#666; cursor:pointer; }
|
||
.map-radio input { margin:0; }
|
||
.map-form-footer { display:flex; justify-content:flex-end; gap:10px; padding:12px 20px; border-top:1px solid #f0f0f0; }
|
||
.map-btn { padding:8px 20px; border:1px solid #dcdfe6; border-radius:6px; background:#fff; color:#333; font-size:14px; cursor:pointer; }
|
||
.map-btn-primary { background:#409eff; color:#fff; border-color:#409eff; }
|
||
.map-btn-danger { background:#f56c6c; color:#fff; border-color:#f56c6c; }
|
||
.map-btn:disabled { opacity:0.5; cursor:not-allowed; }
|
||
.map-btn:hover:not(:disabled) { opacity:0.85; }
|
||
</style>
|