فهرست منبع

邮件、公告、白名单

Administrator 1 سال پیش
والد
کامیت
9960f797ab

+ 54 - 0
src/api/announcement.ts

@@ -0,0 +1,54 @@
+import request from './request'
+
+
+export default {
+
+    add(data) {
+        return request({
+            url: '/announcement/add',
+            method: 'post',
+            data
+        });
+    },
+
+    query(data) {
+        return request({
+            url: '/announcement/query',
+            method: 'post',
+            data
+        });
+    },
+
+    update(data) {
+        return request({
+            url: '/announcement/update',
+            method: 'put',
+            data
+        });
+    },
+
+    delete(data) {
+        return request({
+            url: '/announcement/delete',
+            method: 'delete',
+            data
+        });
+    },
+
+    deleteBatch(data) {
+        return request({
+            url: '/announcement/delete_batch',
+            method: 'delete',  
+            data
+        });
+    },
+
+    importBatch(data) {
+        return request({
+            url: '/announcement/import_batch',
+            method: 'post',  
+            data
+        });
+    }
+
+}

+ 10 - 2
src/api/gmMail.ts

@@ -3,9 +3,9 @@ import request from './request'
 
 export default {
 
-    send(data) {
+    add(data) {
         return request({
-            url: '/gm_mail/send',
+            url: '/gm_mail/add',
             method: 'post',
             data
         });
@@ -41,6 +41,14 @@ export default {
             method: 'delete',  
             data
         });
+    },
+
+    importBatch(data) {
+        return request({
+            url: '/gm_mail/import_batch',
+            method: 'post',  
+            data
+        });
     }
 
 }

+ 54 - 0
src/api/whiteList.ts

@@ -0,0 +1,54 @@
+import request from './request'
+
+
+export default {
+
+    add(data) {
+        return request({
+            url: '/white_list/add',
+            method: 'post',
+            data
+        });
+    },
+
+    query(data) {
+        return request({
+            url: '/white_list/query',
+            method: 'post',
+            data
+        });
+    },
+
+    update(data) {
+        return request({
+            url: '/white_list/update',
+            method: 'put',
+            data
+        });
+    },
+
+    delete(data) {
+        return request({
+            url: '/white_list/delete',
+            method: 'delete',
+            data
+        });
+    },
+
+    deleteBatch(data) {
+        return request({
+            url: '/white_list/delete_batch',
+            method: 'delete',  
+            data
+        });
+    },
+
+    importBatch(data) {
+        return request({
+            url: '/white_list/import_batch',
+            method: 'post',  
+            data
+        });
+    }
+
+}

+ 1 - 1
src/components/Table/PropTable/index.vue

@@ -111,7 +111,7 @@
 </template>
 <script lang="ts" setup>
 import { computed, ref } from 'vue'
-import {Search } from '@element-plus/icons-vue'
+import { Search } from '@element-plus/icons-vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import type { FormInstance } from 'element-plus'
 const ruleFormRef = ref<FormInstance>()

+ 2 - 0
src/views/excel/uploadExcel/index.vue

@@ -75,6 +75,8 @@
           })
           tableData.value = table
           tableHeader.value = headers
+          console.log("table", table)
+          console.log("headers", headers)
         } catch (e) {
           ElMessage.error('解析失败')
         }

+ 293 - 46
src/views/operation/announcement/index.vue

@@ -1,16 +1,24 @@
 <template>
   <div class="app-container" ref="appContainer">
-    <ServerPropTable :loading="loading" @selection-change="selectionChange" :columns="column" :data="list" @reset="reset"
+    <PropTable :loading="loading" @selection-change="selectionChange" :columns="column" :data="list" @reset="reset"
       @onSubmit="onSubmit">
       <template v-slot:btn>
         <div style="display: flex; justify-content: flex-end">
-          <el-button type="primary" @click="add"
-          ><el-icon><plus /></el-icon> 添加</el-button>
+          <el-button type="primary" @click="importByExcel"><el-icon>
+            <Upload />
+          </el-icon>导入</el-button>
+          <el-button type="primary" @click="add"><el-icon>
+              <plus />
+            </el-icon>添加</el-button>
           <el-button type="danger" @click="batchDelete"><el-icon>
               <delete />
             </el-icon>删除</el-button>
         </div>
       </template>
+      <template v-slot:type="scope">{{ getTypeLabel(scope.row.type) }}</template>
+      <template v-slot:validTime="scope">{{ dateUtils.getDate(scope.row.validTime) }}</template>
+      <template v-slot:invalidTime="scope">{{ dateUtils.getDate(scope.row.invalidTime) }}</template>
+      <template v-slot:createTime="scope">{{ dateUtils.getDate(scope.row.createTime) }}</template>
       <template v-slot:operation="scope">
         <el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
           编辑
@@ -19,23 +27,30 @@
           删除
         </el-button>
       </template>
