|
@@ -1,18 +1,548 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div>
|
|
<div>
|
|
|
- <el-card shadow="never">
|
|
|
|
|
- <template #header>
|
|
|
|
|
- <div class="card-header">
|
|
|
|
|
- <span>接入管理</span>
|
|
|
|
|
|
|
+ <div class="gva-search-box">
|
|
|
|
|
+ <el-form ref="searchForm" :inline="true" :model="searchInfo">
|
|
|
|
|
+ <el-form-item label="服务名称">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="searchInfo.name"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请输入服务名称(模糊)"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="外挂服务IP">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="searchInfo.ip"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请输入外挂服务IP"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="接入标识">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="searchInfo.iden"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请输入接入标识"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="服务状态">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="searchInfo.status"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请选择"
|
|
|
|
|
+ style="width: 120px"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option label="启用" value="启用" />
|
|
|
|
|
+ <el-option label="停用" value="停用" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item>
|
|
|
|
|
+ <el-button v-preReClick icon="search" type="primary" @click="onSubmit"
|
|
|
|
|
+ >查询
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button v-preReClick icon="refresh" @click="onReset">重置</el-button>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="gva-table-box">
|
|
|
|
|
+ <div class="gva-btn-list">
|
|
|
|
|
+ <el-button v-preReClick icon="plus" type="primary" @click="openDialog"
|
|
|
|
|
+ >新增
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ ref="multipleTable"
|
|
|
|
|
+ :data="tableData"
|
|
|
|
|
+ row-key="id"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ tooltip-effect="dark"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-table-column align="left" label="服务名称" prop="name" min-width="140" />
|
|
|
|
|
+ <el-table-column align="left" label="外挂服务IP" prop="ip" width="150" />
|
|
|
|
|
+ <el-table-column align="left" label="端口" prop="port" width="90" />
|
|
|
|
|
+ <el-table-column align="left" label="保护目录" min-width="200">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tooltip
|
|
|
|
|
+ v-if="scope.row.directoryText"
|
|
|
|
|
+ :content="scope.row.directoryText"
|
|
|
|
|
+ placement="top"
|
|
|
|
|
+ >
|
|
|
|
|
+ <span class="directory-text">{{ scope.row.directoryText }}</span>
|
|
|
|
|
+ </el-tooltip>
|
|
|
|
|
+ <span v-else>-</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column align="left" label="接入标识" prop="iden" min-width="140" />
|
|
|
|
|
+ <el-table-column align="left" label="密钥标识" prop="keyIden" min-width="140" />
|
|
|
|
|
+ <el-table-column align="left" label="密钥算法" prop="keyAlgo" width="120" />
|
|
|
|
|
+ <el-table-column align="left" label="服务状态" width="110">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="scope.row.status"
|
|
|
|
|
+ active-value="启用"
|
|
|
|
|
+ inactive-value="停用"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ active-text="启用"
|
|
|
|
|
+ inactive-text="停用"
|
|
|
|
|
+ @change="changeStatus(scope.row)"
|
|
|
|
|
+ />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column align="left" label="创建时间" width="180">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <span>{{ formatDate(scope.row.createAt) }}</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column align="left" label="操作" fixed="right" width="200">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-preReClick
|
|
|
|
|
+ icon="view"
|
|
|
|
|
+ link
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ @click="viewService(scope.row)"
|
|
|
|
|
+ >详情
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-preReClick
|
|
|
|
|
+ icon="edit"
|
|
|
|
|
+ link
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ @click="updateServiceInfo(scope.row)"
|
|
|
|
|
+ >编辑
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-popover v-model="scope.row.visible" placement="top" width="160">
|
|
|
|
|
+ <p>确定要删除吗?</p>
|
|
|
|
|
+ <div style="text-align: right; margin-top: 8px">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-preReClick
|
|
|
|
|
+ link
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ @click="scope.row.visible = false"
|
|
|
|
|
+ >取消
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-preReClick
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ @click="deleteServiceInfo(scope.row)"
|
|
|
|
|
+ >确定
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <template #reference>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-preReClick
|
|
|
|
|
+ icon="delete"
|
|
|
|
|
+ link
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ @click="scope.row.visible = true"
|
|
|
|
|
+ >删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-popover>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="gva-pagination">
|
|
|
|
|
+ <el-pagination
|
|
|
|
|
+ :current-page="page"
|
|
|
|
|
+ :page-size="pageSize"
|
|
|
|
|
+ :page-sizes="[10, 30, 50, 100]"
|
|
|
|
|
+ :total="total"
|
|
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
|
|
+ @size-change="handleSizeChange"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 详情弹窗 -->
|
|
|
|
|
+ <el-dialog v-model="detailVisible" title="服务详情" :before-close="closeDetail">
|
|
|
|
|
+ <el-descriptions :column="2" border>
|
|
|
|
|
+ <el-descriptions-item label="服务名称">{{ detail.name }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="外挂服务IP">{{ detail.ip }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="端口">{{ detail.port }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="接入标识">{{ detail.iden }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="密钥标识">{{ detail.keyIden }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="密钥算法">{{ detail.keyAlgo }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="服务状态">{{ detail.status }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="创建时间">{{ formatDate(detail.createAt) }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="保护目录" :span="2">{{ detail.directoryText || '-' }}</el-descriptions-item>
|
|
|
|
|
+ </el-descriptions>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 新增/编辑弹窗 -->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ v-model="dialogFormVisible"
|
|
|
|
|
+ :before-close="closeDialog"
|
|
|
|
|
+ :title="type === 'create' ? '新增服务' : '编辑服务'"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-form ref="dialogForm" :model="form" :rules="rules" label-width="110px">
|
|
|
|
|
+ <el-form-item label="服务名称" prop="name">
|
|
|
|
|
+ <el-input v-model="form.name" clearable placeholder="请输入服务名称" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="外挂服务IP" prop="ip">
|
|
|
|
|
+ <el-input v-model="form.ip" clearable placeholder="请输入外挂服务IP" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="端口" prop="port">
|
|
|
|
|
+ <el-input v-model="form.port" clearable placeholder="请输入外挂服务端口" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="接入标识">
|
|
|
|
|
+ <el-input v-model="form.iden" disabled placeholder="由系统自动生成" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="密钥标识" prop="keyIden">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="form.keyIden"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ filterable
|
|
|
|
|
+ placeholder="请选择密钥标识"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="k in keyOptions"
|
|
|
|
|
+ :key="k.value"
|
|
|
|
|
+ :label="k.label"
|
|
|
|
|
+ :value="k.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="密钥算法" prop="keyAlgo">
|
|
|
|
|
+ <el-input v-model="form.keyAlgo" clearable placeholder="请输入密钥算法" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="服务状态" prop="status">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="form.status"
|
|
|
|
|
+ active-value="启用"
|
|
|
|
|
+ inactive-value="停用"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ active-text="启用"
|
|
|
|
|
+ inactive-text="停用"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="保护目录">
|
|
|
|
|
+ <div style="width: 100%">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="(item, index) in formDirectories"
|
|
|
|
|
+ :key="index"
|
|
|
|
|
+ style="display: flex; align-items: center; margin-bottom: 8px"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="item.directory"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请输入保护目录路径"
|
|
|
|
|
+ style="margin-right: 8px"
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-preReClick
|
|
|
|
|
+ icon="delete"
|
|
|
|
|
+ link
|
|
|
|
|
+ type="danger"
|
|
|
|
|
+ @click="removeDirectory(index)"
|
|
|
|
|
+ >删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-button v-preReClick icon="plus" link type="primary" @click="addDirectory"
|
|
|
|
|
+ >添加目录
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <el-button v-preReClick @click="closeDialog">取 消</el-button>
|
|
|
|
|
+ <el-button v-preReClick type="primary" @click="enterDialog">确 定</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
- <el-empty description="接入管理页面(待实现)" />
|
|
|
|
|
- </el-card>
|
|
|
|
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
|
|
+import {
|
|
|
|
|
+ createService,
|
|
|
|
|
+ deleteService,
|
|
|
|
|
+ getServiceList,
|
|
|
|
|
+ updateService,
|
|
|
|
|
+ getKeyList,
|
|
|
|
|
+ getDirectoryList,
|
|
|
|
|
+ createDirectory,
|
|
|
|
|
+ deleteDirectory
|
|
|
|
|
+} from '@/api/service'
|
|
|
|
|
+import { ref, computed } from 'vue'
|
|
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
|
|
+import { formatDate } from '@/utils/format'
|
|
|
|
|
+
|
|
|
defineOptions({
|
|
defineOptions({
|
|
|
name: 'MaskAccess'
|
|
name: 'MaskAccess'
|
|
|
})
|
|
})
|
|
|
-</script>
|
|
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 查询条件 ----------
|
|
|
|
|
+const searchInfo = ref({
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ ip: '',
|
|
|
|
|
+ iden: '',
|
|
|
|
|
+ status: ''
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 列表(后端列表无分页/过滤,前端做客户端过滤 + 分页) ----------
|
|
|
|
|
+const allData = ref([])
|
|
|
|
|
+const page = ref(1)
|
|
|
|
|
+const pageSize = ref(10)
|
|
|
|
|
+
|
|
|
|
|
+const filterTableData = computed(() => {
|
|
|
|
|
+ const name = searchInfo.value.name.trim()
|
|
|
|
|
+ const ip = searchInfo.value.ip.trim()
|
|
|
|
|
+ const iden = searchInfo.value.iden.trim()
|
|
|
|
|
+ const status = searchInfo.value.status
|
|
|
|
|
+ return allData.value.filter(item => {
|
|
|
|
|
+ if (name && !String(item.name || '').includes(name)) return false
|
|
|
|
|
+ if (ip && !String(item.ip || '').includes(ip)) return false
|
|
|
|
|
+ if (iden && !String(item.iden || '').includes(iden)) return false
|
|
|
|
|
+ if (status && item.status !== status) return false
|
|
|
|
|
+ return true
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const tableData = computed(() => {
|
|
|
|
|
+ const start = (page.value - 1) * pageSize.value
|
|
|
|
|
+ return filterTableData.value.slice(start, start + pageSize.value)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const total = computed(() => filterTableData.value.length)
|
|
|
|
|
+
|
|
|
|
|
+const handleSizeChange = val => {
|
|
|
|
|
+ pageSize.value = val
|
|
|
|
|
+ page.value = 1
|
|
|
|
|
+}
|
|
|
|
|
+const handleCurrentChange = val => {
|
|
|
|
|
+ page.value = val
|
|
|
|
|
+}
|
|
|
|
|
+const onSubmit = () => {
|
|
|
|
|
+ page.value = 1
|
|
|
|
|
+}
|
|
|
|
|
+const onReset = () => {
|
|
|
|
|
+ searchInfo.value = { name: '', ip: '', iden: '', status: '' }
|
|
|
|
|
+ page.value = 1
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 密钥下拉(GET /keys) ----------
|
|
|
|
|
+const keyOptions = ref([])
|
|
|
|
|
+const getKeyData = async () => {
|
|
|
|
|
+ const res = await getKeyList()
|
|
|
|
|
+ keyOptions.value = (res.list || []).map(k => ({
|
|
|
|
|
+ value: k.iden,
|
|
|
|
|
+ label: k.name ? `${k.iden}(${k.name})` : k.iden
|
|
|
|
|
+ }))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 数据加载:合并服务 + 保护目录 ----------
|
|
|
|
|
+const getTableData = async () => {
|
|
|
|
|
+ const [svcRes, dirRes] = await Promise.all([getServiceList(), getDirectoryList()])
|
|
|
|
|
+ const dirs = dirRes.list || []
|
|
|
|
|
+ const dirMap = {}
|
|
|
|
|
+ dirs.forEach(d => {
|
|
|
|
|
+ if (!dirMap[d.serviceId]) dirMap[d.serviceId] = []
|
|
|
|
|
+ dirMap[d.serviceId].push({ id: d.id, directory: d.directory })
|
|
|
|
|
+ })
|
|
|
|
|
+ allData.value = (svcRes.list || []).map(s => {
|
|
|
|
|
+ const list = dirMap[s.id] || []
|
|
|
|
|
+ return { ...s, directories: list, directoryText: list.map(x => x.directory).join('、') }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+getTableData()
|
|
|
|
|
+getKeyData()
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 服务状态开关 ----------
|
|
|
|
|
+const changeStatus = async row => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await updateService(row.id, {
|
|
|
|
|
+ name: row.name,
|
|
|
|
|
+ ip: row.ip,
|
|
|
|
|
+ port: row.port,
|
|
|
|
|
+ iden: row.iden,
|
|
|
|
|
+ keyIden: row.keyIden,
|
|
|
|
|
+ keyAlgo: row.keyAlgo,
|
|
|
|
|
+ status: row.status
|
|
|
|
|
+ })
|
|
|
|
|
+ ElMessage({ type: 'success', message: '状态更新成功' })
|
|
|
|
|
+ getTableData()
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ // 失败回滚开关
|
|
|
|
|
+ row.status = row.status === '启用' ? '停用' : '启用'
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 详情 ----------
|
|
|
|
|
+const detailVisible = ref(false)
|
|
|
|
|
+const detail = ref({})
|
|
|
|
|
+const viewService = row => {
|
|
|
|
|
+ detail.value = row
|
|
|
|
|
+ detailVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+const closeDetail = () => {
|
|
|
|
|
+ detailVisible.value = false
|
|
|
|
|
+ detail.value = {}
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 新增/编辑 ----------
|
|
|
|
|
+const dialogFormVisible = ref(false)
|
|
|
|
|
+const type = ref('')
|
|
|
|
|
+const dialogForm = ref(null)
|
|
|
|
|
+const form = ref({
|
|
|
|
|
+ id: 0,
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ ip: '',
|
|
|
|
|
+ port: '',
|
|
|
|
|
+ iden: '',
|
|
|
|
|
+ keyIden: '',
|
|
|
|
|
+ keyAlgo: 'SM4-CTR',
|
|
|
|
|
+ status: '启用'
|
|
|
|
|
+})
|
|
|
|
|
+const formDirectories = ref([])
|
|
|
|
|
+
|
|
|
|
|
+const rules = ref({
|
|
|
|
|
+ name: [{ required: true, message: '请输入服务名称', trigger: 'blur' }],
|
|
|
|
|
+ ip: [{ required: true, message: '请输入外挂服务IP', trigger: 'blur' }],
|
|
|
|
|
+ port: [{ required: true, message: '请输入外挂服务端口', trigger: 'blur' }],
|
|
|
|
|
+ keyIden: [{ required: true, message: '请选择密钥标识', trigger: 'change' }],
|
|
|
|
|
+ keyAlgo: [{ required: true, message: '请输入密钥算法', trigger: 'blur' }]
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 后端主键无自增,前端生成唯一 int64 作为 id / 目录 id
|
|
|
|
|
+const genId = () => Number(`${Date.now()}${Math.floor(Math.random() * 90) + 10}`)
|
|
|
|
|
+// 接入标识自动生成
|
|
|
|
|
+const genIden = () => `SVC${Date.now()}${Math.floor(Math.random() * 10000)}`
|
|
|
|
|
+
|
|
|
|
|
+const addDirectory = () => {
|
|
|
|
|
+ formDirectories.value.push({ directory: '' })
|
|
|
|
|
+}
|
|
|
|
|
+const removeDirectory = index => {
|
|
|
|
|
+ formDirectories.value.splice(index, 1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const openDialog = () => {
|
|
|
|
|
+ type.value = 'create'
|
|
|
|
|
+ form.value = {
|
|
|
|
|
+ id: 0,
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ ip: '',
|
|
|
|
|
+ port: '',
|
|
|
|
|
+ iden: genIden(),
|
|
|
|
|
+ keyIden: '',
|
|
|
|
|
+ keyAlgo: 'SM4-CTR',
|
|
|
|
|
+ status: '启用'
|
|
|
|
|
+ }
|
|
|
|
|
+ formDirectories.value = [{ directory: '' }]
|
|
|
|
|
+ dialogFormVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const updateServiceInfo = row => {
|
|
|
|
|
+ type.value = 'update'
|
|
|
|
|
+ form.value = {
|
|
|
|
|
+ id: row.id,
|
|
|
|
|
+ name: row.name,
|
|
|
|
|
+ ip: row.ip,
|
|
|
|
|
+ port: row.port,
|
|
|
|
|
+ iden: row.iden,
|
|
|
|
|
+ keyIden: row.keyIden,
|
|
|
|
|
+ keyAlgo: row.keyAlgo,
|
|
|
|
|
+ status: row.status
|
|
|
|
|
+ }
|
|
|
|
|
+ formDirectories.value =
|
|
|
|
|
+ row.directories && row.directories.length
|
|
|
|
|
+ ? row.directories.map(d => ({ id: d.id, directory: d.directory }))
|
|
|
|
|
+ : [{ directory: '' }]
|
|
|
|
|
+ dialogFormVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const closeDialog = () => {
|
|
|
|
|
+ dialogFormVisible.value = false
|
|
|
|
|
+ form.value = {
|
|
|
|
|
+ id: 0,
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ ip: '',
|
|
|
|
|
+ port: '',
|
|
|
|
|
+ iden: '',
|
|
|
|
|
+ keyIden: '',
|
|
|
|
|
+ keyAlgo: 'SM4-CTR',
|
|
|
|
|
+ status: '启用'
|
|
|
|
|
+ }
|
|
|
|
|
+ formDirectories.value = []
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const enterDialog = async () => {
|
|
|
|
|
+ dialogForm.value.validate(async valid => {
|
|
|
|
|
+ if (!valid) return
|
|
|
|
|
+ let serviceId = form.value.id
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (type.value === 'create') {
|
|
|
|
|
+ serviceId = genId()
|
|
|
|
|
+ await createService({
|
|
|
|
|
+ id: serviceId,
|
|
|
|
|
+ name: form.value.name,
|
|
|
|
|
+ ip: form.value.ip,
|
|
|
|
|
+ port: form.value.port,
|
|
|
|
|
+ iden: form.value.iden,
|
|
|
|
|
+ keyIden: form.value.keyIden,
|
|
|
|
|
+ keyAlgo: form.value.keyAlgo,
|
|
|
|
|
+ status: form.value.status
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await updateService(serviceId, {
|
|
|
|
|
+ name: form.value.name,
|
|
|
|
|
+ ip: form.value.ip,
|
|
|
|
|
+ port: form.value.port,
|
|
|
|
|
+ iden: form.value.iden,
|
|
|
|
|
+ keyIden: form.value.keyIden,
|
|
|
|
|
+ keyAlgo: form.value.keyAlgo,
|
|
|
|
|
+ status: form.value.status
|
|
|
|
|
+ })
|
|
|
|
|
+ // 保护目录按「删旧 + 重建」同步
|
|
|
|
|
+ const oldDirs = formDirectories.value.filter(d => d.id).map(d => d.id)
|
|
|
|
|
+ await Promise.all(oldDirs.map(id => deleteDirectory(id)))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 保存保护目录(过滤空值)
|
|
|
|
|
+ const dirs = formDirectories.value.map(d => d.directory.trim()).filter(Boolean)
|
|
|
|
|
+ for (const dir of dirs) {
|
|
|
|
|
+ await createDirectory({ id: genId(), serviceId, directory: dir })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ElMessage({ type: 'success', message: '操作成功' })
|
|
|
|
|
+ closeDialog()
|
|
|
|
|
+ getTableData()
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ // 错误提示已由 service.js 拦截器弹出
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 删除(含关联保护目录) ----------
|
|
|
|
|
+const deleteServiceInfo = async row => {
|
|
|
|
|
+ row.visible = false
|
|
|
|
|
+ try {
|
|
|
|
|
+ const dirIds = (row.directories || []).map(d => d.id).filter(Boolean)
|
|
|
|
|
+ await Promise.all(dirIds.map(id => deleteDirectory(id)))
|
|
|
|
|
+ await deleteService(row.id)
|
|
|
|
|
+ ElMessage({ type: 'success', message: '删除成功' })
|
|
|
|
|
+ if (tableData.value.length === 1 && page.value > 1) {
|
|
|
|
|
+ page.value--
|
|
|
|
|
+ }
|
|
|
|
|
+ getTableData()
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ // 错误提示已由 service.js 拦截器弹出
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.directory-text {
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ max-width: 180px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ vertical-align: middle;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|