feat:1,新增游戏配置 2,新增币种 3,新增角色权限管理

main
YuanJian 2025-09-15 18:40:03 +08:00
parent c6102d995f
commit 4c54307fc5
75 changed files with 10550 additions and 2389 deletions

BIN
dist.rar

Binary file not shown.

View File

@ -38,8 +38,9 @@ export function uploadImgFile(data) {
// 图片上传 // 图片上传
const baseUrlq = getLocalStorage('uploadUrl'); const baseUrlq = getLocalStorage('uploadUrl');
export function uploadImgFileCommon(data, type) { export function uploadImgFileCommon(data, type) {
console.log(baseUrlq);
return request({ return request({
url: baseUrlq + `/file/upload/localSysFile/${type}`, url: `/file/upload/localSysFile/${type}`,
method: 'post', method: 'post',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: data data: data

View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询游戏配置表
export function getGameSecretList(query) {
return request({
url: '/game/secret/list',
method: 'get',
params: query
})
}
//配置详情
export function getGameSecretInfo(id) {
return request({
url: `/game/secret/${id}`,
method: 'get',
})
}
// 新增游戏配置
export function postGameSecret(data) {
return request({
url: '/game/secret',
method: 'post',
data: data
})
}
// 修改游戏配置
export function updateGameSecret(data) {
return request({
url: '/game/secret',
method: 'put',
data: data
})
}
//删除
export function deleteGameSecret(id) {
return request({
url: `/game/secret/${id}`,
method: 'delete',
})
}

View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询用户头像管理 列表
export function listAvatar(query) {
return request({
url: '/operation/avatar/list',
method: 'get',
params: query
})
}
// 查询用户头像管理 详细
export function getAvatar(id) {
return request({
url: '/operation/avatar/' + id,
method: 'get'
})
}
// 新增用户头像管理
export function addAvatar(data) {
return request({
url: '/operation/avatar',
method: 'post',
data: data
})
}
// 修改用户头像管理
export function updateAvatar(data) {
return request({
url: '/operation/avatar',
method: 'put',
data: data
})
}
// 删除用户头像管理
export function delAvatar(id) {
return request({
url: '/operation/avatar/' + id,
method: 'delete'
})
}

View File

@ -0,0 +1,656 @@
import request from '@/utils/request'
// 全局子域名搜索
export function getSonDomainSearch(query) {
return request({
url: '/operation/son/get',
method: 'get',
params: query
})
}
//------------------域名管理---------------------//
// 域名管理商列表
export function getSupplierList(query) {
return request({
url: '/domain/supplier/select',
method: 'get',
params: query
})
}
//域名信息查询
export function getDomainConfig(query) {
return request({
url: '/domain/supplier/config',
method: 'get',
params: query
})
}
// 开通域名管理商
export function tenantSupplier(data) {
return request({
url: '/operation/tenant',
method: 'post',
data: data
})
}
// 开通域名管理商ALB
export function tenantSupplierALB(data) {
return request({
url: '/operation/tenant/alb',
method: 'put',
data: data
})
}
// 域名列表
export function getDomainList(query) {
return request({
url: '/domain/list',
method: 'get',
params: query
})
}
// 域名验证
export function getDomainVerify(data) {
return request({
url: '/domain/check',
method: 'put',
data: data
})
}
// 刷新域名过期时间
export function getDomainExpireTime(data) {
return request({
url: '/domain/expired',
method: 'put',
data: data
})
}
// 强制停用
export function setDomainStop(data) {
return request({
url: '/domain/stop',
method: 'put',
data: data
})
}
// 彻底删除管理域名
export function delDomain(ids) {
return request({
url: '/domain/' + ids,
method: 'delete'
})
}
// 新增域名
export function addDomain(data) {
return request({
url: '/domain',
method: 'post',
data: data
})
}
// 获取供应商节点下域名
export function getSupplierDomainList(query) {
return request({
url: '/domain/select',
method: 'get',
params: query
})
}
// 获取域名NS
export function getDomainNS(id) {
return request({
url: '/domain/get/ns/' + id,
method: 'get'
})
}
//------------------WEB大厅---------------------//
// web大厅子域名列表
export function getWebDomainList(query) {
return request({
url: '/operation/son/web/list',
method: 'get',
params: query
})
}
// 刷新解析说明
export function refreshWebDomainAnalysis(data) {
return request({
url: '/operation/son/web/analysis',
method: 'put',
data: data
})
}
// web大厅列表开关
export function webDomainSwitch(data) {
return request({
url: '/operation/son/web/switch',
method: 'put',
data: data
})
}
// web大厅子域名新增
export function addWebDomain(data) {
return request({
url: '/operation/son/web',
method: 'post',
data: data
})
}
// web大厅子域名状态修改
export function setWebDomainStatus(data) {
return request({
url: '/operation/son/web/status',
method: 'put',
data: data
})
}
// web大厅子域名绑定代理
export function bindWebDomainAgent(data) {
return request({
url: '/operation/son/web/bind/agent',
method: 'put',
data: data
})
}
// web大厅子域名清理缓存
export function clearWebDomainCache(data) {
return request({
url: '/operation/son/web/clear/cache',
method: 'put',
data: data
})
}
// web大厅子域名删除
export function delWebDomain(ids) {
return request({
url: '/operation/son/web/' + ids,
method: 'delete'
})
}
// web大厅子域名修改
export function updateWebDomain(data) {
return request({
url: '/operation/son/web',
method: 'put',
data: data
})
}
// web大厅子域名批量更换端口
export function changeWebDomainPort(data) {
return request({
url: '/operation/son/web/port',
method: 'put',
data: data
})
}
// web系统参数
export function getWebSystemParam() {
return request({
url: '/operation/son/web/configKey/batch',
method: 'post'
})
}
// web系统参数修改
export function updateWebSystemParam(data) {
return request({
url: '/operation/son/web/configKey/batch',
method: 'put',
data: data
})
}
//------------------后端加速域名---------------------//
// 后端加速域名列表
export function getBackendDomainList(query) {
return request({
url: '/operation/son/backend/list',
method: 'get',
params: query
})
}
// 后端加速域名刷新解析说明
export function refreshBackendDomainAnalysis(data) {
return request({
url: '/operation/son/backend/analysis',
method: 'put',
data: data
})
}
// 后端加速域名状态修改
export function setBackendDomainStatus(data) {
return request({
url: '/operation/son/backend/status',
method: 'put',
data: data
})
}
// 后端加速域名清理缓存
export function clearBackendDomainCache(data) {
return request({
url: '/operation/son/backend/clear/cache',
method: 'put',
data: data
})
}
// 后端加速域名删除
export function delBackendDomain(ids) {
return request({
url: '/operation/son/backend/' + ids,
method: 'delete'
})
}
// 后端加速域名新增
export function addBackendDomain(data) {
return request({
url: '/operation/son/backend',
method: 'post',
data: data
})
}
// 后端加速域名修改
export function updateBackendDomain(data) {
return request({
url: '/operation/son/backend',
method: 'put',
data: data
})
}
//------------------App大厅域名---------------------//
// App大厅域名列表
export function getAppDomainList(query) {
return request({
url: '/operation/son/app/list',
method: 'get',
params: query
})
}
// App大厅刷新解析说明
export function refreshAppDomainAnalysis(data) {
return request({
url: '/operation/son/app/analysis',
method: 'put',
data: data
})
}
// App大厅域名状态修改
export function setAppDomainStatus(data) {
return request({
url: '/operation/son/app/status',
method: 'put',
data: data
})
}
// App大厅域名清理缓存
export function clearAppDomainCache(data) {
return request({
url: '/operation/son/app/clear/cache',
method: 'put',
data: data
})
}
// App大厅域名删除
export function delAppDomain(ids) {
return request({
url: '/operation/son/app/' + ids,
method: 'delete'
})
}
// App大厅域名新增
export function addAppDomain(data) {
return request({
url: '/operation/son/app',
method: 'post',
data: data
})
}
// App大厅域名修改
export function updateAppDomain(data) {
return request({
url: '/operation/son/app',
method: 'put',
data: data
})
}
//------------------OSS加速域名---------------------//
// OSS加速域名列表
export function getOssDomainList(query) {
return request({
url: '/operation/son/oss/list',
method: 'get',
params: query
})
}
// OSS加速域名刷新解析说明
export function refreshOssDomainAnalysis(data) {
return request({
url: '/operation/son/oss/analysis',
method: 'put',
data: data
})
}
// OSS加速域名状态修改
export function setOssDomainStatus(data) {
return request({
url: '/operation/son/oss/status',
method: 'put',
data: data
})
}
// OSS加速域名清理缓存
export function clearOssDomainCache(data) {
return request({
url: '/operation/son/oss/clear/cache',
method: 'put',
data: data
})
}
// OSS加速域名删除
export function delOssDomain(ids) {
return request({
url: '/operation/son/oss/' + ids,
method: 'delete'
})
}
// OSS加速域名新增
export function addOssDomain(data) {
return request({
url: '/operation/son/oss',
method: 'post',
data: data
})
}
// OSS加速域名修改
export function updateOssDomain(data) {
return request({
url: '/operation/son/oss',
method: 'put',
data: data
})
}
//------------------下载站域名---------------------//
// 下载站域名列表
export function getDownloadDomainList(query) {
return request({
url: '/operation/son/download/list',
method: 'get',
params: query
})
}
// 下载站域名刷新解析说明
export function refreshDownloadDomainAnalysis(data) {
return request({
url: '/operation/son/download/analysis',
method: 'put',
data: data
})
}
// 下载站域名状态修改
export function setDownloadDomainStatus(data) {
return request({
url: '/operation/son/download/status',
method: 'put',
data: data
})
}
// 下载站域名清理缓存
export function clearDownloadDomainCache(data) {
return request({
url: '/operation/son/download/clear/cache',
method: 'put',
data: data
})
}
// 下载站域名删除
export function delDownloadDomain(ids) {
return request({
url: '/operation/son/download/' + ids,
method: 'delete'
})
}
// 下载站域名新增
export function addDownloadDomain(data) {
return request({
url: '/operation/son/download',
method: 'post',
data: data
})
}
// 下载站域名修改
export function updateDownloadDomain(data) {
return request({
url: '/operation/son/download',
method: 'put',
data: data
})
}
// 下载站域名列表开关
export function downloadDomainSwitch(data) {
return request({
url: '/operation/son/download/switch',
method: 'put',
data: data
})
}
// 下载站绑定代理
export function bindDownloadDomainAgent(data) {
return request({
url: '/operation/son/download/bind/agent',
method: 'put',
data: data
})
}
//------------------支付域名---------------------//
// 支付域名列表
export function getPayDomainList(query) {
return request({
url: '/operation/son/pay/list',
method: 'get',
params: query
})
}
// 支付域名刷新解析说明
export function refreshPayDomainAnalysis(data) {
return request({
url: '/operation/son/pay/analysis',
method: 'put',
data: data
})
}
// 支付域名状态修改
export function setPayDomainStatus(data) {
return request({
url: '/operation/son/pay/status',
method: 'put',
data: data
})
}
// 支付域名清理缓存
export function clearPayDomainCache(data) {
return request({
url: '/operation/son/pay/clear/cache',
method: 'put',
data: data
})
}
// 支付域名删除
export function delPayDomain(ids) {
return request({
url: '/operation/son/pay/' + ids,
method: 'delete'
})
}
// 支付域名新增
export function addPayDomain(data) {
return request({
url: '/operation/son/pay',
method: 'post',
data: data
})
}
// 支付域名修改
export function updatePayDomain(data) {
return request({
url: '/operation/son/pay',
method: 'put',
data: data
})
}
// 支付域名设为主域名
export function setPayDomainMain(data) {
return request({
url: '/operation/son/pay/main/son',
method: 'put',
data: data
})
}
//------------------外部白名单域名---------------------//
// 外部白名单域名列表
export function getWhiteDomainList(query) {
return request({
url: '/operation/white/list',
method: 'get',
params: query
})
}
// 外部白名单域名新增
export function addWhiteDomain(data) {
return request({
url: '/operation/white',
method: 'post',
data: data
})
}
// 外部白名单域名修改
export function updateWhiteDomain(data) {
return request({
url: '/operation/white',
method: 'put',
data: data
})
}
// 外部白名单域名批量修改
export function updateWhiteDomainBatch(data) {
return request({
url: '/operation/white/all',
method: 'put',
data: data
})
}
// 外部白名单域名删除
export function delWhiteDomain(ids) {
return request({
url: '/operation/white/' + ids,
method: 'delete'
})
}
//------------------自定义解析---------------------//
// 自定义解析列表
export function getCustomParsingList(query) {
return request({
url: '/operation/analysis/list',
method: 'get',
params: query
})
}
// 自定义解析新增
export function addCustomParsing(data) {
return request({
url: '/operation/analysis',
method: 'post',
data: data
})
}
// 自定义解析修改
export function updateCustomParsing(data) {
return request({
url: '/operation/analysis',
method: 'put',
data: data
})
}
// 自定义解析删除
export function delCustomParsing(ids) {
return request({
url: '/operation/analysis/' + ids,
method: 'delete'
})
}

View File

@ -0,0 +1,70 @@
import request from '@/utils/request'
// 域名供应商列表
export function getDomainSupplierList(query) {
return request({
url: '/domain/supplier/list',
method: 'get',
params: query
})
}
// 添加域名供应商
export function addDomainSupplier(data) {
return request({
url: '/domain/supplier',
method: 'post',
data: data
})
}
// 修改域名供应商
export function putDomainSupplier(data) {
return request({
url: '/domain/supplier',
method: 'put',
data: data
})
}
//供应商详情
export function getDomainSupplierInfo(id) {
return request({
url: `/domain/supplier/${id}`,
method: 'get',
})
}
// 域名供应商开关
export function switchDomainSupplier(data) {
return request({
url: '/domain/supplier/switch',
method: 'put',
data: data
})
}
// 删除域名供应商
export function delDomainSupplier(ids) {
return request({
url: '/domain/supplier/' + ids,
method: 'delete'
})
}
// 域名供应商配置
export function configDomainSupplier(data) {
return request({
url: '/domain/supplier/config',
method: 'put',
data: data
})
}
//配置详情
export function getDomainSupplierConfig(query) {
return request({
url: '/domain/supplier/config',
method: 'get',
params: query
})
}

View File

@ -171,3 +171,29 @@ export function postSiteDeleteApiGame(data) {
}) })
} }
//站点列表
export function getSiteSelectList(query) {
return request({
url: '/site/select',
method: 'get',
params: query
})
}
//停用
export function postSiteStopApiGameInfo(data) {
return request({
url: '/site/stop/api/game/info',
method: 'post',
data: data
})
}
//启用
export function postSiteOpenApiGameInfo(data) {
return request({
url: '/site/open/api/game/info',
method: 'post',
data: data
})
}

View File

@ -0,0 +1,36 @@
import request from '@/utils/request'
// 数据源列表
export function listDataSource(query) {
return request({
url: '/site/data/source/list',
method: 'get',
params: query
})
}
// 新增数据源
export function addDataSourceSave(data) {
return request({
url: '/site/data/source/save',
method: 'post',
data: data
})
}
// 修改数据源
export function updateDataSource(data) {
return request({
url: '/site/data/source/update',
method: 'put',
data: data
})
}
// 修改开关
export function switchDataSource(data) {
return request({
url: '/site/data/source/switch',
method: 'put',
data: data
})
}

View File

@ -8,6 +8,22 @@ export function listRole(query) {
params: query params: query
}) })
} }
// 获取角色选择框列表
export function optionselect(query) {
return request({
url: '/system/role/optionselect',
method: 'get',
params: query
})
}
// 获取菜单角色
export function getMenu(roleId) {
return request({
url: '/system/role/getMenu/' + roleId,
method: 'get'
})
}
// 查询角色详细 // 查询角色详细
export function getRole(roleId) { export function getRole(roleId) {
@ -97,7 +113,15 @@ export function authUserCancelAll(data) {
return request({ return request({
url: '/system/role/authUser/cancelAll', url: '/system/role/authUser/cancelAll',
method: 'put', method: 'put',
params: data data: data
})
}
// 批量清除关联账号
export function authRolecancelAll(data) {
return request({
url: '/system/role/authRole/cancelAll',
method: 'put',
data: data
}) })
} }
@ -106,7 +130,7 @@ export function authUserSelectAll(data) {
return request({ return request({
url: '/system/role/authUser/selectAll', url: '/system/role/authUser/selectAll',
method: 'put', method: 'put',
params: data data: data
}) })
} }

View File

