Kaynağa Gözat

feat(gwnrgl): 实现官网基础信息管理功能

- 新增基础信息的增删改查功能
- 支持公司简介、联系方式等字段管理
- 集成图片上传功能(公司简介图、二维码)
- 实现数据表格展示与分页功能
- 提供查看、编辑、删除等操作入口
- 添加表单验证与文件上传限制- 集成 Element Plus 组件库实现 UI 界面
- 使用 Lucide Vue 图标库优化视觉效果
- 实现批量删除与数据详情查看功能
nahida 8 ay önce
ebeveyn
işleme
dae5b795f9
1 değiştirilmiş dosya ile 720 ekleme ve 0 silme
  1. 720 0
      src/views/gwnrgl/syxxgl/index.vue

+ 720 - 0
src/views/gwnrgl/syxxgl/index.vue

@@ -0,0 +1,720 @@
+<template>
+  <div class="p-6">
+    <!-- 页面标题 -->
+    <div class="mb-6">
+      <h1 class="text-2xl font-bold text-gray-800">官网基础信息管理</h1>
+    </div>
+
+    <!-- 操作栏 -->
+    <div class="mb-4 flex justify-between items-center">
+      <div class="flex gap-3">
+        <!-- 新增按钮:仅当 tableData.length === 0 时显示 -->
+        <el-button
+            v-if="tableData.length === 0"
+            type="primary"
+            @click="handleAdd"
+            :icon="Plus"
+        >
+          新增
+        </el-button>
+        <el-button
+            type="danger"
+            :disabled="selectedIds.length === 0"
+            @click="handleBatchDelete"
+            :icon="Trash2"
+        >
+          批量删除 ({{ selectedIds.length }})
+        </el-button>
+      </div>
+
+      <!-- 已去掉搜索输入框和刷新按钮 -->
+    </div>
+
+    <!-- 数据表格 -->
+    <el-table
+        v-loading="loading"
+        :data="tableData"
+        @selection-change="handleSelectionChange"
+        stripe
+        class="w-full"
+    >
+      <el-table-column type="selection" width="55" />
+
+      <el-table-column prop="companyProfile" label="公司简介" min-width="200">
+        <template #default="{ row }">
+          <div class="truncate" :title="row.companyProfile">
+            {{ row.companyProfile || '-' }}
+          </div>
+        </template>
+      </el-table-column>
+
+      <el-table-column prop="companyProfileTwo" label="公司简介二" min-width="200">
+        <template #default="{ row }">
+          <div class="truncate" :title="row.companyProfileTwo">
+            {{ row.companyProfileTwo || '-' }}
+          </div>
+        </template>
+      </el-table-column>
+
+      <el-table-column prop="telephone" label="电话" width="120">
+        <template #default="{ row }">
+          <div class="flex items-center gap-1" v-if="row.telephone">
+            <Phone class="w-4 h-4 text-gray-500" />
+            {{ row.telephone }}
+          </div>
+          <span v-else class="text-gray-400">-</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column prop="email" label="邮箱" width="180">
+        <template #default="{ row }">
+          <div class="flex items-center gap-1" v-if="row.email">
+            <Mail class="w-4 h-4 text-gray-500" />
+            {{ row.email }}
+          </div>
+          <span v-else class="text-gray-400">-</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column prop="address" label="地址" min-width="150">
+        <template #default="{ row }">
+          <div class="flex items-center gap-1" v-if="row.address">
+            <MapPin class="w-4 h-4 text-gray-500" />
+            <span class="truncate" :title="row.address">{{ row.address }}</span>
+          </div>
+          <span v-else class="text-gray-400">-</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column prop="createTime" label="创建时间" width="160">
+        <template #default="{ row }">
+          {{ formatDate(row.createTime) }}
+        </template>
+      </el-table-column>
+
+      <el-table-column label="操作" width="180" fixed="right">
+        <template #default="{ row }">
+          <div class="flex gap-2">
+            <el-button size="small" type="primary" @click="handleView(row)" :icon="Eye">
+              查看
+            </el-button>
+            <el-button size="small" type="warning" @click="handleEdit(row)" :icon="Edit">
+              编辑
+            </el-button>
+            <el-button size="small" type="danger" @click="handleDelete(row)" :icon="Trash2">
+              删除
+            </el-button>
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页 -->
+    <div class="mt-6 flex justify-center">
+      <el-pagination
+          v-model:current-page="pagination.pageNum"
+          v-model:page-size="pagination.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+      />
+    </div>
+
+    <!-- 新增/编辑对话框 -->
+    <el-dialog
+        v-model="dialogVisible"
+        :title="dialogTitle"
+        width="800px"
+        :close-on-click-modal="false"
+    >
+      <el-form
+          ref="formRef"
+          :model="formData"
+          :rules="formRules"
+          label-width="120px"
+          class="pr-4"
+      >
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="公司简介" prop="companyProfile">
+              <el-input
+                  v-model="formData.companyProfile"
+                  type="textarea"
+                  :rows="3"
+                  placeholder="请输入公司简介"
+              />
+            </el-form-item>
+
+            <el-form-item label="公司简介二" prop="companyProfileTwo">
+              <el-input
+                  v-model="formData.companyProfileTwo"
+                  type="textarea"
+                  :rows="3"
+                  placeholder="请输入公司简介二"
+              />
+            </el-form-item>
+          </el-col>
+
+
+          <el-col :span="24">
+            <el-form-item label="公司简介图片">
+              <el-upload
+                  ref="companyImageUpload"
+                  :auto-upload="false"
+                  :show-file-list="true"
+                  :limit="1"
+                  accept="image/*"
+                  @change="handleCompanyImageChange"
+              >
+                <el-button :icon="Upload">选择图片</el-button>
+                <template #tip>
+                  <div class="text-gray-500 text-sm">只能上传jpg/png文件,且不超过2MB</div>
+                </template>
+              </el-upload>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="软件简介" prop="softwareIntroduction">
+              <el-input
+                  v-model="formData.softwareIntroduction"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="请输入软件简介"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="硬件简介" prop="hardwareIntroduction">
+              <el-input
+                  v-model="formData.hardwareIntroduction"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="请输入硬件简介"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="电话" prop="telephone">
+              <el-input
+                  v-model="formData.telephone"
+                  placeholder="请输入电话"
+                  :prefix-icon="Phone"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="服务热线" prop="serviceHotline">
+              <el-input
+                  v-model="formData.serviceHotline"
+                  placeholder="请输入服务热线"
+                  :prefix-icon="Phone"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="咨询热线" prop="consultationHotline">
+              <el-input
+                  v-model="formData.consultationHotline"
+                  placeholder="请输入咨询热线"
+                  :prefix-icon="Phone"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="邮箱" prop="email">
+              <el-input
+                  v-model="formData.email"
+                  placeholder="请输入邮箱"
+                  :prefix-icon="Mail"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="地址" prop="address">
+              <el-input
+                  v-model="formData.address"
+                  placeholder="请输入地址"
+                  :prefix-icon="MapPin"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="24">
+            <el-form-item label="二维码图片">
+              <el-upload
+                  ref="qrCodeUpload"
+                  :auto-upload="false"
+                  :show-file-list="true"
+                  :limit="1"
+                  accept="image/*"
+                  @change="handleQrCodeChange"
+              >
+                <el-button :icon="Upload">选择二维码</el-button>
+                <template #tip>
+                  <div class="text-gray-500 text-sm">只能上传jpg/png文件,且不超过2MB</div>
+                </template>
+              </el-upload>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <template #footer>
+        <div class="flex justify-end gap-3">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleSubmit" :loading="submitLoading">
+            {{ isEdit ? '更新' : '保存' }}
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 查看详情对话框 -->
+    <el-dialog
+        v-model="viewDialogVisible"
+        title="查看详情"
+        width="700px"
+    >
+      <div class="space-y-4" v-if="viewData">
+        <div class="grid grid-cols-2 gap-4">
+          <div>
+            <label class="text-gray-600 text-sm">公司简介:</label>
+            <p class="mt-1">{{ viewData.companyProfile || '-' }}</p>
+          </div>
+          <div>
+            <label class="text-gray-600 text-sm">公司简介二:</label>
+            <p class="mt-1">{{ viewData.companyProfileTwo || '-' }}</p>
+          </div>
+          <div>
+            <label class="text-gray-600 text-sm">软件简介:</label>
+            <p class="mt-1">{{ viewData.softwareIntroduction || '-' }}</p>
+          </div>
+          <div>
+            <label class="text-gray-600 text-sm">硬件简介:</label>
+            <p class="mt-1">{{ viewData.hardwareIntroduction || '-' }}</p>
+          </div>
+          <div>
+            <label class="text-gray-600 text-sm">电话:</label>
+            <p class="mt-1">{{ viewData.telephone || '-' }}</p>
+          </div>
+          <div>
+            <label class="text-gray-600 text-sm">服务热线:</label>
+            <p class="mt-1">{{ viewData.serviceHotline || '-' }}</p>
+          </div>
+          <div>
+            <label class="text-gray-600 text-sm">咨询热线:</label>
+            <p class="mt-1">{{ viewData.consultationHotline || '-' }}</p>
+          </div>
+          <div>
+            <label class="text-gray-600 text-sm">邮箱:</label>
+            <p class="mt-1">{{ viewData.email || '-' }}</p>
+          </div>
+          <div>
+            <label class="text-gray-600 text-sm">地址:</label>
+            <p class="mt-1">{{ viewData.address || '-' }}</p>
+          </div>
+        </div>
+
+        <div class="flex gap-4" v-if="viewData.companyProfileUrl || viewData.qrCodeUrl">
+          <div v-if="viewData.companyProfileUrl">
+            <label class="text-gray-600 text-sm">公司简介图片:</label>
+            <img :src="BASE_URL + viewData.companyProfileUrl" alt="公司简介" class="mt-2 max-w-48 rounded" />
+          </div>
+          <div v-if="viewData.qrCodeUrl">
+            <label class="text-gray-600 text-sm">二维码:</label>
+            <img :src="BASE_URL + viewData.qrCodeUrl" alt="二维码" class="mt-2 max-w-48 rounded" />
+          </div>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {computed, onMounted, reactive, ref} from 'vue'
+import {ElMessage, ElMessageBox} from 'element-plus'
+import {Edit, Eye, Mail, MapPin, Phone, Plus, Trash2, Upload} from 'lucide-vue-next'
+import request from "@/utils/request.js";
+
+// 响应式数据
+const loading = ref(false)
+const submitLoading = ref(false)
+const tableData = ref([])
+const selectedIds = ref([])
+const searchKeyword = ref('')
+const dialogVisible = ref(false)
+const viewDialogVisible = ref(false)
+const isEdit = ref(false)
+const viewData = ref(null)
+
+const BASE_URL = import.meta.env.VITE_APP_BASE_URL
+
+// 分页数据
+const pagination = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// 表单数据
+const formData = reactive({
+  id: '',
+  companyProfile: '',
+  companyProfileTwo: '',
+  softwareIntroduction: '',
+  hardwareIntroduction: '',
+  telephone: '',
+  serviceHotline: '',
+  consultationHotline: '',
+  email: '',
+  address: ''
+})
+
+// 文件数据
+const companyImageFile = ref(null)
+const qrCodeFile = ref(null)
+
+// 表单引用
+const formRef = ref(null)
+const companyImageUpload = ref(null)
+const qrCodeUpload = ref(null)
+
+// 表单验证规则
+const formRules = {
+  companyProfile: [
+    { required: true, message: '请输入公司简介', trigger: 'blur' }
+  ],
+  email: [
+    { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
+  ]
+}
+
+// 计算属性
+const dialogTitle = computed(() => {
+  return isEdit.value ? '编辑基础信息' : '新增基础信息'
+})
+
+// 获取列表数据
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await apiGetList(pagination.pageNum, pagination.pageSize)
+    if (data) {
+      tableData.value = data.records || []
+      pagination.total = data.total || 0
+    }
+  } finally {
+    loading.value = false
+  }
+}
+
+// 事件处理函数
+const handleAdd = () => {
+  isEdit.value = false
+  resetForm()
+  dialogVisible.value = true
+}
+
+const handleEdit = async (row) => {
+  isEdit.value = true
+  const data = await getById(row.id)
+  if (data) {
+    Object.assign(formData, data)
+    dialogVisible.value = true
+  }
+}
+
+const handleView = async (row) => {
+  const data = await getById(row.id)
+  if (data) {
+    viewData.value = data
+    viewDialogVisible.value = true
+  }
+}
+
+const handleDelete = async (row) => {
+  try {
+    await ElMessageBox.confirm(
+        '确定要删除这条记录吗?',
+        '删除确认',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }
+    )
+
+    const success = await batchDelete([row.id])
+    if (success) {
+      await getList()
+    }
+  } catch (error) {
+    // 用户取消删除
+  }
+}
+
+const handleBatchDelete = async () => {
+  if (selectedIds.value.length === 0) {
+    ElMessage.warning('请选择要删除的记录')
+    return
+  }
+
+  try {
+    await ElMessageBox.confirm(
+        `确定要删除选中的 ${selectedIds.value.length} 条记录吗?`,
+        '批量删除确认',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }
+    )
+
+    const success = await batchDelete(selectedIds.value)
+    if (success) {
+      selectedIds.value = []
+      await getList()
+    }
+  } catch (error) {
+    // 用户取消删除
+  }
+}
+
+const handleSelectionChange = (selection) => {
+  selectedIds.value = selection.map(item => item.id)
+}
+
+const handleSearch = () => {
+  // 这里可以实现搜索逻辑
+  // 由于接口没有提供搜索参数,这里只是演示
+  console.log('搜索关键词:', searchKeyword.value)
+}
+
+const refreshList = () => {
+  getList()
+}
+
+const handleSizeChange = (size) => {
+  pagination.pageSize = size
+  pagination.pageNum = 1
+  getList()
+}
+
+const handleCurrentChange = (page) => {
+  pagination.pageNum = page
+  getList()
+}
+
+const handleCompanyImageChange = (file) => {
+  companyImageFile.value = file.raw
+}
+
+const handleQrCodeChange = (file) => {
+  qrCodeFile.value = file.raw
+}
+
+const handleSubmit = async () => {
+  if (!formRef.value) return
+
+  try {
+    await formRef.value.validate()
+
+    submitLoading.value = true
+
+    // 构建FormData
+    const submitFormData = new FormData()
+
+    if (isEdit.value) {
+      submitFormData.append('id', formData.id)
+    }
+
+    submitFormData.append('companyProfile', formData.companyProfile || '')
+    submitFormData.append('companyProfileTwo', formData.companyProfileTwo || '')
+    submitFormData.append('softwareIntroduction', formData.softwareIntroduction || '')
+    submitFormData.append('hardwareIntroduction', formData.hardwareIntroduction || '')
+    submitFormData.append('telephone', formData.telephone || '')
+    submitFormData.append('serviceHotline', formData.serviceHotline || '')
+    submitFormData.append('consultationHotline', formData.consultationHotline || '')
+    submitFormData.append('email', formData.email || '')
+    submitFormData.append('address', formData.address || '')
+
+    // 添加文件
+    if (companyImageFile.value) {
+      submitFormData.append('companyProMultipartFile', companyImageFile.value)
+    }
+
+    if (qrCodeFile.value) {
+      submitFormData.append('qrCodeMultipartFile', qrCodeFile.value)
+    }
+
+    let success = false
+    if (isEdit.value) {
+      success = await update(submitFormData)
+    } else {
+      success = await save(submitFormData)
+    }
+
+    if (success) {
+      dialogVisible.value = false
+      await getList()
+    }
+  } catch (error) {
+    console.error('表单验证失败:', error)
+  } finally {
+    submitLoading.value = false
+  }
+}
+
+const resetForm = () => {
+  Object.assign(formData, {
+    id: '',
+    companyProfile: '',
+    companyProfileTwo: '',
+    softwareIntroduction: '',
+    hardwareIntroduction: '',
+    telephone: '',
+    serviceHotline: '',
+    consultationHotline: '',
+    email: '',
+    address: ''
+  })
+
+  companyImageFile.value = null
+  qrCodeFile.value = null
+
+  // 清空上传组件
+  if (companyImageUpload.value) {
+    companyImageUpload.value.clearFiles()
+  }
+  if (qrCodeUpload.value) {
+    qrCodeUpload.value.clearFiles()
+  }
+
+  if (formRef.value) {
+    formRef.value.resetFields()
+  }
+}
+
+const formatDate = (dateString) => {
+  if (!dateString) return '-'
+  return new Date(dateString).toLocaleString('zh-CN')
+}
+
+// 获取分页列表
+const apiGetList = async (pageNum = 1, pageSize = 10) => {
+  try {
+    const res = await request.get("/basicInfo/findByPage", {
+      params: {
+        pageNum,
+        pageSize,
+      },
+    })
+    if (res.code !== 200) {
+      ElMessage.error(res.msg)
+      return null
+    }
+    return res.data
+  } catch (error) {
+    ElMessage.error("获取列表失败")
+    console.error(error)
+    return null
+  }
+}
+
+// 根据ID获取详情
+const getById = async (id) => {
+  try {
+    const res = await request.get("/basicInfo/getById/" + id)
+    if (res.code !== 200) {
+      ElMessage.error(res.msg)
+      return null
+    }
+    return res.data
+  } catch (error) {
+    ElMessage.error("获取详情失败")
+    console.error(error)
+    return null
+  }
+}
+
+// 保存数据
+const save = async (formData) => {
+  try {
+    const res = await request.post("/basicInfo/save", formData, {
+      headers: {
+        "Content-Type": "multipart/form-data",
+      },
+    })
+    if (res.code !== 200) {
+      ElMessage.error(res.msg)
+      return false
+    }
+    ElMessage.success(res.msg)
+    return true
+  } catch (error) {
+    ElMessage.error("保存失败")
+    console.error(error)
+    return false
+  }
+}
+
+// 更新数据
+const update = async (formData) => {
+  try {
+    const res = await request.post("/basicInfo/update", formData, {
+      headers: {
+        "Content-Type": "multipart/form-data",
+      },
+    })
+    if (res.code !== 200) {
+      ElMessage.error(res.msg)
+      return false
+    }
+    ElMessage.success(res.msg)
+    return true
+  } catch (error) {
+    ElMessage.error("更新失败")
+    console.error(error)
+    return false
+  }
+}
+
+// 批量删除
+const batchDelete = async (ids) => {
+  try {
+    const res = await request.post("/basicInfo/deleteBatch", ids)
+    if (res.code !== 200) {
+      ElMessage.error(res.msg)
+      return false
+    }
+    ElMessage.success(res.msg)
+    return true
+  } catch (error) {
+    ElMessage.error("删除失败")
+    console.error(error)
+    return false
+  }
+}
+
+// 生命周期
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped>
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+</style>