|
|
@@ -0,0 +1,504 @@
|
|
|
+<template>
|
|
|
+ <div class="point-management-container">
|
|
|
+ <!-- 搜索区域 -->
|
|
|
+ <div class="search-form">
|
|
|
+ <el-form :model="searchForm" inline @submit.prevent="handleSearch">
|
|
|
+ <el-form-item label="姓名">
|
|
|
+ <el-input
|
|
|
+ v-model="searchForm.name"
|
|
|
+ placeholder="请输入姓名"
|
|
|
+ clearable
|
|
|
+ @input="handleSearch"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="联系方式">
|
|
|
+ <el-input
|
|
|
+ v-model="searchForm.phone"
|
|
|
+ placeholder="请输入手机号"
|
|
|
+ clearable
|
|
|
+ @input="handleSearch"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="所属区域">
|
|
|
+ <el-input
|
|
|
+ v-model="searchForm.marker"
|
|
|
+ placeholder="请输入所属区域"
|
|
|
+ clearable
|
|
|
+ @input="handleSearch"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="handleSearch">搜索</el-button>
|
|
|
+ <el-button @click="handleResetSearch">重置</el-button>
|
|
|
+ <el-button type="primary" @click="handleAdd">新增点位</el-button>
|
|
|
+ <!-- <el-button-->
|
|
|
+ <!-- type="danger"-->
|
|
|
+ <!-- @click="handleBatchDelete"-->
|
|
|
+ <!-- :disabled="!selectedIds.length"-->
|
|
|
+ <!-- >-->
|
|
|
+ <!-- 批量删除-->
|
|
|
+ <!-- </el-button>-->
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 点位列表 -->
|
|
|
+ <el-table
|
|
|
+ v-loading="loading"
|
|
|
+ :data="filteredPointList"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ style="width: 100%; margin-top: 20px"
|
|
|
+ >
|
|
|
+ <el-table-column type="selection" width="55"/>
|
|
|
+ <el-table-column prop="name" label="姓名" min-width="100"/>
|
|
|
+ <el-table-column prop="phone" label="联系方式" min-width="150"/>
|
|
|
+ <el-table-column prop="marker" label="所属区域" min-width="120"/>
|
|
|
+ <el-table-column prop="longitude" label="经度" min-width="120"/>
|
|
|
+ <el-table-column prop="latitude" label="纬度" min-width="120"/>
|
|
|
+ <el-table-column label="操作" min-width="200">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button type="primary" size="small" @click="handleView(scope.row)">查看</el-button>
|
|
|
+ <el-button type="warning" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
|
|
+ <el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <!-- 分页 -->
|
|
|
+ <div class="mt-4 flex justify-end">
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="pagination.pageNum"
|
|
|
+ v-model:page-size="pagination.pageSize"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :total="total"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @current-change="handlePageChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 点位表单弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="dialogVisible"
|
|
|
+ :title="dialogTitle"
|
|
|
+ width="500px"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ >
|
|
|
+ <el-form
|
|
|
+ ref="pointFormRef"
|
|
|
+ :model="pointForm"
|
|
|
+ :rules="formRules"
|
|
|
+ label-width="80px"
|
|
|
+ >
|
|
|
+ <el-form-item label="姓名" prop="name">
|
|
|
+ <el-input
|
|
|
+ v-model="pointForm.name"
|
|
|
+ placeholder="请输入姓名"
|
|
|
+ :disabled="dialogType === 'view'"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="联系方式" prop="phone">
|
|
|
+ <el-input
|
|
|
+ v-model="pointForm.phone"
|
|
|
+ placeholder="请输入手机号"
|
|
|
+ :disabled="dialogType === 'view'"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="所属区域" prop="marker">
|
|
|
+ <el-input
|
|
|
+ v-model="pointForm.marker"
|
|
|
+ placeholder="请输入所属区域"
|
|
|
+ :disabled="dialogType === 'view'"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="经度" prop="longitude">
|
|
|
+ <el-input
|
|
|
+ v-model="pointForm.longitude"
|
|
|
+ placeholder="请输入经度(范围:-180~180)"
|
|
|
+ :disabled="dialogType === 'view'"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="纬度" prop="latitude">
|
|
|
+ <el-input
|
|
|
+ v-model="pointForm.latitude"
|
|
|
+ placeholder="请输入纬度(范围:-90~90)"
|
|
|
+ :disabled="dialogType === 'view'"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="handleSubmit"
|
|
|
+ v-if="dialogType !== 'view'"
|
|
|
+ >
|
|
|
+ 确认
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import {ref, reactive, computed, onMounted} from 'vue'
|
|
|
+import {ElMessage, ElMessageBox} from 'element-plus'
|
|
|
+import request from "@/utils/request.js"
|
|
|
+
|
|
|
+const loading = ref(false)
|
|
|
+const total = ref(0)
|
|
|
+// 模拟初始数据
|
|
|
+const pointList = ref([])
|
|
|
+
|
|
|
+// 分页参数
|
|
|
+const pagination = reactive({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10
|
|
|
+})
|
|
|
+
|
|
|
+// 搜索表单数据
|
|
|
+const searchForm = reactive({
|
|
|
+ name: '',
|
|
|
+ phone: '',
|
|
|
+ marker: ''
|
|
|
+})
|
|
|
+
|
|
|
+// 筛选后的点位列表(计算属性)
|
|
|
+const filteredPointList = computed(() => {
|
|
|
+ // 若无搜索条件,返回原始列表
|
|
|
+ if (!searchForm.name && !searchForm.phone && !searchForm.marker) {
|
|
|
+ return pointList.value
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据搜索条件过滤
|
|
|
+ return pointList.value.filter(item => {
|
|
|
+ // 姓名模糊匹配(忽略大小写)
|
|
|
+ const nameMatch = searchForm.name
|
|
|
+ ? item.name.toLowerCase().includes(searchForm.name.toLowerCase())
|
|
|
+ : true
|
|
|
+
|
|
|
+ // 联系方式模糊匹配
|
|
|
+ const phoneMatch = searchForm.phone
|
|
|
+ ? item.phone.includes(searchForm.phone)
|
|
|
+ : true
|
|
|
+
|
|
|
+ // 所属区域模糊匹配(忽略大小写)
|
|
|
+ const markerMatch = searchForm.marker
|
|
|
+ ? item.marker.toLowerCase().includes(searchForm.marker.toLowerCase())
|
|
|
+ : true
|
|
|
+
|
|
|
+ return nameMatch && phoneMatch && markerMatch
|
|
|
+ })
|
|
|
+})
|
|
|
+
|
|
|
+// 表单相关
|
|
|
+const pointFormRef = ref(null)
|
|
|
+const pointForm = reactive({
|
|
|
+ id: '',
|
|
|
+ name: '',
|
|
|
+ phone: '',
|
|
|
+ marker: '',
|
|
|
+ longitude: '',
|
|
|
+ latitude: ''
|
|
|
+})
|
|
|
+
|
|
|
+// 弹窗相关
|
|
|
+const dialogVisible = ref(false)
|
|
|
+const dialogType = ref('add') // add:新增 edit:编辑 view:查看
|
|
|
+const currentItem = ref(null)
|
|
|
+const dialogTitle = computed(() => {
|
|
|
+ const titleMap = {
|
|
|
+ add: '新增点位',
|
|
|
+ edit: '编辑点位',
|
|
|
+ view: '查看点位'
|
|
|
+ }
|
|
|
+ return titleMap[dialogType.value] || '点位信息'
|
|
|
+})
|
|
|
+
|
|
|
+// 批量选择的ID列表
|
|
|
+const selectedIds = ref([])
|
|
|
+
|
|
|
+// 表单校验规则
|
|
|
+const formRules = reactive({
|
|
|
+ name: [
|
|
|
+ {required: true, message: '请输入姓名', trigger: 'blur'}
|
|
|
+ ],
|
|
|
+ phone: [
|
|
|
+ {required: true, message: '请输入联系方式', trigger: 'blur'},
|
|
|
+ {pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur'}
|
|
|
+ ],
|
|
|
+ marker: [
|
|
|
+ {required: true, message: '请输入所属区域', trigger: 'blur'}
|
|
|
+ ],
|
|
|
+ longitude: [
|
|
|
+ {required: true, message: '请输入经度', trigger: 'blur'},
|
|
|
+ {
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ const num = Number(value)
|
|
|
+ if (isNaN(num) || num < -180 || num > 180) {
|
|
|
+ callback(new Error('请输入-180到180之间的有效经度'))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'blur'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ latitude: [
|
|
|
+ {required: true, message: '请输入纬度', trigger: 'blur'},
|
|
|
+ {
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ const num = Number(value)
|
|
|
+ if (isNaN(num) || num < -90 || num > 90) {
|
|
|
+ callback(new Error('请输入-90到90之间的有效纬度'))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'blur'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+})
|
|
|
+
|
|
|
+// 表格选择事件
|
|
|
+const handleSelectionChange = (val) => {
|
|
|
+ selectedIds.value = val.map(item => item.id)
|
|
|
+}
|
|
|
+
|
|
|
+// 重置表单
|
|
|
+const resetForm = () => {
|
|
|
+ pointFormRef.value?.resetFields()
|
|
|
+ Object.assign(pointForm, {
|
|
|
+ id: '',
|
|
|
+ name: '',
|
|
|
+ phone: '',
|
|
|
+ marker: '',
|
|
|
+ longitude: '',
|
|
|
+ latitude: ''
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 新增点位
|
|
|
+const handleAdd = () => {
|
|
|
+ dialogType.value = 'add'
|
|
|
+ resetForm()
|
|
|
+ dialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 查看点位
|
|
|
+const handleView = (row) => {
|
|
|
+ dialogType.value = 'view'
|
|
|
+ Object.assign(pointForm, {...row})
|
|
|
+ dialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 编辑点位
|
|
|
+const handleEdit = (row) => {
|
|
|
+ dialogType.value = 'edit'
|
|
|
+ Object.assign(pointForm, {...row})
|
|
|
+ dialogVisible.value = true
|
|
|
+ getById(row.id)
|
|
|
+}
|
|
|
+
|
|
|
+// 删除单个点位
|
|
|
+const handleDelete = (row) => {
|
|
|
+ ElMessageBox.confirm(
|
|
|
+ '此操作将永久删除该点位, 是否继续?',
|
|
|
+ '提示',
|
|
|
+ {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }
|
|
|
+ ).then(async () => {
|
|
|
+ const res = await request.post("/locationInfor/deleteBatch", [row.id])
|
|
|
+ if (res.code !== 200) {
|
|
|
+ ElMessage.error(res.msg)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ ElMessage.success(res.msg)
|
|
|
+ if (res) {
|
|
|
+ getList()
|
|
|
+ }
|
|
|
+ }).catch(() => {
|
|
|
+ ElMessage({
|
|
|
+ type: 'info',
|
|
|
+ message: '已取消删除'
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 批量删除
|
|
|
+const handleBatchDelete = () => {
|
|
|
+ ElMessageBox.confirm(
|
|
|
+ '此操作将永久删除选中的点位, 是否继续?',
|
|
|
+ '提示',
|
|
|
+ {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }
|
|
|
+ ).then(() => {
|
|
|
+ pointList.value = pointList.value.filter(item => !selectedIds.value.includes(item.id))
|
|
|
+ selectedIds.value = []
|
|
|
+ ElMessage({
|
|
|
+ type: 'success',
|
|
|
+ message: '批量删除成功!'
|
|
|
+ })
|
|
|
+ }).catch(() => {
|
|
|
+ ElMessage({
|
|
|
+ type: 'info',
|
|
|
+ message: '已取消删除'
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// 获取列表数据
|
|
|
+const getList = async () => {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const res = await request.get("/locationInfor/findByPage", {
|
|
|
+ params: {
|
|
|
+ pageNum: pagination.pageNum,
|
|
|
+ pageSize: pagination.pageSize,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (res.code !== 200) {
|
|
|
+ ElMessage.error(res.msg)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ pointList.value = res.data.records
|
|
|
+ total.value = res.data.total
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error('获取数据失败')
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+//根据Id查询单个
|
|
|
+const getById = async (id) => {
|
|
|
+ const res = await request.get("/locationInfor/getById/" + id)
|
|
|
+ if (res.code !== 200) {
|
|
|
+ ElMessage.error(res.msg)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ currentItem.value = res.data
|
|
|
+}
|
|
|
+
|
|
|
+// 提交表单(新增/编辑)
|
|
|
+const handleSubmit = async () => {
|
|
|
+ try {
|
|
|
+ // 表单校验
|
|
|
+ await pointFormRef.value?.validate()
|
|
|
+ let res
|
|
|
+ if (dialogType.value === 'add') {
|
|
|
+ res = await request.post("/locationInfor/save", {
|
|
|
+ name: pointForm.name,
|
|
|
+ phone: pointForm.phone,
|
|
|
+ marker: pointForm.marker,
|
|
|
+ longitude: pointForm.longitude,
|
|
|
+ latitude: pointForm.latitude
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ }
|
|
|
+ })
|
|
|
+ ElMessage.success(res.msg)
|
|
|
+ } else if (dialogType.value === 'edit') {
|
|
|
+ // 编辑:更新对应数据
|
|
|
+ res = await request.post("/locationInfor/update", {
|
|
|
+ id: currentItem.value.id,
|
|
|
+ name: pointForm.name,
|
|
|
+ phone: pointForm.phone,
|
|
|
+ marker: pointForm.marker,
|
|
|
+ longitude: pointForm.longitude,
|
|
|
+ latitude: pointForm.latitude
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ }
|
|
|
+ })
|
|
|
+ ElMessage.success(res.msg)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关闭弹窗并重置
|
|
|
+ dialogVisible.value = false
|
|
|
+ resetForm()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('表单校验失败:', error)
|
|
|
+ }
|
|
|
+ await getList()
|
|
|
+}
|
|
|
+
|
|
|
+// 搜索操作
|
|
|
+const handleSearch = () => {
|
|
|
+ // 计算属性会自动根据searchForm变化更新列表,此处可添加额外逻辑(如提示、加载状态等)
|
|
|
+ const count = filteredPointList.value.length
|
|
|
+ ElMessage.info(`共搜索到 ${count} 个点位`)
|
|
|
+}
|
|
|
+
|
|
|
+// 重置搜索
|
|
|
+const handleResetSearch = () => {
|
|
|
+ // 清空搜索表单
|
|
|
+ Object.assign(searchForm, {
|
|
|
+ name: '',
|
|
|
+ phone: '',
|
|
|
+ marker: ''
|
|
|
+ })
|
|
|
+
|
|
|
+ pagination.pageNum = 1
|
|
|
+ ElMessage.info('搜索条件已重置')
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// 分页改变
|
|
|
+const handlePageChange = (page) => {
|
|
|
+ pagination.pageNum = page
|
|
|
+ getList()
|
|
|
+}
|
|
|
+
|
|
|
+const handleSizeChange = (size) => {
|
|
|
+ pagination.pageSize = size
|
|
|
+ pagination.pageNum = 1
|
|
|
+ getList()
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ getList()
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.point-management-container {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.header h2 {
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.header-actions {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+
|
|
|
+.el-button + .el-button {
|
|
|
+ margin-left: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.search-form {
|
|
|
+ padding: 15px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 4px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+</style>
|