@ -43,13 +43,36 @@ export function delUser(userId) {
method: 'delete' method: 'delete'
}) })
} }
//删除账户/批量删除
export function deleteUserSystem(data) {
return request({
url: '/system/user/delete',
method: 'delete',
data: data
})
}
//冻结账户
export function changeStatus(data) {
return request({
url: '/system/user/changeStatus',
method: 'put',
data: data
})
}
//清空角色
export function resetRole(data) {
return request({
url: '/system/user/resetRole',
method: 'put',
data: data
})
}
// 用户密码重置 // 用户密码重置
export function resetUserPwd(userId, password,codeGoogle) { export function resetUserPwd(userId,inputGoogleCode) {
const data = { const data = {
userId, userId,
password, inputGoogleCode
codeGoogle
} }
return request({ return request({
url: '/system/user/resetPwd', url: '/system/user/resetPwd',
@ -58,6 +81,20 @@ export function resetUserPwd(userId, password,codeGoogle) {
}) })
} }
// 用户重置谷歌验证码
export function resetGoogle(userId,inputGoogleCode) {
const data = {
userId,
inputGoogleCode
}
return request({
url: '/system/user/resetGoogle',
method: 'put',
data: data
})
}
// 用户状态修改 // 用户状态修改
export function changeUserStatus(userId, status) { export function changeUserStatus(userId, status) {
const data = { const data = {
@ -89,10 +126,11 @@ export function updateUserProfile(data) {
} }
// 用户密码重置 // 用户密码重置
export function updateUserPwd(oldPassword, newPassword) { export function updateUserPwd(oldPassword, newPassword,codeGoogle) {
const data = { const data = {
oldPassword, oldPassword,
newPassword newPassword,
codeGoogle
} }
return request({ return request({
url: '/system/user/profile/updatePwd', url: '/system/user/profile/updatePwd',
@ -101,6 +139,18 @@ export function updateUserPwd(oldPassword, newPassword) {
}) })
} }
// 修改密码
export function updateUserPassword(newPassword) {
const data = {
newPassword
}
return request({
url: '/system/user/profile/change/password',
method: 'put',
data: data
})
}
// 用户头像上传 // 用户头像上传
export function uploadAvatar(data) { export function uploadAvatar(data) {
return request({ return request({
@ -136,11 +186,10 @@ export function deptTreeSelect() {
}) })
} }
// 修改密码 // 用户名称检查
export function updateUserPassword(data) { export function getSystemUserCheck(userName) {
return request({ return request({
url: '/resetPwd', url: '/system/user/check/' + userName,
method: 'post', method: 'get'
data: data
}) })
} }

View File

@ -58,7 +58,9 @@ a:hover {
div:focus { div:focus {
outline: none; outline: none;
} }
li {
list-style-type: none;
}
.fr { .fr {
float: right; float: right;
} }

View File

@ -249,11 +249,14 @@ file_picker_callback: (cb, value, meta) => {
}); });
</script> </script>
<style scoped> <style>
.tinymce-wrapper { .tinymce-wrapper {
position: relative; /* ✅ 关键 */ position: relative; /* ✅ 关键 */
width: 100%; width: 100%;
min-height: 500px; min-height: 500px;
z-index: 0; z-index: 0;
}
.tox-tinymce-aux {
z-index: 2600 !important;
} }
</style> </style>

View File

@ -0,0 +1,155 @@
<template>
<el-dialog :title="t('上传头像')" v-model="open" width="800px" align-center append-to-body @opened="modalOpened" @close="closeDialog">
<el-row>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<vue-cropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight"
:fixedBox="options.fixedBox" :outputType="options.outputType" @realTime="realTime" v-if="visible" />
</el-col>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<div class="avatar-upload-preview">
<img :src="options.previews.url" :style="options.previews.img" />
</div>
</el-col>
</el-row>
<br />
<el-row>
<el-col :lg="2" :md="2">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
<el-button>
{{ t('选择') }}
<el-icon class="el-icon--right">
<Upload />
</el-icon>
</el-button>
</el-upload>
</el-col>
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
</el-col>
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
<el-button type="primary" @click="saveUploadImg()">{{ t(' ') }}</el-button>
</el-col>
</el-row>
</el-dialog>
</template>
<script setup>
import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";
import { uploadImgFile,uploadImgFileCommon } from "@/api/common";
const props = defineProps({
imgInfo: {
type: Object,
default: {
img: '',
filename: ''
}
},
autoCropWidth: {
type: [Number, String],
default: 200
},
autoCropHeight: {
type: [Number, String],
default: 200
},
typeUpImg: {
type: [Number, String],
default: 3
}
})
const emit = defineEmits(['saveUpload', 'close']);
const { proxy } = getCurrentInstance();
const open = ref(true);
const visible = ref(false);
//
const options = reactive({
img: props.imgInfo.img, //
autoCrop: true, //
autoCropWidth: props.autoCropWidth, //
autoCropHeight: props.autoCropHeight, //
fixedBox: true, //
outputType: "png", // PNG
filename: props.imgInfo.filename, //
previews: {} //
});
/** 打开弹出层结束时的回调 */
function modalOpened() {
visible.value = true;
}
/** 覆盖默认上传行为 */
function requestUpload() { }
/** 向左旋转 */
function rotateLeft() {
proxy.$refs.cropper.rotateLeft();
}
/** 向右旋转 */
function rotateRight() {
proxy.$refs.cropper.rotateRight();
}
/** 图片缩放 */
function changeScale(num) {
num = num || 1;
proxy.$refs.cropper.changeScale(num);
}
/** 上传预处理 */
function beforeUpload(file) {
if (file.type.indexOf("image/") == -1) {
proxy.$modal.msgError(proxy.t('文件格式错误,请上传图片类型,如JPGPNG后缀的文件'));
} else {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
options.img = reader.result;
options.filename = file.name;
};
}
}
/** 上传图片 */
function saveUploadImg() {
proxy.$refs.cropper.getCropBlob(data => {
let formData = new FormData();
formData.append("file", data, options.filename);
uploadImgFileCommon(formData,props.typeUpImg||3).then(res => {
open.value = false;
visible.value = false;
emit('saveUpload', res);
});
});
}
/** 实时预览 */
function realTime(data) {
options.previews = data;
}
/** 关闭窗口 */
function closeDialog() {
open.value = false;
visible.value = false;
emit('close');
}
</script>
<style lang='scss' scoped></style>

View File

@ -1,60 +1,58 @@
<template> <template>
<div class="component-upload-image"> <div class="component-upload-image">
<el-upload <el-upload multiple :action="uploadImgUrl" :disabled="disabled" list-type="picture-card" :on-success="handleUploadSuccess"
multiple :before-upload="handleBeforeUpload" :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed"
:action="uploadImgUrl" ref="imageUpload" :before-remove="handleDelete" :show-file-list="true" :headers="headers" :file-list="fileList"
list-type="picture-card" :on-preview="handlePictureCardPreview" :class="{ hide: fileList.length >= limit }">
:on-success="handleUploadSuccess" <el-icon class="avatar-uploader-icon">
:before-upload="handleBeforeUpload" <plus />
:limit="limit" </el-icon>
:on-error="handleUploadError"
:on-exceed="handleExceed"
ref="imageUpload"
:before-remove="handleDelete"
:show-file-list="true"
:headers="headers"
:file-list="fileList"
:on-preview="handlePictureCardPreview"
:class="{ hide: fileList.length >= limit }"
>
<el-icon class="avatar-uploader-icon"><plus /></el-icon>
</el-upload> </el-upload>
<!-- 上传提示 --> <!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip"> <div class="el-upload__tip" v-if="showTip">
请上传 {{ t('请上传') }}
<template v-if="fileSize"> <template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> {{ t('大小不超过') }} <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template> </template>
<template v-if="fileType"> <template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> {{ t('格式为') }} <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
</template> </template>
的文件 {{ t('的文件') }}
</div> </div>
<el-dialog <el-dialog v-model="dialogVisible" :title="t('预览')" width="800px" append-to-body>
v-model="dialogVisible" <img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
title="预览"
width="800px"
append-to-body
>
<img
:src="dialogImageUrl"
style="display: block; max-width: 100%; margin: 0 auto"
/>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { getToken } from "@/utils/auth"; import { getToken, getLocalStorage } from "@/utils/auth";
const props = defineProps({ const props = defineProps({
disabled: {
type: Boolean,
default: false,
},
modelValue: [String, Object, Array], modelValue: [String, Object, Array],
// //
limit: { limit: {
type: Number, type: Number,
default: 5, default: 5,
}, },
//
imgSize: {
type: Object,
default: () => ({
width: 0,
height: 0
})
},
//
typeUpImg:{
type: [Number, String],
default: "1"
},
// (MB) // (MB)
fileSize: { fileSize: {
type: Number, type: Number,
@ -63,13 +61,17 @@ const props = defineProps({
// , ['png', 'jpg', 'jpeg'] // , ['png', 'jpg', 'jpeg']
fileType: { fileType: {
type: Array, type: Array,
default: () => ["png", "jpg", "jpeg"], default: () => ["png", "jpg", "jpeg", "gif", "webp",'ico','mp4','mp3'],
}, },
// //
isShowTip: { isShowTip: {
type: Boolean, type: Boolean,
default: true default: true
}, },
disabled: {
type: Boolean,
default: false
}
}); });
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
@ -78,14 +80,14 @@ const number = ref(0);
const uploadList = ref([]); const uploadList = ref([]);
const dialogImageUrl = ref(""); const dialogImageUrl = ref("");
const dialogVisible = ref(false); const dialogVisible = ref(false);
const baseUrl = import.meta.env.VITE_APP_BASE_API; const baseUrl = getLocalStorage('fileUrl');
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // const uploadUrl = getLocalStorage('uploadUrl');
const uploadImgUrl = ref(uploadUrl + `/file/upload/localSysFile/${props.typeUpImg}`); //
const headers = ref({ Authorization: "Bearer " + getToken() }); const headers = ref({ Authorization: "Bearer " + getToken() });
const fileList = ref([]); const fileList = ref([]);
const showTip = computed( const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize) () => props.isShowTip && (props.fileType || props.fileSize)
); );
watch(() => props.modelValue, val => { watch(() => props.modelValue, val => {
if (val) { if (val) {
// //
@ -110,6 +112,8 @@ watch(() => props.modelValue, val => {
// loading // loading
function handleBeforeUpload(file) { function handleBeforeUpload(file) {
let isImg = false; let isImg = false;
//
if (props.fileType.length) { if (props.fileType.length) {
let fileExtension = ""; let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) { if (file.name.lastIndexOf(".") > -1) {
@ -123,12 +127,15 @@ function handleBeforeUpload(file) {
} else { } else {
isImg = file.type.indexOf("image") > -1; isImg = file.type.indexOf("image") > -1;
} }
if (!isImg) { if (!isImg) {
proxy.$modal.msgError( proxy.$modal.msgError(
`文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!` `文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!`
); );
return false; return false;
} }
//
if (props.fileSize) { if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize; const isLt = file.size / 1024 / 1024 < props.fileSize;
if (!isLt) { if (!isLt) {
@ -136,8 +143,29 @@ function handleBeforeUpload(file) {
return false; return false;
} }
} }
//
const checkSize = new Promise(function (resolve, reject) {
//
let width = props.imgSize.width;
//
let height = props.imgSize.height;
let _URL = window.URL || window.webkitURL;
let img = new Image();
img.onload = function () {
let valid = img.width == width && img.height == height;
//
valid || (width === 0 && height === 0) ? resolve() : reject();
}
img.src = _URL.createObjectURL(file);
}).then(() => {
proxy.$modal.loading("正在上传图片,请稍候..."); proxy.$modal.loading("正在上传图片,请稍候...");
number.value++; number.value++;
}, () => {
proxy.$modal.msgError(`上传图片尺寸必须为${props.imgSize.width}x${props.imgSize.height}px!`);
return Promise.reject();
});
return checkSize;
} }
// //
@ -148,7 +176,7 @@ function handleExceed() {
// //
function handleUploadSuccess(res, file) { function handleUploadSuccess(res, file) {
if (res.code === 200) { if (res.code === 200) {
uploadList.value.push({ name: res.fileName, url: res.fileName }); uploadList.value.push({ name: res.fileName, url: res.url });
uploadedSuccessfully(); uploadedSuccessfully();
} else { } else {
number.value--; number.value--;

View File

@ -13,8 +13,9 @@
@clear="handleQuery" :maxlength="inputConfig.maxlength" :isCalculable="false" :show-word-limit="inputConfig.showWordLimit"> @clear="handleQuery" :maxlength="inputConfig.maxlength" :isCalculable="false" :show-word-limit="inputConfig.showWordLimit">
</NumberInput> </NumberInput>
<!-- 下拉选择框字典放到inputConfig.dictKey --> <!-- 下拉选择框字典放到inputConfig.dictKey -->
<custom-select v-else-if="inputType === 'select'" :placeholder="t(inputPlaceholder || `请选择${currentLabel}`)" v-model="queryParams[queryParams[keyName]]" @change="handleQuery" clearable <custom-select v-else-if="inputType === 'select' && loadingShow" filterable :placeholder="t(inputPlaceholder || `请选择${currentLabel}`)" v-model="queryParams[queryParams[keyName]]" @change="handleQuery" clearable
@clear="handleQuery" :options="inputConfig.options"></custom-select> @clear="handleQuery" :options="inputConfig.options"></custom-select>
<!-- 普通输入框 --> <!-- 普通输入框 -->
<el-input v-else v-model="queryParams[queryParams[keyName]]" <el-input v-else v-model="queryParams[queryParams[keyName]]"
:placeholder="t(inputPlaceholder || `请输入${currentLabel}`)" @keyup.enter="handleQuery" clearable :placeholder="t(inputPlaceholder || `请输入${currentLabel}`)" @keyup.enter="handleQuery" clearable
@ -55,6 +56,7 @@ const currentLabel = ref(''), inputPlaceholder = ref(''); // 输入框提示
const inputType = ref(''); // const inputType = ref(''); //
const inputConfig = ref({}); // input const inputConfig = ref({}); // input
const loadingShow = ref(true);
// //
const keyChange = (type) => { const keyChange = (type) => {
props.queryParamsList.forEach(item => { props.queryParamsList.forEach(item => {
@ -69,6 +71,10 @@ const keyChange = (type) => {
// value // value
inputType.value = item.inputType || ''; inputType.value = item.inputType || '';
inputConfig.value = item.inputConfig || {} inputConfig.value = item.inputConfig || {}
loadingShow.value = false;
nextTick(() => {
loadingShow.value = true;
})
} else { } else {
// //
queryParams.value[item.value] = null queryParams.value[item.value] = null

View File

@ -7,7 +7,7 @@ import { isHttp } from '@/utils/validate'
import { isRelogin } from '@/utils/request' import { isRelogin } from '@/utils/request'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings'
import { getLangSelect,getTimeZone,getCurrencySelect,getMaterialGroupSelect } from '@/api/siteManagement' import { getLangSelect,getTimeZone,getCurrencySelect,getMaterialGroupSelect,getSiteSelectList } from '@/api/siteManagement'
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission'
@ -41,6 +41,16 @@ router.beforeEach((to, from, next) => {
getCurrencySelect().then((resCurrency) => { getCurrencySelect().then((resCurrency) => {
setLocalStorage('currencySelect', resCurrency.data); setLocalStorage('currencySelect', resCurrency.data);
}) })
getSiteSelectList().then((resSite) => {
let siteSelect = resSite.data.map((item) => {
return {
...item,
value: item.id,
label: item.siteName,
};
});
setLocalStorage('siteSelect', siteSelect);
})
useUserStore().getDict().then(res => { useUserStore().getDict().then(res => {
setLocalStorage('dict', res.data); setLocalStorage('dict', res.data);

View File

@ -70,7 +70,7 @@
<el-dialog v-model="goodDialogVisible" align-center :title="t('温馨提示')" width="418" :before-close="handleClose"> <el-dialog v-model="goodDialogVisible" align-center :title="t('温馨提示')" width="418" :before-close="handleClose">
<div style="width: 90%;margin-bottom: 20px;">{{ t('请用手机打开客服端Google身份验证器,输入验证码') }}</div> <div style="width: 90%;margin-bottom: 20px;">{{ t('请用手机打开客服端Google身份验证器,输入验证码') }}</div>
<div class=""> <div class="">
<number-input v-model="gooleCode" style="width: 90%" :placeholder="t('验证码只能为数字')" ></number-input> <number-input v-model="gooleCode" style="width: 90%" :maxlength="6" :placeholder="t('验证码只能为数字')" ></number-input>
</div> </div>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@ -90,7 +90,7 @@
</div> </div>
<el-form ref="authenticatorRef" :model="authenticatorForm" :rules="authenticatorRules"> <el-form ref="authenticatorRef" :model="authenticatorForm" :rules="authenticatorRules">
<el-form-item label="谷歌验证码" prop="authenticatorCode"> <el-form-item label="谷歌验证码" prop="authenticatorCode">
<number-input v-model="authenticatorForm.authenticatorCode" style="width: 100%;height: 36px;" :placeholder="t('验证码只能为数字')" ></number-input> <number-input v-model="authenticatorForm.authenticatorCode" :maxlength="6" style="width: 100%;height: 36px;" :placeholder="t('验证码只能为数字')" ></number-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="" style="margin-top: 20px;margin-bottom: 20px;"> <div class="" style="margin-top: 20px;margin-bottom: 20px;">

View File

@ -120,12 +120,26 @@ const currencySelectArr = getLocalStorage('currencySelect')?.map(item => {
}); });
const openView = ref(false); const openView = ref(false);
const siteList = ref(getLocalStorage('siteSelect'));
const queryParamsList = ref([{ const queryParamsList = ref([{
label: proxy.t('站点名称'), label: proxy.t('站点名称'),
value: 'siteName', value: 'siteName',
inputType: 'select',
inputConfig:{
options: siteList.value.map(item => {
return {
label: item.label,
value: item.label,
}
}),
}
},{ },{
label: proxy.t('站点ID'), label: proxy.t('站点ID'),
value: 'siteId', value: 'siteId',
inputType: 'select',
inputConfig:{
options: siteList.value,
}
}]); }]);
const data = reactive({ const data = reactive({
form: {}, form: {},

View File

@ -1,24 +1,47 @@
<template> <template>
<!-- 厅主公告详情 --> <!-- 详情 -->
<el-dialog :title="t('厅主公告详情')" :close-on-click-modal="false" align-center v-model="showDialog" width="900px" append-to-body> <el-dialog :title="t('详情')" :close-on-click-modal="false" align-center v-model="showDialog" width="900px" append-to-body>
<el-scrollbar max-height="700px" > <el-scrollbar max-height="700px" >
<div style="font-size: 20px; font-weight: bold; margin-bottom: 20px;width: 100%;text-align: center;">{{ modifyDate.title }}</div> <div style="font-size: 20px; font-weight: bold; margin-bottom: 20px;width: 100%;text-align: center;">{{ modifyDate.title }}</div>
<el-descriptions :column="1"> <el-descriptions :column="1" border label-width="150px">
<el-descriptions-item label="公告类型"> <el-descriptions-item label-class-name="label-class-name" label="会员ID">
<span v-if="modifyDate.noticeType == 1">{{ t('') }}</span>
<span v-if="modifyDate.noticeType == 2">{{ t('') }}</span> {{ modifyDate.id }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="发送时间"> <el-descriptions-item label-class-name="label-class-name" label="会员账号">
{{ parseTime(modifyDate.noticeStartTime) }} {{ modifyDate.memberAccount }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="公告状态"> <el-descriptions-item label-class-name="label-class-name" label="币种">
<dict-tag :options="noticeStatusArr" :value="modifyDate.noticeStatus" /> {{ modifyDate.currencyType }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="内容"> <el-descriptions-item label-class-name="label-class-name" label="LineCode">
<div style="width: 100%;overflow: hidden;"> --
<div v-html="modifyDate.content"></div> </el-descriptions-item>
</div> <el-descriptions-item label-class-name="label-class-name" label="所属站点(ID)">
{{ modifyDate.siteName }}({{ modifyDate.siteId }})
</el-descriptions-item>
<el-descriptions-item label-class-name="label-class-name" label="总余额">
{{ modifyDate.balance==null?'--':modifyDate.balance }}
</el-descriptions-item>
<el-descriptions-item label-class-name="label-class-name" label="总输赢">
{{ modifyDate.failAndWin== null ? '--' : modifyDate.failAndWin }}
</el-descriptions-item>
<el-descriptions-item label-class-name="label-class-name" label="总打码">
{{ modifyDate.betAmount == null ?'--': modifyDate.betAmount }}
</el-descriptions-item>
<el-descriptions-item label-class-name="label-class-name" label="在线状态">
<span v-if="modifyDate.presenceStatus== 0">线</span>
<span v-if="modifyDate.presenceStatus== 1">线</span>
</el-descriptions-item>
<el-descriptions-item label-class-name="label-class-name" label="最后登录">
{{ formatTime(modifyDate.loginDate) }}
</el-descriptions-item>
<el-descriptions-item label-class-name="label-class-name" label="注册时间">
{{ formatTime(modifyDate.registeTime) }}
</el-descriptions-item>
<el-descriptions-item label-class-name="label-class-name" label="备注">
{{ modifyDate.remark == null ? '--' : modifyDate.remark }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
@ -159,6 +182,10 @@ const closeDialog = () => {
} }
} }
.label-class-name{
width: 180px;
text-align: right !important;
}
</style> </style>
<style > <style >
.el-form-ge .el-form-item__content{ .el-form-ge .el-form-item__content{

View File

@ -48,7 +48,7 @@
</el-table-column> </el-table-column>
<el-table-column :label="t('所属站点(ID)')" min-width="100" align="center" > <el-table-column :label="t('所属站点(ID)')" min-width="100" align="center" >
<template #default="{row}"> <template #default="{row}">
{{ row.siteName }} {{ row.siteName }}({{ row.siteId }})
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="t('币种')" min-width="100" align="center" prop="currencyType" > <el-table-column :label="t('币种')" min-width="100" align="center" prop="currencyType" >
@ -309,7 +309,7 @@ const getSiteSelects = () => {
const handleView = (row) => { const handleView = (row) => {
openView.value = true; openView.value = true;
// modifyDate.value = row; modifyDate.value = row;
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {

View File

@ -0,0 +1,267 @@
<template>
<div class="tp-content-box">
<el-tabs v-model="activeName" @tab-change="handleChange">
<el-tab-pane name="1" :label="t('男头像')"></el-tab-pane>
<el-tab-pane name="2" :label="t('女头像')"></el-tab-pane>
</el-tabs>
<!-- 列表组件 -->
<div class="cpt-main">
<div class="avatar-list">
<li v-for="item in dataList" :key="item.id">
<img :class="item.stopStatus !== 1 ? 'img-disable' : ''" :src="fileHost + item.imgUrl">
<span class="img-tag" v-if="item.systemType"></span>
<div class="mask-box mb-disable" v-if="item.stopStatus !== 1">
<p class="disable-text">{{ t('已停用') }}</p>
</div>
<div class="mask-box mb-btn">
<el-button @click="changeStatus(item, 0)" v-if="item.stopStatus === 1 && item.systemType"
type="danger">{{
t('停用') }}</el-button>
<el-button @click="changeStatus(item, 1)" v-if="item.stopStatus === 0 && item.systemType"
type="success">{{
t('启用') }}</el-button>
<el-button v-if="!item.systemType" type="info" @click="deleteAvatar(item)">{{ t('') }}</el-button>
</div>
</li>
<li style="border: #dddddd dashed 1px;">
<div class="upload-box">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false"
:before-upload="beforeUpload">
<el-icon color="#666666" size="26">
<Plus />
</el-icon>
<div class="upload-tips">
<div>{{ t('图标大小200*200') }}</div>
<div>{{ t('PNG或JPG格式') }}</div>
</div>
</el-upload>
</div>
</li>
</div>
</div>
<!-- 头像上传裁剪 -->
<image-crop v-if="isShowCrop" :imgInfo="uploadInfo" @saveUpload="saveUpload" @close="isShowCrop = false"></image-crop>
</div>
</template>
<!-- 头像管理 -->
<script setup name="Avatar">
import ImageCrop from "@/components/ImageCrop/index.vue";
import { getLocalStorage } from "@/utils/auth";
import { listAvatar, addAvatar, delAvatar, updateAvatar } from "@/api/operation/avatar";
const { proxy } = getCurrentInstance();
const activeName = ref('1'); // tab
//
const queryParams = reactive({
pageNum: 1,
pageSize: 200,
sex: '1'
});
const dataList = ref([]), loading = ref(false);
//
const handleChange = () => {
queryParams.sex = activeName.value;
getAvatarList();
}
//
const getAvatarList = () => {
loading.value = true;
listAvatar(queryParams).then(res => {
dataList.value = res.rows;
loading.value = false;
}).catch(() => {
loading.value = false;
});
}
getAvatarList();
//
const fileHost = getLocalStorage('fileUrl') || ''; // host
const isShowCrop = ref(false);
const uploadInfo = ref({
img: '',
filename: ''
});
//
function requestUpload() { }
//
function beforeUpload(file) {
if (file.type.indexOf("image/") == -1) {
proxy.$modal.msgError(proxy.t('文件格式错误,请上传图片类型,如JPGPNG后缀的文件'));
} else {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
uploadInfo.value.img = reader.result;
uploadInfo.value.filename = file.name;
isShowCrop.value = true; //
};
}
}
//
const saveUpload = (data) => {
const params = {
imgUrl: data.url,
sex: activeName.value
}
addAvatar(params).then(res => {
isShowCrop.value = false;
proxy.$modal.msgSuccess(proxy.t('头像新增成功'));
getAvatarList();
});
}
//
const changeStatus = (row, status) => {
proxy.$modal.confirm(proxy.t('确认{}该头像吗?', status === 0 ? '停用' : '启用')).then(() => {
const params = {
imgUrl: row.imgUrl,
sex: row.sex,
id: row.id,
stopStatus: status
}
updateAvatar(params).then(res => {
proxy.$modal.msgSuccess(proxy.t('{}头像成功!', status === 0 ? '停用' : '启用'));
getAvatarList();
})
}).catch(() => { });
}
//
const deleteAvatar = (row) => {
proxy.$modal.confirm(proxy.t('确认删除该头像吗?')).then(() => {
delAvatar(row.id).then(res => {
proxy.$modal.msgSuccess(proxy.t('删除头像成功!'));
getAvatarList();
})
}).catch(() => { });
}
</script>
<style scoped lang='scss'>
.tp-content-box {
padding: 15px;
box-sizing: border-box;
.cpt-main {
height: calc(100% - 55px);
display: flex;
flex-direction: column;
position: relative;
.avatar-list {
display: flex;
flex-wrap: wrap;
width: 100%;
li {
min-width: 140px;
min-height: 140px;
width: calc(10% - 10px);
margin: 5px;
position: relative;
border-radius: 16px;
overflow: hidden;
img {
width: 100%;
object-fit: contain;
display: block;
}
.img-disable {
filter: grayscale(100%);
}
&:hover {
.mask-box {
opacity: 1;
}
.mb-disable {
opacity: 0;
}
}
.img-tag {
display: block;
position: absolute;
right: 0;
top: 0;
width: 30%;
height: 30%;
background: url(../../../assets/images/avatar-tag.png) no-repeat right top;
background-size: contain;
}
.mask-box {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
background-color: rgba(0, 0, 0, .6);
display: flex;
align-items: center;
justify-content: center;
transition: all .2s ease-in-out;
.disable-text {
padding: 0;
margin: 0;
width: 100%;
text-align: center;
color: #b90505;
font-weight: bold;
font-size: 14px;
padding: 8px 0;
background-color: #ffffff;
user-select: none;
}
}
.mb-disable{
background-color: rgba(0, 0, 0, .3);
}
.mb-btn {
opacity: 0;
}
}
}
}
.upload-box {
height: 100%;
display: flex;
>div {
margin: 0 auto;
}
:deep(.el-upload) {
height: 100%;
flex-direction: column;
}
.upload-tips {
text-align: center;
width: 100%;
color: #999;
font-size: 12px;
line-height: 1.5;
padding-top: 10px;
}
}
}
</style>

View File

@ -0,0 +1,382 @@
<template>
<!-- 新增子域名 -->
<el-dialog :title="t('新增域名')" v-model="isShowDialog" width="800px" append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<el-form ref="formRef" :rules="rules" :model="formData" label-width="120" class="mr0">
<el-form-item label-width="0" prop="domainSupplierId" class="clt-item">
<span slot="label" class="label-box">
<i style="color: #f56c6c; margin-right: 4px;">*</i>{{ t('选择节点') }}
<icon-tips>
<div class="node-tips">
<p>{{ t('1中国市场含港澳台推荐使用alb域名aws防封、三方公共域名aws与azure防封、Wangsu (CDN+IP)、A8、Yundun(云盾)、Funnull(方能)、superEdge、大陆阿里云。')}}</p>
<p>{{ t('2全球市场即非中国推荐使用使用alb域名aws与aliyun防封、三方公共域名aws与azure防封、AWS、CF、腾讯云、 阿里云、Google、Azure、华为云 、Wangsu(CDN+IP)、Yundun (云盾)。')}}</p>
<p>{{ t('3Wangsu独立IP提供独立IP地址适用于会封禁域名但不会封禁IP的国家例如印尼、中国、巴西、越南等国际和中国大陆市场均可使用即全球通用200USDT/IP/月),若超过7天未启用系统自动回收。') }}
</p>
<p>{{ t('4开放回源仅限web大厅提供开放独立源服务可使用外部三方CDN需申请开通300U/月需要专业人士配置websocket、HTTP2、缓存等详见域名管理教程。') }}
</p>
</div>
</icon-tips>
</span>
<div class="item-right">
<supplier-select v-model="formData.domainSupplierId" @change="supplierChange"
:supplierList="supplierList" type="addSonDomain"></supplier-select>
</div>
</el-form-item>
<el-form-item :label="t('选择域名:')" prop="domainId" class="mr0">
<!-- 非防封 -->
<checkbox-select v-if="!nodeProhibitStatus" class="w100" :disabled="!supplierDomainList.length"
:options="supplierDomainList" v-model="formData.domainId" :placeholder="t('节点下域名')"
@change="selectDomain" @allSelect="selectDomain"></checkbox-select>
<!-- 防封 -->
<el-checkbox-group v-else v-model="formData.domainId" style="width: 100%" @change="selectDomain">
<template v-for="item in supplierDomainList" :key="item.value">
<el-checkbox :value="item.value" :label="item.label">
{{ item.label }}
<icon-tips trigger="hover" v-if="item.value == -1 || item.value == -2">
{{ item.value == -1 ? t('由系统固定分配独立ALB域名与端口启用成功开始计费') : t('由系统随机分配云平台原生域名,启用成功开始计费') }}
</icon-tips>
</el-checkbox>
</template>
</el-checkbox-group>
</el-form-item>
<el-form-item :label="t('域名用途:')" class="mr0">
{{ domainTypeName }}
</el-form-item>
<el-form-item :label="t('生效域名:')" prop="domainNames" class="mr0">
<span v-if="!formData.domainNames.length">
{{ t('您当前未添加域名,请先到') }}
<el-button link type="primary" @click="goDomainManage">{{ t('') }}</el-button>
{{ t('添加主域名和验证后再来设置') }}
</span>
<!-- 生效域名增删 -->
<template v-else>
<el-row v-for="(item, index) in formData.domainNames" :key="index" class="domain-list">
<el-col :span="18" class="et-col">
<el-form-item :prop="`domainNames[${index}].sonTitle`" :rules="rules.sonTitle" class="w100 mr0"
label-width="0">
<el-input v-model="item.sonTitle" :placeholder="t('例如:* 或 @ 或 www')" clearable>
<template #append><b>.</b>{{ item.sonDomain }}</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6" style="text-align: right;">
<el-button type="primary" icon="Plus" style="margin-right: 10px;"
@click="setDomainName(index, 'add')" />
<el-button icon="Close" style="margin-left: 0;" @click="setDomainName(index, 'delete')" />
</el-col>
</el-row>
</template>
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">{{ t('确 认') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="AddSonDomain">
import SupplierSelect from '../components/SupplierSelect.vue';
import IconTips from "@/components/IconTips"; //
import CheckboxSelect from "@/components/CheckboxSelect"; //
import {
getSupplierDomainList, //
addWebDomain, // web
addBackendDomain, //
addAppDomain, // app
addOssDomain, // oss
addDownloadDomain, //
addPayDomain, //
}
from "@/api/operation/domain";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const isShowDialog = ref(true); //
const formRef = ref();
//
const props = defineProps({
supplierList: {
type: Array,
default: []
},
type: {
type: String,
default: ''
}
});
//
const domainTypeName = computed(() => {
if (props.type == 'webHall') {
return proxy.t('Web大厅域名');
} else if (props.type == 'backend') {
return proxy.t('后端加速域名');
} else if (props.type == 'appHall') {
return proxy.t('App大厅域名');
} else if (props.type == 'oss') {
return proxy.t('OSS加速域名');
} else if (props.type == 'downloadSite') {
return proxy.t('下载站域名');
} else if (props.type == 'payment') {
return proxy.t('支付域名');
}
});
//
const formData = reactive({
domainSupplierId: '',
domainId: [],
domainNames: []
});
//
const rules = reactive({
domainSupplierId: [
{ required: true, message: proxy.t('请选择节点'), trigger: 'change' },
],
domainId: [
{ required: true, message: proxy.t('请选择域名'), trigger: 'change' }
],
sonTitle: [
{ required: true, message: proxy.t('请输入子域名'), trigger: ['change', 'blur'] },
{
pattern: /^[\w\*@]+$/,
message: proxy.t('只能包含字母、数字、下划线、星号和@'),
trigger: ['change', 'blur']
},
{
validator: (rule, value, callback) => {
const _index = +rule.field.toString().match(/\d+/)[0], _domainNames = formData.domainNames;
let isHas = false;
_domainNames.forEach((item, index) => {
if (index != _index && item.sonTitle != '' && item.sonTitle + '.' + item.sonDomain == _domainNames[_index].sonTitle + '.' + _domainNames[_index].sonDomain) {
isHas = true;
}
});
if (isHas) {
callback(new Error(proxy.t('子域名不能重复')));
} else {
callback();
}
},
trigger: ['change', 'blur']
}
]
});
//
const supplierDomainList = ref([]),
nodeProhibitStatus = ref(false); //
const supplierChange = (val) => {
//
formData.domainId = [];
formData.domainNames = [];
//
nodeProhibitStatus.value = props.supplierList.find(v => v.id == val).prohibit;
getSupplierDomainList({ domainSupplierId: val }).then(res => {
supplierDomainList.value = res.data.map(v => {
return {
label: v.name,
value: v.id
}
});
//
if (nodeProhibitStatus.value) {
const _domainList = [{
label: proxy.t('三方公共域名(防封)'),
value: '-2'
}];
// web
if (props.type == 'webHall') {
_domainList.unshift({
label: proxy.t('ALB防封域名'),
value: '-1'
});
}
supplierDomainList.value = [..._domainList, ...supplierDomainList.value];
}
});
}
//
const selectDomain = (val) => {
const _domainNames = [];
//
val.forEach(thisId => {
if (thisId != -1 && thisId != -2) { //
const domainInfo = supplierDomainList.value.find(v => v.value == thisId);
let isHas = false;
//
formData.domainNames.forEach(item => {
if (item.domainId == domainInfo.value) {
isHas = true;
_domainNames.push({...item});
}
});
//
if (!isHas) {
_domainNames.push({
domainId: thisId,
sonTitle: '',
sonDomain: domainInfo.label
});
}
}
});
formData.domainNames = _domainNames;
}
//
const setDomainName = (index, type) => {
if (type === 'add') {
//
const copyItem = JSON.parse(JSON.stringify(formData.domainNames[index]));
copyItem.sonTitle = '';
formData.domainNames.splice(index + 1, 0, copyItem);
} else {
// domainNamesformData.domainId
const _domainNameId = formData.domainNames[index].domainId;
formData.domainNames.splice(index, 1);
formData.domainNames.findIndex(v => v.domainId == _domainNameId) == -1 && formData.domainId.splice(formData.domainId.findIndex(v => v == _domainNameId), 1);
}
}
//
const submitForm = () => {
formRef.value.validate(valid => {
if (valid) {
//
const operationDomainSons = [];
//
formData.domainId.forEach(item => {
if (item == -1 || item == -2) {
operationDomainSons.push({
domainSupplierId: formData.domainSupplierId,
domainId: item
});
}
});
//
formData.domainNames.forEach(item => {
operationDomainSons.push({
domainSupplierId: formData.domainSupplierId,
domainId: item.domainId,
name: item.sonTitle + '.' + item.sonDomain,
});
});
//
loading.value = true;
switch (props.type) {
case 'webHall':
submitSuccess(addWebDomain({ operationDomainSons: operationDomainSons }));
break;
case 'backend':
submitSuccess(addBackendDomain({ operationDomainSons: operationDomainSons }));
break;
case 'appHall':
submitSuccess(addAppDomain({ operationDomainSons: operationDomainSons }));
break;
case 'oss':
submitSuccess(addOssDomain({ operationDomainSons: operationDomainSons }));
break;
case 'downloadSite':
submitSuccess(addDownloadDomain({ operationDomainSons: operationDomainSons }));
break;
case 'payment':
submitSuccess(addPayDomain({ operationDomainSons: operationDomainSons }));
break;
}
//
function submitSuccess(apiFn) {
apiFn.then(() => {
loading.value = false;
proxy.$message.success(proxy.t('添加成功'));
closeDialog('submit');
}).catch(() => {
loading.value = false;
});
}
}
});
}
//
const emit = defineEmits(['closeDialog', 'goDomainManage']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
//
const goDomainManage = () => {
emit('goDomainManage');
}
</script>
<style scoped lang="scss">
// label
.clt-item {
.label-box {
width: 120px;
}
.item-right {
width: calc(100% - 120px);
}
//
:deep(.el-form-item__error) {
padding-left: 120px;
}
}
.node-tips {
width: 550px;
p {
margin: 0;
padding: 5px 0;
}
}
//
.domain-list {
width: 100%;
margin-bottom: 18px;
.et-col {
line-height: 32px;
padding-right: 5px;
&:last-child {
padding-right: 0;
}
&:nth-child(3) {
text-align: center;
}
}
}
</style>

View File

@ -0,0 +1,265 @@
<template>
<!-- 批量操作弹窗 -->
<el-dialog :title="dialogTitle" v-model="isShowDialog" width="800px" append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<el-table row-key="id" stripe border :data="tableList">
<el-table-column :label="t('主域名')" align="center" min-width="150"
v-if="batchOpType == 'port' || batchOpType == 'clearCache' || batchOpType == 'delete' || batchOpType == 'stop' || batchOpType == 'enable'">
<template #default="{ row }">
{{ row.mainName || '-' }}
</template>
</el-table-column>
<el-table-column :label="t('CDN节点')" prop="cdnNodeName" align="center" min-width="150"></el-table-column>
<el-table-column :label="t('子域名')" prop="name" align="center" min-width="150"></el-table-column>
<el-table-column :label="t('其他担保域名列表')" align="center" min-width="150"
v-if="batchOpType == 'showSecurity' || batchOpType == 'hideSecurity'">
<template #default="{ row }">
{{ row.backupDomain ? t('展示') : t('隐藏') }}
</template>
</el-table-column>
<el-table-column :label="t('作为推广域名')" align="center" min-width="150"
v-if="batchOpType == 'showPromotion' || batchOpType == 'hidePromotion'">
<template #default="{ row }">
{{ row.marketingDomain ? t('开启') : t('关闭') }}
</template>
</el-table-column>
<el-table-column :label="t('启用入口')" align="center" min-width="150" v-if="batchOpType == 'edit'">
<template #default="{ row }">
<dict-select class="w100" v-model="row.startEntrance" dictKey="ff_operation_start_entrance"
:disabled="row.usageStatus != 4"></dict-select>
</template>
</el-table-column>
<el-table-column :label="t('屏蔽设备')" align="center" min-width="150" v-if="batchOpType == 'edit'">
<template #default="{ row }">
<dict-select class="w100" v-model="row.blockDevice" dictKey="ff_operation_block_device"
:disabled="row.usageStatus != 4 || row.startEntrance == 2"></dict-select>
</template>
</el-table-column>
<el-table-column :label="t('强制绑定代理账号(选填)')" align="center" min-width="150"
v-if="batchOpType == 'bindAgent'">
<template #default="{ row }">
<el-input v-model="row.agentAccount" :placeholder="t('请输入代理账号')" clearable></el-input>
</template>
</el-table-column>
</el-table>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">{{ t('确 认') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="BatchOperation">
import {
changeWebDomainPort, //
clearWebDomainCache, //
delWebDomain, //
setWebDomainStatus, //
webDomainSwitch, //
bindWebDomainAgent, //
} from "@/api/operation/domain";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const isShowDialog = ref(true);
//
const props = defineProps({
selectionData: {
type: Array,
default: []
},
batchOpType: {
type: String,
default: ''
}
});
//
const tableList = ref([]);
watch(() => props.selectionData, newVal => {
const _tableList = JSON.parse(JSON.stringify(newVal)); //
switch (props.batchOpType) {
case 'port':
//
tableList.value = _tableList.filter(v => v.sonType != -1 && v.sonType != -2);
break
case 'stop':
//
tableList.value = _tableList.filter(v => v.usageStatus != 5);
break
case 'enable':
//
tableList.value = _tableList.filter(v => v.usageStatus != 4);
break
case 'showSecurity':
//
tableList.value = _tableList.filter(v => !v.backupDomain && v.usageStatus == 4);
break
case 'hideSecurity':
//
tableList.value = _tableList.filter(v => v.backupDomain && v.usageStatus == 4);
break
case 'showPromotion':
// //APP
tableList.value = _tableList.filter(v => !v.marketingDomain && v.usageStatus == 4 && v.startEntrance != 2);
break
case 'hidePromotion':
// //APP
tableList.value = _tableList.filter(v => v.marketingDomain && v.usageStatus == 4 && v.startEntrance != 2);
break
case 'bindAgent':
//
tableList.value = _tableList.filter(v => v.usageStatus == 4);
break
default:
tableList.value = _tableList;
break
}
}, { immediate: true });
//
const dialogTitle = computed(() => {
if (props.batchOpType === 'port') {
return proxy.t('批量更换端口');
} else if (props.batchOpType === 'clearCache') {
return proxy.t('批量清理缓存');
} else if (props.batchOpType === 'delete') {
return proxy.t('批量删除');
} else if (props.batchOpType === 'stop') {
return proxy.t('批量停用');
} else if (props.batchOpType === 'enable') {
return proxy.t('批量启用');
} else if (props.batchOpType === 'issued') {
return proxy.t('批量锁定下发');
} else if (props.batchOpType === 'showSecurity') {
return proxy.t('批量展示其他担保列表');
} else if (props.batchOpType === 'hideSecurity') {
return proxy.t('批量隐藏其他担保列表');
} else if (props.batchOpType === 'showPromotion') {
return proxy.t('批量开启推广域名');
} else if (props.batchOpType === 'hidePromotion') {
return proxy.t('批量关闭推广域名');
} else if (props.batchOpType === 'edit') {
return proxy.t('批量修改');
} else if (props.batchOpType === 'bindAgent') {
return proxy.t('批量绑定代理');
} else {
return '';
}
});
//
const submitForm = () => {
loading.value = true;
const params = {
ids: tableList.value.map(item => item.id)
};
switch (props.batchOpType) {
case 'port':
submitSuccess(changeWebDomainPort(params), proxy.t('批量端口更换成功'));
break;
case 'clearCache':
submitSuccess(clearWebDomainCache(params), proxy.t('批量清理缓存成功'));
break;
case 'delete':
submitSuccess(delWebDomain(params.ids.join(',')), proxy.t('批量删除成功'));
break;
case 'stop':
params.usageStatus = 5;
submitSuccess(setWebDomainStatus(params), proxy.t('批量停用成功'));
break;
case 'enable':
params.usageStatus = 4;
submitSuccess(setWebDomainStatus(params), proxy.t('批量启用成功'));
break;
case 'showSecurity':
const _showParams = [];
params.ids.forEach(id => {
_showParams.push({
id: id,
backupDomain: true
})
});
submitSuccess(webDomainSwitch({ operationDomainSons: _showParams }), proxy.t('批量展示其他担保列表成功'));
break;
case 'hideSecurity':
const _hideParams = [];
params.ids.forEach(id => {
_hideParams.push({
id: id,
backupDomain: false
})
});
submitSuccess(webDomainSwitch({ operationDomainSons: _hideParams }), proxy.t('批量隐藏其他担保列表成功'));
break;
case 'showPromotion':
const _showPromotionParams = [];
params.ids.forEach(id => {
_showPromotionParams.push({
id: id,
marketingDomain: true
})
});
submitSuccess(webDomainSwitch({ operationDomainSons: _showPromotionParams }), proxy.t('批量开启推广域名成功'));
break;
case 'hidePromotion':
const _hidePromotionParams = [];
params.ids.forEach(id => {
_hidePromotionParams.push({
id: id,
marketingDomain: false
})
});
submitSuccess(webDomainSwitch({ operationDomainSons: _hidePromotionParams }), proxy.t('批量关闭推广域名成功'));
case 'edit':
const _editParams = [];
params.ids.forEach(id => {
const item = tableList.value.find(v => v.id == id);
_editParams.push({
id: id,
startEntrance: item.startEntrance,
blockDevice: item.blockDevice
})
});
submitSuccess(webDomainSwitch({ operationDomainSons: _editParams }), proxy.t('批量修改成功'));
break;
case 'bindAgent':
const _agentParams = [];
params.ids.forEach(id => {
const item = tableList.value.find(v => v.id == id);
_agentParams.push({
id: id,
agentAccount: item.agentAccount
})
});
submitSuccess(bindWebDomainAgent({ operationDomainSons: _agentParams }), proxy.t('批量绑定代理成功'));
break;
}
//
function submitSuccess(apiFn, msg) {
apiFn.then(() => {
loading.value = false;
proxy.$message.success(msg);
closeDialog('batchSubmit');
}).catch(() => {
loading.value = false;
});
}
}
//
const emit = defineEmits(['closeDialog']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,126 @@
<template>
<!-- 绑定代理 -->
<el-dialog :title="t('绑定代理')" v-model="isShowDialog" width="700px" append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<el-form ref="formRef" :model="formData" label-width="120">
<el-form-item :label="t('当前节点:')">
{{ currentDomainInfo.cdnNodeName }}
</el-form-item>
<el-form-item :label="t('生效域名:')">
{{ currentDomainInfo.name }}
</el-form-item>
<el-form-item :label="t('域名用途:')">
{{ domainTypeName }}
</el-form-item>
<el-form-item label-width="0" class="clt-item">
<span slot="label" class="label-box">
{{ t('强制绑定代理账号(选填)') }}
</span>
<div class="item-right">
<el-input v-model="formData.agentAccount" :placeholder="t('请输入代理账号')" clearable />
</div>
</el-form-item>
<el-form-item :label="t('功能说明:')">
{{ t('线下代理专属域名,上级归属的优先级:推广链接的上级 > 渠道ID的强制绑定上级 > 域名管理的强制绑定上级(无上级时,域名管理的才生效)') }}
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">{{ t('确 认') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="BindAgent">
import { bindWebDomainAgent, bindDownloadDomainAgent } from "@/api/operation/domain";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const isShowDialog = ref(true); //
const formRef = ref();
//
const props = defineProps({
currentDomainInfo: {
type: Object,
default: () => { }
},
type: {
type: String,
default: ''
}
});
//
const domainTypeName = computed(() => {
if (props.type == 'webHall') {
return proxy.t('Web大厅域名');
} else if (props.type == 'downloadSite') {
return proxy.t('下载站域名');
}
});
//
const formData = reactive({
agentAccount: props.currentDomainInfo.agentAccount
});
//
const submitForm = () => {
const params = {
operationDomainSons: [{
id: props.currentDomainInfo.id,
agentAccount: formData.agentAccount
}]
}
//
loading.value = true;
switch (props.type) {
case 'webHall':
submitSuccess(bindWebDomainAgent(params));
break;
case 'downloadSite':
submitSuccess(bindDownloadDomainAgent(params));
break;
}
//
function submitSuccess(apiFn) {
apiFn.then(() => {
loading.value = false;
proxy.$message.success(proxy.t('代理绑定成功'));
closeDialog('submit');
}).catch(() => {
loading.value = false;
});
}
}
//
const emit = defineEmits(['closeDialog']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
</script>
<style scoped lang="scss">
// label
.clt-item {
.label-box {
width: 120px;
}
.item-right {
width: calc(100% - 120px);
}
//
:deep(.el-form-item__error) {
padding-left: 120px;
}
}
</style>

View File

@ -0,0 +1,174 @@
<template>
<!-- 修改域名 -->
<el-dialog :title="t('修改域名')" v-model="isShowDialog" width="600px" append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<el-form ref="formRef" :rules="rules" :model="formData" label-width="100">
<el-form-item :label="t('当前节点:')">
{{ currentDomainInfo.cdnNodeName }}
</el-form-item>
<el-form-item :label="t('主域名:')">
{{ domainStr[1] }}
</el-form-item>
<el-form-item :label="t('域名用途:')">
{{ domainTypeName }}
</el-form-item>
<el-form-item :label="t('生效域名:')" prop="name" class="mr0">
<el-input class="w100" v-model="formData.name" :placeholder="t('例如:* 或 @ 或 www')" clearable>
<template #append><b>.</b>{{ domainStr[1] }}</template>
</el-input>
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">{{ t('确 认') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="EditSonDomain">
import {
updateWebDomain, // web
updateBackendDomain, //
updateAppDomain, // app
updateOssDomain, // oss
updateDownloadDomain, //
updatePayDomain, //
} from "@/api/operation/domain";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const isShowDialog = ref(true); //
const formRef = ref();
//
const props = defineProps({
currentDomainInfo: {
type: Object,
default: () => { }
},
type: {
type: String,
default: ''
}
});
//
const domainTypeName = computed(() => {
if (props.type == 'webHall') {
return proxy.t('Web大厅域名');
} else if (props.type == 'backend') {
return proxy.t('后端加速域名');
} else if (props.type == 'appHall') {
return proxy.t('App大厅域名');
} else if (props.type == 'oss') {
return proxy.t('OSS加速域名');
} else if (props.type == 'downloadSite') {
return proxy.t('下载站域名');
} else if (props.type == 'payment') {
return proxy.t('支付域名');
}
});
//
const domainStr = computed(() => {
const _domainName = props.currentDomainInfo.name;
if (_domainName) {
const index = _domainName.indexOf('.');
if (index !== -1) {
return [_domainName.substring(0, index), _domainName.substring(index + 1)]
}
}
return [];
});
//
const formData = reactive({
name: domainStr.value[0]
});
//
const rules = reactive({
name: [
{ required: true, message: proxy.t('请输入子域名'), trigger: ['change', 'blur'] },
{
pattern: /^[\w\*@]+$/,
message: proxy.t('只能包含字母、数字、下划线、星号和@'),
trigger: ['change', 'blur']
}
]
});
//
const submitForm = () => {
formRef.value.validate(valid => {
if (valid) {
const params = {
id: props.currentDomainInfo.id,
name: formData.name + '.' + domainStr.value[1]
}
//
loading.value = true;
switch (props.type) {
case 'webHall':
submitSuccess(updateWebDomain(params));
break;
case 'backend':
submitSuccess(updateBackendDomain(params));
break;
case 'appHall':
submitSuccess(updateAppDomain(params));
break;
case 'oss':
submitSuccess(updateOssDomain(params));
break;
case 'downloadSite':
submitSuccess(updateDownloadDomain(params));
break;
case 'payment':
submitSuccess(updatePayDomain(params));
break;
}
//
function submitSuccess(apiFn) {
apiFn.then(() => {
loading.value = false;
proxy.$message.success(proxy.t('修改成功'));
closeDialog('submit');
}).catch(() => {
loading.value = false;
});
}
}
});
}
//
const emit = defineEmits(['closeDialog']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
</script>
<style scoped lang="scss">
// label
.clt-item {
.label-box {
width: 100px;
}
.item-right {
width: calc(100% - 100px);
}
//
:deep(.el-form-item__error) {
padding-left: 100px;
}
}
</style>

View File

@ -0,0 +1,124 @@
<template>
<!-- 列表左侧域名供应商选择按钮 -->
<el-radio-group v-model="domainSupplierId" size="large" class="supplier-button" @change="changeVal">
<el-radio-button value="" :label="t('全部')" />
<template v-for="item in supplierList" :key="item.id">
<el-radio-button :value="item.id">
<template #default>
<span>{{ item.supplierName }}</span>
<span class="rb-tag"
:style="'background-color:' + (item.supplierType == 1 ? '#F56C6C' : item.supplierType == 2 ? '#409EFF' : '#67C23A')">
{{ item.supplierTypeName }}
</span>
</template>
</el-radio-button>
</template>
</el-radio-group>
</template>
<script setup>
import { getSupplierList } from "@/api/operation/domain";
const { proxy } = getCurrentInstance();
const domainSupplierId = defineModel(); // ID
//
onMounted(() => {
getSupplierInfo();
});
//
const supplierList = ref([]);
const supplierTips = reactive({
'Az': proxy.t('此节点属于微软官方CDN国际和中国大陆市场均可使用即全球通用。微软防封此节点提供了第三方公共域名该域名随机生成可防封防屏蔽即无需使用自己域名国际和中国大陆市场均可使用即全球通用。'),
'ALY-CN': proxy.t('此节点是大陆专用的阿里云,只适合中国大陆市场,存活期间不会出现被封,速度最快最稳定,但需要自行购买备案域名和大陆阿里云账号来配置(即购买阿里云小号,一般存活一个多月后被封,所以可能需要频繁换备案域名或换号)。'),
'WS': proxy.t('此节点覆盖中国大陆和国际市场,国际和中国大陆市场均可使用(即全球通用)。'),
'AliYun': proxy.t('此节点只适合海外市场,且禁止中国地区访问和使用(含港澳台)。'),
'WSIP': proxy.t('此节点提供独立IP地址适用于会封禁域名但不会封禁IP的国家例如印尼、中国、巴西、越南等国际和中国大陆市场均可使用即全球通用。'),
'A8': proxy.t('此节点属于大陆到香港CN2专线只推荐中国大陆市场使用海外市场不推荐。'),
'AWS': proxy.t('此节点属于亚马逊官方CDN国际和中国大陆市场均可使用即全球通用。'),
'SE': proxy.t('此节点具备独家移动清洗线路功能,即可以防止中国大陆运营商屏蔽,仅限于中国大陆市场使用。'),
'CF': proxy.t('全球最大CDN供应商主要可以抗攻击国际和中国大陆市场均可使用即全球通用。'),
'YD': proxy.t('节点主要分布在推荐东南亚与印度、中国大陆市场,推荐东南亚和大陆使用。'),
'FN': proxy.t('此节点属于大陆到香港CN2专线只推荐中国大陆市场使用海外市场不推荐。'),
'HWCloud': proxy.t('此节点只适合海外市场,且禁止中国地区访问和使用(含港澳台)。'),
'TCCloud': proxy.t('此节点只适合海外市场,且禁止中国地区访问和使用(含港澳台)。'),
'GC': proxy.t('仅限于国际市场使用,不能用在中国市场。'),
'OSR': proxy.t('我方提供独立源服务IP信息您可使用外部三方CDN绑定到我方源站所以仅限web大厅使用不支持原生APP需要有专业知识才能配置websocket、HTTP2、缓存等信息详见域名管理教程。')
});
//
const getSupplierInfo = () => {
getSupplierList().then(res => {
const _data = res.data;
if (_data.length) {
const supplierType = [
{ value: 1, label: '全球' },
{ value: 2, label: '大陆' },
{ value: 3, label: '国际' }
]; //
//
_data.forEach(item => {
item.tips = supplierTips[item.supplierCode];
item.supplierTypeName = supplierType.find(i => i.value == item.supplierType).label;
});
supplierList.value = _data;
console.log(supplierList.value);
}
});
};
//
const curSupplierNode = computed(() => {
return supplierList.value.find(item => item.id == domainSupplierId.value);
});
const emit = defineEmits(['getCurSupplierNode']);
const changeVal = () => {
emit('getCurSupplierNode', curSupplierNode.value);
}
//
defineExpose({
getSupplierInfo,
supplierList
});
</script>
<style scoped lang='scss'>
.supplier-button {
width: calc(100% - 15px);
:deep(.el-radio-button) {
width: 100%;
margin-bottom: 10px;
.el-radio-button__inner {
position: relative;
width: 100%;
border-radius: 4px;
border: 1px solid #dcdfe6 !important;
}
&.is-active .el-radio-button__inner {
box-shadow: none;
border: 1px solid #409EFF !important;
}
}
}
.rb-tag {
display: inline-block;
position: absolute;
right: -5px;
top: -5px;
color: #ffffff;
font-size: 10px;
padding: 0 5px;
line-height: 1.5;
border-radius: 10px;
}
</style>

View File

@ -0,0 +1,153 @@
<template>
<el-radio-group v-model="supplierId" class="supplier-box">
<el-radio v-for="item in supplierList" :value="item.id" size="large" border
:disabled="!item.isOpen || item.stopStatus" @change="changeSupplier(item)" :class="type == 'addSonDomain' ? 'son-radio' : ''">
<template #default>
{{ item.supplierName }}
<icon-tips trigger="hover">
<p class="c-clear" style="width: 400px;">{{ item.tips }}</p>
</icon-tips>
<span class="rb-tag"
:style="'background-color:' + (item.supplierType == 1 ? '#F56C6C' : item.supplierType == 2 ? '#409EFF' : '#67C23A')">
{{ item.supplierTypeName }}
</span>
<span v-if="item.prohibit" class="prohibit">{{ t('') }}</span>
<!-- 非维护状态且不是增加子域名弹窗 -->
<p class="sl-bottom" v-if="!item.stopStatus && type != 'addSonDomain'">
<span>{{ item.domainCount > 0 ? item.domainCount + t('条') : t('无域名') }} </span>
<el-button v-hasPermi="['operation:tenant:edit']" style="margin-left: 5px;" link type="primary" v-if="item.domainCount == 0" @click="supplierSet(item)">{{ item.isOpen &&
item.domainCount == 0 ?
t('关闭') : t('开通') }}</el-button>
<el-button v-hasPermi="['operation:tenant:alb']" style="margin-left: 5px;" link type="primary" @click="albSet(item)"
v-if="item.domainCount > 0 && item.isOpen && item.supportAlb">{{ item.albStatus ?
t('ALB关闭') : t('ALB开通') }}</el-button>
</p>
<div class="stop-box" v-if="item.stopStatus">
<el-icon size="18">
<WarnTriangleFilled />
</el-icon>
<p class="c-clear" style="line-height: 1;">{{ t('维护中...') }}</p>
</div>
</template>
</el-radio>
</el-radio-group>
</template>
<script setup>
import { tenantSupplierALB, tenantSupplier } from "@/api/operation/domain";
import IconTips from "@/components/IconTips"; //
//
const props = defineProps({
supplierList: {
type: Array,
default: () => []
},
type: {
type: String,
default: ''
}
});
const { proxy } = getCurrentInstance();
const loading = ref(false);
const supplierId = defineModel();
const emits = defineEmits(['changeSupplier']);
const changeSupplier = (item) => {
emits('changeSupplier', item);
};
// alb/
const albSet = (item) => {
proxy.$modal.confirm(proxy.t('确认') + (item.albStatus ? proxy.t('关闭') : proxy.t('开通')) + '节点ALB' ).then(() => {
loading.value = true;
tenantSupplierALB({ supplierId: item.id, albStatus: !item.albStatus }).then(res => {
loading.value = false;
item.albStatus = !item.albStatus;
proxy.$modal.msgSuccess(proxy.t('操作成功!'));
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
};
// /
const supplierSet = (item) => {
const content = item.isOpen ? proxy.t('默认提供')+item.freeNode+proxy.t('个免费节点,该节点无域名解析数据可进行关闭。') : proxy.t('免费提供') + item.freeNode +proxy.t('个免费节点,超出部分额外收取')+ item.exceedCost + proxy.t('USDT/节点/月,每个节点都是独立云,都能扛攻击,建议平均分配。');
const title = (item.isOpen? proxy.t('确认关闭') : proxy.t('确认开通')) + item.supplierName + proxy.t('节点') + '';
proxy.$modal.confirm(content, title).then(() => {
loading.value = true;
tenantSupplier({ supplierId: item.id, isOpen:!item.isOpen }).then(res => {
loading.value = false;
item.isOpen =!item.isOpen;
proxy.$modal.msgSuccess(proxy.t('操作成功!'));
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
};
</script>
<style scoped lang='scss'>
.supplier-box {
:deep(.el-radio) {
display: inline-block;
height: 60px;
margin: 10px 10px 0 0;
&:last-child {
margin-right: 0;
}
}
:deep(.son-radio) {
height: 40px !important;
}
.prohibit,
.rb-tag {
display: inline-block;
position: absolute;
color: #ffffff;
font-size: 10px;
padding: 0 5px;
line-height: 1.5;
border-radius: 10px;
}
.rb-tag {
right: -5px;
top: -8px;
}
.prohibit {
left: -5px;
top: -8px;
background-color: #42b983;
}
.sl-bottom {
position: absolute;
margin: 0;
line-height: 1.5;
}
.stop-box {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
text-align: center;
color: #ffffff;
border-radius: 4px;
:deep(.el-icon) {
transform: translateY(5px);
}
}
}
</style>

View File

@ -0,0 +1,329 @@
<template>
<!-- 新增/修改自定义解析 -->
<el-dialog :title="dialogTitle" v-model="isShowDialog" width="700px" append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120">
<el-form-item :label="t('选择节点:')" prop="domainSupplierId" class="mr0">
<el-radio-group v-model="formData.domainSupplierId" @change="changeCdnNode">
<template v-for="item in supplierList" :key="item.value">
<el-radio :value="item.value">{{ t(item.label) }}</el-radio>
</template>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('选择域名:')" prop="domainId" class="mr0">
<el-select class="w100" v-model="formData.domainId" :placeholder="t('请选择域名')" @change="changeDomain"
:disabled="!supplierDomainList.length">
<el-option v-for="item in supplierDomainList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('主机记录:')" prop="hostRecord" class="mr0">
<el-input v-model="formData.hostRecord" clearable :placeholder="t('请输入主机记录')"
:disabled="!formData.domainId">
<template #append v-if="formData.domainId"><b>.</b>{{ formData.domainName }}</template>
</el-input>
</el-form-item>
<el-form-item :label="t('解析类型:')" prop="analysisType" class="mr0">
<dict-select class="w100" v-model="formData.analysisType" :placeholder="t('请选择解析类型')"
dictKey="ff_operation_domain_analysis_type" @change="analysisTypeChange"></dict-select>
</el-form-item>
<el-form-item :label="t('解析记录值:')" prop="recordValue" class="mr0" v-if="formData.analysisType != 6">
<el-input v-model="formData.recordValue" clearable :placeholder="rvPlaceholder" />
</el-form-item>
<!-- SRV解析类型 -->
<template v-if="formData.analysisType == 6">
<el-form-item :label="t('协议:')" prop="protocolType" class="mr0" v-if="nodeAnalysisType == 2">
<dict-select class="w100" v-model="formData.protocolType" :placeholder="t('请选择协议类型')"
dictKey="ff_operation_domain_analysis_protocol_type"></dict-select>
</el-form-item>
<el-form-item :label="t('优先级:')" prop="priority" :rules="rules.srvNumber" class="mr0">
<number-input v-model="formData.priority" maxlength="5" clearable :placeholder="t('请输入0-65535')" />
</el-form-item>
<el-form-item :label="t('权重:')" prop="weight" :rules="rules.srvNumber" class="mr0">
<number-input v-model="formData.weight" maxlength="5" clearable :placeholder="t('请输入0-65535')" />
</el-form-item>
<el-form-item :label="t('端口:')" prop="port" :rules="rules.srvNumber" class="mr0">
<number-input v-model="formData.port" maxlength="5" clearable :placeholder="t('请输入0-65535')" />
</el-form-item>
<el-form-item :label="t('服务:')" prop="service" class="mr0" v-if="nodeAnalysisType == 2">
<el-input v-model="formData.service" clearable :placeholder="t('请输入例如_serviceName')" />
</el-form-item>
<el-form-item :label="t('目标:')" prop="target" class="mr0">
<el-input v-model="formData.target" clearable :placeholder="t('请输入目标')" />
</el-form-item>
</template>
<el-form-item :label="t('TTL')" prop="ttl" class="mr0">
<number-input v-model="formData.ttl" maxlength="10" clearable :placeholder="t('请输入大于等于600的整数')" />
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">{{ t('确 认') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="AddEditParsing">
import {
getSupplierDomainList, //
addCustomParsing,
updateCustomParsing
} from "@/api/operation/domain";
import NumberInput from "@/components/NumberInput/index.vue"; //
const { proxy } = getCurrentInstance();
const loading = ref(false);
const isShowDialog = ref(true); //
const formRef = ref();
const oldForm = shallowRef({ });
//
const props = defineProps({
supplierList: {
type: Array,
default: []
},
currentParsingInfo: {
type: Object,
default: {}
}
});
//
const dialogTitle = computed(() => {
if (props.currentParsingInfo.id) {
return proxy.t('修改域名解析');
} else {
return proxy.t('新增域名解析');
}
});
//
const formData = reactive({
domainSupplierId: '', // id
domainId: '', // id
domainName: '', //
hostRecord: '', //
analysisType: '', //
recordValue: '', //
protocolType: '', //
priority: '', //
weight: '', //
port: '', //
service: '', //
target: '', //
ttl: '', // ttl
});
//
let initEditStatus = ref(false);
//
const rules = reactive({
domainSupplierId: [
{ required: true, message: proxy.t('请选择节点'), trigger: 'change' }
],
domainId: [
{ required: true, message: proxy.t('请选择域名'), trigger: 'change' }
],
hostRecord: [
{ required: true, message: proxy.t('请输入主机记录'), trigger: ['change', 'blur'] },
//
{
validator: (rule, value, callback) => {
const reg = /^[a-zA-Z0-9\-\_]+$/;
if (value != '' && !reg.test(value)) {
callback(new Error(proxy.t('主机记录只能包含字母、数字、下划线、横线')));
} else {
callback();
}
}, trigger: ['change', 'blur']
}
],
analysisType: [
{ required: true, message: proxy.t('请选择解析类型'), trigger: 'change' }
],
recordValue: [
{ required: true, message: proxy.t('请输入解析记录值'), trigger: ['change', 'blur'] }
],
protocolType: [
{ required: true, message: proxy.t('请选择协议类型'), trigger: 'change' }
],
srvNumber: [
{
required: true,
validator: (rule, value, callback) => {
if (value == '' || (value < 0 || value > 65535)) {
callback(new Error(proxy.t('请输入0-65535的整数')));
} else {
callback();
}
}, trigger: ['change', 'blur']
}
],
service: [
{ required: true, message: proxy.t('请输入服务'), trigger: ['change', 'blur'] }
],
target: [
{ required: true, message: proxy.t('请输入目标'), trigger: ['change', 'blur'] }
],
ttl: [
{
validator: (rule, value, callback) => {
if (value !== '' && value < 600) {
callback(new Error(proxy.t('请输入大于等于600的整数')));
} else {
callback();
}
}, trigger: ['change', 'blur']
}
]
});
//
const supplierDomainList = ref([]),
nodeAnalysisType = ref(1); //
const changeCdnNode = (val) => {
//
formData.domainId = '';
formData.domainName = '';
formData.hostRecord = '';
// SRV
nodeAnalysisType.value = props.supplierList.find(v => v.value == val).analysisType;
//
getSupplierDomainList({ domainSupplierId: val }).then(res => {
supplierDomainList.value = res.data.map(v => {
return {
label: v.name,
value: v.id
}
});
//
if (initEditStatus.value) {
const _currentParsingInfo = props.currentParsingInfo;
formData.domainId = _currentParsingInfo.domainId;
formData.domainName = _currentParsingInfo.domainName;
formData.hostRecord = _currentParsingInfo.hostRecord;
formData.analysisType = _currentParsingInfo.analysisType + '';
formData.recordValue = _currentParsingInfo.recordValue;
formData.protocolType = _currentParsingInfo.protocolType ? _currentParsingInfo.protocolType + '' : '';
formData.priority = _currentParsingInfo.priority;
formData.weight = _currentParsingInfo.weight;
formData.port = _currentParsingInfo.port;
formData.service = _currentParsingInfo.service;
formData.target = _currentParsingInfo.target;
formData.ttl = _currentParsingInfo.ttl || '';
initEditStatus.value = false;
}
});
}
//
const changeDomain = (val) => {
const findDomain = supplierDomainList.value.find(v => v.value == val);
formData.domainId = findDomain.value;
formData.domainName = findDomain.label;
}
// placeholder
const rvPlaceholder = ref(proxy.t('请输入IPv4地址'));
const analysisTypeChange = (val) => {
const _label = proxy.useDict('ff_operation_domain_analysis_type').ff_operation_domain_analysis_type.find(v => v.value == val).label;
if (_label == 'AAAA') {
rvPlaceholder.value = proxy.t('请输入IPv6地址');
} else if (_label == 'CNAME') {
rvPlaceholder.value = proxy.t('请输入域名地址');
} else if (_label == 'NS') {
rvPlaceholder.value = proxy.t('请输入域名服务器地址');
} else {
rvPlaceholder.value = proxy.t('请输入IPv4地址');
}
};
//
const submitForm = () => {
formRef.value.validate((valid) => {
if (!valid) return;
const params = {
domainId: formData.domainId,
hostRecord: formData.hostRecord,
analysisType: formData.analysisType,
ttl: formData.ttl,
};
// SRV
if (formData.analysisType == 6) {
params.priority = formData.priority;
params.weight = formData.weight;
params.port = formData.port;
params.target = formData.target;
// 2
if (nodeAnalysisType.value == 2) {
params.protocolType = formData.protocolType;
params.service = formData.service;
}
} else {
params.recordValue = formData.recordValue;
}
loading.value = true;
if (props.currentParsingInfo.id) {
//
params.id = props.currentParsingInfo.id;
if (JSON.stringify(formData) != oldForm.value) {
submitSuccess(updateCustomParsing(params), proxy.t('修改成功'));
}else{
loading.value = false;
closeDialog('cancel');
}
} else {
//
submitSuccess(addCustomParsing(params), proxy.t('添加成功'));
}
//
function submitSuccess(apiFn, msg) {
apiFn.then(() => {
loading.value = false;
proxy.$message.success(msg);
closeDialog('submit');
}).catch(() => {
loading.value = false;
});
}
});
}
//
const emit = defineEmits(['closeDialog']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
//
watch(() => props.currentParsingInfo, (val) => {
if (val.id) {
//
formData.domainSupplierId = val.domainSupplierId;
changeCdnNode(val.domainSupplierId);
initEditStatus.value = true;
setTimeout(() => {
let objForm = { ...formData }
oldForm.value = JSON.stringify(objForm);
}, 500);
}
}, { immediate: true });
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,259 @@
<template>
<!-- 自定义解析列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<table-search-date ref="searchDateRef" dateType="datetimerange" v-model:dateRange="dateRange"
v-model:operateTimeType="operateTimeType" @dateChange="handleQuery"></table-search-date>
<select-input-form :queryParamsList="queryParamsList" v-model:queryParams="queryParams" :width="[130, 260]"
@handleQuery="handleQuery"></select-input-form>
<el-form-item prop="analysisType" style="width: 120px;">
<dict-select clearable v-model="queryParams.analysisType" :placeholder="t('解析类型')"
dictKey="ff_operation_domain_analysis_type" @change="handleQuery"></dict-select>
</el-form-item>
<el-form-item prop="domainSupplierId" style="width: 150px;">
<el-select v-model="queryParams.domainSupplierId" :placeholder="t('CDN节点')" @change="handleQuery">
<el-option v-for="item in supplierList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</template>
<template #right>
<el-button type="success" plain icon="Plus" @click="isShowAddEdit = true"
v-hasPermi="['operation:analysis:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column :label="t('CDN节点名称')" prop="cdnNodeName" align="center" min-width="160"></el-table-column>
<el-table-column :label="t('主域名')" prop="domainName" align="center" min-width="160"></el-table-column>
<el-table-column :label="t('主机记录')" prop="hostRecord" align="center" min-width="180">
<template #default="{row}">
<div style="width: 100%;">{{ row.hostRecord }}</div>
<div style="width: 100%;font-weight: 600;" v-for="(item, index) in row.operationDomainAnalysisDns" :key="index">
<p> {{ item.typeValue }}:{{ item.value }}</p>
</div>
</template>
</el-table-column>
<el-table-column :label="t('解析类型')" align="center" min-width="120">
<template #default="{ row }">
<dict-text dictKey="ff_operation_domain_analysis_type" :value="row.analysisType" />
</template>
</el-table-column>
<el-table-column :label="t('解析记录值')" prop="recordValue" align="center" min-width="160"></el-table-column>
<el-table-column :label="t('TTL')" align="center" min-width="100">
<template #default="{ row }">
{{row.ttl || '-'}}
</template>
</el-table-column>
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" min-width="120">
<template #default="{ row }">
<el-button link type="primary" @click="opInfo(row, 'edit')" v-hasPermi="['operation:analysis:edit']">
{{ t('修改') }}
</el-button>
<el-button link type="primary" @click="opInfo(row, 'delete')" v-hasPermi="['operation:analysis:remove']">
{{ t('删除') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="getList" />
<!-- 添加解析 -->
<add-edit-parsing v-if="isShowAddEdit" :supplierList="supplierList" :currentParsingInfo="currentParsingInfo"
@closeDialog="closeDialog"></add-edit-parsing>
</template>
<script setup name="CustomParsingList">
import {
getSupplierList, //
getCustomParsingList, //
delCustomParsing, //
} from "@/api/operation/domain";
import TableSearchDate from "@/components/TableSearchDate"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import AddEditParsing from "./AddEditParsing.vue"; //
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
const isShowAddEdit = ref(false); //
//
const supplierList = ref([]);
getSupplierList().then(res => {
supplierList.value = res.data.filter(i => i.isOpen).map(v => {
return {
label: v.supplierName,
value: v.id,
analysisType: v.analysisType
}
});
});
//
const searchDateRef = ref(), dateRange = ref([]), operateTimeType = ref('');
//
const queryParamsList = ref([
{
label: proxy.t('主域名'),
value: 'domainName'
}, {
label: proxy.t('主机记录'),
value: 'hostRecord'
}, {
label: proxy.t('解析记录值'),
value: 'recordValue'
}, {
label: proxy.t('操作人'),
value: 'updateBy'
}
]);
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
searchType: 'domainName',
domainName: '',
hostRecord: '',
recordValue: '',
updateBy: '',
domainSupplierId: '', // CDN
analysisType: '', //
orderByColumn: 'createTime',
isAsc: 'desc'
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
const params = proxy.addDateRange(queryParams, dateRange.value); //
loading.value = true;
getCustomParsingList(params).then(res => {
loading.value = false;
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const currentParsingInfo = ref({}); //
const opInfo = (row, type) => {
switch (type) {
case 'edit':
//
currentParsingInfo.value = row;
isShowAddEdit.value = true;
break;
case 'delete':
//
let msg = proxy.t('确定要删除该域名吗!')
if (row.batchOpType) {
msg = proxy.t('确定要批量删除选中的域名吗!')
}
proxy.$modal.confirm(msg).then(() => {
loading.value = true;
delCustomParsing(row.id).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
batchOpType.value = '';
});
}).catch(() => {
batchOpType.value = '';
});
break;
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量删除'), value: 'delete' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
opInfo({id: selectionData.value.map(item => item.id).join(','), batchOpType: true}, batchOpType.value);
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
searchDateRef.value.dateResetQuery(); //
handleQuery();
}
//
const closeDialog = (type) => {
isShowAddEdit.value = false; //
currentParsingInfo.value = {}; //
batchOpType.value = ''; //
//
if (type === 'batchSubmit') clearBatchOp();
//
if (type === 'submit' || type === 'batchSubmit') {
getList();
}
}
</script>
<style scoped lang='scss'></style>

View File

@ -0,0 +1,205 @@
<template>
<!-- 新增域名 -->
<el-dialog :title="t('新增域名')" v-model="isShowDialog" width="800px" align-center append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<el-form ref="formRef" :rules="rules" :model="formData" label-width="100">
<el-form-item label-width="0" prop="domainSupplierId" class="clt-item mr0">
<span slot="label" class="label-box">
<span class="label-name"><i style="color: #f56c6c; margin-right: 4px;">*</i>{{ t('选择节点') }}</span>
<span class="label-open">({{ t('已开通') }}<b class="c-orange">{{ openCount }}</b>{{ t('个') }})</span>
<span class="label-ds c-blue">{{ t('节点说明') }}</span>
</span>
<div class="item-right">
<supplier-select v-model="formData.domainSupplierId" @changeSupplier="changeSupplier" :supplierList="supplierList"></supplier-select>
</div>
</el-form-item>
<el-form-item :label="t('主域名')" prop="domainNames" class="mr0">
<el-input type="textarea" :rows="5" v-model="formData.domainNames"
:placeholder="t('支持批量添加最多20个多个域名请换行例如\n123.com\n123.com.hk\n123.in')" />
</el-form-item>
</el-form>
<div class="add-tips">
<h3>{{ t('收费和用法教程:') }}</h3>
<p><b>{{t('1、限顶级域名')}}</b>{{ t('此处只能添加独立的顶级域名,请勿添加子域名,例如: 只能添加123.com不是www.123.com详见域名管理教程') }}</p>
<p><b>{{t('2、节点说明')}}</b></p>
<p><b>{{t('1中国市场含港澳台')}}</b>{{ t('推荐使用alb域名aws防封、三方公共域名aws与azure防封、Wangsu (CDN+IP)、A8、Yundun (云盾)、Funnull(方能)、superEdge、大陆阿里云。')}}</p>
<p><b>{{t('2全球市场即非中国')}}</b>{{ t('推荐使用使用alb域名aws与aliyun防封、三方公共域名aws与azure防封、AWS、CF、腾讯云、 阿里云、 Google、Azure、华为云、Wangsu(CDN + IP)、Yundun (云盾)。')}}</p>
<p><b>{{t('3Wangsu独立IP')}}</b>{{ t('提供独立IP地址适用于会封禁域名但不会封禁IP的国家例如印尼、中国、巴西、越南等国际和中国大陆市场均可使用即全球通用200USDT/IP/月),若超过7天未启用系统自动回收。') }}
</p>
<p><b>{{t('4开放回源仅限web大厅')}}</b>{{ t('提供开放独立源服务可使用外部三方CDN需申请开通300U/月需要专业人士配置websocket、HTTP2、缓存等详见域名管理教程。') }}</p>
<p><b>{{t('3、域名解析限额')}}</b>{{ t('前')+domainNameCnt+t('个域名免费(含子域名),超出部分额外收取')+exceedDomainCost+t('USDT/条解析/月;') }}</p>
<p><b>{{t('4、流量限额')}}</b>{{ t('每月前') +trafficG+ t('G免费超出部分额外收取') + exceedTrafficCost +t('USDT/G/月;') }}</p>
<p><b>{{t('5、节点限制')}}</b>{{ t('免费提供') + freeNode + t('个节点,超出部分额外收取') + exceedCost + t('USDT/节点/月,每个节点都是独立云,都能扛攻击,建议均匀分配。') }}</p>
</div>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog('close')">{{ t(' ') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">{{ t('确 认') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="AddDomain">
import SupplierSelect from '../components/SupplierSelect.vue';
import { addDomain } from "@/api/operation/domain";
const { proxy } = getCurrentInstance();
//
const props = defineProps({
supplierList: {
type: Array,
default: () => []
},
supplierConfig: {
type: Object,
default: () => ({})
},
});
const domainNameCnt = ref(0),exceedDomainCost = ref(0),trafficG = ref(0),exceedTrafficCost = ref(0),freeNode = ref(0),exceedCost = ref(0);
const loading = ref(false);
const isShowDialog = ref(true); //
const formRef = ref();
//
const formData = reactive({
domainSupplierId: '',
domainNames: []
});
//
const openCount = computed(() => {
return props.supplierList.filter(item => item.isOpen).length;
});
nextTick(()=>{
console.log(props.supplierConfig)
domainNameCnt.value = props.supplierConfig.domainNameCnt;
exceedDomainCost.value = props.supplierConfig.exceedDomainCost;
trafficG.value = props.supplierConfig.trafficG;
exceedTrafficCost.value = props.supplierConfig.exceedTrafficCost;
})
const changeSupplier = (item) => {
freeNode.value = item.freeNode;
exceedCost.value = item.exceedCost;
};
//
const rules = reactive({
domainSupplierId: [
{ required: true, message: proxy.t('请选择节点'), trigger: 'change' }
],
domainNames: [
{ required: true, message: proxy.t('域名不能为空'), trigger: ['blur', 'change'] },
//
{
validator: (rule, value, callback) => {
const reg = /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$/;
if (value) {
if (value.split('\n').length > 20) {
callback(new Error(proxy.t('最多支持20个域名')));
} else {
const arr = value.split('\n');
for (let i = 0; i < arr.length; i++) {
if (!reg.test(arr[i])) {
callback(new Error(proxy.t('请输入正确的域名')));
}
}
callback();
}
} else {
callback();
}
},
trigger: ['blur', 'change']
}
]
});
//
const submitForm = () => {
formRef.value.validate(valid => {
if (valid) {
const params = {
domainSupplierId: formData.domainSupplierId,
domainNames: formData.domainNames.split('\n')
}
// domainNames
if (params.domainNames.length !== new Set(params.domainNames).size) {
proxy.$message.error(proxy.t('域名不能重复'));
} else {
loading.value = true;
addDomain(params).then(() => {
loading.value = false;
proxy.$message.success(proxy.t('添加成功'));
closeDialog('submit');
}).catch(() => {
loading.value = false;
});
}
}
});
}
//
const emit = defineEmits(['closeDialog']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
</script>
<style scoped lang="scss">
// label
.clt-item {
.label-box {
width: 100px;
span {
display: inline-block;
width: 100%;
}
.label-open {
font-size: 12px;
color: #909399;
}
.label-ds {
font-size: 12px;
}
}
.item-right {
width: calc(100% - 100px);
}
//
:deep(.el-form-item__error) {
padding-left: 100px;
}
}
.add-tips{
background-color: #ededee;
padding: 10px;
margin-bottom: 10px;
h3{
margin: 0;
padding-bottom: 10px;
font-size: 14px;
font-weight: bold;
}
p {
line-height: 26px;
margin: 0;
}
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<!-- 限额说明 -->
<el-dialog :title="t('限额说明')" v-model="isShowDialog" width="900px" append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<div class="limit-box">
<h3>{{ t('1.域名不是越多越好:') }}</h3>
<div class="lb-text">
<p>
{{ t('因为每个节点的域名都要检测网络质量然后再根据网络质量排序若节点域名数量过多每次巡检都要浪费1-2秒来测速进而会导致网络卡顿的情况建议每个功能用途控制在2-10个/节点。') }}
</p>
</div>
<h3>{{ t('2.历史旧域名过多如何处理?:') }}</h3>
<div class="lb-text">
<p>
{{ t('历史使用过或旧的域名建议用url转发跳转不仅能保留权重也能无限量跳转。') }}
</p>
<p><span>{{ t('1站点后台操作方法批量选择域名》批量停用等待5-10分钟生效 》再批量删除域名即可。') }}</span></p>
<p><span>{{ t('2域名商后台操作方法将dns改为域名商默认如godaddy尊享版DNS》勾选域名+批量设置转发。') }}</span></p>
</div>
<h3>{{ t('3.备用域名建议:') }}</h3>
<div class="lb-text">
<p>
{{ t('即暂不启用的域名,建议先只添加到主域名,暂不进行解析,以免还没启用就被拦截、劫持、封锁或被墙;等需要使用的时候再添加到各功能模块使用即可。') }}
</p>
</div>
<h3>{{ t('4.收费说明:') }}</h3>
<div class="lb-text">
<p>
<b>{{ t('1、限顶级域名') }}</b>
</p>
<p>{{ t('此处只能添加独立的顶级域名,请勿添加子域名,例如: 只能添加123.com不是www.123.com详见域名管理教程') }}</p>
<p>
<b>{{ t('2、节点说明') }}</b>
</p>
<p>
<b>{{ t(' 1中国市场含港澳台') }}</b>
{{ t('推荐使用alb域名aws防封、三方公共域名aws与azure防封、Wangsu (CDN+IP)、A8、Yundun(云盾)、Funnull(方能)、superEdge、大陆阿里云。') }}
</p>
<p>
<b>{{ t(' 2全球市场即非中国') }}</b>
{{ t('推荐使用使用alb域名aws与aliyun防封、三方公共域名aws与azure防封、AWS、CF、腾讯云、 阿里云、 Google、Azure、华为云、Wangsu(CDN + IP)、Yundun (云盾)。') }}
</p>
<p>
<b>{{ t(' 3Wangsu独立IP') }}</b>
{{
t('提供独立IP地址适用于会封禁域名但不会封禁IP的国家例如印尼、中国、巴西、越南等国际和中国大陆市场均可使用即全球通用200USDT/IP/月),若超过7天未启用系统自动回收。')
}}
</p>
<p>
<b>{{ t(' 4开放回源仅限web大厅') }}</b>
{{ t('提供开放独立源服务可使用外部三方CDN需申请开通300U/月需要专业人士配置websocket、HTTP2、缓存等详见域名管理教程。') }}
</p>
</div>
<h3>{{ t('3、域名解析限额') }}</h3>
<div class="lb-text">
<p>{{ t('前30个域名免费含子域名超出部分额外收取5USDT/条解析/月;') }}</p>
</div>
<h3>{{ t('4、流量限额') }}</h3>
<div class="lb-text"><p>{{ t('每月前500G免费超出部分额外收取0.3USDT/G/月;') }}</p></div>
<h3>{{ t('5、节点限制') }}</h3>
<div class="lb-text"><p>{{ t('免费提供3个节点超出部分额外收取300USDT/节点/月,每个节点都是独立云,都能扛攻击,建议均匀分配。') }}</p></div>
</div>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="closeDialog">{{ t(' ') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="ShowInfoDialog">
const loading = ref(false);
const isShowDialog = ref(true); //
//
const emit = defineEmits(['closeDialog']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
</script>
<style scoped lang="scss">
.limit-box {
h3{
margin: 0;
padding-bottom: 10px;
font-size: 14px;
font-weight: bold;
}
.lb-text{
background-color: #ededee;
padding: 5px 10px;
margin-bottom: 10px;
}
p {
line-height: 26px;
margin: 0;
}
}
</style>

View File

@ -0,0 +1,469 @@
<template>
<!-- 域名管理列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<select-input-form :queryParamsList="queryParamsList" v-model:queryParams="queryParams" :width="[130, 260]"
@handleQuery="handleQuery"></select-input-form>
<el-form-item prop="domainStatus" style="width: 150px;">
<CustomSelect clearable v-model="queryParams.domainStatus" :placeholder="t('主域名状态')"
:options="domainStatusOptions" @change="handleQuery"></CustomSelect>
</el-form-item>
</template>
<template #right>
<auto-refresh-time apiName="domainList" @refreshFn="getList" style="margin-right: 10px;"></auto-refresh-time>
<el-button type="success" plain icon="Plus" @click="openAddDomain" v-hasPermi="['operation:domain:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<div class="domain-main">
<!-- 左侧供应商 -->
<div class="dm-left">
<el-scrollbar max-height="600px">
<!-- 供应商选择 -->
<supplier-button ref="supplierRef" v-model="queryParams.domainSupplierId" @change="handleQuery"
@getCurSupplierNode="getCurSupplierNode"></supplier-button>
<el-button size="large" style="width: calc(100% - 15px);" @click="isShowInfo = true">
<el-icon color="#E6A23C" style="margin-right: 5px;">
<InfoFilled />
</el-icon>{{ t(' ') }}
</el-button>
</el-scrollbar>
</div>
<!-- 右侧表格 -->
<div class="dm-right">
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" min-width="160">
<template #header>
<span>{{ t('CDN节点名称') }}</span>
<icon-tips v-if="currentNodeTips">
<p class="c-clear" style="width: 400px;">{{ currentNodeTips }}</p>
</icon-tips>
</template>
<template #default="{ row }">
{{ row.cdnNodeName }}
</template>
</el-table-column>
<el-table-column align="center" min-width="280">
<template #header>
<span>{{ t('主域名') }}</span>
<span style="color: #409EFF;"> ({{ t('子域名数') }})</span>
</template>
<template #default="{ row }">
{{ row.name }}
<!-- 子域名展示 -->
<el-popover placement="right" :width="524" trigger="click">
<template #reference>
<span class="c-blue"> ({{ row.operationDomainSons?.length }})</span>
</template>
<el-table :data="row.operationDomainSons">
<el-table-column width="200" align="center" property="name" :label="t('子域名')" />
<el-table-column width="150" align="center" property="date" :label="t('域名用途')">
<template #default="{ row }">
<dict-text dictKey="operation_domain_son_type" :value="row.domainSonTypeType" />
</template>
</el-table-column>
<el-table-column width="150" align="center" property="date" :label="t('使用状态')">
<template #default="{ row }">
<dict-text :colorValues="['c-grey', 'c-orange', 'c-orange', 'c-green', 'c-red']"
dictKey="operation_domain_son_usage_status" :value="row.usageStatus" />
</template>
</el-table-column>
</el-table>
</el-popover>
<!-- 复制 -->
<copy-icon :text="row.name" class="copy-ml"></copy-icon>
</template>
</el-table-column>
<el-table-column :label="t('验证方式')" align="center" min-width="320">
<template #default="{ row }">
<!-- 正常 -->
<p class="c-clear" v-if="row.domainStatus == 2">
<!-- 当前DNS为 -->
<span class="c-green">{{ t('验证已生效') }}</span>
<el-button link type="primary" @click="row.isShowDns = !row.isShowDns">{{ row.isShowDns ? t('收起') :
t('展开')
}}</el-button>
</p>
<!-- 待验证 -->
<p class="c-clear" v-if="row.domainStatus == 1">
<template v-if="row.dnsList.length">
<span>{{ t('请将DNS修改为以下服务器后') }}</span>
<el-button v-if="!row.checkLoading" link type="primary" @click="opInfo(row, 'check')"
v-hasPermi="['operation:domain:check']">
{{ t('验证') }}
</el-button>
<el-icon v-else class="c-rotate" style="position: relative; top: 4px;" size="16"
color="#409EFF">
<Loading />
</el-icon>
</template>
<template v-else>
<el-button v-if="!row.getNSLoading" link type="primary" @click="opInfo(row, 'getNS')"
v-hasPermi="['operation:domain:get:ns']">
{{ t('获取NS') }}
</el-button>
<el-icon v-else class="c-rotate" style="position: relative; top: 4px;" size="16"
color="#409EFF">
<Loading />
</el-icon>
</template>
</p>
<!-- 已过期待找回 -->
<p class="c-clear c-grey" v-if="row.domainStatus == 3 || row.domainStatus == 4">{{ t('') }}</p>
<!-- DNS -->
<template v-if="row.isShowDns || row.domainStatus == 1">
<p class="c-clear c-grey" v-for="(dns, index) in row.dnsList" :key="index">
{{ dns.valueType + ':' + dns.value }}
<copy-icon :text="dns.value" class="copy-ml"></copy-icon>
</p>
</template>
</template>
</el-table-column>
<el-table-column :label="t('主域名状态')" align="center" min-width="150">
<template #default="{ row }">
<dict-text :colorValues="['c-orange', 'c-green', 'c-grey', 'c-red']"
dictKey="ff_operation_domain_status" :value="row.domainStatus" />
</template>
</el-table-column>
<el-table-column :label="t('域名过期日期')" align="center" min-width="160">
<template #default="{ row }">
{{ formatTime(row.overdueDate, 'YYYY-MM-DD') }}
<el-icon @click="opInfo(row, 'refresh')" :class="row.refreshLoading ? 'c-rotate' : ''"
style="position: relative; top: 4px; margin-left: 8px;" size="16" class="c-blue">
<Refresh />
</el-icon>
</template>
</el-table-column>
<el-table-column :label="t('证书过期日期')" align="center" min-width="140">
<template #default="{ row }">
{{ formatTime(row.certificateOverdueDate, 'YYYY-MM-DD') }}
</template>
</el-table-column>
<el-table-column :label="t('操作')" align="center" min-width="180">
<template #default="{ row }">
<el-button v-if="!row.checkLoading && row.domainStatus == 1" link type="primary"
@click="opInfo(row, 'check')" v-hasPermi="['operation:domain:check']">
{{ t('验证') }}
</el-button>
<el-icon v-if="row.checkLoading" class="c-rotate" style="position: relative; top: 4px;" size="16"
color="#409EFF">
<Loading />
</el-icon>
<el-button v-if="row.domainStatus != 2" link type="primary" @click="opInfo(row, 'delete')"
v-hasPermi="['operation:domain:remove']">
{{ t('彻底删除') }}
</el-button>
<el-button v-if="row.domainStatus == 2" link type="primary" @click="opInfo(row, 'stop')"
v-hasPermi="['operation:domain:stop']">
{{ t('强制停用') }}
</el-button>
</template>
</el-table-column>
<table-operation></table-operation>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<!-- 限额说明弹窗 -->
<show-content v-if="isShowInfo" @closeDialog="closeDialog"></show-content>
<!-- 新增域名弹窗 -->
<add-domain v-if="isShowAdd" :supplierConfig="supplierConfig" :supplierList="supplierList" @closeDialog="closeDialog"></add-domain>
</template>
<script setup name="DomainManageList">
import {
getDomainList, //
getDomainVerify, //
getDomainExpireTime, //
setDomainStop, //
getDomainNS, // NS
delDomain, //
getDomainConfig,
} from "@/api/operation/domain";
import AutoRefreshTime from "@/components/AutoRefreshTime"; //
import CopyIcon from "@/components/CopyIcon"; //
import IconTips from "@/components/IconTips"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import ShowContent from "./ShowContent.vue"; //
import AddDomain from "./AddDomain.vue"; //
import SupplierButton from "../components/SupplierButton.vue"; //
import DictSelect from '@/components/DictSelect';
import DictText from '@/components/DictText';
import CustomSelect from '@/components/CustomSelect';
import { ref } from "vue";
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
//
const supplierRef = ref(null), currentNodeTips = ref(''), supplierList = ref([]);
const supplierConfig = ref({ });
//
const getCurSupplierNode = (data) => {
currentNodeTips.value = data?.tips;
}
//
const queryParamsList = ref([{
label: proxy.t('操作人'),
value: 'updateBy'
}, {
label: proxy.t('主域名'),
value: 'name'
}]);
const domainStatusOptions = ref([
{
label: proxy.t('待验证'),
value: 1
},
{
label: proxy.t('正常'),
value: 2
},
{
label: proxy.t('已过期'),
value: 3
},
{
label: proxy.t('待找回'),
value: 4
}
]);
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
searchType: 'updateBy',
name: '',
updateBy: '',
domainStatus: '',
orderByColumn: 'createTime',
isAsc: 'desc',
domainSupplierId: '' //
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
loading.value = true;
getDomainList(queryParams).then(res => {
loading.value = false;
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const openAddDomain = () => {
getDomainConfig({}).then(res => {
supplierConfig.value = res.data
supplierList.value = supplierRef.value?.supplierList; //
isShowAdd.value = true;
});
}
//
const opInfo = (row, type) => {
const _ids = row.id ? [row.id] : row; // id
switch (type) {
case 'check':
//
if (row.id) {
row.checkLoading = true;
} else {
loading.value = true;
};
getDomainVerify({ ids: _ids }).then(res => {
proxy.$modal.msgSuccess(proxy.t('验证成功!'));
row.checkLoading = false;
getList();
}).catch(() => {
row.checkLoading = false;
loading.value = false;
batchOpType.value = '';
});
break
case 'getNS':
// NS
row.getNSLoading = true;
getDomainNS(row.id).then(res => {
proxy.$modal.msgSuccess(proxy.t('获取NS成功'));
row.getNSLoading = false;
getList();
}).catch(() => {
row.getNSLoading = false;
loading.value = false;
});
break
case 'refresh':
//
if (row.id) {
row.refreshLoading = true;
} else {
loading.value = true;
};
getDomainExpireTime({ ids: _ids }).then(res => {
proxy.$modal.msgSuccess(proxy.t('刷新成功!'));
row.refreshLoading = false;
getList();
}).catch(() => {
row.refreshLoading = false;
loading.value = false;
batchOpType.value = '';
});
break
case 'stop':
//
proxy.$modal.confirm(proxy.t('停用将停止解析域名,请谨慎操作!')).then(() => {
loading.value = true;
setDomainStop({ ids: _ids }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('域名停用成功!'));
getList();
}).catch(() => {
loading.value = false;
batchOpType.value = '';
});
}).catch(() => { });
break
case 'delete':
//
proxy.$modal.confirm(proxy.t('删除主域名,子域名解析将被删除,请谨慎操作!')).then(() => {
loading.value = true;
delDomain(_ids.join(',')).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
batchOpType.value = '';
});
}).catch(() => { });
break
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('检查过期日期'), value: 'refresh' },
{ label: proxy.t('域名验证'), value: 'check' },
{ label: proxy.t('彻底删除'), value: 'delete' },
{ label: proxy.t('强制停用'), value: 'stop' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
const ids = selectionData.value.map(v => v.id);
opInfo(ids, batchOpType.value);
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
handleQuery();
}
//
const isShowInfo = ref(false), isShowAdd = ref(false);
const closeDialog = (type) => {
isShowAdd.value = false;
isShowInfo.value = false;
// getList();
if (type == 'submit') {
getList();
}
}
</script>
<style scoped lang='scss'>
.domain-main {
flex: 1;
display: flex;
overflow: hidden;
//
.dm-left {
width: 145px;
height: 100%;
border-right: 1px solid #e5e5e5;
}
//
.dm-right {
width: calc(100% - 160px);
margin-left: 15px;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.copy-ml {
margin-left: 10px;
}
}
}
</style>

View File

@ -0,0 +1,370 @@
<template>
<table-search-card :model="queryParams" @getList="getList" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<el-form-item prop="supplierCode">
<el-input v-model="queryParams.supplierCode" placeholder="请输入服务商code" clearable />
</el-form-item>
</template>
<template #right>
<el-button type="primary" plain icon="Plus" @click="openAddDomainNameProvider" v-hasPermi="['operation:supplier:add']">{{
t('新增') }}</el-button>
<el-button type="primary" @click="openConfigSupplier" v-hasPermi="['operation:supplier:config:query']">{{
t('配置信息') }}</el-button>
</template>
</table-search-card>
<el-table
v-loading="loading"
class="c-table-main"
:data="deptList"
row-key="id"
border
stripe
>
<el-table-column prop="supplierCode" label="服务商code" min-width="160"></el-table-column>
<el-table-column prop="supplierName" label="供应商名称" min-width="160"></el-table-column>
<el-table-column prop="currencyCode" label="币种代码" min-width="110"></el-table-column>
<el-table-column prop="freeNode" label="免费节点数量" min-width="150">
<template #default="{ row }">
{{ row.freeNode }}
</template>
</el-table-column>
<el-table-column :label="t('停用状态')" align="center" prop="stopStatus" v-hasPermi="['operation:supplier:switch']" label-class-name="text-warning">
<template #default="{ row }">
<base-switch v-model="row.stopStatus" :active-value="true" :inactive-value="false"
:before-change="() => beforeSwitchChange(row, 'stopStatus')" />
</template>
</el-table-column>
<el-table-column prop="status" label="超出自由节点" min-width="150">
<template #default="{ row }">
{{ row.exceedFreeNode }}
</template>
</el-table-column>
<el-table-column prop="exceedCost" label="超出费用/节点/月" width="200">
<template #default="{ row }">
{{ row.exceedCost }}
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="200">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime)||'--' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="150" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['operation:supplier:edit']"></el-button>
<el-button v-if="scope.row.parentId != 0" link type="primary" @click="handleDelete(scope.row)" v-hasPermi="['operation:supplier:remove']"></el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改部门对话框 -->
<el-dialog :title="title" align-center v-model="open" width="800px" append-to-body>
<el-form ref="deptRef" :model="form" :rules="rules" label-width="170px">
<el-form-item label="供应商名称" prop="supplierName">
<el-input v-model="form.supplierName" placeholder="请输入供应商名称" />
</el-form-item>
<el-form-item label="服务商code" prop="supplierCode">
<el-input v-model="form.supplierCode" placeholder="请输入服务商code" />
</el-form-item>
<!-- <el-form-item label="币种" prop="deptName">
<custom-select v-model="form.currencyType" :options="currencySelectArr" style="width: 220px;"></custom-select>
</el-form-item> -->
<el-form-item label="供应商类型" prop="supplierType">
<custom-select v-model="form.supplierType" :options="supplierTypeSelectArr" style=""></custom-select>
</el-form-item>
<el-form-item label="自定义解析类型" prop="analysisType">
<custom-select v-model="form.analysisType" :options="analysisTypeSelectArr" style=""></custom-select>
</el-form-item>
<el-form-item label="免费节点数量" prop="freeNode">
<el-input v-model="form.freeNode" placeholder="请输入免费节点数量" />
</el-form-item>
<el-form-item label="允许超出免费节点数量" prop="exceedFreeNode">
<el-input v-model="form.exceedFreeNode" placeholder="请输入允许超出免费节点数量" />
</el-form-item>
<el-form-item label="超出费用/节点/月" prop="exceedCost">
<el-input v-model="form.exceedCost" placeholder="请输入超出费用/节点/月" />
</el-form-item>
<el-form-item label="防封" prop="prohibit">
<el-switch v-model="form.prohibit" />
</el-form-item>
<el-form-item label="是否支持alb负载均衡" prop="supportAlb">
<el-switch v-model="form.supportAlb" />
</el-form-item>
<el-form-item label="停用状态" prop="stopStatus">
<el-switch v-model="form.stopStatus" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<el-dialog :title="t('配置信息')" align-center v-model="showConfigSupplier" width="700px" append-to-body>
<el-form ref="configRef" :model="formConfig" :rules="rulesConfig" label-width="170px">
<el-form-item label="域名解析数量" prop="domainNameCnt">
<el-input v-model="formConfig.domainNameCnt" placeholder="请输入域名解析数量" />
</el-form-item>
<el-form-item label="超出域名数量收费" prop="exceedDomainCost">
<el-input v-model="formConfig.exceedDomainCost" placeholder="请输入超出域名数量收费" />
</el-form-item>
<el-form-item label="流量G" prop="trafficG">
<el-input v-model="formConfig.trafficG" placeholder="请输入流量G" />
</el-form-item>
<el-form-item label="超出流量部分收费" prop="exceedTrafficCost">
<el-input v-model="formConfig.exceedTrafficCost" placeholder="请输入超出流量部分收费" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitConfig"> </el-button>
<el-button @click="cancelConfig"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="Dept">
import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept";
import { getDomainSupplierList,switchDomainSupplier,addDomainSupplier,putDomainSupplier,delDomainSupplier,getDomainSupplierInfo,getDomainSupplierConfig,configDomainSupplier } from "@/api/operation/domainNameProvider";
import baseSwitch from "@/components/BaseSwitch";
import CustomSelect from '@/components/CustomSelect';
const { proxy } = getCurrentInstance();
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const deptList = ref([]);
const total = ref(0);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const title = ref("");
const deptOptions = ref([]);
const isExpandAll = ref(true);
const refreshTable = ref(true);
const oldForm = shallowRef({ });
const showConfigSupplier = ref(false);
const formConfig = ref({
});
const rulesConfig = reactive({
domainNameCnt: [
{ required: true, message: "请输入域名数量", trigger: "change" },
],
exceedDomainCost: [
{ required: true, message: "请输入超出域名单价", trigger: "change" },
],
trafficG: [
{ required: true, message: "请输入流量单价", trigger: "change" },
],
exceedTrafficCost: [
{ required: true, message: "请输入超出流量单价", trigger: "change" },
],
});
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 20,
orderByColumn:'createTime',
isAsc:'desc'
},
rules: {
supplierName: [
{ required: true, message: "请输入供应商名称", trigger: "change" },
{ min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "change" },
],
supplierCode: [
{ required: true, message: "请输入供应商编号", trigger: "change" },
],
supplierType: [
{ required: true, message: "请选择供应商类型", trigger: "change" },
],
analysisType: [
{ required: true, message: "请选择供应商类型", trigger: "change" },
],
freeNode: [
{ required: true, message: "请选择供应商类型", trigger: "change" },
],
exceedFreeNode: [
{ required: true, message: "请选择供应商类型", trigger: "change" },
],
exceedCost: [
{ required: true, message: "请选择供应商类型", trigger: "change" },
],
},
});
const { queryParams, form, rules } = toRefs(data);
const supplierTypeSelectArr = ref([
{ label: "全球", value: "1" },
{ label: "大陆", value: "2" },
{ label: "国际", value: "3" },
])
const analysisTypeSelectArr = ref([
{ label: "正常解析", value: "1" },
{ label: "支持更换请求协议", value: "2" },
])
// const currencySelectArr = getLocalStorage('currencySelect')?.map(item => {
// return {
// label: `${item.currencyName}(${item.currencyCode})`,
// value: item.currencyCode
// };
// });
/** 查询部门列表 */
function getList() {
loading.value = true;
getDomainSupplierList(queryParams.value).then(response => {
deptList.value = response.rows;
total.value = response.total;
loading.value = false;
});
}
/** 取消按钮 */
function cancel() {
open.value = false;
reset();
}
/** 表单重置 */
function reset() {
form.value = {
deptId: undefined,
parentId: undefined,
deptName: undefined,
orderNum: 0,
leader: undefined,
phone: undefined,
email: undefined,
status: "0"
};
proxy.resetForm("deptRef");
}
/** 搜索按钮操作 */
function handleQuery() {
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
handleQuery();
}
const switchBeforeChange = () => {
return false
}
const beforeSwitchChange = async (row, undateKeys) => {
const _status = row[undateKeys] == true ? false : true
const _data = {
id: row.id
}
_data[undateKeys] = _status
try {
await switchDomainSupplier(_data).then(() => {
proxy.$modal.msgSuccess(_status === 1 ? proxy.t('开启成功') : proxy.t('关闭成功'))
getList()
})
return true;
} catch (error) {
console.error(proxy.t('接口调用失败'), error);
return false; //
}
}
/** 新增按钮操作 */
function openAddDomainNameProvider(row) {
reset();
open.value = true;
title.value = "新增域名供应商";
}
//
const openConfigSupplier = async (row) => {
getDomainSupplierConfig({ }).then(res => {
formConfig.value = res.data;
showConfigSupplier.value = true;
oldForm.value = JSON.stringify(formConfig.value);
})
}
const cancelConfig = () => {
reset();
showConfigSupplier.value = false;
}
const submitConfig = async () => {
proxy.$refs["configRef"].validate(valid => {
if (valid) {
if (JSON.stringify(formConfig.value) != oldForm.value) {
configDomainSupplier(formConfig.value).then(res => {
proxy.$message.success(proxy.t('修改成功'));
cancelConfig();
getList();
})
}else{
cancelConfig();
}
}
})
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
getDomainSupplierInfo(row.id).then(response => {
form.value = response.data;
open.value = true;
title.value = "修改域名供应商";
setTimeout(() => {
let objForm = form.value
oldForm.value = JSON.stringify(objForm);
}, 500);
});
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["deptRef"].validate(valid => {
if (valid) {
if (form.value.id != undefined) {
if (JSON.stringify(form.value) != oldForm.value) {
putDomainSupplier(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
}else{
open.value = false;
}
} else {
addDomainSupplier(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
});
}
}
});
}
/** 删除按钮操作 */
function handleDelete(row) {
proxy.$modal.confirm('是否确认删除名称为"' + row.supplierName + '"的数据项?').then(function() {
return delDomainSupplier(row.id);
}).then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
}).catch(() => {});
}
getList();
</script>

View File

@ -0,0 +1,109 @@
<template>
<div class="app-container">
<!-- 全局搜索 -->
<el-input v-model="domainName" class="domain-search-input" :placeholder="t('全局搜索域名')">
<template #suffix>
<el-icon @click="searchDomain" :class="'el-input__icon ' + (searchLoading ? 'c-rotate' : '')"
style="cursor: pointer;">
<Search v-if="!searchLoading" />
<Loading v-else />
</el-icon>
</template>
</el-input>
<!-- 标签页 -->
<el-tabs v-model="activeName" @tab-click="tabClick">
<el-tab-pane name="domainManage" :label="t('域名管理')" v-hasPermi="['operation:domain:list']"></el-tab-pane>
<el-tab-pane name="DomainNameProvider" :label="t('域名供应商管理')" v-hasPermi="['operation:son:web:list']"></el-tab-pane>
<!-- <el-tab-pane name="webHall" :label="t('Web大厅')" v-hasPermi="['operation:son:web:list']"></el-tab-pane> -->
<!-- <el-tab-pane name="backend" :label="t('后端加速域名')" v-hasPermi="['operation:son:backend:list']"></el-tab-pane>
<el-tab-pane name="appHall" :label="t('App大厅')" v-hasPermi="['operation:son:app:list']"></el-tab-pane>
<el-tab-pane name="oss" :label="t('OSS加速域名')" v-hasPermi="['operation:son:oss:list']"></el-tab-pane>
<el-tab-pane name="downloadSite" :label="t('下载站域名')" v-hasPermi="['operation:son:download:list']"></el-tab-pane>
<el-tab-pane name="payment" :label="t('支付域名')" v-hasPermi="['operation:son:pay:list']"></el-tab-pane>
<el-tab-pane name="whiteList" :label="t('外部白名单域名(含担保)')" v-hasPermi="['operation:whitelist:list']"></el-tab-pane>
<el-tab-pane name="customParsing" :label="t('自定义解析')" v-hasPermi="['operation:analysis:list']"></el-tab-pane> -->
</el-tabs>
<!-- 列表组件 -->
<div class="tabs-main-box">
<domain-manage-list v-if="activeName === 'domainManage'"></domain-manage-list>
<domainName-provider v-if="activeName === 'DomainNameProvider'"></domainName-provider>
<!-- <web-hall-list v-if="activeName === 'webHall'" @goDomainManage="goDomainManage" :searchResult="searchResult"></web-hall-list>
<backend-list v-if="activeName === 'backend'" @goDomainManage="goDomainManage" :searchResult="searchResult"></backend-list>
<app-hall-list v-if="activeName === 'appHall'" @goDomainManage="goDomainManage" :searchResult="searchResult"></app-hall-list>
<oss-list v-if="activeName === 'oss'" @goDomainManage="goDomainManage" :searchResult="searchResult"></oss-list>
<download-site-list v-if="activeName === 'downloadSite'" @goDomainManage="goDomainManage" :searchResult="searchResult"></download-site-list>
<payment-list v-if="activeName === 'payment'" @goDomainManage="goDomainManage" :searchResult="searchResult"></payment-list>
<white-list v-if="activeName === 'whiteList'"></white-list>
<custom-parsing v-if="activeName === 'customParsing'"></custom-parsing> -->
</div>
</div>
</template>
<!-- 域名管理 -->
<script setup name="Domain">
import { getSonDomainSearch } from "@/api/operation/domain"; //
import DomainManageList from './domainManage/index.vue'; //
import DomainNameProvider from './domainNameProvider/index.vue'; //
import WebHallList from './sonDomainList/WebHallList.vue'; // web
import BackendList from './sonDomainList/BackendList.vue'; //
import AppHallList from './sonDomainList/AppHallList.vue'; // app
import OssList from './sonDomainList/OssList.vue'; // oss
import DownloadSiteList from './sonDomainList/DownloadSiteList.vue'; //
import PaymentList from './sonDomainList/PaymentList.vue' //
import WhiteList from './whiteList/index.vue'; //
import CustomParsing from './customParsing/index.vue'; //
// tab
const activeName = ref('domainManage');
// tab
const goDomainManage = () => {
activeName.value = 'domainManage';
}
//
const tabNames = ['webHall', 'backend', 'appHall', 'oss', 'downloadSite', 'payment'], // operation_domain_son_type
searchLoading = ref(false), // loading
domainName = ref(''), //
searchResult = ref({}); //
const searchDomain = () => {
if (!domainName.value) return;
searchLoading.value = true;
getSonDomainSearch({ name: domainName.value }).then(res => {
searchLoading.value = false;
//
activeName.value = tabNames[res.data.domainSonTypeType - 1];
searchResult.value = {
type: 'domainName',
domainName: domainName.value,
domainSupplierId: res.data.domainSupplierId
};
}).catch(() => {
searchLoading.value = false;
});
}
// tab
const tabClick = () => {
searchResult.value = {};
}
</script>
<style scoped lang='scss'>
.app-container {
position: relative;
.domain-search-input {
width: 240px;
position: absolute;
right: 15px;
top: 15px;
z-index: 1000;
}
}
</style>

View File

@ -0,0 +1,404 @@
<template>
<!-- APP大厅域名列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<select-input-form :queryParamsList="queryParamsList" v-model:queryParams="queryParams" :width="[130, 260]"
@handleQuery="handleQuery"></select-input-form>
<el-form-item prop="usageStatus" style="width: 150px;">
<dict-select clearable v-model="queryParams.usageStatus" :placeholder="t('使用状态')"
dictKey="operation_domain_son_usage_status" @change="handleQuery"></dict-select>
</el-form-item>
</template>
<template #right>
<auto-refresh-time apiName="appDomainList" @refreshFn="getList"
style="margin-right: 10px;"></auto-refresh-time>
<el-button type="success" plain icon="Plus" @click="openAddDomain" v-hasPermi="['operation:son:app:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<div class="domain-main">
<!-- 左侧供应商 -->
<div class="dm-left">
<el-scrollbar max-height="600px">
<!-- 供应商选择 -->
<supplier-button ref="supplierRef" v-model="queryParams.domainSupplierId" @change="handleQuery"
@getCurSupplierNode="getCurSupplierNode"></supplier-button>
</el-scrollbar>
</div>
<!-- 右侧表格 -->
<div class="dm-right">
<el-alert v-if="currentNodeTips" style="margin-bottom: 15px;" :title="currentNodeTips" :closable="false"
type="warning" show-icon />
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column :label="t('CDN节点名称')" align="center" min-width="160">
<template #default="{ row }">
{{ row.cdnNodeName }}
<dict-text class="c-green" dictKey="ff_operation_son_type" :value="row.sonType" />
</template>
</el-table-column>
<el-table-column align="center" min-width="280">
<template #header>
<span>{{ t('生效域名') }}</span>
</template>
<template #default="{ row }">
{{ row.name }}<copy-icon :text="row.name" class="copy-ml"></copy-icon>
<p :class="'c-clear ' + (row.analyzeStatus ? 'c-green' : 'c-red')">{{ row.analyzeStatus ? t('已解析') :
t('待解析') }}</p>
</template>
</el-table-column>
<el-table-column align="center" min-width="220">
<template #header>
<span>{{ t('解析说明(生效状态和网速)') }}</span>
</template>
<template #default="{ row }">
<span :class="'c-clear ' + (row.analyzeInvalid ? 'c-green' : 'c-red')">{{ row.analyzeInvalid ?
t('已生效') : t('未生效') }}</span>
<el-icon @click="opInfo(row, 'refresh')" :class="row.refreshLoading ? 'c-rotate' : ''"
style="position: relative; top: 4px; margin-left: 8px;" size="16" class="c-blue">
<Refresh />
</el-icon>
</template>
</el-table-column>
<el-table-column :label="t('使用状态')" align="center" min-width="120">
<template #default="{ row }">
<dict-text :colorValues="['c-grey', 'c-orange', 'c-orange', 'c-green', 'c-red']"
dictKey="operation_domain_son_usage_status" :value="row.usageStatus" />
</template>
</el-table-column>
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" min-width="220" fixed="right">
<template #default="{ row }">
<el-button v-if="row.usageStatus == 4" link type="primary" @click="opInfo(row, 'clearCache')"
v-hasPermi="['operation:son:app:clear']">
{{ t('清理缓存') }}
</el-button>
<el-tooltip v-if="row.usageStatus == 1 || row.usageStatus == 5" effect="dark"
:content="t('提示启用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:app:status']">
{{ t('启用') }}
</el-button>
</el-tooltip>
<el-tooltip v-if="row.usageStatus == 2 || row.usageStatus == 3 || row.usageStatus == 4" effect="dark"
:content="t('提示停用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:app:status']">
{{ t('停用') }}
</el-button>
</el-tooltip>
<el-button v-if="row.usageStatus == 1 && row.sonType != -1 && row.sonType != -2" link type="primary"
@click="opInfo(row, 'edit')" v-hasPermi="['operation:son:app:edit']">
{{ t('修改') }}
</el-button>
<el-button v-if="row.usageStatus == 1 || row.usageStatus == 5" link type="primary"
@click="opInfo(row, 'delete')" v-hasPermi="['operation:son:app:remove']">
{{ t('删除') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<!-- 新增域名弹窗 -->
<add-son-domain v-if="isShowAdd" type="appHall" :supplierList="supplierList" @goDomainManage="goDomainManage"
@closeDialog="closeDialog"></add-son-domain>
<!-- 修改域名弹窗 -->
<edit-son-domain v-if="isShowEdit" type="appHall" :currentDomainInfo="currentDomainInfo"
@closeDialog="closeDialog"></edit-son-domain>
<!-- 批量操作弹窗 -->
<batch-operation v-if="isShowBatchOp" type="appHall" :batchOpType="batchOpType" :selectionData="selectionData"
@closeDialog="closeDialog"></batch-operation>
</template>
<script setup name="AppHallList">
import {
getAppDomainList, //
refreshAppDomainAnalysis, //
setAppDomainStatus, //
clearAppDomainCache, //
delAppDomain //
} from "@/api/operation/domain";
import AutoRefreshTime from "@/components/AutoRefreshTime"; //
import CopyIcon from "@/components/CopyIcon"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import SupplierButton from "../components/SupplierButton.vue"; //
import AddSonDomain from "../components/AddSonDomain.vue"; //
import EditSonDomain from "../components/EditSonDomain.vue"; //
import BatchOperation from "../components/BatchOperation.vue"; //
const props = defineProps({
//
searchResult: {
type: Object,
default: {}
}
});
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
const isShowAdd = ref(false), isShowEdit = ref(false), isShowBatchOp = ref(false); //
//
const supplierRef = ref(null), currentNodeTips = ref(''), supplierList = ref([]);
const getCurSupplierNode = (data) => {
currentNodeTips.value = data?.tips;
}
//
const queryParamsList = ref([{
label: proxy.t('操作人'),
value: 'updateBy'
}, {
label: proxy.t('子域名'),
value: 'name'
}]);
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
searchType: 'updateBy',
name: '', //
updateBy: '', //
usageStatus: '', // 使
orderByColumn: 'createTime',
isAsc: 'desc',
domainSupplierId: '' //
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
loading.value = true;
getAppDomainList(queryParams).then(res => {
loading.value = false;
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const openAddDomain = () => {
supplierList.value = supplierRef.value?.supplierList.filter(v => v.isOpen && !v.stopStatus); //
isShowAdd.value = true;
}
//
const currentDomainInfo = ref({}); //
const opInfo = (row, type) => {
const _ids = row.id ? [row.id] : row; // id
switch (type) {
case 'status':
// /
let typeName = '', content = '', _usageStatus = '';
if (row.usageStatus == 1 || row.usageStatus == 5) {
typeName = proxy.t('启用');
content = proxy.t('确认启用该域名吗!');
_usageStatus = 4;
} else {
typeName = proxy.t('停用');
content = proxy.t('确认停用该域名吗!');
_usageStatus = 5;
}
proxy.$modal.confirm(content).then(() => {
loading.value = true;
setAppDomainStatus({ ids: _ids, usageStatus: _usageStatus }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('域名') + typeName + proxy.t('成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'refresh':
//
row.refreshLoading = true;
refreshAppDomainAnalysis({ id: row.id }).then(res => {
proxy.$modal.msgSuccess(proxy.t('刷新成功!'));
row.refreshLoading = false;
getList();
}).catch(() => {
row.refreshLoading = false;
});
break
case 'clearCache':
//
proxy.$modal.confirm(proxy.t('确定要清理该记录的缓存吗?')).then(() => {
loading.value = true;
clearAppDomainCache({ ids: _ids }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('缓存清理成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'delete':
//
proxy.$modal.confirm(proxy.t('确定要删除该域名吗!')).then(() => {
loading.value = true;
delAppDomain(_ids.join(',')).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'edit':
//
currentDomainInfo.value = row;
isShowEdit.value = true;
break
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量清理缓存'), value: 'clearCache' },
{ label: proxy.t('批量删除'), value: 'delete' },
{ label: proxy.t('批量停用'), value: 'stop' },
{ label: proxy.t('批量启用'), value: 'enable' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
isShowBatchOp.value = true;
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
handleQuery();
}
//
const closeDialog = (type) => {
isShowAdd.value = false; //
isShowEdit.value = false; //
isShowBatchOp.value = false; //
batchOpType.value = ''; //
//
if (type === 'batchSubmit') clearBatchOp();
//
if (type === 'submit' || type === 'batchSubmit') {
getList();
}
}
//
const emit = defineEmits(['goDomainManage']);
const goDomainManage = () => {
emit('goDomainManage');
}
//
watch(() => props.searchResult, (val) => {
if (val.type == 'domainName') {
queryParams.searchType = 'name'
queryParams.name = val.domainName;
nextTick(() => {
queryParams.domainSupplierId = val.domainSupplierId;
handleQuery();
});
}
}, { deep: true, immediate: true });
</script>
<style scoped lang='scss'>
.domain-main {
flex: 1;
display: flex;
overflow: hidden;
//
.dm-left {
width: 145px;
height: 100%;
border-right: 1px solid #e5e5e5;
}
//
.dm-right {
width: calc(100% - 160px);
margin-left: 15px;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.copy-ml {
margin-left: 10px;
}
}
}
</style>

View File

@ -0,0 +1,403 @@
<template>
<!-- 后端加速域名列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<select-input-form :queryParamsList="queryParamsList" v-model:queryParams="queryParams" :width="[130, 260]"
@handleQuery="handleQuery"></select-input-form>
<el-form-item prop="usageStatus" style="width: 150px;">
<dict-select clearable v-model="queryParams.usageStatus" :placeholder="t('使用状态')"
dictKey="operation_domain_son_usage_status" @change="handleQuery"></dict-select>
</el-form-item>
</template>
<template #right>
<auto-refresh-time apiName="backendDomainList" @refreshFn="getList" style="margin-right: 10px;"></auto-refresh-time>
<el-button type="success" plain icon="Plus" @click="openAddDomain" v-hasPermi="['operation:son:backend:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<div class="domain-main">
<!-- 左侧供应商 -->
<div class="dm-left">
<el-scrollbar max-height="600px">
<!-- 供应商选择 -->
<supplier-button ref="supplierRef" v-model="queryParams.domainSupplierId" @change="handleQuery"
@getCurSupplierNode="getCurSupplierNode"></supplier-button>
</el-scrollbar>
</div>
<!-- 右侧表格 -->
<div class="dm-right">
<el-alert v-if="currentNodeTips" style="margin-bottom: 15px;" :title="currentNodeTips" :closable="false"
type="warning" show-icon />
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column :label="t('CDN节点名称')" align="center" min-width="160">
<template #default="{ row }">
{{ row.cdnNodeName }}
<dict-text class="c-green" dictKey="ff_operation_son_type" :value="row.sonType" />
</template>
</el-table-column>
<el-table-column align="center" min-width="280">
<template #header>
<span>{{ t('生效域名') }}</span>
</template>
<template #default="{ row }">
{{ row.name }}<copy-icon :text="row.name" class="copy-ml"></copy-icon>
<p :class="'c-clear ' + (row.analyzeStatus ? 'c-green' : 'c-red')">{{ row.analyzeStatus ? t('已解析') :
t('待解析') }}</p>
</template>
</el-table-column>
<el-table-column align="center" min-width="220">
<template #header>
<span>{{ t('解析说明(生效状态和网速)') }}</span>
</template>
<template #default="{ row }">
<span :class="'c-clear ' + (row.analyzeInvalid ? 'c-green' : 'c-red')">{{ row.analyzeInvalid ?
t('已生效') : t('未生效') }}</span>
<el-icon @click="opInfo(row, 'refresh')" :class="row.refreshLoading ? 'c-rotate' : ''"
style="position: relative; top: 4px; margin-left: 8px;" size="16" class="c-blue">
<Refresh />
</el-icon>
</template>
</el-table-column>
<el-table-column :label="t('使用状态')" align="center" min-width="120">
<template #default="{ row }">
<dict-text :colorValues="['c-grey', 'c-orange', 'c-orange', 'c-green', 'c-red']"
dictKey="operation_domain_son_usage_status" :value="row.usageStatus" />
</template>
</el-table-column>
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" min-width="220" fixed="right">
<template #default="{ row }">
<el-button v-if="row.usageStatus == 4" link type="primary" @click="opInfo(row, 'clearCache')"
v-hasPermi="['operation:son:backend:clear']">
{{ t('清理缓存') }}
</el-button>
<el-tooltip v-if="row.usageStatus == 1 || row.usageStatus == 5" effect="dark"
:content="t('提示启用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:backend:status']">
{{ t('启用') }}
</el-button>
</el-tooltip>
<el-tooltip v-if="row.usageStatus == 2 || row.usageStatus == 3 || row.usageStatus == 4" effect="dark"
:content="t('提示停用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:backend:status']">
{{ t('停用') }}
</el-button>
</el-tooltip>
<el-button v-if="row.usageStatus == 1 && row.sonType != -1 && row.sonType != -2" link type="primary"
@click="opInfo(row, 'edit')" v-hasPermi="['operation:son:backend:edit']">
{{ t('修改') }}
</el-button>
<el-button v-if="row.usageStatus == 1 || row.usageStatus == 5" link type="primary"
@click="opInfo(row, 'delete')" v-hasPermi="['operation:son:backend:remove']">
{{ t('删除') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<!-- 新增域名弹窗 -->
<add-son-domain v-if="isShowAdd" type="backend" :supplierList="supplierList" @goDomainManage="goDomainManage"
@closeDialog="closeDialog"></add-son-domain>
<!-- 修改域名弹窗 -->
<edit-son-domain v-if="isShowEdit" type="backend" :currentDomainInfo="currentDomainInfo"
@closeDialog="closeDialog"></edit-son-domain>
<!-- 批量操作弹窗 -->
<batch-operation v-if="isShowBatchOp" type="backend" :batchOpType="batchOpType" :selectionData="selectionData"
@closeDialog="closeDialog"></batch-operation>
</template>
<script setup name="BackendList">
import {
getBackendDomainList, //
refreshBackendDomainAnalysis, //
setBackendDomainStatus, //
clearBackendDomainCache, //
delBackendDomain //
} from "@/api/operation/domain";
import AutoRefreshTime from "@/components/AutoRefreshTime"; //
import CopyIcon from "@/components/CopyIcon"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import SupplierButton from "../components/SupplierButton.vue"; //
import AddSonDomain from "../components/AddSonDomain.vue"; //
import EditSonDomain from "../components/EditSonDomain.vue"; //
import BatchOperation from "../components/BatchOperation.vue"; //
const props = defineProps({
//
searchResult: {
type: Object,
default: {}
}
});
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
const isShowAdd = ref(false), isShowEdit = ref(false), isShowBatchOp = ref(false); //
//
const supplierRef = ref(null), currentNodeTips = ref(''), supplierList = ref([]);
const getCurSupplierNode = (data) => {
currentNodeTips.value = data?.tips;
}
//
const queryParamsList = ref([{
label: proxy.t('操作人'),
value: 'updateBy'
}, {
label: proxy.t('子域名'),
value: 'name'
}]);
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
searchType: 'updateBy',
name: '', //
updateBy: '', //
usageStatus: '', // 使
orderByColumn: 'createTime',
isAsc: 'desc',
domainSupplierId: '' //
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
loading.value = true;
getBackendDomainList(queryParams).then(res => {
loading.value = false;
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const openAddDomain = () => {
supplierList.value = supplierRef.value?.supplierList.filter(v => v.isOpen && !v.stopStatus); //
isShowAdd.value = true;
}
//
const currentDomainInfo = ref({}); //
const opInfo = (row, type) => {
const _ids = row.id ? [row.id] : row; // id
switch (type) {
case 'status':
// /
let typeName = '', content = '', _usageStatus = '';
if (row.usageStatus == 1 || row.usageStatus == 5) {
typeName = proxy.t('启用');
content = proxy.t('确认启用该域名吗!');
_usageStatus = 4;
} else {
typeName = proxy.t('停用');
content = proxy.t('确认停用该域名吗!');
_usageStatus = 5;
}
proxy.$modal.confirm(content).then(() => {
loading.value = true;
setBackendDomainStatus({ ids: _ids, usageStatus: _usageStatus }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('域名') + typeName + proxy.t('成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'refresh':
//
row.refreshLoading = true;
refreshBackendDomainAnalysis({ id: row.id }).then(res => {
proxy.$modal.msgSuccess(proxy.t('刷新成功!'));
row.refreshLoading = false;
getList();
}).catch(() => {
row.refreshLoading = false;
});
break
case 'clearCache':
//
proxy.$modal.confirm(proxy.t('确定要清理该记录的缓存吗?')).then(() => {
loading.value = true;
clearBackendDomainCache({ ids: _ids }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('缓存清理成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'delete':
//
proxy.$modal.confirm(proxy.t('确定要删除该域名吗!')).then(() => {
loading.value = true;
delBackendDomain(_ids.join(',')).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'edit':
//
currentDomainInfo.value = row;
isShowEdit.value = true;
break
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量清理缓存'), value: 'clearCache' },
{ label: proxy.t('批量删除'), value: 'delete' },
{ label: proxy.t('批量停用'), value: 'stop' },
{ label: proxy.t('批量启用'), value: 'enable' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
isShowBatchOp.value = true;
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
handleQuery();
}
//
const closeDialog = (type) => {
isShowAdd.value = false; //
isShowEdit.value = false; //
isShowBatchOp.value = false; //
batchOpType.value = ''; //
//
if (type === 'batchSubmit') clearBatchOp();
//
if (type === 'submit' || type === 'batchSubmit') {
getList();
}
}
//
const emit = defineEmits(['goDomainManage']);
const goDomainManage = () => {
emit('goDomainManage');
}
//
watch(() => props.searchResult, (val) => {
if (val.type == 'domainName') {
queryParams.searchType = 'name'
queryParams.name = val.domainName;
nextTick(() => {
queryParams.domainSupplierId = val.domainSupplierId;
handleQuery();
});
}
}, { deep: true, immediate: true });
</script>
<style scoped lang='scss'>
.domain-main {
flex: 1;
display: flex;
overflow: hidden;
//
.dm-left {
width: 145px;
height: 100%;
border-right: 1px solid #e5e5e5;
}
//
.dm-right {
width: calc(100% - 160px);
margin-left: 15px;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.copy-ml {
margin-left: 10px;
}
}
}
</style>

View File

@ -0,0 +1,503 @@
<template>
<!-- 下载站域名列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<select-input-form :queryParamsList="queryParamsList" v-model:queryParams="queryParams" :width="[130, 260]"
@handleQuery="handleQuery"></select-input-form>
<el-form-item prop="usageStatus" style="width: 150px;">
<dict-select clearable v-model="queryParams.usageStatus" :placeholder="t('使用状态')"
dictKey="operation_domain_son_usage_status" @change="handleQuery"></dict-select>
</el-form-item>
</template>
<template #right>
<auto-refresh-time apiName="downloadSiteDomainList" @refreshFn="getList"
style="margin-right: 10px;"></auto-refresh-time>
<el-button type="success" plain icon="Plus" @click="openAddDomain" v-hasPermi="['operation:son:download:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<div class="domain-main">
<!-- 左侧供应商 -->
<div class="dm-left">
<el-scrollbar max-height="600px">
<!-- 供应商选择 -->
<supplier-button ref="supplierRef" v-model="queryParams.domainSupplierId" @change="handleQuery"
@getCurSupplierNode="getCurSupplierNode"></supplier-button>
</el-scrollbar>
</div>
<!-- 右侧表格 -->
<div class="dm-right">
<el-alert v-if="currentNodeTips" style="margin-bottom: 15px;" :title="currentNodeTips" :closable="false"
type="warning" show-icon />
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column :label="t('CDN节点名称')" align="center" min-width="160">
<template #default="{ row }">
{{ row.cdnNodeName }}
<dict-text class="c-green" dictKey="ff_operation_son_type" :value="row.sonType" />
</template>
</el-table-column>
<el-table-column align="center" min-width="280">
<template #header>
<span>{{ t('生效域名') }}</span>
</template>
<template #default="{ row }">
{{ row.name }}<copy-icon :text="row.name" class="copy-ml"></copy-icon>
<p :class="'c-clear ' + (row.analyzeStatus ? 'c-green' : 'c-red')">{{ row.analyzeStatus ? t('已解析') :
t('待解析') }}</p>
</template>
</el-table-column>
<el-table-column :label="t('端口类型')" align="center" min-width="120">
<template #default="{ row }">
<dict-text dictKey="ff_operation_domain_son_port_type" :value="row.portType" />
</template>
</el-table-column>
<el-table-column :label="t('端口号')" prop="portValue" align="center" min-width="120"></el-table-column>
<el-table-column align="center" min-width="220">
<template #header>
<span>{{ t('解析说明(生效状态和网速)') }}</span>
</template>
<template #default="{ row }">
<span :class="'c-clear ' + (row.analyzeInvalid ? 'c-green' : 'c-red')">{{ row.analyzeInvalid ?
t('已生效') : t('未生效') }}</span>
<el-icon @click="opInfo(row, 'refresh')" :class="row.refreshLoading ? 'c-rotate' : ''"
style="position: relative; top: 4px; margin-left: 8px;" size="16" class="c-blue">
<Refresh />
</el-icon>
</template>
</el-table-column>
<el-table-column align="center" min-width="160">
<template #header>
<span>{{ t('其他担保域名列表') }}</span>
<icon-tips>
<p class="c-clear" style="width: 400px;">
{{ t('1.展示:即会员验证担保域名后,会展示一些站点的其他域名列表,方便会员保存记录、找到回家路或信任其他域名(推荐无封锁的国家)。') }}<br />
{{ t('2.隐藏:即会员验证担保域名后,只能看到当前域名的验证结果,看不到其他域名列表,防止域名全部暴露,有被拦截和举报的风险。') }}
</p>
</icon-tips>
</template>
<template #default="{ row }">
<base-switch :active-value="true" :inactive-value="false" v-model="row.backupDomain"
:tipText="row.status ? t('确认隐藏吗?') : t('确认展示吗?')" :disabled="row.usageStatus != 4"
:before-change="() => beforeSwitchChange(row, 'backupDomain')"></base-switch>
</template>
</el-table-column>
<el-table-column align="center" min-width="150">
<template #header>
<span>{{ t('强制绑定代理') }}</span>
<icon-tips>
<p class="c-clear" style="width: 400px;">
{{ t('代理上级归属优先级:推广链接的上级 > 渠道ID的强制绑定上级 > 域名管理的强制绑定上级(无上级时,域名管理的才生效)') }}
</p>
</icon-tips>
</template>
<template #default="{ row }">
<span v-if="row.agentAccount" class="c-blue" @click="goRoute(row, 'agentAccount')">{{
row.agentAccount }}</span>
<span v-else>{{ t('') }}</span>
</template>
</el-table-column>
<el-table-column :label="t('使用状态')" align="center" min-width="120">
<template #default="{ row }">
<dict-text :colorValues="['c-grey', 'c-orange', 'c-orange', 'c-green', 'c-red']"
dictKey="operation_domain_son_usage_status" :value="row.usageStatus" />
</template>
</el-table-column>
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" min-width="220" fixed="right">
<template #default="{ row }">
<el-button v-if="row.usageStatus == 4" link type="primary" @click="opInfo(row, 'agent')"
v-hasPermi="['operation:son:download:agent']">
{{ t('绑定代理') }}
</el-button>
<el-button v-if="row.usageStatus == 4" link type="primary" @click="opInfo(row, 'clearCache')"
v-hasPermi="['operation:son:download:clear']">
{{ t('清理缓存') }}
</el-button>
<el-tooltip v-if="row.usageStatus == 1 || row.usageStatus == 5" effect="dark"
:content="t('提示启用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:download:status']">
{{ t('启用') }}
</el-button>
</el-tooltip>
<el-tooltip v-if="row.usageStatus == 2 || row.usageStatus == 3 || row.usageStatus == 4" effect="dark"
:content="t('提示停用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:download:status']">
{{ t('停用') }}
</el-button>
</el-tooltip>
<el-button v-if="row.usageStatus == 1 && row.sonType != -1 && row.sonType != -2" link type="primary"
@click="opInfo(row, 'edit')" v-hasPermi="['operation:son:download:edit']">
{{ t('修改') }}
</el-button>
<el-button v-if="row.usageStatus == 1 || row.usageStatus == 5" link type="primary"
@click="opInfo(row, 'delete')" v-hasPermi="['operation:son:download:remove']">
{{ t('删除') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<!-- 新增域名弹窗 -->
<add-son-domain v-if="isShowAdd" type="downloadSite" :supplierList="supplierList" @goDomainManage="goDomainManage"
@closeDialog="closeDialog"></add-son-domain>
<!-- 修改域名弹窗 -->
<edit-son-domain v-if="isShowEdit" type="downloadSite" :currentDomainInfo="currentDomainInfo"
@closeDialog="closeDialog"></edit-son-domain>
<!-- 绑定代理弹窗 -->
<bind-agent v-if="isShowAgent" type="downloadSite" :currentDomainInfo="currentDomainInfo"
@closeDialog="closeDialog"></bind-agent>
<!-- 批量操作弹窗 -->
<batch-operation v-if="isShowBatchOp" type="downloadSite" :batchOpType="batchOpType" :selectionData="selectionData"
@closeDialog="closeDialog"></batch-operation>
</template>
<script setup name="DownloadSiteList">
import {
getDownloadDomainList, //
refreshDownloadDomainAnalysis, //
downloadDomainSwitch, //
setDownloadDomainStatus, //
clearDownloadDomainCache, //
delDownloadDomain, //
} from "@/api/operation/domain";
import AutoRefreshTime from "@/components/AutoRefreshTime"; //
import CopyIcon from "@/components/CopyIcon"; //
import IconTips from "@/components/IconTips"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import SupplierButton from "../components/SupplierButton.vue"; //
import BaseSwitch from "@/components/BaseSwitch"; //
import AddSonDomain from "../components/AddSonDomain.vue"; //
import EditSonDomain from "../components/EditSonDomain.vue"; //
import BindAgent from "../components/BindAgent.vue"; //
import BatchOperation from "../components/BatchOperation.vue"; //
const props = defineProps({
//
searchResult: {
type: Object,
default: {}
}
});
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
const isShowAdd = ref(false), isShowAgent = ref(false), isShowEdit = ref(false), isShowBatchOp = ref(false); //
//
const supplierRef = ref(null), currentNodeTips = ref(''), supplierList = ref([]);
const getCurSupplierNode = (data) => {
currentNodeTips.value = data?.tips;
}
//
const queryParamsList = ref([{
label: proxy.t('操作人'),
value: 'updateBy'
}, {
label: proxy.t('子域名'),
value: 'name'
}]);
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
searchType: 'updateBy',
name: '', //
updateBy: '', //
usageStatus: '', // 使
orderByColumn: 'createTime',
isAsc: 'desc',
domainSupplierId: '' //
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
loading.value = true;
getDownloadDomainList(queryParams).then(res => {
loading.value = false;
//
res.rows.forEach(item => {
item.startEntrance = item.startEntrance.toString();
item.blockDevice = item.blockDevice.toString();
});
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const goRoute = (row, type) => {
proxy.$router.push({
path: '/agent/agent-info',
query: {
[type]: row.agentAccount,
searchType: type
}
})
}
//
const openAddDomain = () => {
supplierList.value = supplierRef.value?.supplierList.filter(v => v.isOpen && !v.stopStatus); //
isShowAdd.value = true;
}
//
const beforeSwitchChange = (row, type) => {
const params = {
id: row.id,
[type]: !row[type]
}
downloadDomainSwitch({ operationDomainSons: [params] }).then(res => {
proxy.$modal.msgSuccess(proxy.t('操作成功!'));
getList();
return true;
}).catch(() => {
return false;
});
}
//
const currentDomainInfo = ref({}); //
const opInfo = (row, type) => {
const _ids = row.id ? [row.id] : row; // id
switch (type) {
case 'status':
// /
let typeName = '', content = '', _usageStatus = '';
if (row.usageStatus == 1 || row.usageStatus == 5) {
typeName = proxy.t('启用');
content = proxy.t('确认启用该域名吗!');
_usageStatus = 4;
} else {
typeName = proxy.t('停用');
content = proxy.t('确认停用该域名吗!');
_usageStatus = 5;
}
proxy.$modal.confirm(content).then(() => {
loading.value = true;
setDownloadDomainStatus({ ids: _ids, usageStatus: _usageStatus }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('域名') + typeName + proxy.t('成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'refresh':
//
row.refreshLoading = true;
refreshDownloadDomainAnalysis({ id: row.id }).then(res => {
proxy.$modal.msgSuccess(proxy.t('刷新成功!'));
row.refreshLoading = false;
getList();
}).catch(() => {
row.refreshLoading = false;
});
break
case 'agent':
//
currentDomainInfo.value = row;
isShowAgent.value = true;
break
case 'clearCache':
//
proxy.$modal.confirm(proxy.t('确定要清理该记录的缓存吗?')).then(() => {
loading.value = true;
clearDownloadDomainCache({ ids: _ids }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('缓存清理成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'delete':
//
proxy.$modal.confirm(proxy.t('确定要删除该域名吗!')).then(() => {
loading.value = true;
delDownloadDomain(_ids.join(',')).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'edit':
//
currentDomainInfo.value = row;
isShowEdit.value = true;
break
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量清理缓存'), value: 'clearCache' },
{ label: proxy.t('批量删除'), value: 'delete' },
{ label: proxy.t('批量停用'), value: 'stop' },
{ label: proxy.t('批量启用'), value: 'enable' },
{ label: proxy.t('批量展示其他担保列表'), value: 'showSecurity' },
{ label: proxy.t('批量隐藏其他担保列表'), value: 'hideSecurity' },
{ label: proxy.t('批量绑定代理'), value: 'bindAgent' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
isShowBatchOp.value = true;
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
handleQuery();
}
//
const closeDialog = (type) => {
isShowAdd.value = false; //
isShowAgent.value = false; //
isShowEdit.value = false; //
isShowBatchOp.value = false; //
batchOpType.value = ''; //
//
if (type === 'batchSubmit') clearBatchOp();
//
if (type === 'submit' || type === 'batchSubmit') {
getList();
}
}
//
const emit = defineEmits(['goDomainManage']);
const goDomainManage = () => {
emit('goDomainManage');
}
//
watch(() => props.searchResult, (val) => {
if (val.type == 'domainName') {
queryParams.searchType = 'name'
queryParams.name = val.domainName;
nextTick(() => {
queryParams.domainSupplierId = val.domainSupplierId;
handleQuery();
});
}
}, { deep: true, immediate: true });
</script>
<style scoped lang='scss'>
.right-switch {
display: inline-block;
color: #606266;
font-size: 14px;
margin-bottom: 10px;
}
.domain-main {
flex: 1;
display: flex;
overflow: hidden;
//
.dm-left {
width: 145px;
height: 100%;
border-right: 1px solid #e5e5e5;
}
//
.dm-right {
width: calc(100% - 160px);
margin-left: 15px;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.copy-ml {
margin-left: 10px;
}
}
}
</style>

View File

@ -0,0 +1,403 @@
<template>
<!-- OSS加速域名列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<select-input-form :queryParamsList="queryParamsList" v-model:queryParams="queryParams" :width="[130, 260]"
@handleQuery="handleQuery"></select-input-form>
<el-form-item prop="usageStatus" style="width: 150px;">
<dict-select clearable v-model="queryParams.usageStatus" :placeholder="t('使用状态')"
dictKey="operation_domain_son_usage_status" @change="handleQuery"></dict-select>
</el-form-item>
</template>
<template #right>
<auto-refresh-time apiName="ossDomainList" @refreshFn="getList" style="margin-right: 10px;"></auto-refresh-time>
<el-button type="success" plain icon="Plus" @click="openAddDomain" v-hasPermi="['operation:son:oss:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<div class="domain-main">
<!-- 左侧供应商 -->
<div class="dm-left">
<el-scrollbar max-height="600px">
<!-- 供应商选择 -->
<supplier-button ref="supplierRef" v-model="queryParams.domainSupplierId" @change="handleQuery"
@getCurSupplierNode="getCurSupplierNode"></supplier-button>
</el-scrollbar>
</div>
<!-- 右侧表格 -->
<div class="dm-right">
<el-alert v-if="currentNodeTips" style="margin-bottom: 15px;" :title="currentNodeTips" :closable="false"
type="warning" show-icon />
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column :label="t('CDN节点名称')" align="center" min-width="160">
<template #default="{ row }">
{{ row.cdnNodeName }}
<dict-text class="c-green" dictKey="ff_operation_son_type" :value="row.sonType" />
</template>
</el-table-column>
<el-table-column align="center" min-width="280">
<template #header>
<span>{{ t('生效域名') }}</span>
</template>
<template #default="{ row }">
{{ row.name }}<copy-icon :text="row.name" class="copy-ml"></copy-icon>
<p :class="'c-clear ' + (row.analyzeStatus ? 'c-green' : 'c-red')">{{ row.analyzeStatus ? t('已解析') :
t('待解析') }}</p>
</template>
</el-table-column>
<el-table-column align="center" min-width="220">
<template #header>
<span>{{ t('解析说明(生效状态和网速)') }}</span>
</template>
<template #default="{ row }">
<span :class="'c-clear ' + (row.analyzeInvalid ? 'c-green' : 'c-red')">{{ row.analyzeInvalid ?
t('已生效') : t('未生效') }}</span>
<el-icon @click="opInfo(row, 'refresh')" :class="row.refreshLoading ? 'c-rotate' : ''"
style="position: relative; top: 4px; margin-left: 8px;" size="16" class="c-blue">
<Refresh />
</el-icon>
</template>
</el-table-column>
<el-table-column :label="t('使用状态')" align="center" min-width="120">
<template #default="{ row }">
<dict-text :colorValues="['c-grey', 'c-orange', 'c-orange', 'c-green', 'c-red']"
dictKey="operation_domain_son_usage_status" :value="row.usageStatus" />
</template>
</el-table-column>
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" min-width="220" fixed="right">
<template #default="{ row }">
<el-button v-if="row.usageStatus == 4" link type="primary" @click="opInfo(row, 'clearCache')"
v-hasPermi="['operation:son:oss:clear']">
{{ t('清理缓存') }}
</el-button>
<el-tooltip v-if="row.usageStatus == 1 || row.usageStatus == 5" effect="dark"
:content="t('提示启用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:oss:status']">
{{ t('启用') }}
</el-button>
</el-tooltip>
<el-tooltip v-if="row.usageStatus == 2 || row.usageStatus == 3 || row.usageStatus == 4" effect="dark"
:content="t('提示停用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:oss:status']">
{{ t('停用') }}
</el-button>
</el-tooltip>
<el-button v-if="row.usageStatus == 1 && row.sonType != -1 && row.sonType != -2" link type="primary"
@click="opInfo(row, 'edit')" v-hasPermi="['operation:son:oss:edit']">
{{ t('修改') }}
</el-button>
<el-button v-if="row.usageStatus == 1 || row.usageStatus == 5" link type="primary"
@click="opInfo(row, 'delete')" v-hasPermi="['operation:son:oss:remove']">
{{ t('删除') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<!-- 新增域名弹窗 -->
<add-son-domain v-if="isShowAdd" type="oss" :supplierList="supplierList" @goDomainManage="goDomainManage"
@closeDialog="closeDialog"></add-son-domain>
<!-- 修改域名弹窗 -->
<edit-son-domain v-if="isShowEdit" type="oss" :currentDomainInfo="currentDomainInfo"
@closeDialog="closeDialog"></edit-son-domain>
<!-- 批量操作弹窗 -->
<batch-operation v-if="isShowBatchOp" type="oss" :batchOpType="batchOpType" :selectionData="selectionData"
@closeDialog="closeDialog"></batch-operation>
</template>
<script setup name="OssList">
import {
getOssDomainList, //
refreshOssDomainAnalysis, //
setOssDomainStatus, //
clearOssDomainCache, //
delOssDomain //
} from "@/api/operation/domain";
import AutoRefreshTime from "@/components/AutoRefreshTime"; //
import CopyIcon from "@/components/CopyIcon"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import SupplierButton from "../components/SupplierButton.vue"; //
import AddSonDomain from "../components/AddSonDomain.vue"; //
import EditSonDomain from "../components/EditSonDomain.vue"; //
import BatchOperation from "../components/BatchOperation.vue"; //
const props = defineProps({
//
searchResult: {
type: Object,
default: {}
}
});
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
const isShowAdd = ref(false), isShowEdit = ref(false), isShowBatchOp = ref(false); //
//
const supplierRef = ref(null), currentNodeTips = ref(''), supplierList = ref([]);
const getCurSupplierNode = (data) => {
currentNodeTips.value = data?.tips;
}
//
const queryParamsList = ref([{
label: proxy.t('操作人'),
value: 'updateBy'
}, {
label: proxy.t('子域名'),
value: 'name'
}]);
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
searchType: 'updateBy',
name: '', //
updateBy: '', //
usageStatus: '', // 使
orderByColumn: 'createTime',
isAsc: 'desc',
domainSupplierId: '' //
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
loading.value = true;
getOssDomainList(queryParams).then(res => {
loading.value = false;
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const openAddDomain = () => {
supplierList.value = supplierRef.value?.supplierList.filter(v => v.isOpen && !v.stopStatus); //
isShowAdd.value = true;
}
//
const currentDomainInfo = ref({}); //
const opInfo = (row, type) => {
const _ids = row.id ? [row.id] : row; // id
switch (type) {
case 'status':
// /
let typeName = '', content = '', _usageStatus = '';
if (row.usageStatus == 1 || row.usageStatus == 5) {
typeName = proxy.t('启用');
content = proxy.t('确认启用该域名吗!');
_usageStatus = 4;
} else {
typeName = proxy.t('停用');
content = proxy.t('确认停用该域名吗!');
_usageStatus = 5;
}
proxy.$modal.confirm(content).then(() => {
loading.value = true;
setOssDomainStatus({ ids: _ids, usageStatus: _usageStatus }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('域名') + typeName + proxy.t('成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'refresh':
//
row.refreshLoading = true;
refreshOssDomainAnalysis({ id: row.id }).then(res => {
proxy.$modal.msgSuccess(proxy.t('刷新成功!'));
row.refreshLoading = false;
getList();
}).catch(() => {
row.refreshLoading = false;
});
break
case 'clearCache':
//
proxy.$modal.confirm(proxy.t('确定要清理该记录的缓存吗?')).then(() => {
loading.value = true;
clearOssDomainCache({ ids: _ids }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('缓存清理成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'delete':
//
proxy.$modal.confirm(proxy.t('确定要删除该域名吗!')).then(() => {
loading.value = true;
delOssDomain(_ids.join(',')).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'edit':
//
currentDomainInfo.value = row;
isShowEdit.value = true;
break
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量清理缓存'), value: 'clearCache' },
{ label: proxy.t('批量删除'), value: 'delete' },
{ label: proxy.t('批量停用'), value: 'stop' },
{ label: proxy.t('批量启用'), value: 'enable' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
isShowBatchOp.value = true;
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
handleQuery();
}
//
const closeDialog = (type) => {
isShowAdd.value = false; //
isShowEdit.value = false; //
isShowBatchOp.value = false; //
batchOpType.value = ''; //
//
if (type === 'batchSubmit') clearBatchOp();
//
if (type === 'submit' || type === 'batchSubmit') {
getList();
}
}
//
const emit = defineEmits(['goDomainManage']);
const goDomainManage = () => {
emit('goDomainManage');
}
//
watch(() => props.searchResult, (val) => {
if (val.type == 'domainName') {
queryParams.searchType = 'name'
queryParams.name = val.domainName;
nextTick(() => {
queryParams.domainSupplierId = val.domainSupplierId;
handleQuery();
});
}
}, { deep: true, immediate: true });
</script>
<style scoped lang='scss'>
.domain-main {
flex: 1;
display: flex;
overflow: hidden;
//
.dm-left {
width: 145px;
height: 100%;
border-right: 1px solid #e5e5e5;
}
//
.dm-right {
width: calc(100% - 160px);
margin-left: 15px;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.copy-ml {
margin-left: 10px;
}
}
}
</style>

View File

@ -0,0 +1,422 @@
<template>
<!-- 支付域名列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<select-input-form :queryParamsList="queryParamsList" v-model:queryParams="queryParams" :width="[130, 260]"
@handleQuery="handleQuery"></select-input-form>
<el-form-item prop="usageStatus" style="width: 150px;">
<dict-select clearable v-model="queryParams.usageStatus" :placeholder="t('使用状态')"
dictKey="operation_domain_son_usage_status" @change="handleQuery"></dict-select>
</el-form-item>
</template>
<template #right>
<auto-refresh-time apiName="paymentDomainList" @refreshFn="getList" style="margin-right: 10px;"></auto-refresh-time>
<el-button type="success" plain icon="Plus" @click="openAddDomain" v-hasPermi="['operation:son:pay:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<div class="domain-main">
<!-- 左侧供应商 -->
<div class="dm-left">
<el-scrollbar max-height="600px">
<!-- 供应商选择 -->
<supplier-button ref="supplierRef" v-model="queryParams.domainSupplierId" @change="handleQuery"
@getCurSupplierNode="getCurSupplierNode"></supplier-button>
</el-scrollbar>
</div>
<!-- 右侧表格 -->
<div class="dm-right">
<el-alert v-if="currentNodeTips" style="margin-bottom: 15px;" :title="currentNodeTips" :closable="false"
type="warning" show-icon />
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column :label="t('CDN节点名称')" align="center" min-width="160">
<template #default="{ row }">
{{ row.cdnNodeName }}
<dict-text class="c-green" dictKey="ff_operation_son_type" :value="row.sonType" />
</template>
</el-table-column>
<el-table-column align="center" min-width="280">
<template #header>
<span>{{ t('生效域名') }}</span>
</template>
<template #default="{ row }">
{{ row.name }}<copy-icon :text="row.name" class="copy-ml"></copy-icon>
<p :class="'c-clear ' + (row.analyzeStatus ? 'c-green' : 'c-red')">{{ row.analyzeStatus ? t('已解析') :
t('待解析') }}</p>
</template>
</el-table-column>
<el-table-column align="center" min-width="220">
<template #header>
<span>{{ t('解析说明(生效状态和网速)') }}</span>
</template>
<template #default="{ row }">
<span :class="'c-clear ' + (row.analyzeInvalid ? 'c-green' : 'c-red')">{{ row.analyzeInvalid ?
t('已生效') : t('未生效') }}</span>
<el-icon @click="opInfo(row, 'refresh')" :class="row.refreshLoading ? 'c-rotate' : ''"
style="position: relative; top: 4px; margin-left: 8px;" size="16" class="c-blue">
<Refresh />
</el-icon>
</template>
</el-table-column>
<el-table-column :label="t('使用状态')" align="center" min-width="120">
<template #default="{ row }">
<dict-text :colorValues="['c-grey', 'c-orange', 'c-orange', 'c-green', 'c-red']"
dictKey="operation_domain_son_usage_status" :value="row.usageStatus" />
</template>
</el-table-column>
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" min-width="220" fixed="right">
<template #default="{ row }">
<el-button v-if="row.usageStatus == 4 && !row.mainSon" link type="primary" @click="opInfo(row, 'main')"
v-hasPermi="['operation:son:pay:main']">
{{ t('设为主域名') }}
</el-button>
<el-button v-if="row.usageStatus == 4" link type="primary" @click="opInfo(row, 'clearCache')"
v-hasPermi="['operation:son:pay:clear']">
{{ t('清理缓存') }}
</el-button>
<el-tooltip v-if="row.usageStatus == 1 || row.usageStatus == 5" effect="dark"
:content="t('提示启用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:pay:status']">
{{ t('启用') }}
</el-button>
</el-tooltip>
<el-tooltip v-if="row.usageStatus == 2 || row.usageStatus == 3 || row.usageStatus == 4" effect="dark"
:content="t('提示停用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:pay:status']">
{{ t('停用') }}
</el-button>
</el-tooltip>
<el-button v-if="row.usageStatus == 1 && row.sonType != -1 && row.sonType != -2" link type="primary"
@click="opInfo(row, 'edit')" v-hasPermi="['operation:son:pay:edit']">
{{ t('修改') }}
</el-button>
<el-button v-if="row.usageStatus == 1 || row.usageStatus == 5" link type="primary"
@click="opInfo(row, 'delete')" v-hasPermi="['operation:son:pay:remove']">
{{ t('删除') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<!-- 新增域名弹窗 -->
<add-son-domain v-if="isShowAdd" type="payment" :supplierList="supplierList" @goDomainManage="goDomainManage"
@closeDialog="closeDialog"></add-son-domain>
<!-- 修改域名弹窗 -->
<edit-son-domain v-if="isShowEdit" type="payment" :currentDomainInfo="currentDomainInfo"
@closeDialog="closeDialog"></edit-son-domain>
<!-- 批量操作弹窗 -->
<batch-operation v-if="isShowBatchOp" type="payment" :batchOpType="batchOpType" :selectionData="selectionData"
@closeDialog="closeDialog"></batch-operation>
</template>
<script setup name="PaymentList">
import {
getPayDomainList, //
setPayDomainMain, //
refreshPayDomainAnalysis, //
setPayDomainStatus, //
clearPayDomainCache, //
delPayDomain //
} from "@/api/operation/domain";
import AutoRefreshTime from "@/components/AutoRefreshTime"; //
import CopyIcon from "@/components/CopyIcon"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import SupplierButton from "../components/SupplierButton.vue"; //
import AddSonDomain from "../components/AddSonDomain.vue"; //
import EditSonDomain from "../components/EditSonDomain.vue"; //
import BatchOperation from "../components/BatchOperation.vue"; //
const props = defineProps({
//
searchResult: {
type: Object,
default: {}
}
});
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
const isShowAdd = ref(false), isShowEdit = ref(false), isShowBatchOp = ref(false); //
//
const supplierRef = ref(null), currentNodeTips = ref(''), supplierList = ref([]);
const getCurSupplierNode = (data) => {
currentNodeTips.value = data?.tips;
}
//
const queryParamsList = ref([{
label: proxy.t('操作人'),
value: 'updateBy'
}, {
label: proxy.t('子域名'),
value: 'name'
}]);
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
searchType: 'updateBy',
name: '', //
updateBy: '', //
usageStatus: '', // 使
orderByColumn: 'createTime',
isAsc: 'desc',
domainSupplierId: '' //
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
loading.value = true;
getPayDomainList(queryParams).then(res => {
loading.value = false;
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const openAddDomain = () => {
supplierList.value = supplierRef.value?.supplierList.filter(v => v.isOpen && !v.stopStatus); //
isShowAdd.value = true;
}
//
const currentDomainInfo = ref({}); //
const opInfo = (row, type) => {
const _ids = row.id ? [row.id] : row; // id
switch (type) {
case'main':
//
proxy.$modal.confirm(proxy.t('确定要设置为主域名吗?')).then(() => {
loading.value = true;
setPayDomainMain({ id: row.id }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('设置成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'status':
// /
let typeName = '', content = '', _usageStatus = '';
if (row.usageStatus == 1 || row.usageStatus == 5) {
typeName = proxy.t('启用');
content = proxy.t('确认启用该域名吗!');
_usageStatus = 4;
} else {
typeName = proxy.t('停用');
content = proxy.t('确认停用该域名吗!');
_usageStatus = 5;
}
proxy.$modal.confirm(content).then(() => {
loading.value = true;
setPayDomainStatus({ ids: _ids, usageStatus: _usageStatus }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('域名') + typeName + proxy.t('成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'refresh':
//
row.refreshLoading = true;
refreshPayDomainAnalysis({ id: row.id }).then(res => {
proxy.$modal.msgSuccess(proxy.t('刷新成功!'));
row.refreshLoading = false;
getList();
}).catch(() => {
row.refreshLoading = false;
});
break
case 'clearCache':
//
proxy.$modal.confirm(proxy.t('确定要清理该记录的缓存吗?')).then(() => {
loading.value = true;
clearPayDomainCache({ ids: _ids }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('缓存清理成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'delete':
//
proxy.$modal.confirm(proxy.t('确定要删除该域名吗!')).then(() => {
loading.value = true;
delPayDomain(_ids.join(',')).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'edit':
//
currentDomainInfo.value = row;
isShowEdit.value = true;
break
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量清理缓存'), value: 'clearCache' },
{ label: proxy.t('批量删除'), value: 'delete' },
{ label: proxy.t('批量停用'), value: 'stop' },
{ label: proxy.t('批量启用'), value: 'enable' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
isShowBatchOp.value = true;
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
handleQuery();
}
//
const closeDialog = (type) => {
isShowAdd.value = false; //
isShowEdit.value = false; //
isShowBatchOp.value = false; //
batchOpType.value = ''; //
//
if (type === 'batchSubmit') clearBatchOp();
//
if (type === 'submit' || type === 'batchSubmit') {
getList();
}
}
//
const emit = defineEmits(['goDomainManage']);
const goDomainManage = () => {
emit('goDomainManage');
}
//
watch(() => props.searchResult, (val) => {
if (val.type == 'domainName') {
queryParams.searchType = 'name'
queryParams.name = val.domainName;
nextTick(() => {
queryParams.domainSupplierId = val.domainSupplierId;
handleQuery();
});
}
}, { deep: true, immediate: true });
</script>
<style scoped lang='scss'>
.domain-main {
flex: 1;
display: flex;
overflow: hidden;
//
.dm-left {
width: 145px;
height: 100%;
border-right: 1px solid #e5e5e5;
}
//
.dm-right {
width: calc(100% - 160px);
margin-left: 15px;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.copy-ml {
margin-left: 10px;
}
}
}
</style>

View File

@ -0,0 +1,645 @@
<template>
<!-- web大厅域名列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<select-input-form :queryParamsList="queryParamsList" v-model:queryParams="queryParams" :width="[130, 260]"
@handleQuery="handleQuery"></select-input-form>
<el-form-item prop="usageStatus" style="width: 150px;">
<dict-select clearable v-model="queryParams.usageStatus" :placeholder="t('使用状态')"
dictKey="operation_domain_son_usage_status" @change="handleQuery"></dict-select>
</el-form-item>
</template>
<template #right>
<span class="right-switch" v-if="h5InterceptOpen > 0">
{{ t('H5防拦截开关') }}
<icon-tips>
<p class="c-clear" style="width: 400px;">
{{ t('说明:(大陆推荐开启) 同品牌与SEO管理--防拦截模式开关功能同步因该功能将隐藏敏感信息所以开启后普通SEO和专业SEO将失效且无法使用') }}
</p>
</icon-tips>
<base-switch style="margin: 0 12px 0 5px;" :active-value="1" :inactive-value="2" v-model="h5InterceptOpen"
:tipText="t('确认') + (h5InterceptOpen == 2 ? t('开启') : t('关闭')) + t('H5防拦截吗')"
:before-change="() => configSwitchChange('h5InterceptOpen')"
v-hasPermi="['operation:son:web:system:edit']"></base-switch>
</span>
<auto-refresh-time apiName="webDomainList" @refreshFn="getList"
style="margin-right: 10px;"></auto-refresh-time>
<el-button type="success" plain icon="Plus" @click="openAddDomain" v-hasPermi="['operation:son:web:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<div class="domain-main">
<!-- 左侧供应商 -->
<div class="dm-left">
<el-scrollbar max-height="600px">
<!-- 供应商选择 -->
<supplier-button ref="supplierRef" v-model="queryParams.domainSupplierId" @change="handleQuery"
@getCurSupplierNode="getCurSupplierNode"></supplier-button>
</el-scrollbar>
</div>
<!-- 右侧表格 -->
<div class="dm-right">
<el-alert v-if="currentNodeTips" style="margin-bottom: 15px;" :title="currentNodeTips" :closable="false"
type="warning" show-icon />
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column :label="t('CDN节点名称')" align="center" min-width="160">
<template #default="{ row }">
{{ row.cdnNodeName }}
<dict-text class="c-green" dictKey="ff_operation_son_type" :value="row.sonType" />
</template>
</el-table-column>
<el-table-column align="center" min-width="280">
<template #header>
<span>{{ t('生效域名') }}</span>
</template>
<template #default="{ row }">
{{ row.name }}<copy-icon :text="row.name" class="copy-ml"></copy-icon>
<p :class="'c-clear ' + (row.analyzeStatus ? 'c-green' : 'c-red')">{{ row.analyzeStatus ? t('已解析') :
t('待解析') }}</p>
<p v-if="row.operationDomainSonDns.length !=0">
<div v-for="(item,index) in row.operationDomainSonDns">
{{ t('DNS修改为以下服务器后刷新解析:') }} {{ item.typeValue }}:{{ item.value }}
</div>
</p>
<p v-if="row.analyzeStatu && row.operationDomainSonDns.length !=0">
<div v-for="(item,index) in row.operationDomainSonDns">
{{ t('已解析完成:') }} {{ item.typeValue }}:{{ item.value }}
</div>
</p>
</template>
</el-table-column>
<el-table-column :label="t('端口类型')" align="center" min-width="120">
<template #default="{ row }">
<dict-text dictKey="ff_operation_domain_son_port_type" :value="row.portType" />
</template>
</el-table-column>
<el-table-column :label="t('端口号')" prop="portValue" align="center" min-width="120"></el-table-column>
<el-table-column align="center" min-width="220">
<template #header>
<span>{{ t('解析说明(生效状态和网速)') }}</span>
</template>
<template #default="{ row }">
<span :class="'c-clear ' + (row.analyzeInvalid ? 'c-green' : 'c-red')">{{ row.analyzeInvalid ?
t('已生效') : t('未生效') }}</span>
<el-icon @click="opInfo(row, 'refresh')" :class="row.refreshLoading ? 'c-rotate' : ''"
style="position: relative; top: 4px; margin-left: 8px;" size="16" class="c-blue">
<Refresh />
</el-icon>
</template>
</el-table-column>
<el-table-column align="center" min-width="160">
<template #header>
<span>{{ t('其他担保域名列表') }}</span>
<icon-tips>
<p class="c-clear" style="width: 400px;">
{{ t('1.展示:即会员验证担保域名后,会展示一些站点的其他域名列表,方便会员保存记录、找到回家路或信任其他域名(推荐无封锁的国家)。') }}<br />
{{ t('2.隐藏:即会员验证担保域名后,只能看到当前域名的验证结果,看不到其他域名列表,防止域名全部暴露,有被拦截和举报的风险。') }}
</p>
</icon-tips>
</template>
<template #default="{ row }">
<base-switch :active-value="true" :inactive-value="false" v-model="row.backupDomain"
:tipText="row.status ? t('确认隐藏吗?') : t('确认展示吗?')" :disabled="row.usageStatus != 4"
:before-change="() => beforeSwitchChange(row, 'backupDomain')"></base-switch>
</template>
</el-table-column>
<el-table-column align="center" min-width="160">
<template #header>
<span>{{ t('作为推广域名') }}</span>
<icon-tips>
{{ t('开启作为前台代理推广域名显示,且建议保持一条域名开启') }}
</icon-tips>
</template>
<template #default="{ row }">
<base-switch :active-value="true" :inactive-value="false" v-model="row.marketingDomain"
:tipText="row.status ? t('确认关闭吗?') : t('确认开启吗?')"
:disabled="row.startEntrance == 2 || row.usageStatus != 4"
:before-change="() => beforeSwitchChange(row, 'marketingDomain')"></base-switch>
</template>
</el-table-column>
<el-table-column align="center" min-width="150">
<template #header>
<span>{{ t('强制绑定代理') }}</span>
<icon-tips>
<p class="c-clear" style="width: 400px;">
{{ t('代理上级归属优先级:推广链接的上级 > 渠道ID的强制绑定上级 > 域名管理的强制绑定上级(无上级时,域名管理的才生效)') }}
</p>
</icon-tips>
</template>
<template #default="{ row }">
<span v-if="row.agentAccount" class="c-blue" @click="goRoute(row, 'agentAccount')">{{
row.agentAccount }}</span>
<span v-else>{{ t('') }}</span>
</template>
</el-table-column>
<el-table-column align="center" min-width="150">
<template #header>
<span>{{ t('启用入口') }}</span>
<icon-tips>
<p class="c-clear" style="width: 400px;">
{{ t('1.全部(默认,不限制)') }} <br />
{{ t('2.仅极速APP无法直接访问网址') }} <br />
{{ t('3.H5和PWA(H5和PWA可以访问网址但不会出现在轮巡列表') }} <br />
{{ t('4.支持CDN节点阿里云,Funnull,Cloudflare,YunDun,WangSu,腾讯云,CloudFront') }}
</p>
</icon-tips>
</template>
<template #default="{ row }">
<dict-select class="w100" v-model="row.startEntrance" dictKey="ff_operation_start_entrance"
@change="changeRowStatus(row, 'startEntrance')" :disabled="row.usageStatus != 4"></dict-select>
</template>
</el-table-column>
<el-table-column align="center" min-width="150">
<template #header>
<span>{{ t('屏蔽设备') }}</span>
<icon-tips>
<p class="c-clear" style="width: 400px;">
{{ t('1.都不屏蔽(默认)') }} <br />
{{ t('2.屏蔽PC端移动端-安卓/ios可访问PC电脑端不可访问') }} <br />
{{ t('3.屏蔽手机端(PC电脑端可访问移动端-安卓/ios不可访问)') }} <br />
{{ t('4.支持CDN节点阿里云,Funnull,Cloudflare,YunDun,WangSu,腾讯云,CloudFront') }}
</p>
</icon-tips>
</template>
<template #default="{ row }">
<dict-select class="w100" v-model="row.blockDevice" dictKey="ff_operation_block_device"
@change="changeRowStatus(row, 'blockDevice')"
:disabled="row.usageStatus != 4 || row.startEntrance == 2"></dict-select>
</template>
</el-table-column>
<el-table-column :label="t('使用状态')" align="center" min-width="120">
<template #default="{ row }">
<dict-text :colorValues="['c-grey', 'c-orange', 'c-orange', 'c-green', 'c-red']"
dictKey="operation_domain_son_usage_status" :value="row.usageStatus" />
</template>
</el-table-column>
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" min-width="220" fixed="right">
<template #default="{ row }">
<el-button v-if="row.usageStatus == 4" link type="primary" @click="opInfo(row, 'agent')"
v-hasPermi="['operation:son:web:agent']">
{{ t('绑定代理') }}
</el-button>
<el-button v-if="row.usageStatus == 4" link type="primary" @click="opInfo(row, 'clearCache')"
v-hasPermi="['operation:son:web:clear']">
{{ t('清理缓存') }}
</el-button>
<el-tooltip v-if="row.usageStatus == 1 || row.usageStatus == 5" effect="dark"
:content="t('提示启用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:web:status']">
{{ t('启用') }}
</el-button>
</el-tooltip>
<el-tooltip v-if="row.usageStatus == 2 || row.usageStatus == 3 || row.usageStatus == 4" effect="dark"
:content="t('提示停用后请等待1分钟再操作')" placement="top">
<el-button link type="primary" @click="opInfo(row, 'status')"
v-hasPermi="['operation:son:web:status']">
{{ t('停用') }}
</el-button>
</el-tooltip>
<el-button v-if="row.usageStatus == 1 && row.sonType != -1 && row.sonType != -2" link type="primary"
@click="opInfo(row, 'edit')" v-hasPermi="['operation:son:web:edit']">
{{ t('修改') }}
</el-button>
<el-button v-if="row.usageStatus == 1 || row.usageStatus == 5" link type="primary"
@click="opInfo(row, 'delete')" v-hasPermi="['operation:son:web:remove']">
{{ t('删除') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<!-- 新增域名弹窗 -->
<add-son-domain v-if="isShowAdd" type="webHall" :supplierList="supplierList" @goDomainManage="goDomainManage"
@closeDialog="closeDialog"></add-son-domain>
<!-- 修改域名弹窗 -->
<edit-son-domain v-if="isShowEdit" type="webHall" :currentDomainInfo="currentDomainInfo"
@closeDialog="closeDialog"></edit-son-domain>
<!-- 绑定代理弹窗 -->
<bind-agent v-if="isShowAgent" type="webHall" :currentDomainInfo="currentDomainInfo"
@closeDialog="closeDialog"></bind-agent>
<!-- 批量操作弹窗 -->
<batch-operation v-if="isShowBatchOp" type="webHall" :batchOpType="batchOpType" :selectionData="selectionData"
@closeDialog="closeDialog"></batch-operation>
</template>
<script setup name="WebHallList">
import {
getWebDomainList, //
refreshWebDomainAnalysis, //
webDomainSwitch, //
setWebDomainStatus, //
clearWebDomainCache, //
delWebDomain, //
getWebSystemParam, //
updateWebSystemParam //
} from "@/api/operation/domain";
import AutoRefreshTime from "@/components/AutoRefreshTime"; //
import CopyIcon from "@/components/CopyIcon"; //
import IconTips from "@/components/IconTips"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import SupplierButton from "../components/SupplierButton.vue"; //
import BaseSwitch from "@/components/BaseSwitch"; //
import AddSonDomain from "../components/AddSonDomain.vue"; //
import EditSonDomain from "../components/EditSonDomain.vue"; //
import BindAgent from "../components/BindAgent.vue"; //
import BatchOperation from "../components/BatchOperation.vue"; //
const props = defineProps({
//
searchResult: {
type: Object,
default: {}
}
});
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
const isShowAdd = ref(false), isShowAgent = ref(false), isShowEdit = ref(false), isShowBatchOp = ref(false); //
// H5
const h5InterceptOpen = ref(0);
getWebSystemParam().then(res => {
h5InterceptOpen.value = +res.data.h5InterceptOpen;
});
// H5
const configSwitchChange = (type) => {
if (type == 'h5InterceptOpen') {
const params = {
h5InterceptOpen: h5InterceptOpen.value == 1 ? 2 : 1
}
updateWebSystemParam(params).then(() => {
proxy.$modal.msgSuccess(proxy.t('操作成功!'));
h5InterceptOpen.value = params.h5InterceptOpen;
}).catch(() => {
return false;
});
}
}
//
const supplierRef = ref(null), currentNodeTips = ref(''), supplierList = ref([]);
const getCurSupplierNode = (data) => {
currentNodeTips.value = data?.tips;
}
//
const queryParamsList = ref([{
label: proxy.t('操作人'),
value: 'updateBy'
}, {
label: proxy.t('子域名'),
value: 'name'
}]);
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
searchType: 'updateBy',
name: '', //
updateBy: '', //
usageStatus: '', // 使
orderByColumn: 'createTime',
isAsc: 'desc',
domainSupplierId: '' //
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
loading.value = true;
getWebDomainList(queryParams).then(res => {
loading.value = false;
//
res.rows.forEach(item => {
item.startEntrance = item.startEntrance.toString();
item.blockDevice = item.blockDevice.toString();
});
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const goRoute = (row, type) => {
proxy.$router.push({
path: '/agent/agent-info',
query: {
[type]: row.agentAccount,
searchType: type
}
})
}
//
const openAddDomain = () => {
supplierList.value = supplierRef.value?.supplierList.filter(v => v.isOpen && !v.stopStatus); //
isShowAdd.value = true;
}
//
const beforeSwitchChange = (row, type) => {
const params = {
id: row.id,
[type]: !row[type]
}
webDomainSwitch({ operationDomainSons: [params] }).then(res => {
proxy.$modal.msgSuccess(proxy.t('操作成功!'));
getList();
return true;
}).catch(() => {
return false;
});
}
//
const changeRowStatus = (row, type) => {
const params = {
id: row.id,
[type]: row[type]
}
if (type === 'startEntrance') {
//
const _label = proxy.useDict('ff_operation_start_entrance').ff_operation_start_entrance.find(v => v.value == row.startEntrance).label;
if (row.startEntrance == 2) params.marketingDomain = false; // APP广
proxy.$modal.confirm(proxy.t('是否确认修改此域名启用入口为:') + _label).then(() => {
loading.value = true;
changeSuccess(webDomainSwitch({ operationDomainSons: [params] }));
}).catch(() => {
getList();
});
} else if (type === 'blockDevice') {
//
const _label = proxy.useDict('ff_operation_block_device').ff_operation_block_device.find(v => v.value == row.blockDevice).label;
proxy.$modal.confirm(proxy.t('是否确认修改此域名屏蔽设备为:') + _label).then(() => {
loading.value = true;
changeSuccess(webDomainSwitch({ operationDomainSons: [params] }));
}).catch(() => {
getList();
});
}
//
function changeSuccess(apiFn) {
apiFn.then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('操作成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}
}
//
const currentDomainInfo = ref({}); //
const opInfo = (row, type) => {
const _ids = row.id ? [row.id] : row; // id
switch (type) {
case 'status':
// /
let typeName = '', content = '', _usageStatus = '';
if (row.usageStatus == 1 || row.usageStatus == 5) {
typeName = proxy.t('启用');
content = proxy.t('确认启用该域名吗!');
_usageStatus = 4;
} else {
typeName = proxy.t('停用');
content = proxy.t('确认停用该域名吗!');
_usageStatus = 5;
}
proxy.$modal.confirm(content).then(() => {
loading.value = true;
setWebDomainStatus({ ids: _ids, usageStatus: _usageStatus }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('域名') + typeName + proxy.t('成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'refresh':
//
row.refreshLoading = true;
refreshWebDomainAnalysis({ id: row.id }).then(res => {
proxy.$modal.msgSuccess(proxy.t('刷新成功!'));
row.refreshLoading = false;
getList();
}).catch(() => {
row.refreshLoading = false;
});
break
case 'agent':
//
currentDomainInfo.value = row;
isShowAgent.value = true;
break
case 'clearCache':
//
proxy.$modal.confirm(proxy.t('确定要清理该记录的缓存吗?')).then(() => {
loading.value = true;
clearWebDomainCache({ ids: _ids }).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('缓存清理成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'delete':
//
proxy.$modal.confirm(proxy.t('确定要删除该域名吗!')).then(() => {
loading.value = true;
delWebDomain(_ids.join(',')).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break
case 'edit':
//
currentDomainInfo.value = row;
isShowEdit.value = true;
break
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量更换端口'), value: 'port' },
{ label: proxy.t('批量清理缓存'), value: 'clearCache' },
{ label: proxy.t('批量删除'), value: 'delete' },
{ label: proxy.t('批量停用'), value: 'stop' },
{ label: proxy.t('批量启用'), value: 'enable' },
// { label: proxy.t(''), value: 'issued' },
{ label: proxy.t('批量展示其他担保列表'), value: 'showSecurity' },
{ label: proxy.t('批量隐藏其他担保列表'), value: 'hideSecurity' },
{ label: proxy.t('批量开启推广域名'), value: 'showPromotion' },
{ label: proxy.t('批量关闭推广域名'), value: 'hidePromotion' },
{ label: proxy.t('批量修改'), value: 'edit' },
{ label: proxy.t('批量绑定代理'), value: 'bindAgent' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
isShowBatchOp.value = true;
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
handleQuery();
}
//
const closeDialog = (type) => {
isShowAdd.value = false; //
isShowAgent.value = false; //
isShowEdit.value = false; //
isShowBatchOp.value = false; //
batchOpType.value = ''; //
//
if (type === 'batchSubmit') clearBatchOp();
//
if (type === 'submit' || type === 'batchSubmit') {
getList();
}
}
//
const emit = defineEmits(['goDomainManage']);
const goDomainManage = () => {
emit('goDomainManage');
}
//
watch(() => props.searchResult, (val) => {
if (val.type == 'domainName') {
queryParams.searchType = 'name'
queryParams.name = val.domainName;
nextTick(() => {
queryParams.domainSupplierId = val.domainSupplierId;
handleQuery();
});
}
}, { deep: true, immediate: true });
</script>
<style scoped lang='scss'>
.right-switch {
display: inline-block;
color: #606266;
font-size: 14px;
margin-bottom: 10px;
}
.domain-main {
flex: 1;
display: flex;
overflow: hidden;
//
.dm-left {
width: 145px;
height: 100%;
border-right: 1px solid #e5e5e5;
}
//
.dm-right {
width: calc(100% - 160px);
margin-left: 15px;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.copy-ml {
margin-left: 10px;
}
}
}
</style>

View File

@ -0,0 +1,173 @@
<template>
<!-- 新增/修改域名白名单 -->
<el-dialog :title="dialogTitle" v-model="isShowDialog" width="600px" append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100">
<el-form-item :label="t('具体域名:')" prop="name" class="mr0" v-if="currentDomainInfo.id">
<el-input v-model="formData.name" :placeholder="t('支持添加主域(test.com)或子域(a.test.com)')" clearable />
</el-form-item>
<el-form-item :label="t('具体域名:')" prop="names" class="mr0" v-else>
<el-input type="textarea" :rows="5" v-model="formData.names"
:placeholder="t('支持批量添加最多20个多个域名请换行例如\n123.com\n123.com.hk\n123.in')" />
</el-form-item>
<el-form-item :label="t('域名类型:')" prop="domainType" class="mr0" v-if="currentDomainInfo.id">
<dict-select class="w100" v-model="formData.domainType" :placeholder="t('请选择域名类型')"
dictKey="operation_domain_white_type"></dict-select>
</el-form-item>
<el-form-item :label="t('用途备注:')" prop="remark" class="mr0">
<el-input type="textarea" :rows="5" show-word-limit maxlength="500" v-model="formData.remark"
:placeholder="t('请输入备注信息')" />
</el-form-item>
<el-form-item :label="t('用途说明:')">
<span style="display: inline-block; line-height: 22px;" class="c-orange">
{{ t('因站点服务器前面设有CDN和防火墙为了防止外部域名调用访问时被拦截所以可将外部域名添加到白名单以免被拦截。') }}
</span>
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">{{ t('确 认') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="AddEditWhiteList">
import { addWhiteDomain, updateWhiteDomain } from "@/api/operation/domain";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const isShowDialog = ref(true); //
const formRef = ref();
//
const props = defineProps({
currentDomainInfo: {
type: Object,
default: {}
}
});
//
const dialogTitle = computed(() => {
if (props.currentDomainInfo.id) {
return proxy.t('修改白名单域名');
} else {
return proxy.t('新增域名白名单');
}
});
//
const formData = reactive({
name: '', //
names: '', //
domainType: '', //
remark: ''
});
//
watch(() => props.currentDomainInfo, (val) => {
if (val.id) {
formData.name = val.name;
formData.domainType = val.domainType.toString();
formData.remark = val.remark;
}
}, { immediate: true });
//
const reg = /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$/;
const rules = reactive({
name: [
{ required: true, message: proxy.t('域名不能为空'), trigger: ['blur', 'change'] },
//
{
validator: (rule, value, callback) => {
if (value) {
if (!reg.test(value)) {
callback(new Error(proxy.t('请输入正确的域名')));
} else {
callback();
}
} else {
callback();
}
},
trigger: ['blur', 'change']
}
],
names: [
{ required: true, message: proxy.t('域名不能为空'), trigger: ['blur', 'change'] },
//
{
validator: (rule, value, callback) => {
if (value) {
if (value.split('\n').length > 20) {
callback(new Error(proxy.t('最多支持20个域名')));
} else {
const arr = value.split('\n');
for (let i = 0; i < arr.length; i++) {
if (!reg.test(arr[i])) {
callback(new Error(proxy.t('请输入正确的域名')));
}
}
callback();
}
} else {
callback();
}
},
trigger: ['blur', 'change']
}
],
domainType: [
{ required: true, message: proxy.t('请选择域名类型'), trigger: 'change' }
]
});
//
const submitForm = () => {
formRef.value.validate((valid) => {
if (!valid) return;
loading.value = true;
const params = {
remark: formData.remark
};
if (props.currentDomainInfo.id) {
//
params.id = props.currentDomainInfo.id;
params.domainType = formData.domainType;
params.name = formData.name;
updateWhiteDomain(params).then(() => {
loading.value = false;
proxy.$message.success(proxy.t('修改成功'));
closeDialog('submit');
}).catch(() => {
loading.value = false;
});
} else {
//
params.names = formData.names.split('\n');
addWhiteDomain(params).then(() => {
loading.value = false;
proxy.$message.success(proxy.t('添加成功'));
closeDialog('submit');
}).catch(() => {
loading.value = false;
});
}
});
}
//
const emit = defineEmits(['closeDialog']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,108 @@
<template>
<!-- 批量操作弹窗 -->
<el-dialog :title="dialogTitle" v-model="isShowDialog" width="800px" append-to-body @close="closeDialog">
<el-scrollbar max-height="600px" v-loading="loading">
<el-table row-key="id" stripe border :data="tableList">
<el-table-column :label="t('域名')" prop="name" align="center" min-width="150"></el-table-column>
<el-table-column :label="t('域名类型')" align="center" min-width="100">
<template #default="{ row }">
<dict-select class="w100" v-model="row.domainType" dictKey="operation_domain_white_type"></dict-select>
</template>
</el-table-column>
<el-table-column :label="t('用途备注')" align="center" min-width="200">
<template #default="{ row }">
<el-input v-model="row.remark" :placeholder="t('请输入备注')" clearable></el-input>
</template>
</el-table-column>
</el-table>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">{{ t('确 认') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="BatchOperation">
import {
updateWhiteDomainBatch //
} from "@/api/operation/domain";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const isShowDialog = ref(true);
//
const props = defineProps({
selectionData: {
type: Array,
default: []
},
batchOpType: {
type: String,
default: ''
}
});
//
const tableList = ref([]), _tableList = [];
JSON.parse(JSON.stringify(props.selectionData)).forEach(item => {
item.domainType = item.domainType + '';
_tableList.push(item);
});
tableList.value = _tableList;
//
const dialogTitle = computed(() => {
if (props.batchOpType === 'edit') {
return proxy.t('批量修改');
} else {
return '';
}
});
//
const submitForm = () => {
loading.value = true;
const params = {
ids: tableList.value.map(item => item.id)
};
switch (props.batchOpType) {
case 'edit':
const _editParams = [];
params.ids.forEach(id => {
const item = tableList.value.find(v => v.id == id);
_editParams.push({
id: id,
domainType: item.domainType,
remark: item.remark
})
});
submitSuccess(updateWhiteDomainBatch({ operationDomainWhites: _editParams }), proxy.t('批量修改成功'));
break;
}
//
function submitSuccess(apiFn, msg) {
apiFn.then(() => {
loading.value = false;
proxy.$message.success(msg);
closeDialog('batchSubmit');
}).catch(() => {
loading.value = false;
});
}
}
//
const emit = defineEmits(['closeDialog']);
const closeDialog = (type) => {
emit('closeDialog', type);
}
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,217 @@
<template>
<!-- 外部白名单域名列表 -->
<table-search-card :model="queryParams" @getList="getList" :leftSpan="16" :rightSpan="8" @handleQuery="handleQuery()"
@resetQuery="resetQuery">
<template #left>
<table-search-date ref="searchDateRef" dateType="datetimerange" v-model:dateRange="dateRange"
v-model:operateTimeType="operateTimeType" @dateChange="handleQuery"></table-search-date>
<el-form-item prop="name" style="width: 200px;">
<el-input v-model="queryParams.name" :placeholder="t('请输入域名')" clearable @change="handleQuery"></el-input>
</el-form-item>
</template>
<template #right>
<el-button type="success" plain icon="Plus" @click="isShowAddEdit = true"
v-hasPermi="['operation:white:add']">{{
t('新增') }}</el-button>
</template>
</table-search-card>
<!-- 查询表格 -->
<el-table ref="tableRef" class="c-table-main" v-loading="loading" row-key="id" stripe border :data="dataList"
@select="tableSelect" @select-all="tableSelect">
<el-table-column align="center" type="selection" width="55" />
<el-table-column :label="t('域名')" prop="name" align="center" min-width="160"></el-table-column>
<el-table-column :label="t('域名类型')" align="center" min-width="120">
<template #default="{ row }">
<dict-text dictKey="operation_domain_white_type" :value="row.domainType" />
</template>
</el-table-column>
<el-table-column :label="t('用途备注')" show-overflow-tooltip prop="remark" align="center"
min-width="260"></el-table-column>
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" min-width="120">
<template #default="{ row }">
<el-button link type="primary" @click="opInfo(row, 'edit')" v-hasPermi="['operation:white:all:edit']">
{{ t('修改') }}
</el-button>
<el-button link type="primary" @click="opInfo(row, 'delete')" v-hasPermi="['operation:white:remove']">
{{ t('删除') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType" v-model:isAllSelection="isAllSelection"
@allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange" :selectionData="selectionData"
:opTypeOptions="opTypeOptions"></table-batch-operate>
<!-- 列表分页 -->
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="getList" />
<!-- 新增/修改白名单 -->
<add-edit-white-list v-if="isShowAddEdit" :currentDomainInfo="currentDomainInfo" @closeDialog="closeDialog"></add-edit-white-list>
<!-- 批量操作弹窗 -->
<batch-operation v-if="isShowBatchOp" :batchOpType="batchOpType" :selectionData="selectionData"
@closeDialog="closeDialog"></batch-operation>
</template>
<script setup name="WhiteList">
import {
getWhiteDomainList, //
delWhiteDomain, //
} from "@/api/operation/domain";
import TableSearchDate from "@/components/TableSearchDate"; //
import TableBatchOperate from '@/components/TableBatchOperate'; //
import AddEditWhiteList from "./AddEditWhiteList.vue"; // /
import BatchOperation from "./BatchOperation.vue"; //
const { proxy } = getCurrentInstance();
const loading = ref(true); // loading
const isShowAddEdit = ref(false), isShowBatchOp = ref(false); //
//
const searchDateRef = ref(), dateRange = ref([]), operateTimeType = ref('');
//
const queryParams = reactive({
pageNum: 1,
pageSize: 20,
name: '', //
orderByColumn: 'createTime',
isAsc: 'desc'
});
//
const dataList = ref([]), total = ref(0);
const getList = () => {
const params = proxy.addDateRange(queryParams, dateRange.value); //
loading.value = true;
getWhiteDomainList(params).then(res => {
loading.value = false;
dataList.value = res.rows;
total.value = +res.total;
clearBatchOp(); //
}).catch(() => {
loading.value = false;
dataList.value = [];
total.value = 0;
clearBatchOp(); //
});
}
getList();
//
const currentDomainInfo = ref({}); //
const opInfo = (row, type) => {
switch (type) {
case 'edit':
//
currentDomainInfo.value = row;
isShowAddEdit.value = true;
break;
case 'delete':
//
proxy.$modal.confirm(proxy.t('确定要删除该域名吗!')).then(() => {
loading.value = true;
delWhiteDomain(row.id).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
}).catch(() => {
loading.value = false;
});
}).catch(() => { });
break;
}
}
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量修改'), value: 'edit' },
{ label: proxy.t('批量删除'), value: 'delete' }
]);
//
const tableSelect = (val) => {
selectionData.value = val;
if (val.length === dataList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
if (batchOpType.value === 'edit') {
isShowBatchOp.value = true;
} else if (batchOpType.value === 'delete') {
proxy.$modal.confirm(proxy.t('确定要删除选中的域名吗!')).then(() => {
loading.value = true;
delWhiteDomain(selectionData.value.map(item => item.id).join(',')).then(res => {
loading.value = false;
proxy.$modal.msgSuccess(proxy.t('删除成功!'));
getList();
clearBatchOp(); //
}).catch(() => {
loading.value = false;
batchOpType.value = '';
});
}).catch(() => {
batchOpType.value = '';
});
}
}
//
const clearBatchOp = () => {
isAllSelection.value = false;
selectionData.value = [];
batchOpType.value = '';
}
//
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
}
//
const resetQuery = () => {
searchDateRef.value.dateResetQuery(); //
handleQuery();
}
//
const closeDialog = (type) => {
isShowAddEdit.value = false; //
isShowBatchOp.value = false; //
currentDomainInfo.value = {}; //
batchOpType.value = ''; //
//
if (type === 'batchSubmit') clearBatchOp();
//
if (type === 'submit' || type === 'batchSubmit') {
getList();
}
}
</script>
<style scoped lang='scss'></style>

View File

@ -0,0 +1,223 @@
<template>
<!-- 新增 -->
<el-dialog :title="addEditStatus=='add' ? t('新增') : t('修改')" align-center :close-on-click-modal="false" v-model="showDialog"
width="800px" append-to-body>
<el-scrollbar max-height="900px">
<el-form ref="formRef" :model="formAll" :rules="rules" label-width="130px" class="add-form">
<el-form-item :label="t('api平台代码')" prop="apiPlatformCode">
<el-input v-model="formAll.apiPlatformCode" />
</el-form-item>
<el-form-item :label="t('平台代码')" prop="platformCode">
<el-input v-model="formAll.platformCode" />
</el-form-item>
<el-form-item :label="t('平台展示代码')" prop="platformShowCode">
<el-input v-model="formAll.platformShowCode" />
</el-form-item>
<el-form-item :label="t('语言信息')" prop="langInfo">
<el-input v-model="formAll.langInfo" type="textarea" :rows="5" />
</el-form-item>
<el-form-item :label="t('货币信息')" prop="currencyInfo">
<el-input v-model="formAll.currencyInfo" type="textarea" :rows="5" />
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm" :loading="loadingButton">{{ t('确 定') }}</el-button>
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { formatTime,finalTimestamp } from '@/utils/ruoyi'; //
import ImageUpload from "@/components/ImageUpload";
import { updateGame } from "@/api/game/game";
import { nextTick, onMounted, ref } from "vue"; //
import { getLocalStorage } from "@/utils/auth";
const langList = getLocalStorage('langList')?.filter(v => v.langType == 1 && v.langStatus).map(item => {
return {
label: item.name,
value: item.id
};
}); //
const loadingButton = ref(false);
const oldForm = shallowRef({ });
const { proxy } = getCurrentInstance() //
const emits = defineEmits(['submit', 'update:show']) //
const props = defineProps({ //
data: {
type: Object, //
default: {}
},
show: {
type: Boolean, //
default: false
},
addEditStatus:{ // /
type: String,
default: 'add'
},
modifyDate: { //
type: Object,
default: {}
}
})
const showDialog = computed({ //
get() {
return props.show
},
set(value) {
emits('update:show', value)
}
})
const activeNameLang = ref(0);//
const langOptionAll = ref([]); // tabs
const formAll = reactive({
syncStatus:false,
})
//
nextTick(() => {
if (props.addEditStatus != 'edit') return;
Object.assign(formAll, props.modifyDate)
setTimeout(() => {
let objForm = { ...formAll }
oldForm.value = JSON.stringify(objForm);
}, 500);
});
//
const rules = reactive({
apiPlatformCode: [
{ required: true, message: '请输入平台编码', trigger: 'change' },
],
platformCode: [
{ required: true, message: '请输入游戏编码', trigger: 'change' },
],
platformShowCode: [
{ required: true, message: '请输入游戏显示编码', trigger: 'change' },
],
langInfo: [
{ required: true, message: '请输入语言信息', trigger: 'change' },
],
})
//
const closeDialog = () => {
showDialog.value = false
}
//
const formRef = ref(null)
//
const submitForm = () => {
//
formRef.value.validate(valid => {
if (valid) {
loadingButton.value = true;
//
let formData = {
...formAll
};
if (JSON.stringify(formAll) != oldForm.value) {
updateGame(formData).then(res => {
loadingButton.value = false;
proxy.$modal.msgSuccess(proxy.t('修改成功!'));
emits('submit');
closeDialog();
}).catch(() => {
loadingButton.value = false;
});
}else{
loadingButton.value = false;
closeDialog();
}
}
});
};
</script>
<style scope lang="scss">
.w100 {
width: 100%;
}
.upload-box-vioce{
height: 90px !important;
overflow: hidden;
.upload-tips {
color: #999;
font-size: 12px;
line-height: 1.5;
padding-left: 10px;
}
.component-upload-image .el-upload--picture-card {
width: 180px !important;
height: 90px !important;
}
.el-upload-list--picture-card .el-upload-list__item {
width: 180px!important;
height: 90px!important;
}
:deep(.el-upload-list) {
width:180px!important;
height: 90px!important;
.el-upload,
.el-upload-list--picture-card .el-upload-list__item {
width: 180px!important;
height: 90px!important;
}
.el-upload-list__item {
margin: 0;
border: none;
width: 180px!important;
height: 90px!important;
}
}
}
.upload-box-vioce11{
height: 400px !important;
overflow: hidden;
.upload-tips {
color: #999;
font-size: 12px;
line-height: 1.5;
padding-left: 10px;
}
.component-upload-image .el-upload--picture-card {
width: 300px !important;
height: 400px !important;
}
.el-upload-list--picture-card .el-upload-list__item {
width: 300px!important;
height: 400px!important;
}
:deep(.el-upload-list) {
width:300px!important;
height: 400px!important;
.el-upload,
.el-upload-list--picture-card .el-upload-list__item {
width: 300px!important;
height: 400px!important;
}
.el-upload-list__item {
margin: 0;
border: none;
width: 300px!important;
height: 400px!important;
}
}
}
</style>

View File

@ -0,0 +1,167 @@
<template>
<table-search-card :model="queryParams" @getList="getList" @handleQuery="handleQuery" @resetQuery="resetQuery">
<template #left>
<el-form-item prop="platformShowCode">
<el-input v-model="queryParams.platformShowCode" :placeholder="t('请输入平台')" />
</el-form-item>
</template>
<template #right>
<el-button type="primary" plain icon="Plus" @click="handleAdd" >{{ t('新增配置') }}</el-button>
</template>
</table-search-card>
<el-table v-loading="loading" :data="gameList" class="c-table-main" stripe
ref="dragTable" row-key="id" border>
<el-table-column :label="t('平台名称')" align="center" prop="platformCode" />
<el-table-column :label="t('api平台代码')" align="center" prop="apiPlatformCode" />
<el-table-column :label="t('货币信息')" align="center" prop="currencyInfo" />
<el-table-column :label="t('语言信息')" align="center" prop="langInfo" />
<table-operation></table-operation>
<el-table-column :label="t('操作')" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)"
v-hasPermi="['game:game:edit']">{{ t('修改') }}</el-button>
<el-button link type="primary" @click="handleSynchronous(scope.row)"
v-hasPermi="['game:game:sync']">{{ t('删除') }}</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="getList" />
<add-dialog v-if="isShowDialog"
:addEditStatus="addEditStatus" :modifyDate="modifyDate" @submit="getList"
v-model:show="isShowDialog"></add-dialog>
</template>
<script setup name="Game">
import AddDialog from "./AddDialog"///
import * as game from "@/api/game/game";
import { platformSelect,getGamePlatformApiSync,getGamePlatformTenantSync } from "@/api/game/platform";
import {getGameSecretList,getGameSecretInfo} from "@/api/game/configuration";
import useInitDataStore from "@/store/modules/initData";
const emits = defineEmits(["reset"])
const useInitData = useInitDataStore();
const { proxy } = getCurrentInstance();
const gameList = ref([]);
const open = ref(false);
const loading = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("");
const gameTypeOptions = ref([
{ label: '电子', value: '1' },
{ label: '棋牌', value: '2' },
{ label: '真人', value: '3' },
{ label: '捕鱼', value: '4' },
{ label: '体育', value: '6' },
{ label: '斗鸡', value: '7' },
{ label: '电竞', value: '8' },
{ label: '彩票', value: '9' },
{ label: '区块链', value: '10' },
]);
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 20,
},
rules: {
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询平台管理列表 */
function getList() {
loading.value = true;
getGameSecretList(queryParams.value).then(response => {
gameList.value = response.rows;
total.value = response.total;
loading.value = false;
});
}
//
function reset() {
form.value = {
id: null
};
proxy.resetForm("gameRef");
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
useInitData.setStateData('currencyCode', String(queryParams.value.currencyCode));
}
/** 重置按钮操作 */
function resetQuery() {
queryParams.value.gameType = useInitData.dictInitData.platformType
queryParams.value.platformId = useInitData.dictInitData.platformId
queryParams.value.pageNum = 1;
platformListInit(true)
}
const addEditStatus = ref('add'), isShowDialog = ref(false), editDataId = ref(''),modifyDate = ref({});
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
const _id = row.id || ids.value
game.getGame(_id).then(response => {
modifyDate.value = response.data;
isShowDialog.value = true;
addEditStatus.value = 'edit'
title.value = proxy.t('修改平台管理');
});
// proxy.$modal.msgError("")
}
const handleAdd = () => {
isShowDialog.value = true;
addEditStatus.value = 'add'
title.value = proxy.t('新增配置');
}
const handleSynchronous = (row) => {
proxy.$modal.confirm(proxy.t('当前设置宣传图同步到所有币种下生效')).then(() => {
game.gameGameSync({id:row.id}).then(() => {
proxy.$modal.msgSuccess(proxy.t('同步成功'));
getList();
})
}).catch(() => {})
}
// api
const handleApiGame = () => {
proxy.$modal.confirm(proxy.t('是否确认同步?')).then(() => {
getGamePlatformApiSync({}).then(() => {
proxy.$modal.msgSuccess(proxy.t('同步成功'));
getList();
})
}).catch(() => {})
}
//
const platformList = ref([])
const platformListInit = async (isFirst = false) => {
let { platformId, gameType } = queryParams.value
await platformSelect({ platformType: gameType,currencyCode:queryParams.value.currencyCode }).then(res => {
platformList.value = res.data
queryParams.value.platformId = isFirst && platformId ? platformId : res?.data?.length ? res.data[0].id : null
getList();
})
}
//
onMounted(() => {
getList();
})
</script>
<style scoped lang="scss"></style>

View File

@ -1,6 +1,6 @@
<template> <template>
<!-- 新增 --> <!-- 新增 -->
<el-dialog :title="addEditStatus=='add' ? t('新增') : t('修改')" :close-on-click-modal="false" v-model="showDialog" <el-dialog :title="addEditStatus=='add' ? t('新增') : t('修改')" align-center :close-on-click-modal="false" v-model="showDialog"
width="700px" append-to-body> width="700px" append-to-body>
<el-scrollbar max-height="900px"> <el-scrollbar max-height="900px">
<el-form ref="formRef" :model="formAll" :rules="rules" label-width="130px" class="add-form"> <el-form ref="formRef" :model="formAll" :rules="rules" label-width="130px" class="add-form">
@ -22,6 +22,8 @@
<el-form-item :label="t('icon图样式')" prop="iconStyle"> <el-form-item :label="t('icon图样式')" prop="iconStyle">
<el-radio-group v-model="formAll.iconStyle"> <el-radio-group v-model="formAll.iconStyle">
<el-radio :value="1">{{ t('默认') }}</el-radio> <el-radio :value="1">{{ t('默认') }}</el-radio>
<el-radio :value="2">{{ t('自定义') }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<div v-if="formAll.iconStyle == 1"> <div v-if="formAll.iconStyle == 1">

View File

@ -17,6 +17,7 @@
<platform v-if="activeName === 'platform'" @toGameManage="toGameManage"></platform> <platform v-if="activeName === 'platform'" @toGameManage="toGameManage"></platform>
<game v-if="activeName === 'game'"> <game v-if="activeName === 'game'">
</game> </game>
<configuration v-if="activeName === 'configuration'"></configuration>
</div> </div>
</div> </div>
</template> </template>
@ -26,12 +27,14 @@
import platform from "./platform" import platform from "./platform"
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
import game from "./game" import game from "./game"
import Configuration from "./configuration"
const platItem = ref({}) const platItem = ref({})
const activeName = ref('platform') const activeName = ref('platform')
const tabs = ref([ const tabs = ref([
{ label: proxy.t('平台管理'), name: 'platform' }, { label: proxy.t('平台管理'), name: 'platform' },
{ label: proxy.t('子游戏管理'), name: 'game' }, { label: proxy.t('子游戏管理'), name: 'game' },
{ label: proxy.t('配置管理'), name: 'configuration' },
]) ])
// //

View File

@ -4,7 +4,8 @@
@resetQuery="resetQuery"> @resetQuery="resetQuery">
<template #left> <template #left>
<!-- 待处理列表没有时间查询 --> <!-- 待处理列表没有时间查询 -->
<span v-show="listType != 'pending'"> <!-- <span v-show="listType != 'pending'">
</span> -->
<el-form-item prop="timeType"> <el-form-item prop="timeType">
<el-select v-model="queryParams.timeType" :placeholder="t('时间类型')" style="width: 150px;" <el-select v-model="queryParams.timeType" :placeholder="t('时间类型')" style="width: 150px;"
@change="handleQuery"> @change="handleQuery">
@ -14,7 +15,7 @@
</el-form-item> </el-form-item>
<table-search-date ref="searchDateRef" v-model:dateRange="dateRange" <table-search-date ref="searchDateRef" v-model:dateRange="dateRange"
v-model:operateTimeType="operateTimeType" @dateChange="handleQuery"></table-search-date> v-model:operateTimeType="operateTimeType" @dateChange="handleQuery"></table-search-date>
</span>
<el-form-item > <el-form-item >
<custom-select style="width: 130px;" v-if="siteSelect.length > 0" collapse-tags collapse-tags-tooltip v-model="queryParams.tenantId" :options="siteSelect" :placeholder="t('请选择')"></custom-select> <custom-select style="width: 130px;" v-if="siteSelect.length > 0" collapse-tags collapse-tags-tooltip v-model="queryParams.tenantId" :options="siteSelect" :placeholder="t('请选择')"></custom-select>
</el-form-item> </el-form-item>
@ -157,6 +158,8 @@ import DictSelect from "@/components/DictSelect";
import { getLocalStorage } from "@/utils/auth"; import { getLocalStorage } from "@/utils/auth";
import { moneyFormat } from "@/utils"; // import { moneyFormat } from "@/utils"; //
import DetailDialog from "./DetailDialog.vue"; // import DetailDialog from "./DetailDialog.vue"; //
import { finalTimestamp } from "@/utils/ruoyi";
const props = defineProps({ const props = defineProps({
listType: { listType: {
@ -184,14 +187,14 @@ const currencySelectArr = getLocalStorage('currencySelect')?.map(item => {
}; };
}); // }); //
// //
const searchDateRef = ref(null), dateRange = ref([]), operateTimeType = ref(''); const searchDateRef = ref(null), dateRange = ref([]), operateTimeType = ref('day');
// //
const queryParams = reactive({ const queryParams = reactive({
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 20,
tenantId: '', tenantId: '',
timeType: '', timeType: '1',
searchType: 'memberAccount', // searchType: 'memberAccount', //
memberAccount: '', memberAccount: '',
feedbackStatus: '', // 1 2- 3- feedbackStatus: '', // 1 2- 3-
@ -205,12 +208,15 @@ const queryParams = reactive({
const dataList = ref([]); const dataList = ref([]);
const total = ref(0); // const total = ref(0); //
const getList = () => { const getList = () => {
let params = {} // if (props.listType !== 'pending') { //
// params = proxy.addDateRange(queryParams, dateRange.value);
if (props.listType !== 'pending') { // // } else {
params = proxy.addDateRange(queryParams, dateRange.value); // params = queryParams;
} else { // }
params = queryParams; let params = {
...queryParams,
beginTime: finalTimestamp(dateRange.value[0]),
endTime: finalTimestamp(dateRange.value[1])
} }
loading.value = true; loading.value = true;
@ -375,14 +381,9 @@ const handleQuery = () => {
// //
const resetQuery = () => { const resetQuery = () => {
if (props.listType !== 'pending') {
// searchDateRef.value = ; //
searchDateRef.value = null;
dateRange.value = []; dateRange.value = [];
operateTimeType.value = ''; operateTimeType.value = "day";
} searchDateRef.value.timeTypeChange(operateTimeType.value)
handleQuery();
} }
const initByListType = (val) => { const initByListType = (val) => {

View File

@ -7,7 +7,10 @@
<el-input style="width: 550px;" v-model="formAll.title" placeholder="请输入标题"></el-input> <el-input style="width: 550px;" v-model="formAll.title" placeholder="请输入标题"></el-input>
</el-form-item> </el-form-item>
<el-form-item :label="t('内容')" prop="content"> <el-form-item :label="t('内容')" prop="content">
<div style="width: 600px;">
<editor v-model="formAll.content" :min-height="260"/> <editor v-model="formAll.content" :min-height="260"/>
</div>
</el-form-item> </el-form-item>
<el-form-item :label="t('公告时间')" prop="ShowStartTime"> <el-form-item :label="t('公告时间')" prop="ShowStartTime">
<div > <div >

View File

@ -154,9 +154,8 @@
<!-- <div style="width: 100%;"> <el-input style="width: 550px;" v-model="formAll.name" disabled placeholder="请输入确认安全码"></el-input></div> --> <!-- <div style="width: 100%;"> <el-input style="width: 550px;" v-model="formAll.name" disabled placeholder="请输入确认安全码"></el-input></div> -->
</el-form-item> </el-form-item>
<el-form-item :label="t('自动额度最小值')" prop="creditQuota"> <el-form-item :label="t('自动额度最小值')" prop="creditQuota">
<div style="width: 100%;"> <el-input style="width: 550px;" v-model="formAll.creditQuota" disabled placeholder="请输入"></el-input></div> <div style="width: 100%;"> <el-input style="width: 550px;" v-model="formAll.creditQuota" placeholder="请输入"></el-input></div>
</el-form-item> </el-form-item>
<el-form-item :label="t('备注')"> <el-form-item :label="t('备注')">
<el-input <el-input
v-model="formAll.remark" v-model="formAll.remark"
@ -194,6 +193,7 @@ const fileHost = getLocalStorage('fileUrl') || ''; // 文件host
const langList = getLocalStorage('langSelect')?.map(item => { const langList = getLocalStorage('langSelect')?.map(item => {
return { return {
...item,
label: item.name, label: item.name,
value: item.id value: item.id
}; };
@ -235,7 +235,7 @@ const formAll = reactive({
currencyPattern:[], currencyPattern:[],
backendDomain:'', backendDomain:'',
creditQuotaType: 1, creditQuotaType: 1,
creditQuota:10000, creditQuota:0,
siteType:0, siteType:0,
}) })
const loadingButton = ref(false) const loadingButton = ref(false)
@ -359,7 +359,7 @@ const changeLangPattern = () => {
let arr = []; let arr = [];
formAll.langPattern.map(item => { formAll.langPattern.map(item => {
let langListname = langList.find(item1 => item1.value == item); let langListname = langList.find(item1 => item1.value == item);
arr.push({ value: item, label: langListname.label }); arr.push({ value: item, label: `${langListname.label}(${langListname.langDisplay})` });
}) })
langListarr.value =arr; langListarr.value =arr;

View File

@ -24,6 +24,12 @@
{{ row.currencyCode}} {{ row.currencyCode}}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="t('状态')" align="center" min-width="120" prop="quota">
<template #default="{ row }">
<el-button v-if="row.status == 1" @click="handleEnable(row)" type="primary">{{t('')}}</el-button>
<el-button v-if="row.status == 0" @click="handleDisable(row)" type="primary">{{t('')}}</el-button>
</template>
</el-table-column>
<el-table-column :label="t('操作时间')" align="center" min-width="120" prop="currencyCode"> <el-table-column :label="t('操作时间')" align="center" min-width="120" prop="currencyCode">
<template #default="{ row }"> <template #default="{ row }">
{{ row.updateTime ? formatTime(row.updateTime): formatTime(row.createTime) }} {{ row.updateTime ? formatTime(row.updateTime): formatTime(row.createTime) }}
@ -32,7 +38,7 @@
<el-table-column :label="t('操作')" align="center" min-width="120" prop="quota"> <el-table-column :label="t('操作')" align="center" min-width="120" prop="quota">
<template #default="{ row }"> <template #default="{ row }">
<el-button @click="handleEdit(row)" type="primary">{{t('修改')}}</el-button> <el-button @click="handleEdit(row)" type="primary">{{t('修改')}}</el-button>
<el-button @click="handleDelect(row)" type="primary">{{t('删除')}}</el-button> <!-- <el-button @click="handleDelect(row)" type="primary">{{t('删除')}}</el-button> -->
</template> </template>
</el-table-column> </el-table-column>
@ -73,7 +79,7 @@ import { formatTime,finalTimestamp } from '@/utils/ruoyi'; // 时间格式化
import CustomSelect from "@/components/CustomSelect"; // import CustomSelect from "@/components/CustomSelect"; //
import { nextTick, onMounted, ref } from "vue"; // import { nextTick, onMounted, ref } from "vue"; //
import { getLocalStorage } from "@/utils/auth"; import { getLocalStorage } from "@/utils/auth";
import { postSiteBindApiGame,getSiteBindApiGameInfo,postSiteUpdateApiGame,postSiteDeleteApiGame } from "@/api/siteManagement"; import { postSiteBindApiGame,getSiteBindApiGameInfo,postSiteUpdateApiGame,postSiteDeleteApiGame,postSiteOpenApiGameInfo,postSiteStopApiGameInfo } from "@/api/siteManagement";
import { id } from 'element-plus/es/locales.mjs'; import { id } from 'element-plus/es/locales.mjs';
const currencySelectArr = getLocalStorage('currencySelect')?.map(item => { const currencySelectArr = getLocalStorage('currencySelect')?.map(item => {
@ -168,6 +174,24 @@ const handleDelect = (row) => {
}) })
}).catch(() => {}) }).catch(() => {})
} }
//
const handleDisable = (row) => {
proxy.$modal.confirm(proxy.t('确认是否停用?')).then(() => {
postSiteStopApiGameInfo({siteId:formAll.siteId,id:row.id}).then(() => {
proxy.$modal.msgSuccess(proxy.t('停用成功'));
getSiteBindApiGameInfos();
})
}).catch(() => {})
}
//
const handleEnable = (row) => {
proxy.$modal.confirm(proxy.t('确认是否启用?')).then(() => {
postSiteOpenApiGameInfo({siteId:formAll.siteId,id:row.id}).then(() => {
proxy.$modal.msgSuccess(proxy.t('启用成功'));
getSiteBindApiGameInfos();
})
}).catch(() => {})
}
// //
const closeDialog = () => { const closeDialog = () => {
showDialog.value = false showDialog.value = false

View File

@ -147,13 +147,26 @@ const currencySelectArr = getLocalStorage('currencySelect')?.map(item => {
}; };
}); });
const openView = ref(false); const openView = ref(false);
const siteList = ref(getLocalStorage('siteSelect'));
const queryParamsList = ref([{ const queryParamsList = ref([{
label: proxy.t('站点名称'), label: proxy.t('站点名称'),
value: 'siteName', value: 'siteName',
inputType: 'select',
inputConfig:{
options: siteList.value.map(item => {
return {
label: item.label,
value: item.label,
}
}),
}
},{ },{
label: proxy.t('站点ID'), label: proxy.t('站点ID'),
value: 'siteId', value: 'siteId',
inputType: 'select',
inputConfig:{
options: siteList.value,
}
}]); }]);
const data = reactive({ const data = reactive({
form: {}, form: {},

View File

@ -70,7 +70,7 @@
v-model:show="openView"></modify-dialog> v-model:show="openView"></modify-dialog>
</template> </template>
<script setup name="Agent"> <script setup name="BillingRecords">
import {getSiteFlowUserList} from "@/api/report"; import {getSiteFlowUserList} from "@/api/report";
import CustomSelect from '@/components/CustomSelect'; import CustomSelect from '@/components/CustomSelect';
import CheckboxSelect from "@/components/CheckboxSelect"; // import CheckboxSelect from "@/components/CheckboxSelect"; //

View File

@ -2,7 +2,7 @@
<table-search-card :model="queryParams" @getList="getList" @handleQuery="handleQuery" @resetQuery="resetQuery"> <table-search-card :model="queryParams" @getList="getList" @handleQuery="handleQuery" @resetQuery="resetQuery">
<template #left> <template #left>
<table-search-date v-model:dateRange="dateRange" v-model:operateTimeType="operateTimeType"></table-search-date> <table-search-date v-model:dateRange="dateRange" v-model:operateTimeType="operateTimeType"></table-search-date>
<select-input-form ref="selectInputFormRef" :queryParamsList="queryParamsList" :queryParams="queryParams" <select-input-form ref="selectInputFormRef" v-if="loadingShow" :queryParamsList="queryParamsList" :queryParams="queryParams"
@handleQuery="handleQuery"> @handleQuery="handleQuery">
</select-input-form> </select-input-form>
<el-form-item prop="changeType"> <el-form-item prop="changeType">
@ -95,7 +95,7 @@
v-model:show="openView"></modify-dialog> v-model:show="openView"></modify-dialog>
</template> </template>
<script setup name="Agent"> <script setup name="BillingRecords">
import {getSiteFlowQuotasList} from "@/api/report"; import {getSiteFlowQuotasList} from "@/api/report";
import CustomSelect from '@/components/CustomSelect'; import CustomSelect from '@/components/CustomSelect';
import CheckboxSelect from "@/components/CheckboxSelect"; // import CheckboxSelect from "@/components/CheckboxSelect"; //
@ -103,9 +103,8 @@ import TableSearchDate from '@/components/TableSearchDate'
import AddSiteDialog from "./components/AddSiteDialog"; // import AddSiteDialog from "./components/AddSiteDialog"; //
import ModifyDialog from "./components/ModifyDialog"; import ModifyDialog from "./components/ModifyDialog";
import { getLocalStorage } from "@/utils/auth"; import { getLocalStorage } from "@/utils/auth";
import Crontab from '@/components/Crontab'
import { parseTime,finalTimestamp } from '@/utils/ruoyi'; // import { parseTime,finalTimestamp } from '@/utils/ruoyi'; //
import { onMounted } from "vue";
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const agentList = ref([]); const agentList = ref([]);
@ -113,14 +112,26 @@ const loading = ref(true);
const total = ref(0); const total = ref(0);
const openView = ref(false); const openView = ref(false);
const siteList = ref(getLocalStorage('siteSelect'));
const queryParamsList = ref([{ const queryParamsList = ref([{
label: proxy.t('站点ID'), label: proxy.t('站点ID'),
value: 'siteId', value: 'siteId',
inputType: 'number', inputType: 'select',
inputConfig:{
options: siteList.value,
}
},{ },{
label: proxy.t('所属站点'), label: proxy.t('所属站点'),
value: 'siteName', value: 'siteName',
inputType: 'select',
inputConfig:{
options: siteList.value.map(item => {
return {
label: item.label,
value: item.label,
}
}),
}
}, },
{ {
label: proxy.t('编号'), label: proxy.t('编号'),
@ -191,7 +202,7 @@ total.value = response.total;
loading.value = false; loading.value = false;
}); });
} }
const loadingShow = ref(true);
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;

View File

@ -8,7 +8,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<table-search-date v-model:dateRange="dateRange" v-model:operateTimeType="operateTimeType"></table-search-date> <table-search-date v-model:dateRange="dateRange" v-model:operateTimeType="operateTimeType"></table-search-date>
<select-input-form ref="selectInputFormRef" :queryParamsList="queryParamsList" :queryParams="queryParams" <select-input-form ref="selectInputFormRef" v-if="loadingShow" :queryParamsList="queryParamsList" :queryParams="queryParams"
@handleQuery="handleQuery"> @handleQuery="handleQuery">
</select-input-form> </select-input-form>
<select-input-form ref="selectInputFormRef" :queryParamsList="queryParamsList2" keyName="searchType2" :queryParams="queryParams" <select-input-form ref="selectInputFormRef" :queryParamsList="queryParamsList2" keyName="searchType2" :queryParams="queryParams"
@ -106,7 +106,7 @@
v-model:show="openView"></modify-dialog> v-model:show="openView"></modify-dialog>
</template> </template>
<script setup name="Agent"> <script setup name="BillingRecords">
import {getSiteFlowGameCurrencyList} from "@/api/report"; import {getSiteFlowGameCurrencyList} from "@/api/report";
import CustomSelect from '@/components/CustomSelect'; import CustomSelect from '@/components/CustomSelect';
import CheckboxSelect from "@/components/CheckboxSelect"; // import CheckboxSelect from "@/components/CheckboxSelect"; //
@ -123,13 +123,26 @@ const agentList = ref([]);
const loading = ref(true); const loading = ref(true);
const total = ref(0); const total = ref(0);
const openView = ref(false); const openView = ref(false);
const siteList = ref(getLocalStorage('siteSelect'));
const queryParamsList = ref([{ const queryParamsList = ref([{
label: proxy.t('站点ID'), label: proxy.t('站点ID'),
value: 'siteId', value: 'siteId',
inputType: 'number', inputType: 'select',
inputConfig:{
options: siteList.value,
}
},{ },{
label: proxy.t('站点名称'), label: proxy.t('站点名称'),
value: 'siteName', value: 'siteName',
inputType: 'select',
inputConfig:{
options: siteList.value.map(item => {
return {
label: item.label,
value: item.label,
}
}),
}
}]); }]);
const queryParamsList2 = ref([{ const queryParamsList2 = ref([{
label: proxy.t('订单号'), label: proxy.t('订单号'),
@ -198,7 +211,7 @@ function resetQuery() {
proxy.resetForm("queryRef"); proxy.resetForm("queryRef");
handleQuery(); handleQuery();
} }
const loadingShow = ref(true);
// //
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -7,7 +7,7 @@
</el-tabs> </el-tabs>
<!-- 列表组件 --> <!-- 列表组件 -->
<div class="tabs-main-box"> <div class="tabs-main-box">
<billing-records v-if="activeName == 'BillingRecords'" v-hasPermi="['report:quotas:flow:list']"></billing-records> <billing-records v-if="activeName == 'BillingRecords'" ></billing-records>
<account-changes v-if="activeName == 'AccountChanges'"></account-changes> <account-changes v-if="activeName == 'AccountChanges'"></account-changes>
<deposit-order v-if="activeName == 'DepositOrder'"></deposit-order> <deposit-order v-if="activeName == 'DepositOrder'"></deposit-order>
</div> </div>
@ -15,7 +15,7 @@
</template> </template>
<!-- 稽核管理 --> <!-- 稽核管理 -->
<script setup name="User"> <script setup name="BillingRecords">
import BillingRecords from "./billingRecords/list.vue"; import BillingRecords from "./billingRecords/list.vue";
import AccountChanges from "./accountChanges/list.vue"; import AccountChanges from "./accountChanges/list.vue";
import DepositOrder from "./depositOrder/list.vue"; import DepositOrder from "./depositOrder/list.vue";

View File

@ -118,7 +118,7 @@
loading.value = true; loading.value = true;
await passwordForm.value.validate(); await passwordForm.value.validate();
let objForm = { let objForm = {
password:form.value.newPassword newPassword:form.value.newPassword
} }
// API // API
updateUserPassword(objForm).then(response => { updateUserPassword(objForm).then(response => {

View File

@ -0,0 +1,249 @@
<template>
<div class="app-container">
<table-search-card :model="queryParams" @getList="getList" @handleQuery="handleQuery" @resetQuery="resetQuery">
<template #left>
<el-form-item label="数据名称" prop="dataName">
<el-input
v-model="queryParams.dataName"
placeholder="请输入数据名称"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item>
</template>
<template #right>
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['site:data:source:save']"
>新增</el-button>
</template>
</table-search-card>
<el-table v-loading="loading" :data="postList" border>
<el-table-column label="数据名称" align="center" prop="dataName" />
<el-table-column label="驱动类名" align="center" prop="driverClassName" />
<el-table-column label="地址" align="center" prop="address" />
<el-table-column label="数据库用户名" align="center" prop="dbUsername" />
<el-table-column label="最大使用量" align="center" prop="maxUse" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<base-switch v-model="scope.row.status" :active-value="1" :inactive-value="0"
:before-change="() => beforeSwitchChange(scope.row, 'status')" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['site:data:source:update']"></el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改岗位对话框 -->
<el-dialog :title="title" align-center v-if="open" v-model="open" width="800px" append-to-body>
<el-form ref="postRef" :model="form" :rules="rules" label-width="150px">
<el-form-item label="数据名称" prop="dataName">
<el-input v-model="form.dataName" placeholder="请输入数据名称" />
</el-form-item>
<el-form-item label="驱动类名" prop="driverClassName">
<el-input v-model="form.driverClassName" placeholder="请输入驱动类名" />
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" placeholder="请输入地址" />
</el-form-item>
<el-form-item label="数据库端口" prop="dbPort">
<el-input v-model="form.dbPort" placeholder="请输入数据库端口" />
</el-form-item>
<el-form-item label="数据库用户名" prop="dbUsername">
<el-input v-model="form.dbUsername" placeholder="请输入数据库用户名" />
</el-form-item>
<el-form-item label="数据库密码" prop="dbPassword">
<el-input v-model="form.dbPassword" type="password" placeholder="请输入数据库密码"/>
</el-form-item>
<el-form-item label="最大使用量" prop="maxUse">
<el-input v-model="form.maxUse" placeholder="请输入最大使用量" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Post">
import { listDataSource, addDataSourceSave, updateDataSource,switchDataSource } from "@/api/system/dataSourceManagement";
import baseSwitch from "@/components/BaseSwitch";
const { proxy } = getCurrentInstance();
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const postList = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("");
const oldForm = shallowRef({ });
const data = reactive({
form: {
maxUse:2,
},
queryParams: {
pageNum: 1,
pageSize: 10,
dataName: "",
},
rules: {
dataName: [{ required: true, message: "数据名称不能为空", trigger: "change" }],
driverClassName: [{ required: true, message: "驱动类名不能为空", trigger: "change" }],
address: [{ required: true, message: "地址不能为空", trigger: "change" }],
dbPort: [{ required: true, message: "数据库端口不能为空", trigger: "change" }],
dbUsername: [{ required: true, message: "数据库用户名不能为空", trigger: "change" }],
dbPassword: [{ required: true, message: "数据库密码不能为空", trigger: "change" }],
maxUse: [{ required: true, message: "最大使用量不能为空", trigger: "change" }],
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询岗位列表 */
function getList() {
loading.value = true;
listDataSource(queryParams.value).then(response => {
postList.value = response.rows;
total.value = response.total;
loading.value = false;
});
}
/** 取消按钮 */
function cancel() {
open.value = false;
reset();
}
/** 表单重置 */
function reset() {
form.value = {
id: undefined,
dataName: undefined,
driverClassName:undefined,
address: undefined,
dbPort: undefined,
dbUsername: undefined,
dbPassword: undefined,
maxUse: 2,
};
proxy.resetForm("postRef");
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
handleQuery();
}
/** 多选框选中数据 */
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.postId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
function handleAdd() {
reset();
open.value = true;
title.value = "新增数据源";
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
const id = row.id || ids.value;
form.value = row;
open.value = true;
oldForm.value = JSON.stringify(form.value);
title.value = "修改数据源";
}
//
const beforeSwitchChange = async (row, undateKeys) => {
const _status = row[undateKeys] === 1 ? 2 : 1
const _data = {
id: row.id
}
_data[undateKeys] = _status
try {
await switchDataSource(_data).then(() => {
proxy.$modal.msgSuccess(_status === 1 ? proxy.t('开启成功') : proxy.t('关闭成功'))
getList()
})
return true;
} catch (error) {
console.error(proxy.t('接口调用失败'), error);
return false; //
}
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["postRef"].validate(valid => {
if (valid) {
if (form.value.id != undefined) {
if (JSON.stringify(form.value) != oldForm.value) {
updateDataSource(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
}else{
open.value = false;
}
} else {
addDataSourceSave(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
});
}
}
});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download("system/post/export", {
...queryParams.value
}, `post_${new Date().getTime()}.xlsx`);
}
getList();
</script>

View File

@ -29,7 +29,7 @@
<el-table-column label="权限字符" align="center" prop="roleKey" /> <el-table-column label="权限字符" align="center" prop="roleKey" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> <el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span> <span>{{ formatTime(scope.row.createTime) }}</span>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>

View File

@ -26,7 +26,7 @@ onActivated(() => {
// //
if (Object.keys(routeParams).length) { if (Object.keys(routeParams).length) {
activeName.value = routeParams.activeName || 'auditManage'; activeName.value = routeParams.activeName || 'userList';
memberAccount.value = routeParams.memberAccount || ''; memberAccount.value = routeParams.memberAccount || '';
} }

View File

@ -35,7 +35,7 @@
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<svg-icon icon-class="date" />创建日期 <svg-icon icon-class="date" />创建日期
<div class="pull-right">{{ state.user.createTime }}</div> <div class="pull-right">{{ formatTime(state.user.createTime) }}</div>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="user-info-head" @click="editCropper()"> <div class="user-info-head" @click="editCropper()">
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" /> <img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
<el-dialog :title="title" align-center v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog"> <el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
<el-row> <el-row>
<el-col :xs="24" :md="12" :style="{ height: '350px' }"> <el-col :xs="24" :md="12" :style="{ height: '350px' }">
<vue-cropper <vue-cropper
@ -63,17 +63,19 @@ import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper"; import { VueCropper } from "vue-cropper";
import { uploadAvatar } from "@/api/system/user"; import { uploadAvatar } from "@/api/system/user";
import useUserStore from "@/store/modules/user"; import useUserStore from "@/store/modules/user";
import { getLocalStorage } from "@/utils/auth";
const userStore = useUserStore(); const userStore = useUserStore();
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const fileHost = getLocalStorage('fileUrl') || ''; // host
const open = ref(false); const open = ref(false);
const visible = ref(false); const visible = ref(false);
const title = ref("修改头像"); const title = ref("修改头像");
// //
const options = reactive({ const options = reactive({
img: userStore.avatar, // img: fileHost + userStore.avatar, //
autoCrop: true, // autoCrop: true, //
autoCropWidth: 200, // autoCropWidth: 200, //
autoCropHeight: 200, // autoCropHeight: 200, //
@ -133,7 +135,7 @@ function uploadImg() {
formData.append("avatarfile", data, options.filename); formData.append("avatarfile", data, options.filename);
uploadAvatar(formData).then(response => { uploadAvatar(formData).then(response => {
open.value = false; open.value = false;
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl; options.img = response.imgUrl;
userStore.avatar = options.img; userStore.avatar = options.img;
proxy.$modal.msgSuccess("修改成功"); proxy.$modal.msgSuccess("修改成功");
visible.value = false; visible.value = false;
@ -148,7 +150,7 @@ function realTime(data) {
/** 关闭窗口 */ /** 关闭窗口 */
function closeDialog() { function closeDialog() {
options.img = userStore.avatar; options.img = fileHost + userStore.avatar;
options.visible = false; options.visible = false;
} }
</script> </script>

View File

@ -1,221 +0,0 @@
# 权限树状表格组件使用说明
## 概述
这个权限树状表格组件实现了类似表格的树形权限管理界面,左侧显示树形菜单结构,右侧显示对应的权限复选框,完全符合您截图中的设计需求。
## 文件结构
```
src/views/system/user/role/
├── render.vue # 核心权限树状表格组件
├── permission-dialog.vue # 权限选择对话框组件
├── permission-example.vue # 完整使用示例
└── README.md # 使用说明
```
## 核心功能
### 1. 树状表格展示
- 表格样式的树形结构展示
- 支持多级树形结构
- 可展开/收起节点
- 支持节点复选框选择(含半选状态)
### 2. 权限复选框管理
- 每个节点可以配置独立的权限项
- 权限项以复选框形式展示在右侧
- 支持权限项的选中状态管理
- 支持热门权限标记
### 3. 表格式布局
- 左侧:菜单名称列(支持树形缩进)
- 右侧:权限操作列(权限复选框)
- 表格头部标题
- 行分隔线和悬停效果
## 数据结构
```javascript
const treeData = [
{
id: 1, // 节点唯一标识
label: '开站管理', // 节点显示名称
children: [ // 子节点(可选)
{
id: 11,
label: '待审核',
perms: [] // 权限列表(可选)
},
{
id: 12,
label: '待付款',
perms: [ // 权限项配置
{
key: 'pay',
label: '付款',
checked: false
}
]
},
{
id: 15,
label: '已上线',
perms: [
{
key: 'add_brand',
label: '新增子品牌',
checked: false,
isHot: true // 热门权限标记
},
{
key: 'edit',
label: '修改',
checked: false
}
]
}
]
}
]
```
## 使用方法
### 1. 基础使用
```vue
<template>
<PermissionTreeTable ref="treeTableRef" />
</template>
<script setup>
import { ref } from 'vue'
import PermissionTreeTable from './render.vue'
const treeTableRef = ref()
// 获取选中的数据
const getSelectedData = () => {
if (treeTableRef.value) {
return treeTableRef.value.getCheckedData()
}
}
</script>
```
### 2. 对话框形式使用
```vue
<template>
<PermissionDialog
v-model="dialogVisible"
:role-id="currentRoleId"
@confirm="handlePermissionConfirm"
/>
</template>
<script setup>
import PermissionDialog from './permission-dialog.vue'
const dialogVisible = ref(false)
const currentRoleId = ref(1)
const handlePermissionConfirm = (data) => {
console.log('选中的权限:', data)
}
</script>
```
## 样式说明
### 1. 表格式布局
- 使用 CSS Grid 布局实现表格效果
- 左列:菜单名称(固定宽度 300px
- 右列:权限操作(自适应宽度)
- 表格头部和内容分离
### 2. 主要样式类名
- `.permission-tree-table`: 组件容器
- `.table-header`: 表格头部
- `.table-content`: 表格内容区域
- `.table-row`: 每行的容器
- `.row-left`: 左侧菜单名称区域
- `.row-right`: 右侧权限操作区域
- `.expand-icon`: 展开/收起图标
- `.hot-tag`: 热门权限标记
### 3. 核心样式
```css
.permission-tree-table {
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
}
.table-header {
display: grid;
grid-template-columns: 300px 1fr;
background: #f5f7fa;
border-bottom: 1px solid #ebeef5;
}
.table-row {
display: grid;
grid-template-columns: 300px 1fr;
border-bottom: 1px solid #ebeef5;
}
.table-row:hover {
background-color: #f5f7fa;
}
```
## API 说明
### Props
- `modelValue`: 对话框显示状态(仅对话框组件)
- `roleId`: 角色ID可选
### Events
- `confirm`: 确认选择权限时触发,返回选中的权限数据
### Methods
- `getCheckedData()`: 获取当前选中的数据
- 返回格式:
```javascript
{
checkedNodes: [ // 选中的节点
{ id: 1, label: '开站管理' }
],
checkedPermissions: { // 选中的权限
"12": [
{ key: 'pay', label: '付款' }
]
}
}
```
## 扩展功能
### 1. 权限标记
可以为特定权限添加标记(如"热门"标记):
```javascript
const isHot = perm === '新增子品牌'
// 在渲染时添加标记
h('span', { class: 'hot-tag' }, '热')
```
### 2. 权限分组
可以通过树形结构对权限进行分组管理。
### 3. 权限搜索
可以添加搜索功能来快速定位特定权限。
## 注意事项
1. 确保每个节点的 `id` 唯一
2. `perms` 数组为空时不会显示权限复选框
3. 权限状态通过 `permissionState` 响应式对象管理
4. 使用 `:deep()` 选择器来修改 Element Plus 组件的默认样式

View File

@ -70,7 +70,7 @@
</el-table-column> </el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> <el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span> <span>{{ formatTime(scope.row.createTime) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
@ -91,7 +91,7 @@
</div> </div>
</template> </template>
<script setup name="User"> <script setup name="AuthUser">
import selectUser from "./selectUser"; import selectUser from "./selectUser";
import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role"; import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
@ -108,7 +108,7 @@ const userIds = ref([]);
const queryParams = reactive({ const queryParams = reactive({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 20,
roleId: route.params.roleId, roleId: route.params.roleId,
userName: undefined, userName: undefined,
phonenumber: undefined, phonenumber: undefined,

View File

@ -1,185 +1,163 @@
<template> <template>
<el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true" label-width="68px">
<el-form-item label="角色名称" prop="roleName">
<el-input
v-model="queryParams.roleName"
placeholder="请输入角色名称"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="权限字符" prop="roleKey">
<el-input
v-model="queryParams.roleKey"
placeholder="请输入权限字符"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="角色状态"
clearable
style="width: 240px"
>
<el-option
v-for="dict in sys_normal_disable"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"></el-button>
<el-button icon="Refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['system:role:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:role:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:role:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['system:role:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<table-search-card :model="queryParams" @getList="getList" @handleQuery="handleQuery" @resetQuery="resetQuery">
<template #left>
<select-input-form ref="selectInputFormRef" :queryParamsList="queryParamsList" :queryParams="queryParams"
@handleQuery="handleQuery">
</select-input-form>
</template>
<template #right>
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:role:add']"></el-button>
</template>
</table-search-card>
<el-alert :title="t('提示: 删除权限后台账号数为0时才能删除权限')" style="margin-bottom: 10px;" :closable="false" type="warning"
show-icon />
<!-- 表格数据 --> <!-- 表格数据 -->
<el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="roleList" ref="tableRef" class="c-table-main" @select="tableSelect"
<el-table-column type="selection" width="55" align="center" /> @select-all="tableSelect" stripe border>
<el-table-column label="角色编号" prop="roleId" width="120" /> <el-table-column type="selection" width="55" :selectable="rowSelectable" align="center" />
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" /> <el-table-column label="权限名称" prop="roleName" min-width="150" align="center" >
<el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="150" /> <template #default="{ row }">
<el-table-column label="显示顺序" prop="roleSort" width="100" /> <span>{{ row.roleName }} <span v-if="row.defaultStatus == 0">()</span></span>
<el-table-column label="状态" align="center" width="100">
<template #default="scope">
<el-switch
v-model="scope.row.status"
active-value="0"
inactive-value="1"
@change="handleStatusChange(scope.row)"
></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime"> <!-- <el-table-column label="权限类型" prop="roleName" min-width="110" align="center" /> -->
<template #default="scope"> <el-table-column label="权限适用范围" prop="roleScope" min-width="150" align="center">
<span>{{ parseTime(scope.row.createTime) }}</span> <template #default="{ row }">
{{ row.roleScope || '--' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="用户列表" prop="roleSort" min-width="200">
<template #default="{ row }">
<div v-if="row.sysUsers.length > 0">
<span v-for="(item, index) in row.sysUsers.slice(0, 5)" :key="item.userId" @click="handleUserListClick(row)" style="color: rgb(64, 158, 255);cursor: pointer;">
{{ item.userName }}
<span v-if="index < Math.min(5, row.sysUsers.length) - 1">,</span>
</span>
<span v-if="row.sysUsers.length > 5"> ...</span>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column label="权限数" prop="menuCount" align="center" min-width="80">
<template #default="{ row }">
{{ row.menuCount || '--' }}
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" min-width="130">
<template #default="{ row }">
{{ row.remark || '--' }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="200" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top" v-if="scope.row.roleId !== 1"> <el-button link type="primary" @click="handleDetail(scope.row)" v-hasPermi="['system:role:edit']">{{ t('')
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button> }}</el-button>
</el-tooltip> <el-button link type="primary" v-if="scope.row.defaultStatus == 1" @click="handleUpdate(scope.row)"
<el-tooltip content="删除" placement="top" v-if="scope.row.roleId !== 1"> v-hasPermi="['system:role:edit']">{{ t('修改') }}</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button> <el-button link type="primary" @click="handleManage(scope.row)" v-hasPermi="['system:role:edit']">{{ t('')
</el-tooltip> }}</el-button>
<el-tooltip content="数据权限" placement="top" v-if="scope.row.roleId !== 1"> <el-button link type="primary" v-if="scope.row.defaultStatus == 1" @click="handleDelete(scope.row)"
<el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button> v-hasPermi="['system:role:remove']">{{ t('删除') }}</el-button>
</el-tooltip>
<el-tooltip content="分配用户" placement="top" v-if="scope.row.roleId !== 1">
<el-button link type="primary" icon="User" @click="handleAuthUser(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
</el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
<pagination @pagination="getList" />
v-show="total > 0" <!-- 批量操作 -->
:total="total" <table-batch-operate v-if="total > 0" v-model:batchOpType="batchOpType"
v-model:page="queryParams.pageNum" v-model:isAllSelection="isAllSelection" @allSelectionChange="allSelectionChange" @opTypeChange="opTypeChange"
v-model:limit="queryParams.pageSize" :selectionData="selectionData" :opTypeOptions="opTypeOptions"></table-batch-operate>
@pagination="getList"
/>
<!-- 添加或修改角色配置对话框 --> <!-- 添加或修改角色配置对话框 -->
<el-dialog :title="title" align-center v-model="open" width="860px" append-to-body> <el-dialog :title="title" align-center v-model="open" width="860px" append-to-body>
<el-scrollbar max-height="600px"> <el-scrollbar max-height="800px">
<el-form ref="roleRef" :model="form" :rules="rules" label-width="100px"> <el-form ref="roleRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="角色名称" prop="roleName"> <el-form-item label="角色名称" prop="roleName">
<el-input v-model="form.roleName" placeholder="请输入角色名称" /> <el-input v-model="form.roleName" :disabled="modifyStatus == 'detail' || modifyStatus == 'Manage'"
placeholder="请输入角色名称" maxlength="20" show-word-limit />
</el-form-item> </el-form-item>
<el-form-item prop="roleKey"> <el-form-item label="权限范围">
<template #label> <el-select disabled v-model="form.dataScope" style="width: 350px;" @change="dataScopeSelectChange">
<span> <el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label"
<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasRole('admin')`)" placement="top"> :value="item.value"></el-option>
<el-icon><question-filled /></el-icon> </el-select>
</el-tooltip>
权限字符
</span>
</template>
<el-input v-model="form.roleKey" placeholder="请输入权限字符" />
</el-form-item> </el-form-item>
<el-form-item label="角色顺序" prop="roleSort"> <el-form-item v-if="modifyStatus == 'add'" label="复制权限">
<el-input-number v-model="form.roleSort" controls-position="right" :min="0" /> <el-select v-model="form.copyRoleId" style="width: 350px;" clearable @change="changeMenu">
<el-option v-for="item in roleOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="状态"> <el-form-item label="备注">
<el-radio-group v-model="form.status"> <el-input v-model="form.remark" :disabled="modifyStatus == 'detail' || modifyStatus == 'Manage'"
<el-radio type="textarea" placeholder="请输入内容"></el-input>
v-for="dict in sys_normal_disable"
:key="dict.value"
:value="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="菜单权限"> <el-form-item v-if="modifyStatus == 'Manage'" label="添加账号">
<!-- <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">/</el-checkbox> --> <el-row style="width: 100%;">
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">/</el-checkbox> <el-col :span="16">
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')"></el-checkbox> <div style="width: 100%;">
<checkbox-select v-if="showLoding" collapse-tags collapse-tags-tooltip style="width: 100%;" v-model="accountLinking"
:options="accountOptions" :placeholder="t('请选择账号进行权限关联')"></checkbox-select>
</div>
</el-col>
<el-col :span="8" style="display: flex;justify-content: right;">
<div style="margin-left: 10px;">
<el-button type="primary" @click="handleAddAccount">{{ t('') }}</el-button>
</div>
</el-col>
</el-row>
<el-row v-if="sysUsersArr.length > 0" style="width: 100%;margin-top: 20px;">
<el-col :span="20">
<div style="width: 100%;">
<el-input v-model="userNameText" style="width: 100%" :placeholder="t('输入关键字进行用户列表过滤,支持空格隔开的多关键字匹配')" @input="handleFilter" />
</div>
</el-col>
<el-col :span="4" style="display: flex;justify-content: right;">
<div style="margin-left: 10px;">
<el-button type="primary" @click="handleRemoveAccount">{{ t('') }}</el-button>
</div>
</el-col>
</el-row>
<div class="accounts-about-scroll-container" v-if="sysUsersArr.length > 0">
<el-tag v-for="(item,index) in form.sysUsers" :key="item.userId" closable type="info" style="margin-right: 10px;" @close="handleClose(item)">
{{ item.userName }}
</el-tag>
</div>
</el-form-item>
<div style="display: flex;">
<div style="font-size: 14px;font-weight: 600;">{{ t('权限设置') }}</div>
<div style="margin-left: 10px;">{{ t('权限数') }}{{ countTree }}</div>
<div @click="handleCheckedTreeExpand($event, 'menu')"
style="cursor: pointer;color: rgb(64, 158, 255);margin-left: 10px;">
<span v-if="!defaultExpandAll">{{ t('') }}</span>
<span v-if="defaultExpandAll">{{ t('') }}</span>
</div>
</div>
<div style="width: 100%;margin-top: 10px;">
<el-input v-model="filterText" style="width: 100%" :placeholder="t('输入关键字进行权限过滤,支持空格隔开的多关键字匹配')" />
</div>
<div style="margin-top: 10px;">
<el-checkbox v-model="menuNodeAll" :disabled="modifyStatus == 'Manage' || modifyStatus == 'detail'"
@change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
<el-checkbox v-model="form.menuCheckStrictly" :disabled="modifyStatus == 'Manage' || modifyStatus == 'detail'"
@change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
<div class="table"> <div class="table">
<div class="menu-list"> <div class="menu-list">
<el-tree ref="menuRef" :data="menuOptions" default-expand-all :expand-on-click-node='false' check-on-click-node show-checkbox node-key="id" <el-tree ref="menuRef" :data="menuOptions" v-if="defaultExpandShow" :default-expand-all="defaultExpandAll"
:check-strictly="!form.menuCheckStrictly" :props="{ label: 'label', children: 'children' }"></el-tree> :expand-on-click-node='false' check-on-click-node show-checkbox node-key="id"
:check-strictly="!form.menuCheckStrictly"
:props="{ label: 'label', children: 'children', disabled: (data, node) => modifyStatus == 'Manage' || modifyStatus == 'detail', }"
:filter-node-method="filterNode"></el-tree>
</div> </div>
</div> </div>
</div>
<!-- <el-form-item label="菜单权限">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">/</el-checkbox>
<!-- <el-tree
<el-tree
class="tree-border" class="tree-border"
:data="menuOptions" :data="menuOptions"
show-checkbox show-checkbox
@ -189,21 +167,47 @@
empty-text="加载中,请稍候" empty-text="加载中,请稍候"
:render-content="renderContent" :render-content="renderContent"
:props="{ label: 'label', children: 'children' }" :props="{ label: 'label', children: 'children' }"
></el-tree> --> ></el-tree>
</el-form-item> -->
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer" style="display: flex;justify-content: center;">
<el-button v-if="modifyStatus == 'detail' || modifyStatus == 'Manage'" @click="cancel"> </el-button>
<el-button v-if="modifyStatus == 'add' || modifyStatus == 'edit'" type="primary" @click="submitForm">
</el-button>
<el-button v-if="modifyStatus == 'add' || modifyStatus == 'edit'" @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<el-dialog :title="t('用户列表')" align-center v-model="openUserList" width="1050px" append-to-body>
<el-scrollbar max-height="800px">
<el-form ref="roleRef" label-width="100px">
<el-form-item label="权限名称">
{{ formUserList.roleName }}<span v-if="formUserList.defaultStatus == 0">()</span>
</el-form-item> </el-form-item>
<el-form-item label="备注"> <el-form-item label="用户列表">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> <el-row v-if="sysUsersArr.length > 0" style="width: 100%;margin-top: 20px;">
<el-col :span="24">
<div style="width: 100%;">
<el-input v-model="userListText" style="width: 100%" :placeholder="t('输入关键字进行用户列表过滤,支持空格隔开的多关键字匹配')" @input="handleUserListFilter" />
</div>
</el-col>
</el-row>
<div class="accounts-about-scroll-container" v-if="sysUsersArr.length > 0">
<el-tag v-for="(item,index) in formUserList.sysUsers" :key="item.userId" type="info" style="margin-right: 10px;" >
{{ item.userName }}
</el-tag>
</div>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer" style="display: flex;justify-content: center;">
<el-button type="primary" @click="submitForm"> </el-button> <el-button @click="cancelUserList"> </el-button>
<el-button @click="cancel"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<!-- 分配角色数据权限对话框 --> <!-- 分配角色数据权限对话框 -->
<el-dialog :title="title" align-center v-model="openDataScope" width="500px" append-to-body> <el-dialog :title="title" align-center v-model="openDataScope" width="500px" append-to-body>
<el-form :model="form" label-width="80px"> <el-form :model="form" label-width="80px">
@ -214,30 +218,19 @@
<el-input v-model="form.roleKey" :disabled="true" /> <el-input v-model="form.roleKey" :disabled="true" />
</el-form-item> </el-form-item>
<el-form-item label="权限范围"> <el-form-item label="权限范围">
<el-select v-model="form.dataScope" @change="dataScopeSelectChange"> <el-select @change="dataScopeSelectChange">
<el-option <el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label"
v-for="item in dataScopeOptions" :value="item.value"></el-option>
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="数据权限" v-show="form.dataScope == 2"> <el-form-item label="数据权限" v-show="form.dataScope == 2">
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">/</el-checkbox> <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">/</el-checkbox>
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">/</el-checkbox> <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">/</el-checkbox>
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')"></el-checkbox> <el-checkbox v-model="form.deptCheckStrictly"
<el-tree @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
class="tree-border" <el-tree class="tree-border" :data="deptOptions" show-checkbox default-expand-all ref="deptRef" node-key="id"
:data="deptOptions" :check-strictly="!form.deptCheckStrictly" empty-text="加载中,请稍候"
show-checkbox :props="{ label: 'label', children: 'children' }"></el-tree>
default-expand-all
ref="deptRef"
node-key="id"
:check-strictly="!form.deptCheckStrictly"
empty-text="加载中,请稍候"
:props="{ label: 'label', children: 'children' }"
></el-tree>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -250,13 +243,16 @@
</template> </template>
<script setup name="User"> <script setup name="User">
import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from "@/api/system/role"; import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect, optionselect, getMenu, unallocatedUserList,authUserSelectAll,authUserCancelAll,authUserCancel,authRolecancelAll } from "@/api/system/role";
import { roleMenuTreeselect, treeselect as menuTreeselect } from "@/api/system/menu"; import { roleMenuTreeselect, treeselect as menuTreeselect } from "@/api/system/menu";
import Render from "./render.vue"; import NumberInput from "@/components/NumberInput";
import TableBatchOperate from '@/components/TableBatchOperate'; //
import CheckboxSelect from "@/components/CheckboxSelect"; // /
import { nextTick, ref } from "vue";
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const { sys_normal_disable } = proxy.useDict("sys_normal_disable"); const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
import { getLocalStorage } from "@/utils/auth";
const roleList = ref([]); const roleList = ref([]);
const open = ref(false); const open = ref(false);
const loading = ref(true); const loading = ref(true);
@ -276,6 +272,23 @@ const deptOptions = ref([]);
const openDataScope = ref(false); const openDataScope = ref(false);
const menuRef = ref(null); const menuRef = ref(null);
const deptRef = ref(null); const deptRef = ref(null);
const filterText = ref('');
const modifyStatus = ref('add');
const accountLinking = ref([]);
const accountOptions = ref([]);
const sysUsersArr = ref([]);
const formUserList = ref({});
const oldForm = shallowRef({ });
const queryParamsList = ref([{
label: proxy.t('角色名称'),
value: 'roleName',
}, {
label: proxy.t('后台账户'),
value: 'account',
}, {
label: proxy.t('后台呢称'),
value: 'nickName',
}])
/** 数据范围选项*/ /** 数据范围选项*/
const dataScopeOptions = ref([ const dataScopeOptions = ref([
@ -289,7 +302,8 @@ const data = reactive({
form: {}, form: {},
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 20,
searchType: 'roleName',
roleName: undefined, roleName: undefined,
roleKey: undefined, roleKey: undefined,
status: undefined status: undefined
@ -297,12 +311,73 @@ const data = reactive({
rules: { rules: {
roleName: [{ required: true, message: "角色名称不能为空", trigger: "blur" }], roleName: [{ required: true, message: "角色名称不能为空", trigger: "blur" }],
roleKey: [{ required: true, message: "权限字符不能为空", trigger: "blur" }], roleKey: [{ required: true, message: "权限字符不能为空", trigger: "blur" }],
roleSort: [{ required: true, message: "角色顺序不能为空", trigger: "blur" }] roleSort: [{ required: true, message: "角色顺序不能为空", trigger: "blur" }],
roleLevel: [{ required: true, message: "角色级别不能为空", trigger: "blur" }],
}, },
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
//
const tableRef = ref(),
isAllSelection = ref(false), //
selectionData = ref([]), //
batchOpType = ref(''); //
//
const opTypeOptions = ref([
{ label: proxy.t('批量清除关联账户'), value: 'update' },
{ label: proxy.t('批量删除'), value: 'ignore' },
]);
//
const tableSelect = (val) => {
selectionData.value = val;
//
if (val.length == roleList.value.length) {
isAllSelection.value = true;
} else {
isAllSelection.value = false;
}
}
//
const allSelectionChange = () => {
tableRef.value.toggleAllSelection();
}
//
const opTypeChange = () => {
if (batchOpType.value === 'ignore') {
const params = selectionData.value.map(i => i.roleId); // id
proxy.$modal.confirm(proxy.t('确认批量删除已选的' + params.length + '条记录吗?')).then(() => {
loading.value = true;
delRole(params).then(res => {
loading.value = false;
batchOpType.value = '';
proxy.$modal.msgSuccess(proxy.t('操作成功!'));
handleQuery();
}).catch(() => {
batchOpType.value = '';
loading.value = false;
});
}).catch(() => {
batchOpType.value = '';
});
}else if (batchOpType.value === 'update') {
const params = selectionData.value.map(i => i.roleId); // id
proxy.$modal.confirm(proxy.t(`确认清除这些权限的关联账号(共${params.length}个)?`)).then(() => {
loading.value = true;
authRolecancelAll({roleIds: params}).then(res => {
loading.value = false;
batchOpType.value = '';
proxy.$modal.msgSuccess(proxy.t('操作成功!'));
handleQuery();
}).catch(() => {
batchOpType.value = '';
loading.value = false;
});
}).catch(() => {
batchOpType.value = '';
});
}
}
/** 查询角色列表 */ /** 查询角色列表 */
function getList() { function getList() {
loading.value = true; loading.value = true;
@ -313,6 +388,31 @@ function getList() {
}); });
} }
// el-tree filter
watch(filterText, (val) => {
menuRef.value && menuRef.value.filter(val);
});
const rowSelectable = (row, index) => {
return row.defaultStatus == 0 ? false:true;
}
const openUserList = ref(false);
//
const handleUserListClick = (row) => {
openUserList.value = true;
sysUsersArr.value = row.sysUsers;
formUserList.value = row;
};
const cancelUserList = () => {
openUserList.value = false;
}
//
const filterNode = (value, data) => {
if (!value) return true;
//
const keywords = value.split(" ");
return keywords.every((kw) => data.label.includes(kw));
};
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
@ -382,14 +482,115 @@ function handleCommand(command, row) {
function handleAuthUser(row) { function handleAuthUser(row) {
router.push("/system/role-auth/user/" + row.roleId); router.push("/system/role-auth/user/" + row.roleId);
} }
const countTree = ref(0);
/** 查询菜单树结构 */ /** 查询菜单树结构 */
function getMenuTreeselect() { function getMenuTreeselect() {
menuTreeselect().then(response => { menuTreeselect().then(response => {
countTree.value = response.count;
menuOptions.value = response.data; menuOptions.value = response.data;
}); });
} }
const showLoding = ref(true);
/** 获取用户列表 */
const getunallocatedUserList = (roleId) => {
unallocatedUserList({roleId: roleId }).then(response => {
accountOptions.value = response.data.map(item => {
return {
...item,
label: item.userName,
value: item.userId
}
});
showLoding.value = false;
nextTick(() => {
showLoding.value = true;
})
});
}
//
const handleAddAccount = () => {
if (accountLinking.value.length == 0){
proxy.$modal.msgError(proxy.t('请选择要关联的账户'));
return;
}
proxy.$modal.confirm('请确认此操作,是否继续?').then(function () {
let obj = {
roleId:form.value.roleId,
userIds:accountLinking.value
}
return authUserSelectAll(obj);
}).then(() => {
getRole(form.value.roleId).then(response => {
form.value = response.data;
sysUsersArr.value = response.data.sysUsers;
});
accountLinking.value = [];
getunallocatedUserList(form.value.roleId);
handleQuery();
proxy.$modal.msgSuccess("关联成功");
}).catch(() => { });
}
const userNameText = ref('');
//2
const handleFilter = () => {
if (!userNameText.value.trim()) return form.value.sysUsers = sysUsersArr.value
const keywords = userNameText.value.trim().split(/\s+/) //
form.value.sysUsers = sysUsersArr.value.filter(user =>
keywords.every(k => user.userName.includes(k))
)
}
const userListText = ref('');
//
const handleUserListFilter = () => {
if (!userListText.value.trim()) return formUserList.value.sysUsers = sysUsersArr.value;
const keywords = userListText.value.trim().split(/\s+/) //
formUserList.value.sysUsers = sysUsersArr.value.filter(user =>
keywords.every(k => user.userName.includes(k))
)
}
//
const handleRemoveAccount = () => {
if (form.value.sysUsers.length == 0){
proxy.$modal.msgError("请选择要移出的账户");
return
}
proxy.$modal.confirm('请确认此操作,是否继续?').then(function () {
let userIds = form.value.sysUsers.map(item => item.userId);
let obj = {
roleId:form.value.roleId,
userIds:userIds
}
return authUserCancelAll(obj);
}).then(() => {
getRole(form.value.roleId).then(response => {
form.value = response.data;
sysUsersArr.value = response.data.sysUsers;
});
handleQuery();
getunallocatedUserList(form.value.roleId);
proxy.$modal.msgSuccess("移出成功");
}).catch(() => { });
}
//
const handleClose = (row) => {
proxy.$modal.confirm('请确认此操作,是否继续?').then(function () {
let userIds = form.value.sysUsers.map(item => item.userId);
let obj = {
roleId:form.value.roleId,
userId:row.userId
}
return authUserCancel(obj);
}).then(() => {
getRole(form.value.roleId).then(response => {
form.value = response.data;
sysUsersArr.value = response.data.sysUsers;
});
handleQuery();
getunallocatedUserList(form.value.roleId);
proxy.$modal.msgSuccess("移出成功");
}).catch(() => { });
}
/** 所有部门节点数据 */ /** 所有部门节点数据 */
function getDeptAllCheckedKeys() { function getDeptAllCheckedKeys() {
// //
@ -423,15 +624,94 @@ function reset() {
}; };
proxy.resetForm("roleRef"); proxy.resetForm("roleRef");
} }
const roleOptions = ref([]);
/** 添加角色 */ /** 添加角色 */
function handleAdd() { function handleAdd() {
reset(); reset();
getMenuTreeselect(); getMenuTreeselect();
open.value = true; open.value = true;
title.value = "添加角色"; title.value = "新增角色";
modifyStatus.value = 'add';
form.value.dataScope = getLocalStorage('userInfo')?.nickName;
optionselect({}).then(response => {
roleOptions.value = response.data.map(item => {
return {
...item,
label: item.roleName,
value: item.roleId
}
});
})
} }
//
const handleDetail = (row) => {
reset();
const roleId = row.roleId || ids.value;
const roleMenu = getRoleMenuTreeselect(roleId);
getRole(roleId).then(response => {
form.value = response.data;
form.value.roleSort = Number(form.value.roleSort);
open.value = true;
nextTick(() => {
roleMenu.then((res) => {
let checkedKeys = res.checkedKeys;
checkedKeys.forEach((v) => {
nextTick(() => {
menuRef.value.setChecked(v, true, false);
});
});
});
});
form.value.dataScope = response.data.roleScope;
modifyStatus.value = 'detail';
title.value = "角色详情";
});
}
//
const handleManage = (row) => {
reset();
const roleId = row.roleId || ids.value;
const roleMenu = getRoleMenuTreeselect(roleId);
getunallocatedUserList(roleId);
getRole(roleId).then(response => {
form.value = response.data;
sysUsersArr.value = response.data.sysUsers;
form.value.roleSort = Number(form.value.roleSort);
open.value = true;
nextTick(() => {
roleMenu.then((res) => {
let checkedKeys = res.checkedKeys;
checkedKeys.forEach((v) => {
nextTick(() => {
menuRef.value.setChecked(v, true, false);
});
});
});
});
form.value.dataScope = response.data.roleScope;
modifyStatus.value = 'Manage';
title.value = "角色管理";
form.value.roleId = row.roleId;
});
}
//
const changeMenu = (roleId) => {
if (roleId) {
getMenu(roleId).then(response => {
let checkedKeys = response.data;
checkedKeys.forEach((v) => {
nextTick(() => {
menuRef.value.setChecked(v, true, false);
});
});
});
} else {
menuRef.value.setCheckedKeys([]);
}
}
/** 修改角色 */ /** 修改角色 */
function handleUpdate(row) { function handleUpdate(row) {
reset(); reset();
@ -451,6 +731,13 @@ function handleUpdate(row) {
}); });
}); });
}); });
setTimeout(() => {
form.value.menuIds = getMenuAllCheckedKeys();
oldForm.value = JSON.stringify(form.value);
}, 600);
form.value.dataScope = response.data.roleScope;
modifyStatus.value = 'edit';
title.value = "修改角色"; title.value = "修改角色";
}); });
} }
@ -470,14 +757,25 @@ function getDeptTree(roleId) {
return response; return response;
}); });
} }
const defaultExpandAll = ref(false);
const defaultExpandShow = ref(true);
/** 树权限(展开/折叠)*/ /** 树权限(展开/折叠)*/
function handleCheckedTreeExpand(value, type) { function handleCheckedTreeExpand(value, type) {
if (type == "menu") { if (type == "menu") {
let treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) { // let treeList = menuOptions.value;
menuRef.value.store.nodesMap[treeList[i].id].expanded = value; // for (let i = 0; i < treeList.length; i++) {
// menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
// }
if (!defaultExpandAll.value) {
defaultExpandAll.value = true;
} else {
defaultExpandAll.value = false;
} }
defaultExpandShow.value = false;
nextTick(() => {
defaultExpandShow.value = true;
});
} else if (type == "dept") { } else if (type == "dept") {
let treeList = deptOptions.value; let treeList = deptOptions.value;
for (let i = 0; i < treeList.length; i++) { for (let i = 0; i < treeList.length; i++) {
@ -520,14 +818,32 @@ function submitForm() {
if (valid) { if (valid) {
if (form.value.roleId != undefined) { if (form.value.roleId != undefined) {
form.value.menuIds = getMenuAllCheckedKeys(); form.value.menuIds = getMenuAllCheckedKeys();
updateRole(form.value).then(response => {
if (JSON.stringify(form.value) != oldForm.value) {
let params = {
menuIds: form.value.menuIds,
roleName: form.value.roleName,
remark: form.value.remark,
roleSort: form.value.roleSort,
roleId: form.value.roleId,
};
updateRole(params).then(response => {
proxy.$modal.msgSuccess("修改成功"); proxy.$modal.msgSuccess("修改成功");
open.value = false; open.value = false;
getList(); getList();
}); });
}else{
open.value = false;
}
} else { } else {
form.value.menuIds = getMenuAllCheckedKeys(); form.value.menuIds = getMenuAllCheckedKeys();
addRole(form.value).then(response => { let params = {
menuIds: form.value.menuIds,
roleName: form.value.roleName,
remark: form.value.remark,
roleSort: form.value.roleSort,
};
addRole(params).then(response => {
proxy.$modal.msgSuccess("新增成功"); proxy.$modal.msgSuccess("新增成功");
open.value = false; open.value = false;
getList(); getList();
@ -587,8 +903,10 @@ function cancelDataScope() {
openDataScope.value = false; openDataScope.value = false;
reset(); reset();
} }
//
onMounted(() => {
getList(); getList();
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.custom-node-row { .custom-node-row {
@ -617,6 +935,7 @@ getList();
width: 240px; width: 240px;
color: #aaa; color: #aaa;
} }
.table { .table {
// padding: 20px; // padding: 20px;
// min-height: 88vh; // min-height: 88vh;
@ -624,39 +943,51 @@ getList();
width: 100%; width: 100%;
// margin: 20px; // margin: 20px;
} }
.menu-list { .menu-list {
::v-deep .el-tree { ::v-deep .el-tree {
border-top: 1px solid #ebeef5; border-top: 1px solid #ebeef5;
border-left: 1px solid #ebeef5; border-left: 1px solid #ebeef5;
border-right: 1px solid #ebeef5; border-right: 1px solid #ebeef5;
.el-tree-node__expand-icon { .el-tree-node__expand-icon {
// display: none !important; // display: none !important;
} }
.el-tree-node { .el-tree-node {
&.is-expanded, &.is-expanded,
&.is-current, &.is-current,
&.is-focusable { &.is-focusable {
background-color: transparent !important; background-color: transparent !important;
} }
.el-tree-node__content { .el-tree-node__content {
background-color: transparent !important; background-color: transparent !important;
&:hover { &:hover {
background-color: transparent !important; background-color: transparent !important;
} }
} }
} }
>.el-tree-node { >.el-tree-node {
display: flex; display: flex;
align-items: center; align-items: center;
border-bottom: 1px solid #ebeef5; border-bottom: 1px solid #ebeef5;
position: relative; position: relative;
padding-top: 10px;
padding-bottom: 10px;
>.el-tree-node__content { >.el-tree-node__content {
width: 300px; width: 300px;
height: 100%; height: 100%;
padding-left: 20px !important; padding-left: 20px !important;
} }
.el-tree-node__children { .el-tree-node__children {
position: relative; position: relative;
&::after { &::after {
content: ' '; content: ' ';
display: block; display: block;
@ -667,6 +998,7 @@ getList();
left: 0px; left: 0px;
top: 0px; top: 0px;
} }
.el-tree-node { .el-tree-node {
// width: 350px; // width: 350px;
padding: 10px 0px; padding: 10px 0px;
@ -678,4 +1010,16 @@ getList();
} }
} }
} }
.accounts-about-scroll-container {
align-items: flex-start;
content-visibility: auto;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin-top: 10px;
max-height: 100px;
overflow: auto;
width: 100%;
}
</style> </style>

View File

@ -1,78 +0,0 @@
<template>
<el-dialog
v-model="dialogVisible"
align-center
title="新增权限"
width="800px"
:before-close="handleClose"
>
<div class="permission-dialog">
<PermissionTreeTable ref="treeTableRef" />
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleCancel"></el-button>
<el-button type="primary" @click="handleConfirm"></el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, computed } from 'vue'
import PermissionTreeTable from './render.vue'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
roleId: {
type: [String, Number],
default: null
}
})
const emit = defineEmits(['update:modelValue', 'confirm'])
const treeTableRef = ref()
const dialogVisible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
//
const getSelectedPermissions = () => {
if (treeTableRef.value) {
return treeTableRef.value.getCheckedData()
}
return { checkedNodes: [], checkedPermissions: {} }
}
const handleClose = () => {
dialogVisible.value = false
}
const handleCancel = () => {
dialogVisible.value = false
}
const handleConfirm = () => {
const selectedData = getSelectedPermissions()
emit('confirm', selectedData)
dialogVisible.value = false
}
</script>
<style scoped>
.permission-dialog {
max-height: 500px;
overflow-y: auto;
}
.dialog-footer {
text-align: right;
}
</style>

View File

@ -1,195 +0,0 @@
<template>
<div class="permission-example">
<div class="header">
<h2>权限树使用示例</h2>
<el-button type="primary" @click="showPermissionDialog">
新增权限
</el-button>
</div>
<!-- 权限对话框 -->
<PermissionDialog
v-model="dialogVisible"
:role-id="currentRoleId"
@confirm="handlePermissionConfirm"
/>
<!-- 直接展示权限树状表格 -->
<div class="tree-container">
<h3>权限树状表格展示</h3>
<PermissionTreeTable ref="treeTableRef" />
<div class="action-buttons">
<el-button @click="getSelectedData"></el-button>
<el-button type="primary" @click="savePermissions"></el-button>
</div>
</div>
<!-- 已选权限展示 -->
<div v-if="selectedData" class="selected-permissions">
<h3>已选择的权限</h3>
<div class="permission-summary">
<div class="section">
<h4>选中的节点</h4>
<div class="node-list">
<el-tag
v-for="node in selectedData.checkedNodes"
:key="node.id"
size="small"
class="node-tag"
>
{{ node.label }}
</el-tag>
</div>
</div>
<div class="section">
<h4>选中的权限</h4>
<div v-for="(perms, nodeId) in selectedData.checkedPermissions" :key="nodeId" class="node-permissions">
<strong>节点 {{ nodeId }}:</strong>
<el-tag
v-for="perm in perms"
:key="perm.key"
size="small"
type="success"
class="perm-tag"
>
{{ perm.label }}
</el-tag>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import PermissionDialog from './permission-dialog.vue'
import PermissionTreeTable from './render.vue'
const dialogVisible = ref(false)
const currentRoleId = ref(1)
const selectedData = ref(null)
const treeTableRef = ref()
const showPermissionDialog = () => {
dialogVisible.value = true
}
const handlePermissionConfirm = (data) => {
console.log('确认选择的权限:', data)
selectedData.value = data
// API
// await saveRolePermissions(currentRoleId.value, data)
}
const getSelectedData = () => {
if (treeTableRef.value) {
const data = treeTableRef.value.getCheckedData()
selectedData.value = data
console.log('当前选中的数据:', data)
}
}
const savePermissions = async () => {
const data = getSelectedData()
if (data) {
try {
// API
// await saveRolePermissions(currentRoleId.value, data)
console.log('保存权限成功:', data)
//
// ElMessage.success('')
} catch (error) {
console.error('保存权限失败:', error)
// ElMessage.error('')
}
}
}
</script>
<style scoped>
.permission-example {
padding: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.tree-container {
margin-bottom: 30px;
padding: 20px;
border: 1px solid #e4e7ed;
border-radius: 4px;
}
.action-buttons {
margin-top: 20px;
text-align: right;
}
.action-buttons .el-button {
margin-left: 10px;
}
.selected-permissions {
margin-top: 20px;
padding: 20px;
background: #f9f9f9;
border-radius: 4px;
}
.permission-summary {
margin-top: 15px;
}
.section {
margin-bottom: 20px;
}
.section h4 {
margin: 0 0 10px 0;
color: #303133;
font-size: 14px;
font-weight: 500;
}
.node-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.node-permissions {
margin-bottom: 15px;
padding: 10px;
background: white;
border-radius: 4px;
border: 1px solid #e4e7ed;
}
.node-permissions strong {
display: block;
margin-bottom: 8px;
color: #303133;
font-size: 13px;
}
.node-tag,
.perm-tag {
margin-right: 8px;
margin-bottom: 4px;
}
.node-tag {
background-color: #409eff;
border-color: #409eff;
}
</style>

View File

@ -1,588 +0,0 @@
<template>
<div class="table">
<!-- default-expand-all 默认展开全部数据 -->
<!-- expand-on-click-node 只有点击箭头才会收缩节点 -->
<!-- check-on-click-node 点击文本选中 -->
<!-- show-checkbox 复选框 -->
<div class="menu-list">
<el-tree ref="tree" :data="list" default-expand-all :expand-on-click-node='false' check-on-click-node show-checkbox node-key="menuId" :props="defaultProps"></el-tree>
</div>
<el-button @click="getCheckedKeys"> key </el-button>
</div>
</template>
<script>
export default {
data () {
return {
list: [
{
"menuId": 1,
"menuName": "首页(首页)",
"menuCode": "首页",
"parentId": 0,
"orderNum": 0,
"path": "/page",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 0,
"visible": 1,
"perms": null,
"icon": "iconfont icon-shouye",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 1,
"children": [
{
"menuId": 2,
"menuName": "首页(首页)",
"menuCode": "首页",
"parentId": 1,
"orderNum": 1,
"path": "/welcome",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
}
]
},
{
"menuId": 3,
"menuName": "随访数据查询(随访数据查询)",
"menuCode": "随访数据查询",
"parentId": 0,
"orderNum": 1,
"path": "/followUpDataQuery",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 0,
"visible": 1,
"perms": null,
"icon": "iconfont icon-shengwuxuejiance",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 1,
"children": [
{
"menuId": 4,
"menuName": "随访数据查询(随访数据查询)",
"menuCode": "随访数据查询",
"parentId": 3,
"orderNum": 1,
"path": "index",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
},
{
"menuId": 5,
"menuName": "随访详情(随访详情)",
"menuCode": "随访详情",
"parentId": 3,
"orderNum": 2,
"path": "detail",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
}
]
},
{
"menuId": 21,
"menuName": "此单名称(11)",
"menuCode": "11",
"parentId": 0,
"orderNum": 1,
"path": "请问请问饿```",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 0,
"visible": 1,
"perms": null,
"icon": "321",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 1,
// "children": null
},
{
"menuId": 6,
"menuName": "调查数据查询(调查数据查询)",
"menuCode": "调查数据查询",
"parentId": 0,
"orderNum": 2,
"path": "/surveyDataQuery",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 0,
"visible": 1,
"perms": null,
"icon": "iconfont icon-shaichaxinxichaxun",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 1,
"children": [
{
"menuId": 7,
"menuName": "调查数据查询(调查数据查询)",
"menuCode": "调查数据查询",
"parentId": 6,
"orderNum": 1,
"path": "index",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
},
{
"menuId": 8,
"menuName": "新建问卷(新建问卷)",
"menuCode": "新建问卷",
"parentId": 6,
"orderNum": 2,
"path": "evaluationreport",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
},
{
"menuId": 9,
"menuName": "随访详情(随访详情)",
"menuCode": "随访详情",
"parentId": 6,
"orderNum": 3,
"path": "evaluationlook",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
}
]
},
{
"menuId": 10,
"menuName": "任务配置(任务配置)",
"menuCode": "任务配置",
"parentId": 0,
"orderNum": 3,
"path": "/taskSet",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 0,
"visible": 1,
"perms": null,
"icon": "iconfont icon-renwuguanli",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 1,
"children": [
{
"menuId": 11,
"menuName": "任务配置(任务配置)",
"menuCode": "任务配置",
"parentId": 10,
"orderNum": 1,
"path": "index",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
}
]
},
{
"menuId": 12,
"menuName": "数据统计(数据统计)",
"menuCode": "数据统计",
"parentId": 0,
"orderNum": 4,
"path": "/dataStatistics",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 0,
"visible": 1,
"perms": null,
"icon": "iconfont icon-shujutongji",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 1,
"children": [
{
"menuId": 13,
"menuName": "数据统计(数据统计)",
"menuCode": "数据统计",
"parentId": 12,
"orderNum": 1,
"path": "index",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
}
]
},
{
"menuId": 14,
"menuName": "数据导出(数据导出)",
"menuCode": "数据导出",
"parentId": 0,
"orderNum": 5,
"path": "/dataExport",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 0,
"visible": 1,
"perms": null,
"icon": "iconfont icon-suifangrenwuguanli",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 1,
"children": [
{
"menuId": 15,
"menuName": "数据导出(数据导出)",
"menuCode": "数据导出",
"parentId": 14,
"orderNum": 1,
"path": "index",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
}
]
},
{
"menuId": 16,
"menuName": "系统管理(系统管理)",
"menuCode": "系统管理",
"parentId": 0,
"orderNum": 6,
"path": "/systemManage",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 0,
"visible": 1,
"perms": null,
"icon": "iconfont icon-xitongguanli",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 1,
"children": [
{
"menuId": 17,
"menuName": "用户管理(用户管理)",
"menuCode": "用户管理",
"parentId": 16,
"orderNum": 1,
"path": "usersManage",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
},
{
"menuId": 18,
"menuName": "角色管理(角色管理)",
"menuCode": "角色管理",
"parentId": 16,
"orderNum": 2,
"path": "roleManage",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
},
{
"menuId": 19,
"menuName": "权限配置(权限配置)",
"menuCode": "权限配置",
"parentId": 16,
"orderNum": 3,
"path": "permissionSetting",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
},
{
"menuId": 20,
"menuName": "菜单管理(菜单管理)",
"menuCode": "菜单管理",
"parentId": 16,
"orderNum": 4,
"path": "menuManage",
"component": null,
"query": null,
"isFrame": 0,
"isCache": 0,
"menuType": 1,
"visible": 1,
"perms": null,
"icon": "",
"status": 1,
"remark": null,
"roleId": null,
"selected": true,
"level": 2,
"children": null
}
]
}
],
defaultProps: {
children: 'children', //
label: 'menuName' //
}
}
},
mounted () {
//
this.setCheckedKeys()
},
methods: {
//
getCheckedKeys () {
// key
const childMenu = this.$refs.tree.getCheckedKeys()
// key
const partMenu = this.$refs.tree.getHalfCheckedKeys()
//
const menu = partMenu.concat(childMenu)
console.log(menu)
},
//
setCheckedKeys () {
//
// default-expand-all使,
// const nodesMap = this.$refs.tree.store.nodesMap
// const keys = Object.keys(nodesMap)
// keys.forEach(key => {
// nodesMap[key].expanded = true
// })
//
this.$nextTick(() => {
this.$refs.tree.setCheckedKeys([3])
})
},
}
}
</script>
<style scoped lang="scss">
.table {
padding: 20px;
min-height: 88vh;
background: #fff;
margin: 20px;
}
.menu-list {
::v-deep .el-tree {
border-top: 1px solid #ebeef5;
border-left: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
.el-tree-node__expand-icon {
display: none !important;
}
.el-tree-node {
&.is-expanded,
&.is-current,
&.is-focusable {
background-color: transparent !important;
}
.el-tree-node__content {
background-color: transparent !important;
&:hover {
background-color: transparent !important;
}
}
}
> .el-tree-node {
display: flex;
align-items: center;
border-bottom: 1px solid #ebeef5;
position: relative;
> .el-tree-node__content {
width: 300px;
height: 100%;
padding-left: 20px !important;
}
.el-tree-node__children {
position: relative;
&::after {
content: ' ';
display: block;
width: 1px;
height: 100%;
border-left: 1px solid #ebeef5;
position: absolute;
left: 0px;
top: 0px;
}
.el-tree-node {
width: 350px;
padding: 10px 0px;
}
}
}
}
}
</style>

View File

@ -1,6 +1,6 @@
<template> <template>
<!-- 授权用户 --> <!-- 授权用户 -->
<el-dialog title="选择用户" align-center v-model="visible" width="800px" top="5vh" append-to-body> <el-dialog title="选择用户" v-model="visible" width="800px" top="5vh" append-to-body>
<el-form :model="queryParams" ref="queryRef" :inline="true"> <el-form :model="queryParams" ref="queryRef" :inline="true">
<el-form-item label="用户名称" prop="userName"> <el-form-item label="用户名称" prop="userName">
<el-input <el-input
@ -39,7 +39,7 @@
</el-table-column> </el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> <el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span> <span>{{ formatTime(scope.row.createTime) }}</span>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -60,7 +60,7 @@
</el-dialog> </el-dialog>
</template> </template>
<script setup name="User"> <script setup name="SelectUser">
import { authUserSelectAll, unallocatedUserList } from "@/api/system/role"; import { authUserSelectAll, unallocatedUserList } from "@/api/system/role";
const props = defineProps({ const props = defineProps({
@ -79,7 +79,7 @@ const userIds = ref([]);
const queryParams = reactive({ const queryParams = reactive({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 20,
roleId: undefined, roleId: undefined,
userName: undefined, userName: undefined,
phonenumber: undefined phonenumber: undefined

View File

@ -1,132 +0,0 @@
<template>
<div class="test-page">
<el-card>
<template #header>
<div class="card-header">
<span>权限树状表格测试页面</span>
</div>
</template>
<!-- 权限树状表格 -->
<PermissionTreeTable ref="treeTableRef" />
<!-- 操作按钮 -->
<div class="actions">
<el-button @click="getSelectedData"></el-button>
<el-button type="primary" @click="showDialog"></el-button>
<el-button type="success" @click="setDefaultData"></el-button>
<el-button type="warning" @click="clearAll"></el-button>
</div>
<!-- 结果展示 -->
<div v-if="selectedData" class="result">
<h3>选中结果</h3>
<el-alert
title="数据获取成功"
type="success"
:closable="false"
show-icon
/>
<pre>{{ JSON.stringify(selectedData, null, 2) }}</pre>
</div>
</el-card>
<!-- 对话框测试 -->
<PermissionDialog
v-model="dialogVisible"
@confirm="handleDialogConfirm"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import PermissionTreeTable from './render.vue'
import PermissionDialog from './permission-dialog.vue'
const treeTableRef = ref()
const dialogVisible = ref(false)
const selectedData = ref(null)
//
const getSelectedData = () => {
if (treeTableRef.value) {
const data = treeTableRef.value.getCheckedData()
selectedData.value = data
console.log('选中的数据:', data)
}
}
//
const showDialog = () => {
dialogVisible.value = true
}
//
const handleDialogConfirm = (data) => {
selectedData.value = data
console.log('对话框确认的数据:', data)
}
//
const setDefaultData = () => {
//
console.log('设置默认选中数据')
//
// 使props
}
//
const clearAll = () => {
//
console.log('清空所有选中')
selectedData.value = null
}
</script>
<style scoped>
.test-page {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.actions {
margin: 20px 0;
text-align: center;
}
.actions .el-button {
margin: 0 5px;
}
.result {
margin-top: 20px;
padding: 20px;
background: #f9f9f9;
border-radius: 4px;
}
.result h3 {
margin-top: 0;
color: #303133;
}
.result pre {
background: #fff;
padding: 15px;
border-radius: 4px;
border: 1px solid #e4e7ed;
overflow-x: auto;
font-size: 12px;
line-height: 1.5;
}
.el-alert {
margin-bottom: 15px;
}
</style>

View File

@ -0,0 +1,121 @@
<template>
<!-- 账户详情 -->
<el-dialog :title="t('账户详情')" align-center v-model="showDialog" width="700px" append-to-body
class="el-dialog">
<el-scrollbar max-height="700px">
<el-descriptions :column="1" border class="c-descriptions">
<el-descriptions-item :label="t('所属部门')" label-width="150">
{{ modifyDate.dept?.deptName||'--' }}
</el-descriptions-item>
<el-descriptions-item :label="t('后台账号')" label-width="150" >
{{ modifyDate.userName }}
</el-descriptions-item>
<el-descriptions-item :label="t('后台昵称')" label-width="150">
{{ modifyDate.nickName }}
</el-descriptions-item>
<el-descriptions-item :label="t('权限名称')" label-width="150">
{{ modifyDate.roleName }}
</el-descriptions-item>
<el-descriptions-item :label="t('创建人')" label-width="150">
{{ modifyDate.updateBy||modifyDate.createBy }}
</el-descriptions-item>
<el-descriptions-item :label="t('创建时间')" label-width="150">
{{ formatTime(modifyDate.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="t('最后登录方式')" label-width="150">
{{ t('密码登录') }}
</el-descriptions-item>
<el-descriptions-item :label="t('最后登录时间')" label-width="150">
{{ formatTime(modifyDate.loginDate) || '--' }}
</el-descriptions-item>
<el-descriptions-item :label="t('最后登出时间')" label-width="150">
{{ formatTime(modifyDate.exitDate) || '--' }}
</el-descriptions-item>
<el-descriptions-item :label="t('最后登入IP')" label-width="150">
{{ modifyDate.loginIp || '--' }}
</el-descriptions-item>
</el-descriptions>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { nextTick, ref } from "vue";
const { proxy } = getCurrentInstance()
const emits = defineEmits(['submit', 'update:show']) //
const props = defineProps({ //
show: {
type: Boolean,
default: false
},
modifyDate: {
type: Object,
default: {}
}
})
const showDialog = computed({//
get() {
return props.show
},
set(value) {
emits('update:show', value)
}
})
const formAll = reactive({ //
id: '',
rewardAmount:'',
auditRate:'',
remark:'',
password:''
})
//
nextTick(() => {
formAll.id = props.modifyDate.id;
})
//
onMounted(() => {
});
//
const closeDialog = () => {
showDialog.value = false
}
</script>
<style scope lang="scss">
.icon-box {
display: flex;
align-items: center;
gap: 20px;
}
.disable-click {
pointer-events: none;
}
.clt-item {
.label-box {
vertical-align: middle;
width: 150px;
text-align: right;
padding-right: 12px;
word-break: break-all;
line-height: 20px;
box-sizing: border-box;
}
:deep(.el-form-item__error) {
padding-left: 150px;
}
.item-right {
width: calc(100% - 150px);
}
}
</style>

View File

@ -0,0 +1,101 @@
<template>
<!-- Google验证码 -->
<el-dialog v-model="showDialog" align-center width="500px" append-to-body
class="el-dialog">
<template #title>
<div class="title" style="width: 100%;text-align: center;">Google验证码</div>
</template>
<el-form :model="formGoogle" :rules="rulesGoogle" ref="googleRef" label-width="130px">
<el-form-item label="Google验证码" prop="googleCode">
<el-input v-model="formGoogle.googleCode" placeholder="请输入您的Google验证码" maxlength="6" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer" style="display: flex;justify-content: center;">
<el-button @click="showDialog = false">取消</el-button>
<el-button type="primary" @click="submitGoogle">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { nextTick, ref } from "vue";
const { proxy } = getCurrentInstance()
const emits = defineEmits(['submit', 'update:show']) //
const props = defineProps({ //
show: {
type: Boolean,
default: false
},
modifyDate: {
type: Object,
default: {}
}
})
const showDialog = computed({//
get() {
return props.show
},
set(value) {
emits('update:show', value)
}
})
const rulesGoogle = reactive({
googleCode: [{ required: true, message: "请输入谷歌验证码", trigger: "change" }]
});
const formGoogle = reactive({
googleCode: ''
});
//
nextTick(() => {
})
const submitGoogle = () => {
proxy.$refs["googleRef"].validate(valid => {
if (valid) {
emits('submit', formGoogle.googleCode)
}
})
}
//
onMounted(() => {
});
//
const closeDialog = () => {
showDialog.value = false
}
</script>
<style scope lang="scss">
.icon-box {
display: flex;
align-items: center;
gap: 20px;
}
.disable-click {
pointer-events: none;
}
.clt-item {
.label-box {
vertical-align: middle;
width: 150px;
text-align: right;
padding-right: 12px;
word-break: break-all;
line-height: 20px;
box-sizing: border-box;
}
:deep(.el-form-item__error) {
padding-left: 150px;
}
.item-right {
width: calc(100% - 150px);
}
}
</style>

View File

@ -0,0 +1,44 @@
<template>
<div>
<div style="margin-bottom: 10px;">
<span>批量数量</span>
<b>{{ batchCount }}</b>
</div>
<el-form :model="form">
<el-form-item label="操作类型">
<el-select v-model="form.actionType" placeholder="请选择操作类型" style="width: 300px">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { reactive } from "vue";
const props = defineProps({
batchCount: {
type: Number,
required: true
},
options: {
type: Array,
default: () => []
}
});
const form = reactive({
actionType: ""
});
//
defineExpose({ form });
</script>

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,9 @@ export default defineConfig(({ mode, command }) => {
proxy: { proxy: {
'/dev-api': { '/dev-api': {
// target: 'http://192.168.50.139:9080', // target: 'http://192.168.50.139:9080',
// target: 'http://192.168.50.233:8081', target: 'http://192.168.50.233:8081',
// target: 'http://192.168.50.178:8080', // target: 'http://192.168.50.178:8080',
target: 'http://192.168.50.11:8081', // target: 'http://192.168.50.11:8081',
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '') rewrite: (p) => p.replace(/^\/dev-api/, '')
} }