-    </ServerPropTable>
+    </PropTable>
 
     <el-dialog v-model="dialogVisible" :title='title' width="50%">
       <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="120px" class="demo-ruleForm"
         :size="formSize">
-        <el-form-item label="ID" prop="id">
+        <el-form-item label="ID" prop="id" v-show="isIdShow">
           <el-input readonly v-model="ruleForm.id" />
         </el-form-item>
         <el-form-item label="公告标题" prop="title">
           <el-input v-model="ruleForm.title" />
         </el-form-item>
+        <el-form-item label="公共类型" prop="type">
+          <el-radio-group v-model="ruleForm.type">
+            <el-radio :label="0">运营公告</el-radio>
+            <el-radio :label="1">维护更新</el-radio>
+          </el-radio-group>
+        </el-form-item>
         <el-form-item label="公告生效时间" required>
           <el-col :span="11">
             <el-form-item prop="validTime">
               <el-date-picker
                   v-model="ruleForm.validTime"
                   type="datetime"
+                  value-format="YYYY-MM-DD HH:mm:ss"
                   placeholder="选择开始时间"
                   style="width: 100%"
               />
@@ -46,7 +61,7 @@
           </el-col>
           <el-col :span="11">
             <el-form-item prop="invalidTime">
-              <el-date-picker v-model="ruleForm.invalidTime"  type="datetime" placeholder="选择结束时间" style="width: 100%" />
+              <el-date-picker v-model="ruleForm.invalidTime"  type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择结束时间" style="width: 100%" />
             </el-form-item>
           </el-col>
         </el-form-item>
@@ -61,23 +76,129 @@
         </span>
       </template>
     </el-dialog>
+
+    <el-dialog v-model="importVisible" :title='title' width="70%" @close="importClose">
+      <PageWrapLayout class="m-upload-excel">
+        <el-upload style="width: 100%" ref="upload" class="upload-demo" drag action="/" :before-upload="beforeUploadAction" type="file"
+          accept=".xlsx, .xls" :limit="1" :on-remove="handleRemove">
+          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+          <div class="el-upload__text"> 拖拽上传 <em>或者点击上传 Excel</em> </div>
+        </el-upload>
+        <div>
+          <el-table :data="tableData" border highlight-current-row style="width: 100%; margin-top: 20px">
+            <el-table-column v-for="item of tableHeader" :prop="item.value" :label="item.label" :key="item">
+            </el-table-column>
+          </el-table>
+        </div>
+      </PageWrapLayout>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="importClose">取消</el-button>
+          <el-button type="primary" @click="handleConfirm">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
   </div>
 </template>
 <script lang="ts" setup name="comprehensive">
-import { ref, reactive, onMounted, nextTick } from 'vue'
+import { ref, reactive, onMounted, nextTick, onBeforeMount } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import type { FormInstance } from 'element-plus'
+import announcementApi from '@/api/announcement'
+import dateUtils from '@/common/dateUtils'
+
 const loading = ref(true)
 const appContainer = ref(null)
-import ServerPropTable from '@/components/Table/ServerPropTable/index.vue'
-const data = []
-for (let i = 0; i < 100; i++) {
-  data.push({
-    id: i + 1,
-    title: '公告' + (i+1),
-    validTime: '2023-08-21 10:00:00',
-    invalidTime: '2023-09-21 10:00:00',
-    content: '公告内容',
+import PropTable from '@/components/Table/PropTable/index.vue'
+
+// import { ref } from 'vue'
+
+import ExcelJS from 'exceljs'
+import { UploadFilled } from '@element-plus/icons-vue'
+import type { UploadProps, UploadInstance } from 'element-plus'
+// import { ElMessage } from 'element-plus'
+const tableData = ref([])
+const tableHeader = ref([])
+const upload = ref<UploadInstance>()
+
+const tableColumn = ["title","type","validTime","invalidTime","content"]
+const beforeUploadAction = (file, fileLi) => {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.onload = async (e) => {
+      const data = e.target.result
+      const workbook = new ExcelJS.Workbook()
+      try {
+        let res = await workbook.xlsx.load(data)
+        const sheets =
+          res._worksheets && res._worksheets.filter((item) => typeof item !== 'undefined')
+        const table = []
+        let headers = []
+        sheets.forEach((sheet) => {
+          sheet._rows.forEach((row, index) => {
+            if (index === 0) {
+              row.values.forEach((it, i) => {
+                // console.log("string" + it + " " + i + "" + tableColumn[i])
+                headers.push({label: it, value: tableColumn[i - 1]})
+              })
+            } else {
+              let obj = {}
+              let arr = []
+              row.values.forEach((it) => {
+                arr.push(it)
+              })
+              tableColumn.forEach((ite, i) => {
+                if (arr[i] == undefined)  {
+                  obj[ite] = null
+                } else {
+                  obj[ite] = arr[i]
+                }
+              })
+              table.push(obj)
+            }
+            // const tableRow = {
+            //   position: "",
+            //   val: "",
+            // };
+            // row._cells.forEach((cell) => {
+            //   tableRow.position = cell._address;
+            //   tableRow.val = cell._value.model.value || "";
+            // });
+          })
+        })
+        tableData.value = table
+        tableHeader.value = headers
+        console.log("table", table)
+        console.log("headers", headers)
+      } catch (e) {
+        ElMessage.error('解析失败')
+      }
+    }
+    reader.readAsArrayBuffer(file)
+  })
+}
+
+const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
+  tableData.value = null
+  tableHeader.value = null
+}
+
+const data = ref([])
+let currPageNum = 1;
+const pageSize = 1000;
+onBeforeMount(() => {
+  loadAnnouncementInfo()
+})
+
+const loadAnnouncementInfo = () => {
+  let queryParams = reactive({
+    pageNum: currPageNum,
+    pageSize: pageSize
+  })
+  announcementApi.query(queryParams).then(res => {
+    data.value = res.data.result;
+    console.log(data)
   })
 }
 
@@ -85,18 +206,41 @@ const column = [
   { type: 'selection', width: 60, fixed: 'left' },
   { name: 'id', label: 'ID', width: 80 },
   { name: 'title', label: '公告标题', inSearch: true, valueType: 'input' },
-  { name: 'validTime', label: '公告开始时间'},
-  { name: 'invalidTime', label: '公告截止时间'},
+  { name: 'type', label: '公共类型', slot: true, inSearch: true,
+    options: [
+      {
+        value: 0,
+        label: '运营公告',
+      },
+      {
+        value: 1,
+        label: '维护更新',
+      }
+    ],
+    valueType: 'select',},
+  { name: 'validTime', label: '公告开始时间', slot: true, valueType: 'datetime'},
+  { name: 'invalidTime', label: '公告截止时间', slot: true, valueType: 'datetime'},
   { name: 'content', label: '公告内容'},
+  { name: 'createTime', label: '公告创建时间', slot: true },
   { name: 'operation', slot: true, fixed: 'right', width: 200, label: '操作' },
 ]
 
+const getTypeLabel = (val) => {
+  if (val == 0) {
+    return '运营公告'
+  } else if (val == 1) {
+    return '维护更新'
+  }
+}
+
 const list = ref(data)
 
 const formSize = ref('default')
 const ruleFormRef = ref<FormInstance>()
+
 const ruleForm = reactive({
     id: null,
+    type: null,
     title: null,
     validTime: null,
     invalidTime: null,
@@ -104,6 +248,13 @@ const ruleForm = reactive({
 })
 
 const rules = reactive({
+  type: [
+    {
+      required: true,
+      message: '请输入公告类型',
+      trigger: 'change',
+    },
+  ],
   title: [
     {
       required: true,
@@ -138,28 +289,90 @@ const rules = reactive({
 
 
 const dialogVisible = ref(false)
+const importVisible = ref(false)
+
 const rowObj = ref({})
 const selectObj = ref([])
-const title = ref('编辑')
+const title = ref('详情')
+const isIdShow = ref(true)
+
+const importClose = () => {
+  importVisible.value = false
+  upload.value!.clearFiles()
+  tableData.value = null
+  tableHeader.value = null
+}
+
+const importByExcel = () => {
+  title.value = '导入'
+  importVisible.value = true
+  tableData.value = null
+  tableHeader.value = null
+}
 
 const add = () => {
   title.value = '新增'
+  isIdShow.value = false
   dialogVisible.value = true
+  ruleForm.id = null
+  ruleForm.title = null
+  ruleForm.type = null
+  ruleForm.validTime = null
+  ruleForm.invalidTime = null
+  ruleForm.content = null
+}
+
+const handleConfirm = () => {
+  // 验证table数据
+  // ...
+  let importParams = reactive({
+    importValue: tableData.value
+  })
+  announcementApi.importBatch(importParams).then(res => {
+    console.log('result', res)
+    if (res.data.code = 200) {
+      loadAnnouncementInfo()
+      importVisible.value = false
+      return ElMessage.success(res.data.msg)
+    } else {
+      return ElMessage.error(res.data.msg)
+    }
+  })
+  console.log('import submit!')
 }
 
 const handleClose = async (done: () => void) => {
   await ruleFormRef.value.validate((valid, fields) => {
     if (valid) {
-      list.value.forEach((item) => {
-        if (item.id === rowObj.value.id) {
-          item.title = ruleForm.title
-          item.validTime = ruleForm.validTime
-          item.invalidTime = ruleForm.invalidTime
-          item.content = ruleForm.content
-        }
-      })
-      dialogVisible.value = false
-      console.log('submit!', ruleForm.title)
+      if (title.value == '新增') {
+        announcementApi.add(ruleForm).then(res => {
+          console.log('result', res)
+          if (res.data.code = 200) {
+            loadAnnouncementInfo()
+            dialogVisible.value = false
+            return ElMessage.success(res.data.msg)
+          } else {
+            return ElMessage.error(res.data.msg)
+          }
+        })
+        console.log('add submit!')
+      } else {
+        // let updateParams = reactive({
+        //   id: rowObj.value.id,
+        //   updateValue: ruleForm
+        // })
+        announcementApi.update(ruleForm).then(res => {
+          if (res.data.code = 200) {
+            loadAnnouncementInfo()
+            dialogVisible.value = false
+            return ElMessage.success('修改成功')
+          } else {
+            return ElMessage.error('修改失败')
+          }
+        })
+        console.log('update submit!')
+      }
+      console.log('submit!', ruleForm)
     } else {
       console.log('error submit!', fields)
     }
@@ -177,22 +390,38 @@ const batchDelete = () => {
     draggable: true,
   })
     .then(() => {
-      ElMessage.success('模拟删除成功')
+      let selectAnnouncementIds = reactive([])
+      for (let value of selectObj.value) {
+        selectAnnouncementIds.push(value.id)
+      }
+      let deleteParams = reactive({
+        announcementIds: selectAnnouncementIds
+      })
+      announcementApi.deleteBatch(deleteParams).then(res => {
+        loadAnnouncementInfo()
+        ElMessage.success('批量删除成功')
+      })
+
       list.value = list.value.concat([])
     })
     .catch(() => { })
 }
+
+
 const selectionChange = (val) => {
   selectObj.value = val
 }
 
 const edit = (row) => {
+  title.value = '编辑'
+  isIdShow.value = true
   rowObj.value = row
   dialogVisible.value = true
   ruleForm.id = row.id
+  ruleForm.type = row.type
   ruleForm.title = row.title
-  ruleForm.validTime = row.validTime
-  ruleForm.invalidTime = row.invalidTime
+  ruleForm.validTime = dateUtils.getDate(row.validTime)
+  ruleForm.invalidTime = dateUtils.getDate(row.invalidTime)
   ruleForm.content = row.content
 }
 
@@ -205,8 +434,13 @@ const del = (row) => {
     draggable: true,
   })
     .then(() => {
-      list.value = list.value.filter((item) => item.id !== row.id)
-      ElMessage.success('删除成功')
+      let deleteParams = reactive({
+        id: row.id
+      })
+      announcementApi.delete(deleteParams).then(res => {
+        loadAnnouncementInfo()
+        ElMessage.success('删除成功')
+      })
       loading.value = true
       setTimeout(() => {
         loading.value = false
@@ -216,25 +450,28 @@ const del = (row) => {
 }
 
 const reset = () => {
+  loadAnnouncementInfo()
   loading.value = true
   setTimeout(() => {
     loading.value = false
   }, 500)
-  ElMessage.success('触发重置方法')
+  ElMessage.success('重置成功')
 }
 
 const onSubmit = (val) => {
-  console.log('val===', val)
-  ElMessage.success('触发查询方法')
-  loading.value = true
-  setTimeout(() => {
-    loading.value = false
-  }, 500)
-}
-
-const onUpdate = (val) => {
-  console.log('val===', val)
-  ElMessage.success('触发修改方法')
+  if ((val.title == null || val.title == "") && val.type == null) {
+    ElMessage.warning('请输入查询条件')
+    return
+  }
+  let queryParams = reactive({
+    pageNum: 1,
+    pageSize: pageSize,
+    condition: val
+  })
+  announcementApi.query(queryParams).then(res => {
+    data.value = res.data.result;
+    console.log(data.value)
+  })
   loading.value = true
   setTimeout(() => {
     loading.value = false
@@ -273,4 +510,14 @@ onMounted(() => {
   right: 15px;
   top: 10px;
 }
+
+.m-upload-excel {
+  .el-upload {
+    width: 100%;
+  }
+
+  .el-upload-dragger {
+    width: 100%;
+  }
+}
 </style>

+ 174 - 13
src/views/operation/gmmail/index.vue

@@ -4,9 +4,12 @@
       @onSubmit="onSubmit">
       <template v-slot:btn>
         <div style="display: flex; justify-content: flex-end">
+          <el-button type="primary" @click="importByExcel"><el-icon>
+            <Upload />
+          </el-icon>导入</el-button>
           <el-button type="primary" @click="add"><el-icon>
               <plus />
-            </el-icon> 添加</el-button>
+            </el-icon>添加</el-button>
           <el-button type="danger" @click="batchDelete"><el-icon>
               <delete />
             </el-icon>删除</el-button>
@@ -103,6 +106,29 @@
         </span>
       </template>
     </el-dialog>
+
+    <el-dialog v-model="importVisible" :title='title' width="70%" @close="importClose">
+      <PageWrapLayout class="m-upload-excel">
+        <el-upload style="width: 100%" ref="upload" class="upload-demo" drag action="/" :before-upload="beforeUploadAction" type="file"
+          accept=".xlsx, .xls" :limit="1" :on-remove="handleRemove">
+          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+          <div class="el-upload__text"> 拖拽上传 <em>或者点击上传 Excel</em> </div>
+        </el-upload>
+        <div>
+          <el-table :data="tableData" border highlight-current-row style="width: 100%; margin-top: 20px">
+            <el-table-column v-for="item of tableHeader" :prop="item.value" :label="item.label" :key="item">
+            </el-table-column>
+          </el-table>
+        </div>
+      </PageWrapLayout>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="importClose">取消</el-button>
+          <el-button type="primary" @click="handleConfirm">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
   </div>
 </template>
 <script lang="ts" setup name="comprehensive">
@@ -116,9 +142,82 @@ const loading = ref(true)
 const appContainer = ref(null)
 import PropTable from '@/components/Table/PropTable/index.vue'
 
+// import { ref } from 'vue'
+
+import ExcelJS from 'exceljs'
+import { UploadFilled } from '@element-plus/icons-vue'
+import type { UploadProps, UploadInstance } from 'element-plus'
+// import { ElMessage } from 'element-plus'
+const tableData = ref([])
+const tableHeader = ref([])
+const upload = ref<UploadInstance>()
+
+const tableColumn = ["title","sendName","items","validTime","invalidTime",
+  "content","toServerIds","toPlayerIds","levelCondition","roleCreateTimeStart","roleCreateTimeEnd"]
+const beforeUploadAction = (file, fileLi) => {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.onload = async (e) => {
+      const data = e.target.result
+      const workbook = new ExcelJS.Workbook()
+      try {
+        let res = await workbook.xlsx.load(data)
+        const sheets =
+          res._worksheets && res._worksheets.filter((item) => typeof item !== 'undefined')
+        const table = []
+        let headers = []
+        sheets.forEach((sheet) => {
+          sheet._rows.forEach((row, index) => {
+            if (index === 0) {
+              row.values.forEach((it, i) => {
+                // console.log("string" + it + " " + i + "" + tableColumn[i])
+                headers.push({label: it, value: tableColumn[i - 1]})
+              })
+            } else {
+              let obj = {}
+              let arr = []
+              row.values.forEach((it) => {
+                arr.push(it)
+              })
+              tableColumn.forEach((ite, i) => {
+                if (arr[i] == undefined)  {
+                  obj[ite] = null
+                } else {
+                  obj[ite] = String(arr[i])
+                }
+              })
+              table.push(obj)
+            }
+            // const tableRow = {
+            //   position: "",
+            //   val: "",
+            // };
+            // row._cells.forEach((cell) => {
+            //   tableRow.position = cell._address;
+            //   tableRow.val = cell._value.model.value || "";
+            // });
+          })
+        })
+        tableData.value = table
+        tableHeader.value = headers
+        console.log("table", table)
+        console.log("headers", headers)
+      } catch (e) {
+        ElMessage.error('解析失败')
+      }
+    }
+    reader.readAsArrayBuffer(file)
+  })
+}
+
+const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
+  tableData.value = null
+  tableHeader.value = null
+}
+
 const data = ref([])
 let currPageNum = 1;
-const pageSize = 10;
+const pageSize = 1000;
 onBeforeMount(() => {
   loadGmMailInfo()
 })
@@ -201,22 +300,23 @@ const rules = reactive({
       required: true,
       message: '请输入邮件标题',
       trigger: 'change',
-    },
+      
+    }, {max: 7, message: "邮件标题长度不能超过7字"}
   ],
   sendName: [
     {
       required: true,
       message: '请输入发送者名称',
       trigger: 'change',
-    },
-  ],
-  items: [
-    {
-      required: true,
-      message: '请输入邮件道具',
-      trigger: 'change',
-    },
+    }, {max: 10, message: "发送者名称长度不能超过10字"}
   ],
+  // items: [
+  //   {
+  //     required: true,
+  //     message: '请输入邮件道具',
+  //     trigger: 'change',
+  //   },
+  // ],
   validTime: [
     {
       required: true,
@@ -238,17 +338,33 @@ const rules = reactive({
       required: true,
       message: '请输入邮件内容',
       trigger: 'change',
-    },
+    }, {max: 3000, message: "邮件内容长度不能超过3000字"}
   ],
 })
 
 
 const dialogVisible = ref(false)
+const importVisible = ref(false)
+
 const rowObj = ref({})
 const selectObj = ref([])
 const title = ref('详情')
 const isIdShow = ref(true)
 
+const importClose = () => {
+  importVisible.value = false
+  upload.value!.clearFiles()
+  tableData.value = null
+  tableHeader.value = null
+}
+
+const importByExcel = () => {
+  title.value = '导入'
+  importVisible.value = true
+  tableData.value = null
+  tableHeader.value = null
+}
+
 const add = () => {
   title.value = '新增'
   isIdShow.value = false
@@ -269,11 +385,46 @@ const add = () => {
   ruleForm.content = null
 }
 
+const handleConfirm = () => {
+  // 验证table数据
+  let isValid = true
+  tableData.value.forEach((ite) => {
+    if (ite["title"].length > 7) {
+      isValid = false
+      return ElMessage.error("邮件标题长度不能超过7字")
+    }
+    if (ite["sendName"].length > 10) {
+      isValid = false
+      return ElMessage.error("发送者名称长度不能超过10字")
+    }
+    if (ite["content"].length > 3000) {
+      isValid = false
+      return ElMessage.error("邮件内容长度不能超过3000字")
+    }      
+  })
+  if (isValid) {
+    let importParams = reactive({
+      importValue: tableData.value
+    })
+    gmMailApi.importBatch(importParams).then(res => {
+      console.log('result', res)
+      if (res.data.code = 200) {
+        loadGmMailInfo()
+        importVisible.value = false
+        return ElMessage.success(res.data.msg)
+      } else {
+        return ElMessage.error(res.data.msg)
+      }
+    })
+    console.log('import submit!')
+  }
+}
+
 const handleClose = async (done: () => void) => {
   await ruleFormRef.value.validate((valid, fields) => {
     if (valid) {
       if (title.value == '新增') {
-        gmMailApi.send(ruleForm).then(res => {
+        gmMailApi.add(ruleForm).then(res => {
           console.log('result', res)
           if (res.data.code = 200) {
             loadGmMailInfo()
@@ -449,4 +600,14 @@ onMounted(() => {
   right: 15px;
   top: 10px;
 }
+
+.m-upload-excel {
+  .el-upload {
+    width: 100%;
+  }
+
+  .el-upload-dragger {
+    width: 100%;
+  }
+}
 </style>

+ 247 - 43
src/views/operation/whitelist/index.vue

@@ -1,9 +1,12 @@
 <template>
   <div class="app-container" ref="appContainer">
-    <ServerPropTable :loading="loading" @selection-change="selectionChange" :columns="column" :data="list" @reset="reset"
+    <PropTable :loading="loading" @selection-change="selectionChange" :columns="column" :data="list" @reset="reset"
       @onSubmit="onSubmit">
       <template v-slot:btn>
         <div style="display: flex; justify-content: flex-end">
+          <el-button type="primary" @click="importByExcel"><el-icon>
+            <Upload />
+          </el-icon>导入</el-button>
           <el-button type="primary" @click="add"
           ><el-icon><plus /></el-icon> 添加</el-button>
           <el-button type="danger" @click="batchDelete"><el-icon>
@@ -12,19 +15,23 @@
         </div>
       </template>
       <template v-slot:type="scope">{{ getStateLabel(scope.row.type) }}</template>
+      <template v-slot:createTime="scope">{{ dateUtils.getDate(scope.row.createTime) }}</template>
       <template v-slot:operation="scope">
-        <el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
+        <!-- <el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
           编辑
-        </el-button>
+        </el-button> -->
         <el-button @click="del(scope.row)" type="danger" size="small" icon="Delete">
           删除
         </el-button>
       </template>
-    </ServerPropTable>
+    </PropTable>
 
     <el-dialog v-model="dialogVisible" :title='title' width="50%">
       <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="120px" class="demo-ruleForm"
         :size="formSize">
+        <el-form-item label="白名单值" prop="value">
+          <el-input v-model="ruleForm.value" />
+        </el-form-item>
         <el-form-item label="白名单类型" prop="type">
           <el-radio-group v-model="ruleForm.type">
             <el-radio :label="0">IP</el-radio>
@@ -32,9 +39,6 @@
             <el-radio :label="2">角色</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item label="白名单值" prop="value">
-          <el-input v-model="ruleForm.value" />
-        </el-form-item>
       </el-form>
       <template #footer>
         <span class="dialog-footer">
@@ -43,27 +47,141 @@
         </span>
       </template>
     </el-dialog>
+
+    <el-dialog v-model="importVisible" :title='title' width="70%" @close="importClose">
+      <PageWrapLayout class="m-upload-excel">
+        <el-upload style="width: 100%" ref="upload" class="upload-demo" drag action="/" :before-upload="beforeUploadAction" type="file"
+          accept=".xlsx, .xls" :limit="1" :on-remove="handleRemove">
+          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+          <div class="el-upload__text"> 拖拽上传 <em>或者点击上传 Excel</em> </div>
+        </el-upload>
+        <div>
+          <el-table :data="tableData" border highlight-current-row style="width: 100%; margin-top: 20px">
+            <el-table-column v-for="item of tableHeader" :prop="item.value" :label="item.label" :key="item">
+            </el-table-column>
+          </el-table>
+        </div>
+      </PageWrapLayout>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="importClose">取消</el-button>
+          <el-button type="primary" @click="handleConfirm">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
   </div>
 </template>
 <script lang="ts" setup name="comprehensive">
-import { ref, reactive, onMounted, nextTick } from 'vue'
+import { ref, reactive, onMounted, nextTick, onBeforeMount } from 'vue'
 import * as dayjs from 'dayjs'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import type { FormInstance } from 'element-plus'
 const loading = ref(true)
 const appContainer = ref(null)
-import ServerPropTable from '@/components/Table/ServerPropTable/index.vue'
-const data = []
-for (let i = 0; i < 100; i++) {
-  data.push({
-    id: i + 1,
-    type: i % 3,
-    value: '127.0.0.1',
+import PropTable from '@/components/Table/PropTable/index.vue'
+import dateUtils from '@/common/dateUtils'
+import whiteListApi from '@/api/whiteList'
+
+import ExcelJS from 'exceljs'
+import { UploadFilled } from '@element-plus/icons-vue'
+import type { UploadProps, UploadInstance } from 'element-plus'
+// import { ElMessage } from 'element-plus'
+const tableData = ref([])
+const tableHeader = ref([])
+const upload = ref<UploadInstance>()
+
+const tableColumn = ["value","type"]
+const beforeUploadAction = (file, fileLi) => {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.onload = async (e) => {
+      const data = e.target.result
+      const workbook = new ExcelJS.Workbook()
+      try {
+        let res = await workbook.xlsx.load(data)
+        const sheets =
+          res._worksheets && res._worksheets.filter((item) => typeof item !== 'undefined')
+        const table = []
+        let headers = []
+        sheets.forEach((sheet) => {
+          sheet._rows.forEach((row, index) => {
+            if (index === 0) {
+              row.values.forEach((it, i) => {
+                headers.push({label: it, value: tableColumn[i - 1]})
+              })
+            } else {
+              let obj = {}
+              let arr = []
+              row.values.forEach((it) => {
+                arr.push(it)
+              })
+              tableColumn.forEach((ite, i) => {
+                if (arr[i] == undefined)  {
+                  obj[ite] = null
+                } else {
+                  obj[ite] = arr[i]
+                }
+              })
+              table.push(obj)
+            }
+            // const tableRow = {
+            //   position: "",
+            //   val: "",
+            // };
+            // row._cells.forEach((cell) => {
+            //   tableRow.position = cell._address;
+            //   tableRow.val = cell._value.model.value || "";
+            // });
+          })
+        })
+        tableData.value = table
+        tableHeader.value = headers
+        console.log("table", table)
+        console.log("headers", headers)
+      } catch (e) {
+        ElMessage.error('解析失败')
+      }
+    }
+    reader.readAsArrayBuffer(file)
+  })
+}
+
+const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
+  tableData.value = null
+  tableHeader.value = null
+}
+
+// const data = []
+// for (let i = 0; i < 100; i++) {
+//   data.push({
+//     id: i + 1,
+//     type: i % 3,
+//     value: '127.0.0.1',
+//   })
+// }
+
+const data = ref([])
+let currPageNum = 1;
+const pageSize = 1000;
+onBeforeMount(() => {
+  loadWhiteListInfo()
+})
+
+const loadWhiteListInfo = () => {
+  let queryParams = reactive({
+    pageNum: currPageNum,
+    pageSize: pageSize
+  })
+  whiteListApi.query(queryParams).then(res => {
+    data.value = res.data.result;
+    console.log(data)
   })
 }
+
 const column = [
   { type: 'selection', width: 60, fixed: 'left' },
-  { name: 'id', label: 'ID', width: 80 },
+  { name: 'value', label: '白名单值', inSearch: true, valueType: 'input'},
   {
     name: 'type',
     label: '白名单类型',
@@ -85,7 +203,7 @@ const column = [
     ],
     valueType: 'select',
   },
-  { name: 'value', label: '白名单值', inSearch: true, valueType: 'input'},
+  { name: 'createTime', label: '白名单创建时间', slot: true },
   { name: 'operation', slot: true, fixed: 'right', width: 200, label: '操作' },
 ]
 
@@ -104,39 +222,105 @@ const list = ref(data)
 const formSize = ref('default')
 const ruleFormRef = ref<FormInstance>()
 const ruleForm = reactive({
-  state: null,
+  value: null,
+  type: null
 })
 
 const rules = reactive({
-  state: [
+  value: [
     {
       required: true,
-      message: '请选择状态',
+      message: '请输入白名单值',
+      trigger: 'change',
+    },
+  ],
+  type: [
+    {
+      required: true,
+      message: '请选择白名单类型',
       trigger: 'change',
     },
   ],
 })
 
 const dialogVisible = ref(false)
+const importVisible = ref(false)
 const rowObj = ref({})
 const selectObj = ref([])
 const title = ref('编辑')
 
+const importClose = () => {
+  importVisible.value = false
+  upload.value!.clearFiles()
+  tableData.value = null
+  tableHeader.value = null
+}
+
+const importByExcel = () => {
+  title.value = '导入'
+  importVisible.value = true
+  tableData.value = null
+  tableHeader.value = null
+}
+
 const add = () => {
   title.value = '新增'
   dialogVisible.value = true
+  ruleForm.value = null
+  ruleForm.type = null
+}
+
+const handleConfirm = () => {
+  // 验证table数据
+  // ...
+  let importParams = reactive({
+    importValue: tableData.value
+  })
+  whiteListApi.importBatch(importParams).then(res => {
+    console.log('result', res)
+    if (res.data.code = 200) {
+      loadWhiteListInfo
+      importVisible.value = false
+      return ElMessage.success(res.data.msg)
+    } else {
+      return ElMessage.error(res.data.msg)
+    }
+  })
+  console.log('import submit!')
 }
 
 const handleClose = async (done: () => void) => {
   await ruleFormRef.value.validate((valid, fields) => {
     if (valid) {
-      list.value.forEach((item) => {
-        if (item.id === rowObj.value.id) {
-          item.state = ruleForm.state
-        }
-      })
-      dialogVisible.value = false
-      console.log('submit!', ruleForm.state)
+      if (title.value == '新增') {
+        whiteListApi.add(ruleForm).then(res => {
+          console.log('result', res)
+          if (res.data.code = 200) {
+            loadWhiteListInfo()
+            dialogVisible.value = false
+            return ElMessage.success(res.data.msg)
+          } else {
+            return ElMessage.error(res.data.msg)
+          }
+        })
+        console.log('add submit!')
+      } else {
+        // let updateParams = reactive({
+        //   value: rowObj.value,
+        //   updateValue: ruleForm
+        // })
+        whiteListApi.update(ruleForm).then(res => {
+          if (res.data.code = 200) {
+            loadWhiteListInfo()
+            dialogVisible.value = false
+            return ElMessage.success('修改成功')
+          } else {
+            return ElMessage.error('修改失败')
+          }
+        })
+        console.log('update submit!')
+      }
+      console.log('submit!', ruleForm)
     } else {
       console.log('error submit!', fields)
     }
@@ -154,7 +338,18 @@ const batchDelete = () => {
     draggable: true,
   })
     .then(() => {
-      ElMessage.success('模拟删除成功')
+      let whiteListValues = reactive([])
+      for (let value of selectObj.value) {
+        whiteListValues.push(value.value)
+      }
+      let deleteParams = reactive({
+        values: whiteListValues
+      })
+      whiteListApi.deleteBatch(deleteParams).then(res => {
+        loadWhiteListInfo()
+        ElMessage.success('批量删除成功')
+      })
+
       list.value = list.value.concat([])
     })
     .catch(() => { })
@@ -166,7 +361,8 @@ const selectionChange = (val) => {
 const edit = (row) => {
   rowObj.value = row
   dialogVisible.value = true
-  ruleForm.state = row.state
+  ruleForm.type = row.type
+  ruleForm.value = row.value
 }
 
 const del = (row) => {
@@ -178,8 +374,13 @@ const del = (row) => {
     draggable: true,
   })
     .then(() => {
-      list.value = list.value.filter((item) => item.id !== row.id)
-      ElMessage.success('删除成功')
+      let deleteParams = reactive({
+        value: row.value
+      })
+      whiteListApi.delete(deleteParams).then(res => {
+        loadWhiteListInfo()
+        ElMessage.success('删除成功')
+      })
       loading.value = true
       setTimeout(() => {
         loading.value = false
@@ -189,25 +390,28 @@ const del = (row) => {
 }
 
 const reset = () => {
+  loadWhiteListInfo()
   loading.value = true
   setTimeout(() => {
     loading.value = false
   }, 500)
-  ElMessage.success('触发重置方法')
+  ElMessage.success('重置成功')
 }
 
 const onSubmit = (val) => {
-  console.log('val===', val)
-  ElMessage.success('触发查询方法')
-  loading.value = true
-  setTimeout(() => {
-    loading.value = false
-  }, 500)
-}
-
-const onUpdate = (val) => {
-  console.log('val===', val)
-  ElMessage.success('触发修改方法')
+  if ((val.value == null || val.value == "") && val.type == null) {
+    ElMessage.warning('请输入查询条件')
+    return
+  }
+  let queryParams = reactive({
+    pageNum: 1,
+    pageSize: pageSize,
+    condition: val
+  })
+  whiteListApi.query(queryParams).then(res => {
+    data.value = res.data.result;
+    console.log(data.value)
+  })
   loading.value = true
   setTimeout(() => {
     loading.value = false

+ 1 - 1
src/views/server/chatTable/index.vue

@@ -65,7 +65,7 @@ import ServerPropTable from '@/components/Table/ServerPropTable/index.vue'
 // }
 
 const data = ref([])
-const serverName = "gateway"
+const serverName = "chat"
 
 onBeforeMount(() => {
   loadServerInfo()