mirror of
https://github.com/certd/certd.git
synced 2026-04-24 12:27:25 +08:00
More translation
This commit is contained in:
@@ -447,4 +447,237 @@ export default {
|
|||||||
subdomainManagement: "Subdomain Management",
|
subdomainManagement: "Subdomain Management",
|
||||||
isDisabled: "Is Disabled",
|
isDisabled: "Is Disabled",
|
||||||
enabled: "Enabled",
|
enabled: "Enabled",
|
||||||
|
uploadCustomCert: "Upload Custom Certificate",
|
||||||
|
sourcee: "Source",
|
||||||
|
sourcePipeline: "Pipeline",
|
||||||
|
sourceManualUpload: "Manual Upload",
|
||||||
|
domains: "Domains",
|
||||||
|
enterDomain: "Please enter domain",
|
||||||
|
validDays: "Valid Days",
|
||||||
|
expires: " expires",
|
||||||
|
days: " days",
|
||||||
|
expireTime: "Expiration Time",
|
||||||
|
certIssuer: "Certificate Issuer",
|
||||||
|
applyTime: "Application Time",
|
||||||
|
relatedPipeline: "Related Pipeline",
|
||||||
|
statusSuccess: "Success",
|
||||||
|
statusChecking: "Checking",
|
||||||
|
statusError: "Error",
|
||||||
|
actionImportBatch: "Batch Import",
|
||||||
|
actionSyncIp: "Sync IP",
|
||||||
|
modalTitleSyncIp: "Sync IP",
|
||||||
|
modalContentSyncIp: "Are you sure to sync IP?",
|
||||||
|
notificationSyncComplete: "Sync Complete",
|
||||||
|
actionCheckAll: "Check All",
|
||||||
|
modalTitleConfirm: "Confirm",
|
||||||
|
modalContentCheckAll: "Confirm to trigger checking all IP site's certificates?",
|
||||||
|
notificationCheckSubmitted: "Check task submitted",
|
||||||
|
notificationCheckDescription: "Please refresh later to see results",
|
||||||
|
tooltipCheckNow: "Check Now",
|
||||||
|
notificationCheckSubmittedPleaseRefresh: "Check task submitted, please refresh later",
|
||||||
|
columnId: "ID",
|
||||||
|
columnIp: "IP",
|
||||||
|
helperIpCname: "Supports entering CNAME domain name",
|
||||||
|
ruleIpRequired: "Please enter IP",
|
||||||
|
columnCertDomains: "Certificate Domains",
|
||||||
|
columnCertProvider: "Issuer",
|
||||||
|
columnCertStatus: "Certificate Status",
|
||||||
|
statusNormal: "Normal",
|
||||||
|
statusExpired: "Expired",
|
||||||
|
columnCertExpiresTime: "Certificate Expiration Time",
|
||||||
|
expired: "expired",
|
||||||
|
columnCheckStatus: "Check Status",
|
||||||
|
columnLastCheckTime: "Last Check Time",
|
||||||
|
columnSource: "Source",
|
||||||
|
sourceSync: "Sync",
|
||||||
|
sourceManual: "Manual",
|
||||||
|
sourceImport: "Import",
|
||||||
|
columnDisabled: "Enabled/Disabled",
|
||||||
|
columnRemark: "Remark",
|
||||||
|
pluginFile: "Plugin File",
|
||||||
|
selectPluginFile: "Select plugin file",
|
||||||
|
overrideSameName: "Override same name",
|
||||||
|
override: "Override",
|
||||||
|
noOverride: "No override",
|
||||||
|
overrideHelper: "If a plugin with the same name exists, override it directly",
|
||||||
|
importPlugin: "Import Plugin",
|
||||||
|
operationSuccess: "Operation successful",
|
||||||
|
customPlugin: "Custom Plugin",
|
||||||
|
import: "Import",
|
||||||
|
export: "Export",
|
||||||
|
pluginType: "Plugin Type",
|
||||||
|
auth: "Authorization",
|
||||||
|
dns: "DNS",
|
||||||
|
deployPlugin: "Deploy Plugin",
|
||||||
|
icon: "Icon",
|
||||||
|
pluginName: "Plugin Name",
|
||||||
|
pluginNameHelper: "Must be English letters or digits, camelCase with type prefix\nExample: AliyunDeployToCDN\nDo not modify name once plugin is used",
|
||||||
|
pluginNameRuleMsg: "Must be English letters or digits, camelCase with type prefix",
|
||||||
|
author: "Author",
|
||||||
|
authorHelper: "Used as prefix when uploading to plugin store, e.g., greper/pluginName",
|
||||||
|
authorRuleMsg: "Must be English letters or digits",
|
||||||
|
titleHelper: "Plugin name in Chinese",
|
||||||
|
descriptionHelper: "Description of the plugin",
|
||||||
|
builtIn: "Built-in",
|
||||||
|
custom: "Custom",
|
||||||
|
store: "Store",
|
||||||
|
version: "Version",
|
||||||
|
pluginDependencies: "Plugin Dependencies",
|
||||||
|
pluginDependenciesHelper: "Dependencies to install first in format: [author/]pluginName[:version]",
|
||||||
|
editableRunStrategy: "Editable Run Strategy",
|
||||||
|
editable: "Editable",
|
||||||
|
notEditable: "Not Editable",
|
||||||
|
runStrategy: "Run Strategy",
|
||||||
|
normalRun: "Normal Run",
|
||||||
|
skipOnSuccess: "Skip on success (Deploy task)",
|
||||||
|
defaultRunStrategyHelper: "Default run strategy",
|
||||||
|
enableDisable: "Enable/Disable",
|
||||||
|
clickToToggle: "Click to toggle enable/disable",
|
||||||
|
confirmToggle: "Are you sure to",
|
||||||
|
disable: "disable",
|
||||||
|
enable: "enable",
|
||||||
|
pluginGroup: "Plugin Group",
|
||||||
|
icpRegistrationNumber: "ICP Registration Number",
|
||||||
|
icpPlaceholder: "Guangdong ICP xxxxxxx Number",
|
||||||
|
publicSecurityRegistrationNumber: "Public Security Registration Number",
|
||||||
|
publicSecurityPlaceholder: "Beijing Public Security xxxxxxx Number",
|
||||||
|
enableAssistant: "Enable Assistant",
|
||||||
|
allowCrawlers: "Allow Crawlers",
|
||||||
|
httpProxy: "HTTP Proxy",
|
||||||
|
httpProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||||
|
httpProxyHelper: "Configure when some websites are blocked",
|
||||||
|
httpsProxy: "HTTPS Proxy",
|
||||||
|
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||||
|
saveThenTestTitle: "Save first, then click test",
|
||||||
|
testButton: "Test",
|
||||||
|
httpsProxyHelper: "Usually both proxies are the same, save first then test",
|
||||||
|
dualStackNetwork: "Dual Stack Network",
|
||||||
|
default: "Default",
|
||||||
|
ipv4Priority: "IPv4 Priority",
|
||||||
|
ipv6Priority: "IPv6 Priority",
|
||||||
|
dualStackNetworkHelper: "If IPv6 priority is selected, enable IPv6 in docker-compose.yaml",
|
||||||
|
enableCommonCnameService: "Enable Public CNAME Service",
|
||||||
|
commonCnameHelper: "Allow use of public CNAME service. If disabled and no <router-link to='/sys/cname/provider'>custom CNAME service</router-link> is set, CNAME proxy certificate application will not work.",
|
||||||
|
saveButton: "Save",
|
||||||
|
stopSuccess: "Stopped successfully",
|
||||||
|
google: "Google",
|
||||||
|
baidu: "Baidu",
|
||||||
|
success: "Success",
|
||||||
|
testFailed: "Test Failed",
|
||||||
|
testCompleted: "Test Completed",
|
||||||
|
manageOtherUserPipeline: "Manage other users' pipelines",
|
||||||
|
limitUserPipelineCount: "Limit user pipeline count",
|
||||||
|
limitUserPipelineCountHelper: "0 means no limit",
|
||||||
|
enableSelfRegistration: "Enable self-registration",
|
||||||
|
enableUserValidityPeriod: "Enable user validity period",
|
||||||
|
userValidityPeriodHelper: "Users can use normally within validity; pipelines disabled after expiry",
|
||||||
|
enableUsernameRegistration: "Enable username registration",
|
||||||
|
enableEmailRegistration: "Enable email registration",
|
||||||
|
proFeature: "Pro feature",
|
||||||
|
emailServerSetup: "Set up email server",
|
||||||
|
enableSmsLoginRegister: "Enable SMS login and registration",
|
||||||
|
commFeature: "Commercial feature",
|
||||||
|
smsProvider: "SMS provider",
|
||||||
|
aliyunSms: "Aliyun SMS",
|
||||||
|
yfySms: "YFY SMS",
|
||||||
|
smsTest: "SMS test",
|
||||||
|
testMobilePlaceholder: "Enter test mobile number",
|
||||||
|
saveThenTest: "Save first then test",
|
||||||
|
enterTestMobile: "Please enter test mobile number",
|
||||||
|
sendSuccess: "Sent successfully",
|
||||||
|
atLeastOneLoginRequired: "At least one of password login or SMS login must be enabled",
|
||||||
|
fieldRequired: "This field is required",
|
||||||
|
siteHide: "Site Hide",
|
||||||
|
enableSiteHide: "Enable Site Hide",
|
||||||
|
siteHideDescription: "You can disable site accessibility normally and enable it when needed to enhance site security",
|
||||||
|
helpDoc: "Help Document",
|
||||||
|
randomAddress: "Random Address",
|
||||||
|
siteHideUrlHelper: "After the site is hidden, you need to visit this URL to unlock to access normally",
|
||||||
|
fullUnlockUrl: "Full Unlock URL",
|
||||||
|
saveThisUrl: "Please save this URL carefully",
|
||||||
|
unlockPassword: "Unlock Password",
|
||||||
|
unlockPasswordHelper: "Password needed to unlock the hide; set on first time or reset when filled",
|
||||||
|
autoHideTime: "Auto Hide Time",
|
||||||
|
autoHideTimeHelper: "Minutes without requests before auto hiding",
|
||||||
|
hideOpenApi: "Hide Open API",
|
||||||
|
hideOpenApiHelper: "Whether to hide open APIs; whether to expose /api/v1 prefixed endpoints",
|
||||||
|
hideSiteImmediately: "Hide Site Immediately",
|
||||||
|
hideImmediately: "Hide Immediately",
|
||||||
|
confirmHideSiteTitle: "Are you sure to hide the site immediately?",
|
||||||
|
confirmHideSiteContent: "After hiding, the site will be inaccessible. Please operate cautiously.",
|
||||||
|
siteHiddenSuccess: "Site has been hidden",
|
||||||
|
emailServerSettings: "Email Server Settings",
|
||||||
|
setEmailSendingServer: "Set the email sending server",
|
||||||
|
useCustomEmailServer: "Use Custom Email Server",
|
||||||
|
smtpDomain: "SMTP Domain",
|
||||||
|
pleaseEnterSmtpDomain: "Please enter SMTP domain or IP",
|
||||||
|
smtpPort: "SMTP Port",
|
||||||
|
pleaseEnterSmtpPort: "Please enter SMTP port",
|
||||||
|
username: "Username",
|
||||||
|
pleaseEnterUsername: "Please enter username",
|
||||||
|
password: "Password",
|
||||||
|
pleaseEnterPassword: "Please enter password",
|
||||||
|
qqEmailAuthCodeHelper: "If using QQ email, get an authorization code in QQ email settings as the password",
|
||||||
|
senderEmail: "Sender Email",
|
||||||
|
pleaseEnterSenderEmail: "Please enter sender email",
|
||||||
|
useSsl: "Use SSL",
|
||||||
|
sslPortNote: "SSL and non-SSL SMTP ports are different, please adjust port accordingly",
|
||||||
|
ignoreCertValidation: "Ignore Certificate Validation",
|
||||||
|
useOfficialEmailServer: "Use Official Email Server",
|
||||||
|
useOfficialEmailServerHelper: "Send emails directly using the official server to avoid complicated setup",
|
||||||
|
testReceiverEmail: "Test Receiver Email",
|
||||||
|
pleaseEnterTestReceiverEmail: "Please enter test receiver email",
|
||||||
|
saveBeforeTest: "Save before testing",
|
||||||
|
sendFailHelpDoc: "Failed to send??? ",
|
||||||
|
emailConfigHelpDoc: "Email configuration help document",
|
||||||
|
tryOfficialEmailServer: "You can also try using the official email server ↗↗↗↗↗↗↗↗",
|
||||||
|
pluginManagement: "Plugin Management",
|
||||||
|
pluginBetaWarning: "Custom plugins are in BETA and may have breaking changes in future",
|
||||||
|
pleaseSelectRecord: "Please select records first",
|
||||||
|
permissionManagement: "Permission Management",
|
||||||
|
adda: "Add",
|
||||||
|
rootNode: "Root Node",
|
||||||
|
permissionName: "Permission Name",
|
||||||
|
enterPermissionName: "Please enter permission name",
|
||||||
|
permissionCode: "Permission Code",
|
||||||
|
enterPermissionCode: "Please enter permission code",
|
||||||
|
max100Chars: "Maximum 100 characters",
|
||||||
|
examplePermissionCode: "e.g.: sys:user:view",
|
||||||
|
sortOrder: "Sort Order",
|
||||||
|
sortRequired: "Sort order is required",
|
||||||
|
parentNode: "Parent Node",
|
||||||
|
roleManagement: "Role Management",
|
||||||
|
assignPermissions: "Assign Permissions",
|
||||||
|
roleName: "Role Name",
|
||||||
|
enterRoleName: "Please enter role name",
|
||||||
|
unlockLogin: "Unlock Login",
|
||||||
|
notice: "Notice",
|
||||||
|
confirmUnlock: "Are you sure you want to unlock this user's login?",
|
||||||
|
unlockSuccess: "Unlock successful",
|
||||||
|
enterUsername: "Please enter username",
|
||||||
|
modifyPasswordIfFilled: "Fill in to change the password",
|
||||||
|
emaila: "Email",
|
||||||
|
mobile: "Mobile",
|
||||||
|
avatar: "Avatar",
|
||||||
|
validTime: "Valid Time",
|
||||||
|
remark: "Remark",
|
||||||
|
roles: "Roles",
|
||||||
|
cnameTitle: "CNAME Service Configuration",
|
||||||
|
cnameDescription:
|
||||||
|
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
|
||||||
|
cnameLinkText: "CNAME principle and usage instructions",
|
||||||
|
confirmTitle: "Confirm",
|
||||||
|
confirmDeleteBatch: "Are you sure you want to delete these {count} records?",
|
||||||
|
selectRecordsFirst: "Please select records first",
|
||||||
|
cnameDomain: "CNAME Domain",
|
||||||
|
cnameDomainPlaceholder: "cname.handsfree.work",
|
||||||
|
cnameDomainHelper:
|
||||||
|
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
|
||||||
|
dnsProvider: "DNS Provider",
|
||||||
|
dnsProviderAuthorization: "DNS Provider Authorization",
|
||||||
|
setDefault: "Set Default",
|
||||||
|
confirmSetDefault: "Are you sure to set as default?",
|
||||||
|
setAsDefault: "Set as Default",
|
||||||
|
disabledLabel: "Disabled",
|
||||||
|
confirmToggleStatus: "Are you sure to {action}?",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -453,4 +453,238 @@ export default {
|
|||||||
subdomainManagement: "子域管理",
|
subdomainManagement: "子域管理",
|
||||||
isDisabled: "是否禁用",
|
isDisabled: "是否禁用",
|
||||||
enabled: "启用",
|
enabled: "启用",
|
||||||
|
uploadCustomCert: "上传自定义证书",
|
||||||
|
sourcee: "来源",
|
||||||
|
sourcePipeline: "流水线",
|
||||||
|
sourceManualUpload: "手动上传",
|
||||||
|
domains: "域名",
|
||||||
|
enterDomain: "请输入域名",
|
||||||
|
validDays: "有效天数",
|
||||||
|
expires: "过期",
|
||||||
|
days: "天",
|
||||||
|
expireTime: "过期时间",
|
||||||
|
certIssuer: "证书颁发机构",
|
||||||
|
applyTime: "申请时间",
|
||||||
|
relatedPipeline: "关联流水线",
|
||||||
|
statusSuccess: "成功",
|
||||||
|
statusChecking: "检查中",
|
||||||
|
statusError: "异常",
|
||||||
|
actionImportBatch: "批量导入",
|
||||||
|
actionSyncIp: "同步IP",
|
||||||
|
modalTitleSyncIp: "同步IP",
|
||||||
|
modalContentSyncIp: "确定要同步IP吗?",
|
||||||
|
notificationSyncComplete: "同步完成",
|
||||||
|
actionCheckAll: "检查全部",
|
||||||
|
modalTitleConfirm: "确认",
|
||||||
|
modalContentCheckAll: "确认触发检查全部IP站点的证书吗?",
|
||||||
|
notificationCheckSubmitted: "检查任务已提交",
|
||||||
|
notificationCheckDescription: "请稍后刷新页面查看结果",
|
||||||
|
tooltipCheckNow: "立即检查",
|
||||||
|
notificationCheckSubmittedPleaseRefresh: "检查任务已提交,请稍后刷新查看结果",
|
||||||
|
columnId: "ID",
|
||||||
|
columnIp: "IP",
|
||||||
|
helperIpCname: "也支持填写CNAME域名",
|
||||||
|
ruleIpRequired: "请输入IP",
|
||||||
|
columnCertDomains: "证书域名",
|
||||||
|
columnCertProvider: "颁发机构",
|
||||||
|
columnCertStatus: "证书状态",
|
||||||
|
statusNormal: "正常",
|
||||||
|
statusExpired: "过期",
|
||||||
|
columnCertExpiresTime: "证书到期时间",
|
||||||
|
expired: "过期",
|
||||||
|
columnCheckStatus: "检查状态",
|
||||||
|
columnLastCheckTime: "上次检查时间",
|
||||||
|
columnSource: "来源",
|
||||||
|
sourceSync: "同步",
|
||||||
|
sourceManual: "手动",
|
||||||
|
sourceImport: "导入",
|
||||||
|
columnDisabled: "禁用启用",
|
||||||
|
columnRemark: "备注",
|
||||||
|
pluginFile: "插件文件",
|
||||||
|
selectPluginFile: "选择插件文件",
|
||||||
|
overrideSameName: "同名覆盖",
|
||||||
|
override: "覆盖",
|
||||||
|
noOverride: "不覆盖",
|
||||||
|
overrideHelper: "如果已有相同名称插件,直接覆盖",
|
||||||
|
importPlugin: "导入插件",
|
||||||
|
operationSuccess: "操作成功",
|
||||||
|
customPlugin: "自定义插件",
|
||||||
|
import: "导入",
|
||||||
|
export: "导出",
|
||||||
|
pluginType: "插件类型",
|
||||||
|
auth: "授权",
|
||||||
|
dns: "DNS",
|
||||||
|
deployPlugin: "部署插件",
|
||||||
|
icon: "图标",
|
||||||
|
pluginName: "插件名称",
|
||||||
|
pluginNameHelper: "必须为英文或数字,驼峰命名,类型作为前缀\n示例:AliyunDeployToCDN\n插件使用后,名称不可修改",
|
||||||
|
pluginNameRuleMsg: "必须为英文或数字,驼峰命名,类型作为前缀",
|
||||||
|
author: "作者",
|
||||||
|
authorHelper: "上传插件市场时作为前缀,如 greper/pluginName",
|
||||||
|
authorRuleMsg: "必须为英文或数字",
|
||||||
|
titleHelper: "插件中文名称",
|
||||||
|
descriptionHelper: "插件描述",
|
||||||
|
builtIn: "内置",
|
||||||
|
custom: "自定义",
|
||||||
|
store: "市场",
|
||||||
|
version: "版本",
|
||||||
|
pluginDependencies: "插件依赖",
|
||||||
|
pluginDependenciesHelper: "格式: [作者/]插件名[:版本],需先安装依赖插件",
|
||||||
|
editableRunStrategy: "可编辑运行策略",
|
||||||
|
editable: "可编辑",
|
||||||
|
notEditable: "不可编辑",
|
||||||
|
runStrategy: "运行策略",
|
||||||
|
normalRun: "正常运行",
|
||||||
|
skipOnSuccess: "成功跳过(部署任务)",
|
||||||
|
defaultRunStrategyHelper: "默认运行策略",
|
||||||
|
enableDisable: "启用/禁用",
|
||||||
|
clickToToggle: "点击切换启用/禁用",
|
||||||
|
confirmToggle: "确认要",
|
||||||
|
disable: "禁用",
|
||||||
|
enable: "启用",
|
||||||
|
pluginGroup: "插件分组",
|
||||||
|
icpRegistrationNumber: "ICP备案号",
|
||||||
|
icpPlaceholder: "粤ICP备xxxxxxx号",
|
||||||
|
publicSecurityRegistrationNumber: "网安备案号",
|
||||||
|
publicSecurityPlaceholder: "京公网安备xxxxxxx号",
|
||||||
|
enableAssistant: "开启小助手",
|
||||||
|
allowCrawlers: "允许爬虫",
|
||||||
|
httpProxy: "HTTP代理",
|
||||||
|
httpProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||||
|
httpProxyHelper: "当某些网站被墙时可以配置",
|
||||||
|
httpsProxy: "HTTPS代理",
|
||||||
|
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||||
|
saveThenTestTitle: "保存后,再点击测试",
|
||||||
|
testButton: "测试",
|
||||||
|
httpsProxyHelper: "一般这两个代理填一样的,保存后再测试",
|
||||||
|
dualStackNetwork: "双栈网络",
|
||||||
|
default: "默认",
|
||||||
|
ipv4Priority: "IPV4优先",
|
||||||
|
ipv6Priority: "IPV6优先",
|
||||||
|
dualStackNetworkHelper: "如果选择IPv6优先,需要在docker-compose.yaml中启用ipv6",
|
||||||
|
enableCommonCnameService: "启用公共CNAME服务",
|
||||||
|
commonCnameHelper: "是否可以使用公共CNAME服务,如果禁用,且没有设置<router-link to='/sys/cname/provider'>自定义CNAME服务</router-link>,则无法使用CNAME代理方式申请证书",
|
||||||
|
saveButton: "保存",
|
||||||
|
stopSuccess: "停止成功",
|
||||||
|
google: "Google",
|
||||||
|
baidu: "百度",
|
||||||
|
success: "成功",
|
||||||
|
testFailed: "测试失败",
|
||||||
|
testCompleted: "测试完成",
|
||||||
|
manageOtherUserPipeline: "管理其他用户流水线",
|
||||||
|
limitUserPipelineCount: "限制用户流水线数量",
|
||||||
|
limitUserPipelineCountHelper: "0为不限制",
|
||||||
|
enableSelfRegistration: "开启自助注册",
|
||||||
|
enableUserValidityPeriod: "开启用户有效期",
|
||||||
|
userValidityPeriodHelper: "有效期内用户可正常使用,失效后流水线将被停用",
|
||||||
|
enableUsernameRegistration: "开启用户名注册",
|
||||||
|
enableEmailRegistration: "开启邮箱注册",
|
||||||
|
proFeature: "专业版功能",
|
||||||
|
emailServerSetup: "设置邮箱服务器",
|
||||||
|
enableSmsLoginRegister: "开启手机号登录、注册",
|
||||||
|
commFeature: "商业版功能",
|
||||||
|
smsProvider: "短信提供商",
|
||||||
|
aliyunSms: "阿里云短信",
|
||||||
|
yfySms: "易发云短信",
|
||||||
|
smsTest: "短信测试",
|
||||||
|
testMobilePlaceholder: "输入测试手机号",
|
||||||
|
saveThenTest: "保存后再点击测试",
|
||||||
|
enterTestMobile: "请输入测试手机号",
|
||||||
|
sendSuccess: "发送成功",
|
||||||
|
atLeastOneLoginRequired: "密码登录和手机号登录至少开启一个",
|
||||||
|
fieldRequired: "此项必填",
|
||||||
|
siteHide: "站点隐藏",
|
||||||
|
enableSiteHide: "启用站点隐藏",
|
||||||
|
siteHideDescription: "可以在平时关闭站点的可访问性,需要时再打开,增强站点安全性",
|
||||||
|
helpDoc: "帮助说明",
|
||||||
|
randomAddress: "随机地址",
|
||||||
|
siteHideUrlHelper: "站点被隐藏后,需要访问此URL解锁,才能正常访问",
|
||||||
|
fullUnlockUrl: "完整解除隐藏地址",
|
||||||
|
saveThisUrl: "请保存好此地址",
|
||||||
|
unlockPassword: "解除密码",
|
||||||
|
unlockPasswordHelper: "解除隐藏时需要输入密码,第一次需要设置密码,填写则重置密码",
|
||||||
|
autoHideTime: "自动隐藏时间",
|
||||||
|
autoHideTimeHelper: "多少分钟内无请求自动隐藏",
|
||||||
|
hideOpenApi: "隐藏开放接口",
|
||||||
|
hideOpenApiHelper: "是否隐藏开放接口,是否放开/api/v1开头的接口",
|
||||||
|
hideSiteImmediately: "立即隐藏站点",
|
||||||
|
hideImmediately: "立即隐藏",
|
||||||
|
confirmHideSiteTitle: "确定要立即隐藏站点吗?",
|
||||||
|
confirmHideSiteContent: "隐藏后,将无法访问站点,请谨慎操作",
|
||||||
|
siteHiddenSuccess: "站点已隐藏",
|
||||||
|
emailServerSettings: "邮件服务器设置",
|
||||||
|
setEmailSendingServer: "设置邮件发送服务器",
|
||||||
|
useCustomEmailServer: "使用自定义邮件服务器",
|
||||||
|
smtpDomain: "SMTP域名",
|
||||||
|
pleaseEnterSmtpDomain: "请输入smtp域名或ip",
|
||||||
|
smtpPort: "SMTP端口",
|
||||||
|
pleaseEnterSmtpPort: "请输入smtp端口号",
|
||||||
|
username: "用户名",
|
||||||
|
pleaseEnterUsername: "请输入用户名",
|
||||||
|
password: "密码",
|
||||||
|
pleaseEnterPassword: "请输入密码",
|
||||||
|
qqEmailAuthCodeHelper: "如果是qq邮箱,需要到qq邮箱的设置里面申请授权码作为密码",
|
||||||
|
senderEmail: "发件邮箱",
|
||||||
|
pleaseEnterSenderEmail: "请输入发件邮箱",
|
||||||
|
useSsl: "是否ssl",
|
||||||
|
sslPortNote: "ssl和非ssl的smtp端口是不一样的,注意修改端口",
|
||||||
|
ignoreCertValidation: "忽略证书校验",
|
||||||
|
useOfficialEmailServer: "使用官方邮件服务器",
|
||||||
|
useOfficialEmailServerHelper: "使用官方邮箱服务器直接发邮件,免除繁琐的配置",
|
||||||
|
testReceiverEmail: "测试收件邮箱",
|
||||||
|
pleaseEnterTestReceiverEmail: "请输入测试收件邮箱",
|
||||||
|
saveBeforeTest: "保存后再点击测试",
|
||||||
|
sendFailHelpDoc: "发送失败???",
|
||||||
|
emailConfigHelpDoc: "邮件配置帮助文档",
|
||||||
|
tryOfficialEmailServer: "您还可以试试使用官方邮件服务器↗↗↗↗↗↗↗↗",
|
||||||
|
pluginManagement: "插件管理",
|
||||||
|
pluginBetaWarning: "自定义插件处于BETA测试版,后续可能会有破坏性变更",
|
||||||
|
pleaseSelectRecord: "请先勾选记录",
|
||||||
|
permissionManagement: "权限管理",
|
||||||
|
adda: "添加",
|
||||||
|
rootNode: "根节点",
|
||||||
|
permissionName: "权限名称",
|
||||||
|
enterPermissionName: "请输入权限名称",
|
||||||
|
permissionCode: "权限代码",
|
||||||
|
enterPermissionCode: "请输入权限代码",
|
||||||
|
max100Chars: "最大100个字符",
|
||||||
|
examplePermissionCode: "例如:sys:user:view",
|
||||||
|
sortOrder: "排序",
|
||||||
|
sortRequired: "排序号必填",
|
||||||
|
parentNode: "父节点",
|
||||||
|
roleManagement: "角色管理",
|
||||||
|
assignPermissions: "分配权限",
|
||||||
|
roleName: "角色名称",
|
||||||
|
enterRoleName: "请输入角色名称",
|
||||||
|
unlockLogin: "解除登录锁定",
|
||||||
|
notice: "提示",
|
||||||
|
confirmUnlock: "确定要解除该用户的登录锁定吗?",
|
||||||
|
unlockSuccess: "解除成功",
|
||||||
|
enterUsername: "请输入用户名",
|
||||||
|
modifyPasswordIfFilled: "填写则修改密码",
|
||||||
|
emaila: "邮箱",
|
||||||
|
mobile: "手机号",
|
||||||
|
avatar: "头像",
|
||||||
|
validTime: "有效期",
|
||||||
|
remark: "备注",
|
||||||
|
roles: "角色",
|
||||||
|
cnameTitle: "CNAME服务配置",
|
||||||
|
cnameDescription:
|
||||||
|
"此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。",
|
||||||
|
cnameLinkText: "CNAME功能原理及使用说明",
|
||||||
|
confirmTitle: "确认",
|
||||||
|
confirmDeleteBatch: "确定要批量删除这{count}条记录吗",
|
||||||
|
selectRecordsFirst: "请先勾选记录",
|
||||||
|
cnameDomain: "CNAME域名",
|
||||||
|
cnameDomainPlaceholder: "cname.handsfree.work",
|
||||||
|
cnameDomainHelper:
|
||||||
|
"需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名",
|
||||||
|
dnsProvider: "DNS提供商",
|
||||||
|
dnsProviderAuthorization: "DNS提供商授权",
|
||||||
|
setDefault: "设置默认",
|
||||||
|
confirmSetDefault: "确定要设置为默认吗?",
|
||||||
|
setAsDefault: "设为默认",
|
||||||
|
disabledLabel: "禁用",
|
||||||
|
confirmToggleStatus: "确定要{action}吗?",
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
show: true,
|
show: true,
|
||||||
buttons: {
|
buttons: {
|
||||||
add: {
|
add: {
|
||||||
text: "上传自定义证书",
|
text: t('certd.uploadCustomCert'),
|
||||||
type: "primary",
|
type: "primary",
|
||||||
show: false,
|
show: false,
|
||||||
async click() {
|
async click() {
|
||||||
@@ -150,15 +150,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
fromType: {
|
fromType: {
|
||||||
title: "来源",
|
title: t('certd.sourcee'),
|
||||||
search: {
|
search: {
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "流水线", value: "pipeline" },
|
{ label: t('certd.sourcePipeline'), value: "pipeline" },
|
||||||
{ label: "手动上传", value: "upload" },
|
{ label: t('certd.sourceManualUpload'), value: "upload" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
@@ -179,13 +179,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
domains: {
|
domains: {
|
||||||
title: "域名",
|
title: t('certd.domains'),
|
||||||
search: {
|
search: {
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: "text",
|
type: "text",
|
||||||
form: {
|
form: {
|
||||||
rules: [{ required: true, message: "请输入域名" }],
|
rules: [{ required: true, message: t('certd.enterDomain') }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 450,
|
width: 450,
|
||||||
@@ -197,7 +197,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
domainCount: {
|
domainCount: {
|
||||||
title: "域名数量",
|
title: t('certd.domainCount'),
|
||||||
type: "number",
|
type: "number",
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
@@ -209,7 +209,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
expiresLeft: {
|
expiresLeft: {
|
||||||
title: "有效天数",
|
title: t('certd.validDays'),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@@ -229,12 +229,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
const leftDays = dayjs(value).diff(dayjs(), "day");
|
const leftDays = dayjs(value).diff(dayjs(), "day");
|
||||||
const color = leftDays < 20 ? "red" : "#389e0d";
|
const color = leftDays < 20 ? "red" : "#389e0d";
|
||||||
const percent = (leftDays / 90) * 100;
|
const percent = (leftDays / 90) * 100;
|
||||||
return <a-progress title={expireDate + "过期"} percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}天`} />;
|
return <a-progress title={expireDate + t('certd.expires')} percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}${t('certd.days')}`} />;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expiresTime: {
|
expiresTime: {
|
||||||
title: "过期时间",
|
title: t('certd.expireTime'),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@@ -247,7 +247,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
certProvider: {
|
certProvider: {
|
||||||
title: "证书颁发机构",
|
title: t('certd.certIssuer'),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@@ -260,7 +260,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
applyTime: {
|
applyTime: {
|
||||||
title: "申请时间",
|
title: t('certd.applyTime'),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@@ -273,7 +273,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"pipeline.title": {
|
"pipeline.title": {
|
||||||
title: "关联流水线",
|
title: t('certd.relatedPipeline'),
|
||||||
search: { show: false },
|
search: { show: false },
|
||||||
type: "link",
|
type: "link",
|
||||||
form: {
|
form: {
|
||||||
@@ -291,6 +291,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,353 +7,354 @@ import { Modal, notification } from "ant-design-vue";
|
|||||||
import { useSiteIpMonitor } from "/@/views/certd/monitor/site/ip/use";
|
import { useSiteIpMonitor } from "/@/views/certd/monitor/site/ip/use";
|
||||||
|
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const api = siteIpApi;
|
const { t } = useI18n();
|
||||||
|
const api = siteIpApi;
|
||||||
|
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
if (!query.query) {
|
if (!query.query) {
|
||||||
query.query = {};
|
query.query = {};
|
||||||
}
|
}
|
||||||
query.query.siteId = context.props.siteId;
|
query.query.siteId = context.props.siteId;
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async (req: EditReq) => {
|
const editRequest = async (req: EditReq) => {
|
||||||
const { form, row } = req;
|
const { form, row } = req;
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
const res = await api.UpdateObj(form);
|
const res = await api.UpdateObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
const delRequest = async (req: DelReq) => {
|
const delRequest = async (req: DelReq) => {
|
||||||
const { row } = req;
|
const { row } = req;
|
||||||
return await api.DelObj(row.id);
|
return await api.DelObj(row.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addRequest = async (req: AddReq) => {
|
const addRequest = async (req: AddReq) => {
|
||||||
const { form } = req;
|
const { form } = req;
|
||||||
form.siteId = context.props.siteId;
|
form.siteId = context.props.siteId;
|
||||||
const res = await api.AddObj(form);
|
const res = await api.AddObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkStatusDict = dict({
|
const checkStatusDict = dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "成功", value: "ok", color: "green" },
|
{ label: t("certd.statusSuccess"), value: "ok", color: "green" },
|
||||||
{ label: "检查中", value: "checking", color: "blue" },
|
{ label: t("certd.statusChecking"), value: "checking", color: "blue" },
|
||||||
{ label: "异常", value: "error", color: "red" },
|
{ label: t("certd.statusError"), value: "error", color: "red" },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const { openSiteIpImportDialog } = useSiteIpMonitor();
|
const { openSiteIpImportDialog } = useSiteIpMonitor();
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
pageRequest,
|
pageRequest,
|
||||||
addRequest,
|
addRequest,
|
||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
labelCol: {
|
labelCol: {
|
||||||
//固定label宽度
|
//固定label宽度
|
||||||
span: null,
|
span: null,
|
||||||
style: {
|
style: {
|
||||||
width: "100px",
|
width: "100px",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
col: {
|
col: {
|
||||||
span: 22,
|
span: 22,
|
||||||
},
|
},
|
||||||
wrapper: {
|
wrapper: {
|
||||||
width: 600,
|
width: 600,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actionbar: {
|
actionbar: {
|
||||||
buttons: {
|
buttons: {
|
||||||
add: {
|
add: {
|
||||||
async click() {
|
async click() {
|
||||||
await crudExpose.openAdd({});
|
await crudExpose.openAdd({});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
import: {
|
import: {
|
||||||
show: true,
|
show: true,
|
||||||
text: "批量导入",
|
text: t("certd.actionImportBatch"),
|
||||||
type: "primary",
|
type: "primary",
|
||||||
async click() {
|
async click() {
|
||||||
openSiteIpImportDialog({
|
openSiteIpImportDialog({
|
||||||
siteId: context.props.siteId,
|
siteId: context.props.siteId,
|
||||||
afterSubmit() {
|
afterSubmit() {
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
load: {
|
load: {
|
||||||
text: "同步IP",
|
text: t("certd.actionSyncIp"),
|
||||||
type: "primary",
|
type: "primary",
|
||||||
async click() {
|
async click() {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "同步IP",
|
title: t("certd.modalTitleSyncIp"),
|
||||||
content: "确定要同步IP吗?",
|
content: t("certd.modalContentSyncIp"),
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await api.DoSync(context.props.siteId);
|
await api.DoSync(context.props.siteId);
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "同步完成",
|
message: t("certd.notificationSyncComplete"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
checkAll: {
|
checkAll: {
|
||||||
text: "检查全部",
|
text: t("certd.actionCheckAll"),
|
||||||
type: "primary",
|
type: "primary",
|
||||||
click: () => {
|
click: () => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "确认",
|
title: t("certd.modalTitleConfirm"),
|
||||||
content: "确认触发检查全部IP站点的证书吗?",
|
content: t("certd.modalContentCheckAll"),
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await siteIpApi.CheckAll(context.props.siteId);
|
await siteIpApi.CheckAll(context.props.siteId);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "检查任务已提交",
|
message: t("certd.notificationCheckSubmitted"),
|
||||||
description: "请稍后刷新页面查看结果",
|
description: t("certd.notificationCheckDescription"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
width: 240,
|
width: 240,
|
||||||
buttons: {
|
buttons: {
|
||||||
check: {
|
check: {
|
||||||
order: 0,
|
order: 0,
|
||||||
type: "link",
|
type: "link",
|
||||||
text: null,
|
text: null,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
title: "立即检查",
|
title: t("certd.tooltipCheckNow"),
|
||||||
},
|
},
|
||||||
icon: "ion:play-sharp",
|
icon: "ion:play-sharp",
|
||||||
click: async ({ row }) => {
|
click: async ({ row }) => {
|
||||||
await api.DoCheck(row.id);
|
await api.DoCheck(row.id);
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "检查任务已提交,请稍后刷新查看结果",
|
message: t("certd.notificationCheckSubmittedPleaseRefresh"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
title: "ID",
|
title: t("certd.columnId"),
|
||||||
key: "id",
|
key: "id",
|
||||||
type: "number",
|
type: "number",
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 80,
|
width: 80,
|
||||||
align: "center",
|
align: "center",
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ipAddress: {
|
ipAddress: {
|
||||||
title: "IP",
|
title: t("certd.columnIp"),
|
||||||
search: {
|
search: {
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: "text",
|
type: "text",
|
||||||
helper: "也支持填写CNAME域名",
|
helper: t("certd.helperIpCname"),
|
||||||
form: {
|
form: {
|
||||||
rules: [{ required: true, message: "请输入IP" }],
|
rules: [{ required: true, message: t("certd.ruleIpRequired") }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 160,
|
width: 160,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
certDomains: {
|
certDomains: {
|
||||||
title: "证书域名",
|
title: t("certd.columnCertDomains"),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: "text",
|
type: "text",
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 200,
|
width: 200,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
show: false,
|
show: false,
|
||||||
cellRender({ value }) {
|
cellRender({ value }) {
|
||||||
return (
|
return (
|
||||||
<a-tooltip title={value} placement="left">
|
<a-tooltip title={value} placement="left">
|
||||||
{value}
|
{value}
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
certProvider: {
|
certProvider: {
|
||||||
title: "颁发机构",
|
title: t("certd.columnCertProvider"),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: "text",
|
type: "text",
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 200,
|
width: 200,
|
||||||
show: false,
|
show: false,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
cellRender({ value }) {
|
cellRender({ value }) {
|
||||||
return <a-tooltip title={value}>{value}</a-tooltip>;
|
return <a-tooltip title={value}>{value}</a-tooltip>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
certStatus: {
|
certStatus: {
|
||||||
title: "证书状态",
|
title: t("certd.columnCertStatus"),
|
||||||
search: {
|
search: {
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "正常", value: "ok", color: "green" },
|
{ label: t("certd.statusNormal"), value: "ok", color: "green" },
|
||||||
{ label: "过期", value: "expired", color: "red" },
|
{ label: t("certd.statusExpired"), value: "expired", color: "red" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
show: true,
|
show: true,
|
||||||
align: "center",
|
align: "center",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
certExpiresTime: {
|
certExpiresTime: {
|
||||||
title: "证书到期时间",
|
title: t("certd.columnCertExpiresTime"),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: "date",
|
type: "date",
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
sorter: true,
|
sorter: true,
|
||||||
cellRender({ value }) {
|
cellRender({ value }) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return "-";
|
return "-";
|
||||||
}
|
}
|
||||||
const expireDate = dayjs(value).format("YYYY-MM-DD");
|
const expireDate = dayjs(value).format("YYYY-MM-DD");
|
||||||
const leftDays = dayjs(value).diff(dayjs(), "day");
|
const leftDays = dayjs(value).diff(dayjs(), "day");
|
||||||
const color = leftDays < 20 ? "red" : "#389e0d";
|
const color = leftDays < 20 ? "red" : "#389e0d";
|
||||||
const percent = (leftDays / 90) * 100;
|
const percent = (leftDays / 90) * 100;
|
||||||
return <a-progress title={expireDate + "过期"} percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}天`} />;
|
return <a-progress title={expireDate + " " + t("certd.expired")} percent={percent} strokeColor={color} format={(percent: number) => `${leftDays} ${t("certd.days")}`} />;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
checkStatus: {
|
checkStatus: {
|
||||||
title: "检查状态",
|
title: t("certd.columnCheckStatus"),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
dict: checkStatusDict,
|
dict: checkStatusDict,
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
align: "center",
|
align: "center",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
cellRender({ value, row, key }) {
|
cellRender({ value, row, key }) {
|
||||||
return (
|
return (
|
||||||
<a-tooltip title={row.error}>
|
<a-tooltip title={row.error}>
|
||||||
<fs-values-format v-model={value} dict={checkStatusDict}></fs-values-format>
|
<fs-values-format v-model={value} dict={checkStatusDict}></fs-values-format>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lastCheckTime: {
|
lastCheckTime: {
|
||||||
title: "上次检查时间",
|
title: t("certd.columnLastCheckTime"),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
sorter: true,
|
sorter: true,
|
||||||
width: 155,
|
width: 155,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
from: {
|
from: {
|
||||||
title: "来源",
|
title: t("certd.columnSource"),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: "dict-switch",
|
type: "dict-switch",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "同步", value: "sync", color: "green" },
|
{ label: t("certd.sourceSync"), value: "sync", color: "green" },
|
||||||
{ label: "手动", value: "manual", color: "blue" },
|
{ label: t("certd.sourceManual"), value: "manual", color: "blue" },
|
||||||
{ label: "导入", value: "import", color: "blue" },
|
{ label: t("certd.sourceImport"), value: "import", color: "blue" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
align: "center",
|
align: "center",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
title: "禁用启用",
|
title: t("certd.columnDisabled"),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: "dict-switch",
|
type: "dict-switch",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "启用", value: false, color: "green" },
|
{ label: t("certd.enabled"), value: false, color: "green" },
|
||||||
{ label: "禁用", value: true, color: "red" },
|
{ label: t("certd.disabled"), value: true, color: "red" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
align: "center",
|
align: "center",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
remark: {
|
remark: {
|
||||||
title: "备注",
|
title: t("certd.columnRemark"),
|
||||||
search: {
|
search: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: "text",
|
type: "text",
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 200,
|
width: 200,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,150 +1,152 @@
|
|||||||
import * as api from "./api.js";
|
import * as api from "./api.js";
|
||||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const { t } = useI18n();
|
||||||
const list = await api.GetTree();
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
|
const list = await api.GetTree();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
records: list,
|
records: list,
|
||||||
total: 10000,
|
total: 10000,
|
||||||
limit: 10000
|
limit: 10000
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
async function afterChange() {
|
async function afterChange() {
|
||||||
await permissionTreeDict.reloadDict();
|
await permissionTreeDict.reloadDict();
|
||||||
}
|
}
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
const ret = await api.UpdateObj(form);
|
const ret = await api.UpdateObj(form);
|
||||||
await afterChange();
|
await afterChange();
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
const delRequest = async ({ row }: DelReq) => {
|
const delRequest = async ({ row }: DelReq) => {
|
||||||
const ret = await api.DelObj(row.id);
|
const ret = await api.DelObj(row.id);
|
||||||
await afterChange();
|
await afterChange();
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
const addRequest = async ({ form }: AddReq) => {
|
const addRequest = async ({ form }: AddReq) => {
|
||||||
const ret = await api.AddObj(form);
|
const ret = await api.AddObj(form);
|
||||||
await afterChange();
|
await afterChange();
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
const permissionTreeDict = dict({
|
const permissionTreeDict = dict({
|
||||||
url: "/sys/authority/permission/tree",
|
url: "/sys/authority/permission/tree",
|
||||||
isTree: true,
|
isTree: true,
|
||||||
value: "id",
|
value: "id",
|
||||||
label: "title",
|
label: "title",
|
||||||
async onReady({ dict }: any) {
|
async onReady({ dict }: any) {
|
||||||
dict.setData([{ id: -1, title: "根节点", children: dict.data }]);
|
dict.setData([{ id: -1, title: t("certd.rootNode"), children: dict.data }]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
pageRequest,
|
pageRequest,
|
||||||
addRequest,
|
addRequest,
|
||||||
editRequest,
|
editRequest,
|
||||||
delRequest
|
delRequest
|
||||||
},
|
},
|
||||||
actionbar: {
|
actionbar: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
toolbar: {
|
toolbar: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
show: false
|
show: false
|
||||||
// scroll: { fixed: true }
|
// scroll: { fixed: true }
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
fixed: "right"
|
fixed: "right"
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
pagination: {
|
pagination: {
|
||||||
show: false,
|
show: false,
|
||||||
pageSize: 100000
|
pageSize: 100000
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
title: "id",
|
title: "id",
|
||||||
type: "number",
|
type: "number",
|
||||||
form: { show: false }, // 表单配置
|
form: { show: false }, // 表单配置
|
||||||
column: {
|
column: {
|
||||||
width: 120,
|
width: 120,
|
||||||
sortable: "custom"
|
sortable: "custom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
title: "权限名称",
|
title: t("certd.permissionName"),
|
||||||
type: "text",
|
type: "text",
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
{ required: true, message: "请输入权限名称" },
|
{ required: true, message: t("certd.enterPermissionName") },
|
||||||
{ max: 50, message: "最大50个字符" }
|
{ max: 50, message: t("certd.max50Chars") }
|
||||||
],
|
],
|
||||||
component: {
|
component: {
|
||||||
placeholder: "权限名称"
|
placeholder: t("certd.permissionName")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 200
|
width: 200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
permission: {
|
||||||
|
title: t("certd.permissionCode"),
|
||||||
|
type: "text",
|
||||||
|
column: {
|
||||||
|
width: 170
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: t("certd.enterPermissionCode") },
|
||||||
|
{ max: 100, message: t("certd.max100Chars") }
|
||||||
|
],
|
||||||
|
component: {
|
||||||
|
placeholder: t("certd.examplePermissionCode")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
title: t("certd.sortOrder"),
|
||||||
|
type: "number",
|
||||||
|
column: {
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
value: 100,
|
||||||
|
rules: [{ required: true, type: "number", message: t("certd.sortRequired") }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parentId: {
|
||||||
|
title: t("certd.parentNode"),
|
||||||
|
type: "dict-tree",
|
||||||
|
column: {
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
dict: permissionTreeDict,
|
||||||
|
form: {
|
||||||
|
value: -1,
|
||||||
|
component: {
|
||||||
|
multiple: false,
|
||||||
|
defaultExpandAll: true,
|
||||||
|
dict: { cache: false },
|
||||||
|
fieldNames: {
|
||||||
|
value: "id",
|
||||||
|
label: "title"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
permission: {
|
}
|
||||||
title: "权限代码",
|
}
|
||||||
type: "text",
|
};
|
||||||
column: {
|
|
||||||
width: 170
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
rules: [
|
|
||||||
{ required: true, message: "请输入权限代码" },
|
|
||||||
{ max: 100, message: "最大100个字符" }
|
|
||||||
],
|
|
||||||
component: {
|
|
||||||
placeholder: "例如:sys:user:view"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sort: {
|
|
||||||
title: "排序",
|
|
||||||
type: "number",
|
|
||||||
column: {
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
value: 100,
|
|
||||||
rules: [{ required: true, type: "number", message: "排序号必填" }]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
parentId: {
|
|
||||||
title: "父节点",
|
|
||||||
type: "dict-tree",
|
|
||||||
column: {
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
dict: permissionTreeDict,
|
|
||||||
form: {
|
|
||||||
value: -1,
|
|
||||||
component: {
|
|
||||||
multiple: false,
|
|
||||||
defaultExpandAll: true,
|
|
||||||
dict: { cache: false },
|
|
||||||
fieldNames: {
|
|
||||||
value: "id",
|
|
||||||
label: "title"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
+173
-172
@@ -1,193 +1,194 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-tree
|
<a-tree v-if="computedTree" ref="treeRef" class="fs-permission-tree" :class="{ 'is-editable': editable }"
|
||||||
v-if="computedTree"
|
:selectable="false" show-line :show-icon="false" :default-expand-all="true" :tree-data="computedTree"
|
||||||
ref="treeRef"
|
@check="onChecked">
|
||||||
class="fs-permission-tree"
|
<template #title="scope">
|
||||||
:class="{ 'is-editable': editable }"
|
<div class="node-title-pane">
|
||||||
:selectable="false"
|
<div class="node-title">{{ scope.title }}</div>
|
||||||
show-line
|
<div v-if="editable === true" class="node-suffix">
|
||||||
:show-icon="false"
|
<fs-icon v-if="actions.add !== false" :icon="$fsui.icons.add" @click.stop="add(scope)" />
|
||||||
:default-expand-all="true"
|
<fs-icon v-if="actions.edit !== false && scope.id !== -1" :icon="$fsui.icons.edit"
|
||||||
:tree-data="computedTree"
|
@click.stop="edit(scope)" />
|
||||||
@check="onChecked"
|
<fs-icon v-if="actions.remove !== false && scope.id !== -1" :icon="$fsui.icons.remove"
|
||||||
>
|
@click.stop="remove(scope)" />
|
||||||
<template #title="scope">
|
</div>
|
||||||
<div class="node-title-pane">
|
</div>
|
||||||
<div class="node-title">{{ scope.title }}</div>
|
</template>
|
||||||
<div v-if="editable === true" class="node-suffix">
|
</a-tree>
|
||||||
<fs-icon v-if="actions.add !== false" :icon="$fsui.icons.add" @click.stop="add(scope)" />
|
|
||||||
<fs-icon v-if="actions.edit !== false && scope.id !== -1" :icon="$fsui.icons.edit" @click.stop="edit(scope)" />
|
|
||||||
<fs-icon v-if="actions.remove !== false && scope.id !== -1" :icon="$fsui.icons.remove" @click.stop="remove(scope)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-tree>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { utils } from "@fast-crud/fast-crud";
|
import { utils } from "@fast-crud/fast-crud";
|
||||||
import { cloneDeep } from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
import { computed, defineComponent, ref } from "vue";
|
import { computed, defineComponent, ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "FsPermissionTree",
|
name: "FsPermissionTree",
|
||||||
props: {
|
props: {
|
||||||
/**
|
/**
|
||||||
* 树形数据
|
* 树形数据
|
||||||
* */
|
* */
|
||||||
tree: {},
|
tree: {},
|
||||||
/**
|
/**
|
||||||
* 是否可编辑
|
* 是否可编辑
|
||||||
*/
|
*/
|
||||||
editable: {
|
editable: {
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
default: {}
|
default: {}
|
||||||
}
|
}
|
||||||
} as any,
|
} as any,
|
||||||
emits: ["add", "edit", "remove"],
|
emits: ["add", "edit", "remove"],
|
||||||
setup(props: any, ctx) {
|
setup(props: any, ctx) {
|
||||||
const treeRef = ref();
|
const { t } = useI18n();
|
||||||
const computedTree = computed(() => {
|
const treeRef = ref();
|
||||||
if (props.tree == null) {
|
const computedTree = computed(() => {
|
||||||
return null;
|
if (props.tree == null) {
|
||||||
}
|
return null;
|
||||||
const clone = cloneDeep(props.tree);
|
}
|
||||||
utils.deepdash.forEachDeep(clone, (value: any, key: any, pNode: any, context: any) => {
|
const clone = cloneDeep(props.tree);
|
||||||
if (value == null) {
|
utils.deepdash.forEachDeep(clone, (value: any, key: any, pNode: any, context: any) => {
|
||||||
return;
|
if (value == null) {
|
||||||
}
|
return;
|
||||||
if (!(value instanceof Object) || value instanceof Array) {
|
}
|
||||||
return;
|
if (!(value instanceof Object) || value instanceof Array) {
|
||||||
}
|
return;
|
||||||
if (value.class === "is-leaf") {
|
}
|
||||||
//处理过,无需再次处理
|
if (value.class === "is-leaf") {
|
||||||
return;
|
//处理过,无需再次处理
|
||||||
}
|
return;
|
||||||
value.class = "is-twig";
|
}
|
||||||
if (value.children != null && value.children.length > 0) {
|
value.class = "is-twig";
|
||||||
return;
|
if (value.children != null && value.children.length > 0) {
|
||||||
}
|
return;
|
||||||
const parents = context.parents;
|
}
|
||||||
if (parents.length < 2) {
|
const parents = context.parents;
|
||||||
return;
|
if (parents.length < 2) {
|
||||||
}
|
return;
|
||||||
const parent = parents[parents.length - 2].value;
|
}
|
||||||
//看parent下面的children,是否全部都没有children
|
const parent = parents[parents.length - 2].value;
|
||||||
for (const child of parent.children) {
|
//看parent下面的children,是否全部都没有children
|
||||||
if (child.children != null && child.children.length > 0) {
|
for (const child of parent.children) {
|
||||||
//存在child有children
|
if (child.children != null && child.children.length > 0) {
|
||||||
return;
|
//存在child有children
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
// 所有的子节点都没有children
|
}
|
||||||
parent.class = "is-twig"; // 连接叶子节点的末梢枝杈节点
|
// 所有的子节点都没有children
|
||||||
let i = 0;
|
parent.class = "is-twig"; // 连接叶子节点的末梢枝杈节点
|
||||||
for (const child of parent.children) {
|
let i = 0;
|
||||||
child.class = "is-leaf";
|
for (const child of parent.children) {
|
||||||
if (i !== 0) {
|
child.class = "is-leaf";
|
||||||
child.class += " leaf-after";
|
if (i !== 0) {
|
||||||
}
|
child.class += " leaf-after";
|
||||||
i++;
|
}
|
||||||
}
|
i++;
|
||||||
});
|
}
|
||||||
return [
|
});
|
||||||
{
|
return [
|
||||||
title: "根节点",
|
{
|
||||||
id: -1,
|
title: t("certd.rootNode"),
|
||||||
children: clone
|
id: -1,
|
||||||
}
|
children: clone
|
||||||
];
|
}
|
||||||
});
|
];
|
||||||
function add(scope: any) {
|
});
|
||||||
ctx.emit("add", scope.dataRef);
|
function add(scope: any) {
|
||||||
}
|
ctx.emit("add", scope.dataRef);
|
||||||
function edit(scope: any) {
|
}
|
||||||
ctx.emit("edit", scope.dataRef);
|
function edit(scope: any) {
|
||||||
}
|
ctx.emit("edit", scope.dataRef);
|
||||||
function remove(scope: any) {
|
}
|
||||||
ctx.emit("remove", scope.dataRef);
|
function remove(scope: any) {
|
||||||
}
|
ctx.emit("remove", scope.dataRef);
|
||||||
function onChecked(a: any, b: any, c: any) {
|
}
|
||||||
utils.logger.info("chedcked", a, b, c);
|
function onChecked(a: any, b: any, c: any) {
|
||||||
}
|
utils.logger.info("chedcked", a, b, c);
|
||||||
function getChecked() {
|
}
|
||||||
const checked = treeRef.value.checkedKeys;
|
function getChecked() {
|
||||||
const halfChecked = treeRef.value.halfCheckedKeys;
|
const checked = treeRef.value.checkedKeys;
|
||||||
return {
|
const halfChecked = treeRef.value.halfCheckedKeys;
|
||||||
checked,
|
return {
|
||||||
halfChecked
|
checked,
|
||||||
};
|
halfChecked
|
||||||
}
|
};
|
||||||
return {
|
}
|
||||||
computedTree,
|
return {
|
||||||
add,
|
computedTree,
|
||||||
edit,
|
add,
|
||||||
remove,
|
edit,
|
||||||
treeRef,
|
remove,
|
||||||
onChecked,
|
treeRef,
|
||||||
getChecked
|
onChecked,
|
||||||
};
|
getChecked,
|
||||||
}
|
t
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.fs-permission-tree {
|
.fs-permission-tree {
|
||||||
.ant-tree-list-holder-inner {
|
.ant-tree-list-holder-inner {
|
||||||
flex-direction: row !important;
|
flex-direction: row !important;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
.is-twig {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-leaf {
|
.is-twig {
|
||||||
//border-bottom: 1px solid #ddd;
|
width: 100%;
|
||||||
&::before {
|
}
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.leaf-after {
|
.is-leaf {
|
||||||
.ant-tree-indent-unit {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-title-pane {
|
//border-bottom: 1px solid #ddd;
|
||||||
border-bottom: 1px solid #ddd;
|
&::before {
|
||||||
}
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
//.is-twig ul {
|
|
||||||
// display: flex;
|
|
||||||
// flex-wrap: wrap;
|
|
||||||
//}
|
|
||||||
.node-title-pane {
|
|
||||||
display: flex;
|
|
||||||
.node-title {
|
|
||||||
width: 110px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-editable {
|
&.leaf-after {
|
||||||
.ant-tree-title {
|
.ant-tree-indent-unit {
|
||||||
&:hover {
|
display: none;
|
||||||
.node-suffix {
|
}
|
||||||
visibility: visible;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-suffix {
|
.node-title-pane {
|
||||||
visibility: hidden;
|
border-bottom: 1px solid #ddd;
|
||||||
> * {
|
}
|
||||||
margin-left: 3px;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
//.is-twig ul {
|
||||||
|
// display: flex;
|
||||||
|
// flex-wrap: wrap;
|
||||||
|
//}
|
||||||
|
.node-title-pane {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.node-title {
|
||||||
|
width: 110px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-editable {
|
||||||
|
.ant-tree-title {
|
||||||
|
&:hover {
|
||||||
|
.node-suffix {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-suffix {
|
||||||
|
visibility: hidden;
|
||||||
|
|
||||||
|
>* {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,76 +1,81 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page>
|
<fs-page>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">权限管理</div>
|
<div class="title">{{ t("certd.permissionManagement") }}</div>
|
||||||
</template>
|
</template>
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||||
<a-button v-permission="'1sys:auth:per:add'" style="margin-left: 20px" @click="addHandle({})">
|
<a-button v-permission="'1sys:auth:per:add'" style="margin-left: 20px" @click="addHandle({})">
|
||||||
<fs-icon :icon="ui.icons.add"></fs-icon>
|
<fs-icon :icon="ui.icons.add"></fs-icon>
|
||||||
添加
|
{{ t("certd.adda") }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<fs-permission-tree class="permission-tree mt-10" :tree="crudBinding.data" :checkable="false" :actions="permission" @add="addHandle" @edit="editHandle" @remove="removeHandle"></fs-permission-tree>
|
<fs-permission-tree class="permission-tree mt-10" :tree="crudBinding.data" :checkable="false"
|
||||||
</fs-crud>
|
:actions="permission" @add="addHandle" @edit="editHandle" @remove="removeHandle"></fs-permission-tree>
|
||||||
</fs-page>
|
</fs-crud>
|
||||||
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onActivated, onMounted, ref } from "vue";
|
import { defineComponent, onActivated, onMounted, ref } from "vue";
|
||||||
import createCrudOptions from "./crud.js";
|
import createCrudOptions from "./crud.js";
|
||||||
import FsPermissionTree from "./fs-permission-tree.vue";
|
import FsPermissionTree from "./fs-permission-tree.vue";
|
||||||
import { usePermission } from "/src/plugin/permission";
|
import { usePermission } from "/src/plugin/permission";
|
||||||
import { useFs, useUi } from "@fast-crud/fast-crud";
|
import { useFs, useUi } from "@fast-crud/fast-crud";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "AuthorityManager",
|
name: "AuthorityManager",
|
||||||
components: { FsPermissionTree },
|
components: { FsPermissionTree },
|
||||||
setup() {
|
setup() {
|
||||||
// 此处传入permission进行通用按钮权限设置,会通过commonOptions去设置actionbar和rowHandle的按钮的show属性
|
// 此处传入permission进行通用按钮权限设置,会通过commonOptions去设置actionbar和rowHandle的按钮的show属性
|
||||||
// 更多关于按钮权限的源代码设置,请参考 ./src/plugin/fast-crud/index.js (75-77行)
|
// 更多关于按钮权限的源代码设置,请参考 ./src/plugin/fast-crud/index.js (75-77行)
|
||||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: "sys:auth:per" } });
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: "sys:auth:per" } });
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
onActivated(async () => {
|
onActivated(async () => {
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
const { ui } = useUi();
|
const { ui } = useUi();
|
||||||
|
|
||||||
//用户业务代码
|
//用户业务代码
|
||||||
|
|
||||||
async function addHandle(item: any) {
|
async function addHandle(item: any) {
|
||||||
await crudExpose.openAdd({ row: { parentId: item?.id ?? -1 } });
|
await crudExpose.openAdd({ row: { parentId: item?.id ?? -1 } });
|
||||||
}
|
}
|
||||||
async function editHandle(item: any) {
|
async function editHandle(item: any) {
|
||||||
await crudExpose.openEdit({ row: item });
|
await crudExpose.openEdit({ row: item });
|
||||||
}
|
}
|
||||||
async function removeHandle(item: any) {
|
async function removeHandle(item: any) {
|
||||||
await crudExpose.doRemove({ row: { id: item.id }, index: null });
|
await crudExpose.doRemove({ row: { id: item.id }, index: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { hasPermissions } = usePermission();
|
const { hasPermissions } = usePermission();
|
||||||
const permission = ref({
|
const permission = ref({
|
||||||
add: hasPermissions("1sys:auth:per:add"),
|
add: hasPermissions("1sys:auth:per:add"),
|
||||||
edit: hasPermissions("1sys:auth:per:edit"),
|
edit: hasPermissions("1sys:auth:per:edit"),
|
||||||
remove: hasPermissions("1sys:auth:per:remove"),
|
remove: hasPermissions("1sys:auth:per:remove"),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ui,
|
ui,
|
||||||
crudBinding,
|
crudBinding,
|
||||||
crudRef,
|
crudRef,
|
||||||
addHandle,
|
addHandle,
|
||||||
editHandle,
|
editHandle,
|
||||||
removeHandle,
|
removeHandle,
|
||||||
permission,
|
permission,
|
||||||
};
|
t
|
||||||
},
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.permission-tree {
|
.permission-tree {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,84 +1,86 @@
|
|||||||
import * as api from "./api";
|
import * as api from "./api";
|
||||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
export default function ({ crudExpose, context: { authz } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context: { authz } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const { t } = useI18n();
|
||||||
return await api.GetList(query);
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
};
|
return await api.GetList(query);
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
};
|
||||||
form.id = row.id;
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
return await api.UpdateObj(form);
|
form.id = row.id;
|
||||||
};
|
return await api.UpdateObj(form);
|
||||||
const delRequest = async ({ row }: DelReq) => {
|
};
|
||||||
return await api.DelObj(row.id);
|
const delRequest = async ({ row }: DelReq) => {
|
||||||
};
|
return await api.DelObj(row.id);
|
||||||
|
};
|
||||||
|
|
||||||
const addRequest = async ({ form }: AddReq) => {
|
const addRequest = async ({ form }: AddReq) => {
|
||||||
return await api.AddObj(form);
|
return await api.AddObj(form);
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
pageRequest,
|
pageRequest,
|
||||||
addRequest,
|
addRequest,
|
||||||
editRequest,
|
editRequest,
|
||||||
delRequest
|
delRequest
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
width: 300,
|
width: 300,
|
||||||
buttons: {
|
buttons: {
|
||||||
authz: {
|
authz: {
|
||||||
type: "link",
|
type: "link",
|
||||||
text: "授权",
|
text: "授权",
|
||||||
async click(context) {
|
async click(context) {
|
||||||
await authz.authzOpen(context.record.id);
|
await authz.authzOpen(context.record.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
title: "id",
|
title: "id",
|
||||||
type: "text",
|
type: "text",
|
||||||
form: { show: false }, // 表单配置
|
form: { show: false }, // 表单配置
|
||||||
column: {
|
column: {
|
||||||
width: 70,
|
width: 70,
|
||||||
sorter: true
|
sorter: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
title: "角色名称",
|
title: t("certd.roleName"),
|
||||||
type: "text",
|
type: "text",
|
||||||
search: { show: true },
|
search: { show: true },
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
{ required: true, message: "请输入角色名称" },
|
{ required: true, message: t("certd.enterRoleName") },
|
||||||
{ max: 50, message: "最大50个字符" }
|
{ max: 50, message: t("certd.max50Chars") }
|
||||||
]
|
]
|
||||||
}, // 表单配置
|
}, // 表单配置
|
||||||
column: {
|
column: {
|
||||||
sorter: true
|
sorter: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createTime: {
|
createTime: {
|
||||||
title: "创建时间",
|
title: t("certd.createTime"),
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
column: {
|
column: {
|
||||||
sorter: true
|
sorter: true
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateTime: {
|
updateTime: {
|
||||||
title: "更新时间",
|
title: t("certd.updateTime"),
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
column: {
|
column: {
|
||||||
sorter: true
|
sorter: true
|
||||||
},
|
},
|
||||||
form: { show: false } // 表单配置
|
form: { show: false } // 表单配置
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page>
|
<fs-page>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">角色管理</div>
|
<div class="title">{{ t("certd.roleManagement") }}</div>
|
||||||
</template>
|
</template>
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||||
<a-modal v-model:open="authzDialogVisible" width="860px" title="分配权限" @ok="updatePermission">
|
<a-modal v-model:open="authzDialogVisible" width="860px" :title="t('certd.assignPermissions')"
|
||||||
<fs-permission-tree ref="permissionTreeRef" v-model:checked-keys="checkedKeys" :tree="permissionTreeData" :editable="false" checkable :replace-fields="{ key: 'id', label: 'title' }"> </fs-permission-tree>
|
@ok="updatePermission">
|
||||||
</a-modal>
|
<fs-permission-tree ref="permissionTreeRef" v-model:checked-keys="checkedKeys" :tree="permissionTreeData"
|
||||||
</fs-page>
|
:editable="false" checkable :replace-fields="{ key: 'id', label: 'title' }"> </fs-permission-tree>
|
||||||
|
</a-modal>
|
||||||
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onActivated, onMounted, ref } from "vue";
|
import { defineComponent, onActivated, onMounted, ref } from "vue";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
@@ -19,103 +22,106 @@ import * as api from "./api";
|
|||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import FsPermissionTree from "../permission/fs-permission-tree.vue";
|
import FsPermissionTree from "../permission/fs-permission-tree.vue";
|
||||||
import { UseCrudPermissionCompProps, UseCrudPermissionExtraProps } from "/@/plugin/permission";
|
import { UseCrudPermissionCompProps, UseCrudPermissionExtraProps } from "/@/plugin/permission";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
function useAuthz() {
|
function useAuthz() {
|
||||||
const checkedKeys = ref();
|
const checkedKeys = ref();
|
||||||
|
|
||||||
const permissionTreeData = ref();
|
const permissionTreeData = ref();
|
||||||
|
|
||||||
const permissionTreeRef = ref();
|
const permissionTreeRef = ref();
|
||||||
const authzDialogVisible = ref(false);
|
const authzDialogVisible = ref(false);
|
||||||
|
|
||||||
const currentRoleId = ref();
|
const currentRoleId = ref();
|
||||||
|
|
||||||
// 如果勾选节点中存在非叶子节点,tree组件会将其所有子节点全部勾选
|
// 如果勾选节点中存在非叶子节点,tree组件会将其所有子节点全部勾选
|
||||||
// 所以要找出所有叶子节点,仅勾选叶子节点,tree组件会将父节点同步勾选
|
// 所以要找出所有叶子节点,仅勾选叶子节点,tree组件会将父节点同步勾选
|
||||||
function getAllCheckedLeafNodeId(tree: any, checkedIds: any, temp: any) {
|
function getAllCheckedLeafNodeId(tree: any, checkedIds: any, temp: any) {
|
||||||
for (let i = 0; i < tree.length; i++) {
|
for (let i = 0; i < tree.length; i++) {
|
||||||
const item = tree[i];
|
const item = tree[i];
|
||||||
if (item.children && item.children.length !== 0) {
|
if (item.children && item.children.length !== 0) {
|
||||||
getAllCheckedLeafNodeId(item.children, checkedIds, temp);
|
getAllCheckedLeafNodeId(item.children, checkedIds, temp);
|
||||||
} else {
|
} else {
|
||||||
if (checkedIds.indexOf(item.id) !== -1) {
|
if (checkedIds.indexOf(item.id) !== -1) {
|
||||||
temp.push(item.id);
|
temp.push(item.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
function authzClose() {
|
function authzClose() {
|
||||||
authzDialogVisible.value = false;
|
authzDialogVisible.value = false;
|
||||||
}
|
}
|
||||||
async function authzOpen(roleId: any) {
|
async function authzOpen(roleId: any) {
|
||||||
permissionTreeData.value = await permissionApi.GetTree();
|
permissionTreeData.value = await permissionApi.GetTree();
|
||||||
checkedKeys.value = [];
|
checkedKeys.value = [];
|
||||||
currentRoleId.value = roleId;
|
currentRoleId.value = roleId;
|
||||||
// this.treeData = ret.data
|
// this.treeData = ret.data
|
||||||
await updateChecked(roleId);
|
await updateChecked(roleId);
|
||||||
authzDialogVisible.value = true;
|
authzDialogVisible.value = true;
|
||||||
}
|
}
|
||||||
async function updateChecked(roleId: any) {
|
async function updateChecked(roleId: any) {
|
||||||
let checkedIds = await api.getPermissionIds(roleId);
|
let checkedIds = await api.getPermissionIds(roleId);
|
||||||
// 找出所有的叶子节点
|
// 找出所有的叶子节点
|
||||||
checkedIds = getAllCheckedLeafNodeId(permissionTreeData.value, checkedIds, []);
|
checkedIds = getAllCheckedLeafNodeId(permissionTreeData.value, checkedIds, []);
|
||||||
checkedKeys.value = checkedIds;
|
checkedKeys.value = checkedIds;
|
||||||
}
|
}
|
||||||
async function updatePermission() {
|
async function updatePermission() {
|
||||||
const roleId = currentRoleId.value;
|
const roleId = currentRoleId.value;
|
||||||
const { checked, halfChecked } = permissionTreeRef.value.getChecked();
|
const { checked, halfChecked } = permissionTreeRef.value.getChecked();
|
||||||
const allChecked = [...checked, ...halfChecked];
|
const allChecked = [...checked, ...halfChecked];
|
||||||
await api.DoAuthz(roleId, allChecked);
|
await api.DoAuthz(roleId, allChecked);
|
||||||
authzClose();
|
authzClose();
|
||||||
//await updateChecked(roleId);
|
//await updateChecked(roleId);
|
||||||
|
|
||||||
message.success("授权成功");
|
message.success("授权成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
authzOpen,
|
authzOpen,
|
||||||
updatePermission,
|
updatePermission,
|
||||||
authzDialogVisible,
|
authzDialogVisible,
|
||||||
permissionTreeData,
|
permissionTreeData,
|
||||||
checkedKeys,
|
checkedKeys,
|
||||||
permissionTreeRef,
|
permissionTreeRef,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "RoleManager",
|
name: "RoleManager",
|
||||||
components: { FsPermissionTree },
|
components: { FsPermissionTree },
|
||||||
setup() {
|
setup() {
|
||||||
//授权配置
|
//授权配置
|
||||||
const authz = useAuthz();
|
const { t } = useI18n();
|
||||||
const permission: UseCrudPermissionCompProps = {
|
const authz = useAuthz();
|
||||||
prefix: "sys:auth:role", //权限代码前缀
|
const permission: UseCrudPermissionCompProps = {
|
||||||
extra: ({ hasActionPermission }: UseCrudPermissionExtraProps): any => {
|
prefix: "sys:auth:role", //权限代码前缀
|
||||||
//额外按钮权限控制
|
extra: ({ hasActionPermission }: UseCrudPermissionExtraProps): any => {
|
||||||
return { rowHandle: { buttons: { authz: { show: hasActionPermission("authz") } } } };
|
//额外按钮权限控制
|
||||||
},
|
return { rowHandle: { buttons: { authz: { show: hasActionPermission("authz") } } } };
|
||||||
};
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化crud配置
|
// 初始化crud配置
|
||||||
// 此处传入permission进行通用按钮权限设置,会通过commonOptions去设置actionbar和rowHandle的按钮的show属性
|
// 此处传入permission进行通用按钮权限设置,会通过commonOptions去设置actionbar和rowHandle的按钮的show属性
|
||||||
// 更多关于按钮权限的源代码设置,请参考 ./src/plugin/fast-crud/index.js (75-77行)
|
// 更多关于按钮权限的源代码设置,请参考 ./src/plugin/fast-crud/index.js (75-77行)
|
||||||
|
|
||||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { authz, permission } });
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { authz, permission } });
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
onActivated(async () => {
|
onActivated(async () => {
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
crudBinding,
|
crudBinding,
|
||||||
crudRef,
|
crudRef,
|
||||||
...authz,
|
...authz,
|
||||||
};
|
t
|
||||||
},
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -42,18 +42,18 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
fixed: "right",
|
fixed: "right",
|
||||||
buttons: {
|
buttons: {
|
||||||
unlock: {
|
unlock: {
|
||||||
title: "解除登录锁定",
|
title: t("certd.unlockLogin"),
|
||||||
text: null,
|
text: null,
|
||||||
type: "link",
|
type: "link",
|
||||||
icon: "ion:lock-open-outline",
|
icon: "ion:lock-open-outline",
|
||||||
click: async ({ row }) => {
|
click: async ({ row }) => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "提示",
|
title: t("certd.notice"),
|
||||||
content: "确定要解除该用户的登录锁定吗?",
|
content: t("certd.confirmUnlock"),
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await api.Unlock(row.id);
|
await api.Unlock(row.id);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "解除成功",
|
message: t("certd.unlockSuccess"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -78,7 +78,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
createTime: {
|
createTime: {
|
||||||
title: "创建时间",
|
title: t("certd.createTime"),
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
form: { show: false }, // 表单配置
|
form: { show: false }, // 表单配置
|
||||||
column: {
|
column: {
|
||||||
@@ -96,13 +96,13 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
username: {
|
username: {
|
||||||
title: "用户名",
|
title: t("certd.username"),
|
||||||
type: "text",
|
type: "text",
|
||||||
search: { show: true }, // 开启查询
|
search: { show: true }, // 开启查询
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
{ required: true, message: "请输入用户名" },
|
{ required: true, message: t("certd.enterUsername") },
|
||||||
{ max: 50, message: "最大50个字符" },
|
{ max: 50, message: t("certd.max50Chars") },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
editForm: { component: { disabled: false } },
|
editForm: { component: { disabled: false } },
|
||||||
@@ -112,18 +112,18 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
title: "密码",
|
title: t("certd.password"),
|
||||||
type: "text",
|
type: "text",
|
||||||
key: "password",
|
key: "password",
|
||||||
column: {
|
column: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [{ max: 50, message: "最大50个字符" }],
|
rules: [{ max: 50, message: t("certd.max50Chars") }],
|
||||||
component: {
|
component: {
|
||||||
showPassword: true,
|
showPassword: true,
|
||||||
},
|
},
|
||||||
helper: "填写则修改密码",
|
helper: t("certd.modifyPasswordIfFilled"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
nickName: {
|
nickName: {
|
||||||
@@ -138,11 +138,11 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
title: "邮箱",
|
title: t("certd.emaila"),
|
||||||
type: "text",
|
type: "text",
|
||||||
search: { show: true }, // 开启查询
|
search: { show: true }, // 开启查询
|
||||||
form: {
|
form: {
|
||||||
rules: [{ max: 50, message: "最大50个字符" }],
|
rules: [{ max: 50, message: t("certd.max50Chars") }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
sorter: true,
|
sorter: true,
|
||||||
@@ -150,11 +150,11 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mobile: {
|
mobile: {
|
||||||
title: "手机号",
|
title: t("certd.mobile"),
|
||||||
type: "text",
|
type: "text",
|
||||||
search: { show: true }, // 开启查询
|
search: { show: true }, // 开启查询
|
||||||
form: {
|
form: {
|
||||||
rules: [{ max: 50, message: "最大50个字符" }],
|
rules: [{ max: 50, message: t("certd.max50Chars") }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
sorter: true,
|
sorter: true,
|
||||||
@@ -162,12 +162,11 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
title: "头像",
|
title: t("certd.avatar"),
|
||||||
type: "cropper-uploader",
|
type: "cropper-uploader",
|
||||||
column: {
|
column: {
|
||||||
width: 70,
|
width: 70,
|
||||||
component: {
|
component: {
|
||||||
//设置高度,修复操作列错位的问题
|
|
||||||
style: {
|
style: {
|
||||||
height: "30px",
|
height: "30px",
|
||||||
width: "auto",
|
width: "auto",
|
||||||
@@ -205,12 +204,12 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
title: "状态",
|
title: t("certd.status"),
|
||||||
type: "dict-switch",
|
type: "dict-switch",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "启用", value: 1, color: "green" },
|
{ label: t("certd.enabled"), value: 1, color: "green" },
|
||||||
{ label: "禁用", value: 0, color: "red" },
|
{ label: t("certd.disabled"), value: 0, color: "red" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
column: {
|
column: {
|
||||||
@@ -220,7 +219,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
validTime: {
|
validTime: {
|
||||||
title: "有效期",
|
title: t("certd.validTime"),
|
||||||
type: "date",
|
type: "date",
|
||||||
form: {
|
form: {
|
||||||
show: userValidTimeEnabled,
|
show: userValidTimeEnabled,
|
||||||
@@ -235,7 +234,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (value < dayjs().valueOf()) {
|
if (value < dayjs().valueOf()) {
|
||||||
return <a-tag color={"red"}>已过期</a-tag>;
|
return <a-tag color={"red"}>{t("certd.expired")}</a-tag>;
|
||||||
}
|
}
|
||||||
const date = dayjs(value).format("YYYY-MM-DD");
|
const date = dayjs(value).format("YYYY-MM-DD");
|
||||||
return (
|
return (
|
||||||
@@ -257,17 +256,17 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
remark: {
|
remark: {
|
||||||
title: "备注",
|
title: t("certd.remark"),
|
||||||
type: "text",
|
type: "text",
|
||||||
column: {
|
column: {
|
||||||
sorter: true,
|
sorter: true,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [{ max: 100, message: "最大100个字符" }],
|
rules: [{ max: 100, message: t("certd.max100Chars") }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
roles: {
|
roles: {
|
||||||
title: "角色",
|
title: t("certd.roles"),
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
url: "/sys/authority/role/list",
|
url: "/sys/authority/role/list",
|
||||||
|
|||||||
@@ -8,244 +8,244 @@ import { useSettingStore } from "/@/store/settings";
|
|||||||
import { Modal } from "ant-design-vue";
|
import { Modal } from "ant-design-vue";
|
||||||
|
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
const res = await api.UpdateObj(form);
|
const res = await api.UpdateObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
const delRequest = async ({ row }: DelReq) => {
|
const delRequest = async ({ row }: DelReq) => {
|
||||||
return await api.DelObj(row.id);
|
return await api.DelObj(row.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addRequest = async ({ form }: AddReq) => {
|
const addRequest = async ({ form }: AddReq) => {
|
||||||
const res = await api.AddObj(form);
|
const res = await api.AddObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const selectedRowKeys: Ref<any[]> = ref([]);
|
const selectedRowKeys: Ref<any[]> = ref([]);
|
||||||
context.selectedRowKeys = selectedRowKeys;
|
context.selectedRowKeys = selectedRowKeys;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
settings: {
|
settings: {
|
||||||
plugins: {
|
plugins: {
|
||||||
//这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并
|
//这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并
|
||||||
rowSelection: {
|
rowSelection: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
order: -2,
|
order: -2,
|
||||||
before: true,
|
before: true,
|
||||||
// handle: (pluginProps,useCrudProps)=>CrudOptions,
|
// handle: (pluginProps,useCrudProps)=>CrudOptions,
|
||||||
props: {
|
props: {
|
||||||
multiple: true,
|
multiple: true,
|
||||||
crossPage: true,
|
crossPage: true,
|
||||||
selectedRowKeys
|
selectedRowKeys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
pageRequest,
|
pageRequest,
|
||||||
addRequest,
|
addRequest,
|
||||||
editRequest,
|
editRequest,
|
||||||
delRequest
|
delRequest
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
fixed: "right"
|
fixed: "right"
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
title: "ID",
|
title: "ID",
|
||||||
key: "id",
|
key: "id",
|
||||||
type: "number",
|
type: "number",
|
||||||
column: {
|
column: {
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
domain: {
|
domain: {
|
||||||
title: "CNAME域名",
|
title: t("certd.cnameDomain"),
|
||||||
type: "text",
|
type: "text",
|
||||||
editForm: {
|
editForm: {
|
||||||
component: {
|
component: {
|
||||||
disabled: true
|
disabled: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
show: true
|
show: true,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
placeholder: "cname.handsfree.work"
|
placeholder: t("certd.cnameDomainPlaceholder"),
|
||||||
},
|
},
|
||||||
helper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名",
|
helper: t("certd.cnameDomainHelper"),
|
||||||
rules: [{ required: true, message: "此项必填" }]
|
rules: [{ required: true, message: t("certd.requiredField") }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 200
|
width: 200,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
dnsProviderType: {
|
dnsProviderType: {
|
||||||
title: "DNS提供商",
|
title: t("certd.dnsProvider"),
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
search: {
|
search: {
|
||||||
show: true
|
show: true,
|
||||||
},
|
},
|
||||||
dict: dict({
|
dict: dict({
|
||||||
url: "pi/dnsProvider/list",
|
url: "pi/dnsProvider/list",
|
||||||
value: "key",
|
value: "key",
|
||||||
label: "title"
|
label: "title",
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
rules: [{ required: true, message: "此项必填" }]
|
rules: [{ required: true, message: t("certd.requiredField") }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 150,
|
width: 150,
|
||||||
component: {
|
component: {
|
||||||
color: "auto"
|
color: "auto",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
accessId: {
|
accessId: {
|
||||||
title: "DNS提供商授权",
|
title: t("certd.dnsProviderAuthorization"),
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
url: "/pi/access/list",
|
url: "/pi/access/list",
|
||||||
value: "id",
|
value: "id",
|
||||||
label: "name"
|
label: "name",
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
name: "access-selector",
|
name: "access-selector",
|
||||||
vModel: "modelValue",
|
vModel: "modelValue",
|
||||||
type: compute(({ form }) => {
|
type: compute(({ form }) => {
|
||||||
return form.dnsProviderType;
|
return form.dnsProviderType;
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
rules: [{ required: true, message: "此项必填" }]
|
rules: [{ required: true, message: t("certd.requiredField") }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 150,
|
width: 150,
|
||||||
component: {
|
component: {
|
||||||
color: "auto"
|
color: "auto",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
isDefault: {
|
isDefault: {
|
||||||
title: "是否默认",
|
title: t("certd.isDefault"),
|
||||||
type: "dict-switch",
|
type: "dict-switch",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "是", value: true, color: "success" },
|
{ label: t("certd.yes"), value: true, color: "success" },
|
||||||
{ label: "否", value: false, color: "default" }
|
{ label: t("certd.no"), value: false, color: "default" },
|
||||||
]
|
],
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
value: false,
|
value: false,
|
||||||
rules: [{ required: true, message: "请选择是否默认" }]
|
rules: [{ required: true, message: t("certd.selectIsDefault") }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
align: "center",
|
align: "center",
|
||||||
width: 100
|
width: 100,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
setDefault: {
|
setDefault: {
|
||||||
title: "设置默认",
|
title: t("certd.setDefault"),
|
||||||
type: "text",
|
type: "text",
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
align: "center",
|
align: "center",
|
||||||
conditionalRenderDisabled: true,
|
conditionalRenderDisabled: true,
|
||||||
cellRender: ({ row }) => {
|
cellRender: ({ row }) => {
|
||||||
if (row.isDefault) {
|
if (row.isDefault) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const onClick = async () => {
|
const onClick = async () => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "提示",
|
title: t("certd.prompt"),
|
||||||
content: `确定要设置为默认吗?`,
|
content: t("certd.confirmSetDefault"),
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await api.SetDefault(row.id);
|
await api.SetDefault(row.id);
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a-button type={"link"} size={"small"} onClick={onClick}>
|
<a-button type={"link"} size={"small"} onClick={onClick}>
|
||||||
设为默认
|
{t("certd.setAsDefault")}
|
||||||
</a-button>
|
</a-button>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
title: "禁用/启用",
|
title: t("certd.disabled"),
|
||||||
type: "dict-switch",
|
type: "dict-switch",
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "启用", value: false, color: "success" },
|
{ label: t("certd.enabled"), value: false, color: "success" },
|
||||||
{ label: "禁用", value: true, color: "error" }
|
{ label: t("certd.disabledLabel"), value: true, color: "error" },
|
||||||
]
|
],
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
value: false
|
value: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
component: {
|
component: {
|
||||||
title: "点击可禁用/启用",
|
title: t("certd.clickToToggle"),
|
||||||
on: {
|
on: {
|
||||||
async click({ value, row }) {
|
async click({ value, row }) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "提示",
|
title: t("certd.prompt"),
|
||||||
content: `确定要${!value ? "禁用" : "启用"}吗?`,
|
content: t("certd.confirmToggleStatus", { action: !value ? t("certd.disable") : t("certd.enable") }),
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await api.SetDisabled(row.id, !value);
|
await api.SetDisabled(row.id, !value);
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
createTime: {
|
createTime: {
|
||||||
title: "创建时间",
|
title: t("certd.createTime"),
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
sorter: true,
|
sorter: true,
|
||||||
width: 160,
|
width: 160,
|
||||||
align: "center"
|
align: "center",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
updateTime: {
|
updateTime: {
|
||||||
title: "更新时间",
|
title: t("certd.updateTime"),
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
show: true,
|
show: true,
|
||||||
width: 160
|
width: 160,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,67 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page class="page-cert">
|
<fs-page class="page-cert">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
CNAME服务配置
|
{{ t("certd.cnameTitle") }}
|
||||||
<span class="sub">
|
<span class="sub">
|
||||||
此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。
|
{{ t("certd.cnameDescription") }}
|
||||||
<a href="https://certd.docmirror.cn/guide/feature/cname/" target="_blank">CNAME功能原理及使用说明</a>
|
<a href="https://certd.docmirror.cn/guide/feature/cname/" target="_blank">
|
||||||
</span>
|
{{ t("certd.cnameLinkText") }}
|
||||||
</div>
|
</a>
|
||||||
</template>
|
</span>
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
</div>
|
||||||
<template #pagination-left>
|
</template>
|
||||||
<a-tooltip title="批量删除">
|
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||||
<fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>
|
<template #pagination-left>
|
||||||
</a-tooltip>
|
<a-tooltip :title="t('certd.batchDelete')">
|
||||||
</template>
|
<fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>
|
||||||
</fs-crud>
|
</a-tooltip>
|
||||||
</fs-page>
|
</template>
|
||||||
|
</fs-crud>
|
||||||
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onActivated, onMounted } from "vue";
|
import { onActivated, onMounted } from "vue";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
import createCrudOptions from "./crud";
|
import createCrudOptions from "./crud";
|
||||||
import { message, Modal } from "ant-design-vue";
|
import { message, Modal } from "ant-design-vue";
|
||||||
import { DeleteBatch } from "./api";
|
import { DeleteBatch } from "./api";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "CnameProvider",
|
name: "CnameProvider",
|
||||||
});
|
});
|
||||||
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
|
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
|
||||||
|
|
||||||
const selectedRowKeys = context.selectedRowKeys;
|
const selectedRowKeys = context.selectedRowKeys;
|
||||||
const handleBatchDelete = () => {
|
const handleBatchDelete = () => {
|
||||||
if (selectedRowKeys.value?.length > 0) {
|
if (selectedRowKeys.value?.length > 0) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "确认",
|
title: t("certd.confirmTitle"),
|
||||||
content: `确定要批量删除这${selectedRowKeys.value.length}条记录吗`,
|
content: t("certd.confirmDeleteBatch", { count: selectedRowKeys.value.length }),
|
||||||
async onOk() {
|
async onOk() {
|
||||||
await DeleteBatch(selectedRowKeys.value);
|
await DeleteBatch(selectedRowKeys.value);
|
||||||
message.info("删除成功");
|
message.info(t("certd.deleteSuccess"));
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
selectedRowKeys.value = [];
|
selectedRowKeys.value = [];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
message.error("请先勾选记录");
|
message.error(t("certd.selectRecordsFirst"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
onActivated(async () => {
|
onActivated(async () => {
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="less"></style>
|
<style lang="less"></style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,58 +1,63 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page class="page-cert">
|
<fs-page class="page-cert">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
插件管理
|
{{ t("certd.pluginManagement") }}
|
||||||
<span class="sub">自定义插件处于BETA测试版,后续可能会有破坏性变更</span>
|
<span class="sub">{{ t("certd.pluginBetaWarning") }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||||
<!-- <template #pagination-left>-->
|
<!-- <template #pagination-left>-->
|
||||||
<!-- <a-tooltip title="批量删除">-->
|
<!-- <a-tooltip :title="t('certd.batchDelete')">-->
|
||||||
<!-- <fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>-->
|
<!-- <fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>-->
|
||||||
<!-- </a-tooltip>-->
|
<!-- </a-tooltip>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
</fs-crud>
|
</fs-crud>
|
||||||
</fs-page>
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onActivated, onMounted } from "vue";
|
import { onActivated, onMounted } from "vue";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
import createCrudOptions from "./crud";
|
import createCrudOptions from "./crud";
|
||||||
import { message, Modal } from "ant-design-vue";
|
import { message, Modal } from "ant-design-vue";
|
||||||
import { DeleteBatch } from "./api";
|
import { DeleteBatch } from "./api";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SysPlugin",
|
name: "SysPlugin",
|
||||||
});
|
});
|
||||||
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
|
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
|
||||||
|
|
||||||
onActivated(async () => {
|
onActivated(async () => {
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedRowKeys = context.selectedRowKeys;
|
const selectedRowKeys = context.selectedRowKeys;
|
||||||
const handleBatchDelete = () => {
|
const handleBatchDelete = () => {
|
||||||
if (selectedRowKeys.value?.length > 0) {
|
if (selectedRowKeys.value?.length > 0) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "确认",
|
title: t("certd.confirm"),
|
||||||
content: `确定要批量删除这${selectedRowKeys.value.length}条记录吗`,
|
content: t("certd.batchDeleteConfirm", { count: selectedRowKeys.value.length }),
|
||||||
async onOk() {
|
async onOk() {
|
||||||
await DeleteBatch(selectedRowKeys.value);
|
await DeleteBatch(selectedRowKeys.value);
|
||||||
message.info("删除成功");
|
message.info(t("certd.deleteSuccess"));
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
selectedRowKeys.value = [];
|
selectedRowKeys.value = [];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
message.error("请先勾选记录");
|
message.error(t("certd.pleaseSelectRecord"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="less"></style>
|
<style lang="less"></style>
|
||||||
|
|||||||
@@ -1,73 +1,86 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page class="page-setting-email">
|
<fs-page class="page-setting-email">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
邮件服务器设置
|
{{ t('certd.emailServerSettings') }}
|
||||||
<span class="sub">设置邮件发送服务器</span>
|
<span class="sub">{{ t('certd.setEmailSendingServer') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="flex-o">
|
<div class="flex-o">
|
||||||
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" class="email-form-box" @finish="onFinish" @finish-failed="onFinishFailed">
|
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }"
|
||||||
<div v-if="!formState.usePlus" class="email-form">
|
autocomplete="off" class="email-form-box" @finish="onFinish" @finish-failed="onFinishFailed">
|
||||||
<a-form-item label="使用自定义邮件服务器"> </a-form-item>
|
<div v-if="!formState.usePlus" class="email-form">
|
||||||
<a-form-item label="SMTP域名" name="host" :rules="[{ required: true, message: '请输入smtp域名或ip' }]">
|
<a-form-item :label="t('certd.useCustomEmailServer')"> </a-form-item>
|
||||||
<a-input v-model:value="formState.host" />
|
<a-form-item :label="t('certd.smtpDomain')" name="host"
|
||||||
</a-form-item>
|
:rules="[{ required: true, message: t('certd.pleaseEnterSmtpDomain') }]">
|
||||||
|
<a-input v-model:value="formState.host" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="SMTP端口" name="port" :rules="[{ required: true, message: '请输入smtp端口号' }]">
|
<a-form-item :label="t('certd.smtpPort')" name="port"
|
||||||
<a-input v-model:value="formState.port" />
|
:rules="[{ required: true, message: t('certd.pleaseEnterSmtpPort') }]">
|
||||||
</a-form-item>
|
<a-input v-model:value="formState.port" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="用户名" :name="['auth', 'user']" :rules="[{ required: true, message: '请输入用户名' }]">
|
<a-form-item :label="t('certd.username')" :name="['auth', 'user']"
|
||||||
<a-input v-model:value="formState.auth.user" />
|
:rules="[{ required: true, message: t('certd.pleaseEnterUsername') }]">
|
||||||
</a-form-item>
|
<a-input v-model:value="formState.auth.user" />
|
||||||
<a-form-item label="密码" :name="['auth', 'pass']" :rules="[{ required: true, message: '请输入密码' }]">
|
</a-form-item>
|
||||||
<a-input-password v-model:value="formState.auth.pass" />
|
<a-form-item :label="t('certd.password')" :name="['auth', 'pass']"
|
||||||
<div class="helper">如果是qq邮箱,需要到qq邮箱的设置里面申请授权码作为密码</div>
|
:rules="[{ required: true, message: t('certd.pleaseEnterPassword') }]">
|
||||||
</a-form-item>
|
<a-input-password v-model:value="formState.auth.pass" />
|
||||||
<a-form-item label="发件邮箱" name="sender" :rules="[{ required: true, message: '请输入发件邮箱' }]">
|
<div class="helper">{{ t('certd.qqEmailAuthCodeHelper') }}</div>
|
||||||
<a-input v-model:value="formState.sender" />
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item :label="t('certd.senderEmail')" name="sender"
|
||||||
<a-form-item label="是否ssl" name="secure">
|
:rules="[{ required: true, message: t('certd.pleaseEnterSenderEmail') }]">
|
||||||
<a-switch v-model:checked="formState.secure" />
|
<a-input v-model:value="formState.sender" />
|
||||||
<div class="helper">ssl和非ssl的smtp端口是不一样的,注意修改端口</div>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item :label="t('certd.useSsl')" name="secure">
|
||||||
<a-form-item label="忽略证书校验" :name="['tls', 'rejectUnauthorized']">
|
<a-switch v-model:checked="formState.secure" />
|
||||||
<a-switch v-model:checked="formState.tls.rejectUnauthorized" />
|
<div class="helper">{{ t('certd.sslPortNote') }}</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('certd.ignoreCertValidation')" :name="['tls', 'rejectUnauthorized']">
|
||||||
|
<a-switch v-model:checked="formState.tls.rejectUnauthorized" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||||
<a-button type="primary" html-type="submit">保存</a-button>
|
<a-button type="primary" html-type="submit">{{ t('certd.save') }}</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="email-form">
|
<div class="email-form">
|
||||||
<a-form-item label="使用官方邮件服务器" name="usePlus">
|
<a-form-item :label="t('certd.useOfficialEmailServer')" name="usePlus">
|
||||||
<div class="flex-o">
|
<div class="flex-o">
|
||||||
<a-switch v-model:checked="formState.usePlus" :disabled="!settingStore.isPlus" @change="onUsePlusChanged" />
|
<a-switch v-model:checked="formState.usePlus" :disabled="!settingStore.isPlus"
|
||||||
<vip-button class="ml-5" mode="button"></vip-button>
|
@change="onUsePlusChanged" />
|
||||||
</div>
|
<vip-button class="ml-5" mode="button"></vip-button>
|
||||||
<div class="helper">使用官方邮箱服务器直接发邮件,免除繁琐的配置</div>
|
</div>
|
||||||
</a-form-item>
|
<div class="helper">{{ t('certd.useOfficialEmailServerHelper') }}</div>
|
||||||
</div>
|
</a-form-item>
|
||||||
</a-form>
|
</div>
|
||||||
</div>
|
</a-form>
|
||||||
<div class="email-form">
|
</div>
|
||||||
<a-form :model="testFormState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onTestSend">
|
<div class="email-form">
|
||||||
<a-form-item label="测试收件邮箱" name="receiver" :rules="[{ required: true, message: '请输入测试收件邮箱' }]">
|
<a-form :model="testFormState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }"
|
||||||
<a-input v-model:value="testFormState.receiver" />
|
autocomplete="off" @finish="onTestSend">
|
||||||
<div class="helper">保存后再点击测试</div>
|
<a-form-item :label="t('certd.testReceiverEmail')" name="receiver"
|
||||||
<div class="helper">发送失败???<a href="https://certd.docmirror.cn/guide/use/email/" target="_blank">邮件配置帮助文档</a></div>
|
:rules="[{ required: true, message: t('certd.pleaseEnterTestReceiverEmail') }]">
|
||||||
<div class="helper">您还可以试试使用官方邮件服务器↗↗↗↗↗↗↗↗</div>
|
<a-input v-model:value="testFormState.receiver" />
|
||||||
</a-form-item>
|
<div class="helper">{{ t('certd.saveBeforeTest') }}</div>
|
||||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
<div class="helper">{{ t('certd.sendFailHelpDoc') }}<a
|
||||||
<a-button type="primary" :loading="testFormState.loading" html-type="submit">测试</a-button>
|
href="https://certd.docmirror.cn/guide/use/email/" target="_blank">{{
|
||||||
</a-form-item>
|
t('certd.emailConfigHelpDoc') }}</a></div>
|
||||||
</a-form>
|
<div class="helper">{{ t('certd.tryOfficialEmailServer') }}</div>
|
||||||
</div>
|
</a-form-item>
|
||||||
</fs-page>
|
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||||
|
<a-button type="primary" :loading="testFormState.loading" html-type="submit">{{ t('certd.test')
|
||||||
|
}}</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive } from "vue";
|
import { reactive } from "vue";
|
||||||
import * as api from "../api";
|
import * as api from "../api";
|
||||||
@@ -75,96 +88,102 @@ import * as emailApi from "./api.email";
|
|||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { useSettingStore } from "/src/store/settings";
|
import { useSettingStore } from "/src/store/settings";
|
||||||
import * as _ from "lodash-es";
|
import * as _ from "lodash-es";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "EmailSetting",
|
name: "EmailSetting",
|
||||||
});
|
});
|
||||||
|
|
||||||
interface FormState {
|
interface FormState {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
auth: {
|
auth: {
|
||||||
user: string;
|
user: string;
|
||||||
pass: string;
|
pass: string;
|
||||||
};
|
};
|
||||||
secure: boolean; // use TLS
|
secure: boolean; // use TLS
|
||||||
tls: {
|
tls: {
|
||||||
// do not fail on invalid certs
|
// do not fail on invalid certs
|
||||||
rejectUnauthorized?: boolean;
|
rejectUnauthorized?: boolean;
|
||||||
};
|
};
|
||||||
sender: string;
|
sender: string;
|
||||||
usePlus: boolean;
|
usePlus: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formState = reactive<Partial<FormState>>({
|
const formState = reactive<Partial<FormState>>({
|
||||||
auth: {
|
auth: {
|
||||||
user: "",
|
user: "",
|
||||||
pass: "",
|
pass: "",
|
||||||
},
|
},
|
||||||
tls: {},
|
tls: {},
|
||||||
usePlus: false,
|
usePlus: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
const data: any = await api.EmailSettingsGet();
|
const data: any = await api.EmailSettingsGet();
|
||||||
_.merge(formState, data);
|
_.merge(formState, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
load();
|
load();
|
||||||
|
|
||||||
const onFinish = async (form: any) => {
|
const onFinish = async (form: any) => {
|
||||||
console.log("Success:", form);
|
console.log("Success:", form);
|
||||||
await api.EmailSettingsSave(form);
|
await api.EmailSettingsSave(form);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "保存成功",
|
message: t("certd.saveSuccess"),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const onFinishFailed = (errorInfo: any) => {
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
// console.log("Failed:", errorInfo);
|
// console.log("Failed:", errorInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function onUsePlusChanged() {
|
async function onUsePlusChanged() {
|
||||||
await api.EmailSettingsSave(formState);
|
await api.EmailSettingsSave(formState);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TestFormState {
|
interface TestFormState {
|
||||||
receiver: string;
|
receiver: string;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
const testFormState = reactive<TestFormState>({
|
const testFormState = reactive<TestFormState>({
|
||||||
receiver: "",
|
receiver: "",
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
async function onTestSend() {
|
async function onTestSend() {
|
||||||
testFormState.loading = true;
|
testFormState.loading = true;
|
||||||
try {
|
try {
|
||||||
await emailApi.TestSend(testFormState.receiver);
|
await emailApi.TestSend(testFormState.receiver);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "发送成功",
|
message: t("certd.sendSuccess"),
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
testFormState.loading = false;
|
testFormState.loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.page-setting-email {
|
.page-setting-email {
|
||||||
.email-form-box {
|
.email-form-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.email-form {
|
|
||||||
width: 500px;
|
|
||||||
margin: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.helper {
|
.email-form {
|
||||||
padding: 1px;
|
width: 500px;
|
||||||
margin: 0px;
|
margin: 20px;
|
||||||
color: #999;
|
}
|
||||||
font-size: 10px;
|
|
||||||
}
|
.helper {
|
||||||
|
padding: 1px;
|
||||||
|
margin: 0px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,54 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sys-settings-form sys-settings-base">
|
<div class="sys-settings-form sys-settings-base">
|
||||||
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onFinish" @finish-failed="onFinishFailed">
|
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off"
|
||||||
<a-form-item label="ICP备案号" :name="['public', 'icpNo']">
|
@finish="onFinish" @finish-failed="onFinishFailed">
|
||||||
<a-input v-model:value="formState.public.icpNo" placeholder="粤ICP备xxxxxxx号" />
|
<a-form-item :label="t('certd.icpRegistrationNumber')" :name="['public', 'icpNo']">
|
||||||
</a-form-item>
|
<a-input v-model:value="formState.public.icpNo" :placeholder="t('certd.icpPlaceholder')" />
|
||||||
<a-form-item label="网安备案号" :name="['public', 'mpsNo']">
|
</a-form-item>
|
||||||
<a-input v-model:value="formState.public.mpsNo" placeholder="京公网安备xxxxxxx号" />
|
<a-form-item :label="t('certd.publicSecurityRegistrationNumber')" :name="['public', 'mpsNo']">
|
||||||
</a-form-item>
|
<a-input v-model:value="formState.public.mpsNo" :placeholder="t('certd.publicSecurityPlaceholder')" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="开启小助手" :name="['public', 'aiChatEnabled']">
|
<a-form-item :label="t('certd.enableAssistant')" :name="['public', 'aiChatEnabled']">
|
||||||
<a-switch v-model:checked="formState.public.aiChatEnabled" />
|
<a-switch v-model:checked="formState.public.aiChatEnabled" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="允许爬虫" :name="['public', 'robots']">
|
<a-form-item :label="t('certd.allowCrawlers')" :name="['public', 'robots']">
|
||||||
<a-switch v-model:checked="formState.public.robots" />
|
<a-switch v-model:checked="formState.public.robots" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="HTTP代理" :name="['private', 'httpProxy']" :rules="urlRules">
|
<a-form-item :label="t('certd.httpProxy')" :name="['private', 'httpProxy']" :rules="urlRules">
|
||||||
<a-input v-model:value="formState.private.httpProxy" placeholder="http://192.168.1.2:18010/" />
|
<a-input v-model:value="formState.private.httpProxy" :placeholder="t('certd.httpProxyPlaceholder')" />
|
||||||
<div class="helper">当某些网站被墙时可以配置</div>
|
<div class="helper">{{ t('certd.httpProxyHelper') }}</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="HTTPS代理" :name="['private', 'httpsProxy']" :rules="urlRules">
|
<a-form-item :label="t('certd.httpsProxy')" :name="['private', 'httpsProxy']" :rules="urlRules">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<a-input v-model:value="formState.private.httpsProxy" placeholder="http://192.168.1.2:18010/" />
|
<a-input v-model:value="formState.private.httpsProxy"
|
||||||
<a-button class="ml-5" type="primary" :loading="testProxyLoading" title="保存后,再点击测试" @click="testProxy">测试</a-button>
|
:placeholder="t('certd.httpsProxyPlaceholder')" />
|
||||||
</div>
|
<a-button class="ml-5" type="primary" :loading="testProxyLoading"
|
||||||
<div class="helper">一般这两个代理填一样的,保存后再测试</div>
|
:title="t('certd.saveThenTestTitle')" @click="testProxy">{{ t('certd.testButton') }}</a-button>
|
||||||
</a-form-item>
|
</div>
|
||||||
|
<div class="helper">{{ t('certd.httpsProxyHelper') }}</div>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="双栈网络" :name="['private', 'dnsResultOrder']">
|
<a-form-item :label="t('certd.dualStackNetwork')" :name="['private', 'dnsResultOrder']">
|
||||||
<a-select v-model:value="formState.private.dnsResultOrder">
|
<a-select v-model:value="formState.private.dnsResultOrder">
|
||||||
<a-select-option value="verbatim">默认</a-select-option>
|
<a-select-option value="verbatim">{{ t('certd.default') }}</a-select-option>
|
||||||
<a-select-option value="ipv4first">IPV4优先</a-select-option>
|
<a-select-option value="ipv4first">{{ t('certd.ipv4Priority') }}</a-select-option>
|
||||||
<a-select-option value="ipv6first">IPV6优先</a-select-option>
|
<a-select-option value="ipv6first">{{ t('certd.ipv6Priority') }}</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<div class="helper">如果选择IPv6优先,需要在docker-compose.yaml中启用ipv6</div>
|
<div class="helper">{{ t('certd.dualStackNetworkHelper') }}</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="启用公共CNAME服务" :name="['private', 'commonCnameEnabled']">
|
<a-form-item :label="t('certd.enableCommonCnameService')" :name="['private', 'commonCnameEnabled']">
|
||||||
<a-switch v-model:checked="formState.private.commonCnameEnabled" />
|
<a-switch v-model:checked="formState.private.commonCnameEnabled" />
|
||||||
<div class="helper">是否可以使用公共CNAME服务,如果禁用,且没有设置<router-link to="/sys/cname/provider">自定义CNAME服务</router-link>,则无法使用CNAME代理方式申请证书</div>
|
<div class="helper" v-html="t('certd.commonCnameHelper')"></div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 8 }">
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 8 }">
|
||||||
<a-button :loading="saveLoading" type="primary" html-type="submit">保存</a-button>
|
<a-button :loading="saveLoading" type="primary" html-type="submit">{{ t('certd.saveButton')
|
||||||
</a-form-item>
|
}}</a-button>
|
||||||
</a-form>
|
</a-form-item>
|
||||||
</div>
|
</a-form>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { reactive, ref } from "vue";
|
import { reactive, ref } from "vue";
|
||||||
import { SysSettings } from "/@/views/sys/settings/api";
|
import { SysSettings } from "/@/views/sys/settings/api";
|
||||||
@@ -57,90 +62,94 @@ import { merge } from "lodash-es";
|
|||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { util } from "/@/utils";
|
import { util } from "/@/utils";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SettingBase",
|
name: "SettingBase",
|
||||||
});
|
});
|
||||||
|
|
||||||
const formState = reactive<Partial<SysSettings>>({
|
const formState = reactive<Partial<SysSettings>>({
|
||||||
public: {
|
public: {
|
||||||
icpNo: "",
|
icpNo: "",
|
||||||
mpsNo: "",
|
mpsNo: "",
|
||||||
},
|
},
|
||||||
private: {},
|
private: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const urlRules = ref({
|
const urlRules = ref({
|
||||||
type: "url",
|
type: "url",
|
||||||
message: "请输入正确的URL",
|
message: "请输入正确的URL",
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadSysSettings() {
|
async function loadSysSettings() {
|
||||||
const data: any = await api.SysSettingsGet();
|
const data: any = await api.SysSettingsGet();
|
||||||
merge(formState, data);
|
merge(formState, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveLoading = ref(false);
|
const saveLoading = ref(false);
|
||||||
loadSysSettings();
|
loadSysSettings();
|
||||||
const settingsStore = useSettingStore();
|
const settingsStore = useSettingStore();
|
||||||
const onFinish = async (form: any) => {
|
const onFinish = async (form: any) => {
|
||||||
try {
|
try {
|
||||||
saveLoading.value = true;
|
saveLoading.value = true;
|
||||||
await api.SysSettingsSave(form);
|
await api.SysSettingsSave(form);
|
||||||
await settingsStore.loadSysSettings();
|
await settingsStore.loadSysSettings();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "保存成功",
|
message: t('certd.saveSuccess'),
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
saveLoading.value = false;
|
saveLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const onFinishFailed = (errorInfo: any) => {
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
// console.log("Failed:", errorInfo);
|
// console.log("Failed:", errorInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function stopOtherUserTimer() {
|
async function stopOtherUserTimer() {
|
||||||
await api.stopOtherUserTimer();
|
await api.stopOtherUserTimer();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "停止成功",
|
message: t('certd.stopSuccess'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const testProxyLoading = ref(false);
|
const testProxyLoading = ref(false);
|
||||||
async function testProxy() {
|
async function testProxy() {
|
||||||
testProxyLoading.value = true;
|
testProxyLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await api.TestProxy();
|
const res = await api.TestProxy();
|
||||||
let success = true;
|
let success = true;
|
||||||
if (res.google !== true || res.baidu !== true) {
|
if (res.google !== true || res.baidu !== true) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
const content = () => {
|
const content = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>Google: {res.google === true ? "成功" : util.maxLength(res.google)}</div>
|
<div>{t('certd.google')}: {res.google === true ? t('certd.success') : util.maxLength(res.google)}</div>
|
||||||
<div>Baidu: {res.baidu === true ? "成功" : util.maxLength(res.google)}</div>
|
<div>{t('certd.baidu')}: {res.baidu === true ? t('certd.success') : util.maxLength(res.baidu)}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
if (!success) {
|
if (!success) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "测试失败",
|
message: t('certd.testFailed'),
|
||||||
description: content,
|
description: content,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "测试完成",
|
message: t('certd.testCompleted'),
|
||||||
description: content,
|
description: content,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
testProxyLoading.value = false;
|
testProxyLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.sys-settings-base {
|
.sys-settings-base {}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,67 +1,78 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sys-settings-form sys-settings-register">
|
<div class="sys-settings-form sys-settings-register">
|
||||||
<a-form :model="formState" name="register" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onFinish">
|
<a-form :model="formState" name="register" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }"
|
||||||
<a-form-item label="管理其他用户流水线" :name="['public', 'managerOtherUserPipeline']">
|
autocomplete="off" @finish="onFinish">
|
||||||
<a-switch v-model:checked="formState.public.managerOtherUserPipeline" />
|
<a-form-item :label="t('certd.manageOtherUserPipeline')" :name="['public', 'managerOtherUserPipeline']">
|
||||||
</a-form-item>
|
<a-switch v-model:checked="formState.public.managerOtherUserPipeline" />
|
||||||
<a-form-item label="限制用户流水线数量" :name="['public', 'limitUserPipelineCount']">
|
</a-form-item>
|
||||||
<a-input-number v-model:value="formState.public.limitUserPipelineCount" />
|
<a-form-item :label="t('certd.limitUserPipelineCount')" :name="['public', 'limitUserPipelineCount']">
|
||||||
<div class="helper">0为不限制</div>
|
<a-input-number v-model:value="formState.public.limitUserPipelineCount" />
|
||||||
</a-form-item>
|
<div class="helper">{{ t('certd.limitUserPipelineCountHelper') }}</div>
|
||||||
<a-form-item label="开启自助注册" :name="['public', 'registerEnabled']">
|
</a-form-item>
|
||||||
<a-switch v-model:checked="formState.public.registerEnabled" />
|
<a-form-item :label="t('certd.enableSelfRegistration')" :name="['public', 'registerEnabled']">
|
||||||
</a-form-item>
|
<a-switch v-model:checked="formState.public.registerEnabled" />
|
||||||
<a-form-item label="开启用户有效期" :name="['public', 'userValidTimeEnabled']">
|
</a-form-item>
|
||||||
<div class="flex-o">
|
<a-form-item :label="t('certd.enableUserValidityPeriod')" :name="['public', 'userValidTimeEnabled']">
|
||||||
<a-switch v-model:checked="formState.public.userValidTimeEnabled" :disabled="!settingsStore.isPlus" />
|
<div class="flex-o">
|
||||||
<vip-button class="ml-5" mode="button"></vip-button>
|
<a-switch v-model:checked="formState.public.userValidTimeEnabled"
|
||||||
</div>
|
:disabled="!settingsStore.isPlus" />
|
||||||
<div class="helper">有效期内用户可正常使用,失效后流水线将被停用</div>
|
<vip-button class="ml-5" mode="button"></vip-button>
|
||||||
</a-form-item>
|
</div>
|
||||||
<template v-if="formState.public.registerEnabled">
|
<div class="helper">{{ t('certd.userValidityPeriodHelper') }}</div>
|
||||||
<a-form-item label="开启用户名注册" :name="['public', 'usernameRegisterEnabled']">
|
</a-form-item>
|
||||||
<a-switch v-model:checked="formState.public.usernameRegisterEnabled" />
|
<template v-if="formState.public.registerEnabled">
|
||||||
</a-form-item>
|
<a-form-item :label="t('certd.enableUsernameRegistration')"
|
||||||
|
:name="['public', 'usernameRegisterEnabled']">
|
||||||
|
<a-switch v-model:checked="formState.public.usernameRegisterEnabled" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="开启邮箱注册" :name="['public', 'emailRegisterEnabled']">
|
<a-form-item :label="t('certd.enableEmailRegistration')" :name="['public', 'emailRegisterEnabled']">
|
||||||
<div class="flex-o">
|
<div class="flex-o">
|
||||||
<a-switch v-model:checked="formState.public.emailRegisterEnabled" :disabled="!settingsStore.isPlus" title="专业版功能" />
|
<a-switch v-model:checked="formState.public.emailRegisterEnabled"
|
||||||
<vip-button class="ml-5" mode="button"></vip-button>
|
:disabled="!settingsStore.isPlus" :title="t('certd.proFeature')" />
|
||||||
</div>
|
<vip-button class="ml-5" mode="button"></vip-button>
|
||||||
<div class="helper">需要<router-link to="/sys/settings/email">设置邮箱服务器</router-link></div>
|
</div>
|
||||||
</a-form-item>
|
<div class="helper">
|
||||||
<a-form-item label="开启手机号登录、注册" :name="['public', 'smsLoginEnabled']">
|
<router-link to="/sys/settings/email">{{ t('certd.emailServerSetup') }}</router-link>
|
||||||
<div class="flex-o">
|
</div>
|
||||||
<a-switch v-model:checked="formState.public.smsLoginEnabled" :disabled="!settingsStore.isComm" title="商业版功能" />
|
</a-form-item>
|
||||||
<vip-button class="ml-5" mode="comm"></vip-button>
|
<a-form-item :label="t('certd.enableSmsLoginRegister')" :name="['public', 'smsLoginEnabled']">
|
||||||
</div>
|
<div class="flex-o">
|
||||||
</a-form-item>
|
<a-switch v-model:checked="formState.public.smsLoginEnabled" :disabled="!settingsStore.isComm"
|
||||||
<template v-if="formState.public.smsLoginEnabled">
|
:title="t('certd.commFeature')" />
|
||||||
<a-form-item label="短信提供商" :name="['private', 'sms', 'type']">
|
<vip-button class="ml-5" mode="comm"></vip-button>
|
||||||
<a-select v-model:value="formState.private.sms.type" @change="smsTypeChange">
|
</div>
|
||||||
<a-select-option value="aliyun">阿里云短信</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option value="yfysms">易发云短信</a-select-option>
|
<template v-if="formState.public.smsLoginEnabled">
|
||||||
</a-select>
|
<a-form-item :label="t('certd.smsProvider')" :name="['private', 'sms', 'type']">
|
||||||
</a-form-item>
|
<a-select v-model:value="formState.private.sms.type" @change="smsTypeChange">
|
||||||
<template v-for="item of smsTypeDefineInputs" :key="item.simpleKey">
|
<a-select-option value="aliyun">{{ t('certd.aliyunSms') }}</a-select-option>
|
||||||
<fs-form-item v-model="formState.private.sms.config[item.simpleKey]" :path="'private.sms.config' + item.key" :item="item" />
|
<a-select-option value="yfysms">{{ t('certd.yfySms') }}</a-select-option>
|
||||||
</template>
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<template v-for="item of smsTypeDefineInputs" :key="item.simpleKey">
|
||||||
|
<fs-form-item v-model="formState.private.sms.config[item.simpleKey]"
|
||||||
|
:path="'private.sms.config' + item.key" :item="item" />
|
||||||
|
</template>
|
||||||
|
|
||||||
<a-form-item label="短信测试">
|
<a-form-item :label="t('certd.smsTest')">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<a-input v-model:value="testMobile" placeholder="输入测试手机号" />
|
<a-input v-model:value="testMobile" :placeholder="t('certd.testMobilePlaceholder')" />
|
||||||
<loading-button class="ml-5" title="保存后再点击测试" type="primary" :click="testSendSms">测试</loading-button>
|
<loading-button class="ml-5" :title="t('certd.saveThenTest')" type="primary"
|
||||||
</div>
|
:click="testSendSms">{{
|
||||||
<div class="helper">保存后再点击测试</div>
|
t('certd.testButton') }}</loading-button>
|
||||||
</a-form-item>
|
</div>
|
||||||
</template>
|
<div class="helper">{{ t('certd.saveThenTest') }}</div>
|
||||||
</template>
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||||
<a-button :loading="saveLoading" type="primary" html-type="submit">保存</a-button>
|
<a-button :loading="saveLoading" type="primary" html-type="submit">{{ t('certd.saveButton')
|
||||||
</a-form-item>
|
}}</a-button>
|
||||||
</a-form>
|
</a-form-item>
|
||||||
</div>
|
</a-form>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
@@ -71,123 +82,127 @@ import * as api from "/@/views/sys/settings/api";
|
|||||||
import { merge } from "lodash-es";
|
import { merge } from "lodash-es";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SettingRegister",
|
name: "SettingRegister",
|
||||||
});
|
});
|
||||||
|
|
||||||
const testMobile = ref("");
|
const testMobile = ref("");
|
||||||
async function testSendSms() {
|
async function testSendSms() {
|
||||||
if (!testMobile.value) {
|
if (!testMobile.value) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "请输入测试手机号",
|
message: t('certd.enterTestMobile'),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await api.TestSms({
|
await api.TestSms({
|
||||||
mobile: testMobile.value,
|
mobile: testMobile.value,
|
||||||
});
|
});
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "发送成功",
|
message: t('certd.sendSuccess'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const formState = reactive<Partial<SysSettings>>({
|
const formState = reactive<Partial<SysSettings>>({
|
||||||
public: {
|
public: {
|
||||||
registerEnabled: false,
|
registerEnabled: false,
|
||||||
},
|
},
|
||||||
private: {
|
private: {
|
||||||
sms: {
|
sms: {
|
||||||
type: "aliyun",
|
type: "aliyun",
|
||||||
config: {},
|
config: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
leastOneLogin: {
|
leastOneLogin: {
|
||||||
validator: (rule: any, value: any) => {
|
validator: (rule: any, value: any) => {
|
||||||
if (!formState.public.passwordLoginEnabled && !formState.public.smsLoginEnabled) {
|
if (!formState.public.passwordLoginEnabled && !formState.public.smsLoginEnabled) {
|
||||||
return Promise.reject("密码登录和手机号登录至少开启一个");
|
return Promise.reject(t('certd.atLeastOneLoginRequired'));
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required: {
|
required: {
|
||||||
required: true,
|
required: true,
|
||||||
trigger: "change",
|
trigger: "change",
|
||||||
message: "此项必填",
|
message: t('certd.fieldRequired'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function smsTypeChange(value: string) {
|
|
||||||
if (formState.private?.sms?.config) {
|
|
||||||
formState.private.sms.config = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
await loadTypeDefine(value);
|
async function smsTypeChange(value: string) {
|
||||||
|
if (formState.private?.sms?.config) {
|
||||||
|
formState.private.sms.config = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadTypeDefine(value);
|
||||||
}
|
}
|
||||||
const smsTypeDefineInputs: Ref = ref({});
|
const smsTypeDefineInputs: Ref = ref({});
|
||||||
async function loadTypeDefine(type: string) {
|
async function loadTypeDefine(type: string) {
|
||||||
const define: any = await api.GetSmsTypeDefine(type);
|
const define: any = await api.GetSmsTypeDefine(type);
|
||||||
const keys = Object.keys(define.input);
|
const keys = Object.keys(define.input);
|
||||||
const inputs: any = {};
|
const inputs: any = {};
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
const value = define.input[key];
|
const value = define.input[key];
|
||||||
value.simpleKey = key;
|
value.simpleKey = key;
|
||||||
value.key = "private.sms.config." + key;
|
value.key = "private.sms.config." + key;
|
||||||
if (!value.component) {
|
if (!value.component) {
|
||||||
value.component = {
|
value.component = {
|
||||||
name: "a-input",
|
name: "a-input",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!value.component.name) {
|
if (!value.component.name) {
|
||||||
value.component.vModel = "value";
|
value.component.vModel = "value";
|
||||||
}
|
}
|
||||||
if (!value.rules) {
|
if (!value.rules) {
|
||||||
value.rules = [];
|
value.rules = [];
|
||||||
}
|
}
|
||||||
if (value.required) {
|
if (value.required) {
|
||||||
value.rules.push(rules.required);
|
value.rules.push(rules.required);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs[key] = define.input[key];
|
inputs[key] = define.input[key];
|
||||||
});
|
});
|
||||||
smsTypeDefineInputs.value = inputs;
|
smsTypeDefineInputs.value = inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSysSettings() {
|
async function loadSysSettings() {
|
||||||
const data: any = await api.SysSettingsGet();
|
const data: any = await api.SysSettingsGet();
|
||||||
merge(formState, data);
|
merge(formState, data);
|
||||||
if (data?.private.sms?.type) {
|
if (data?.private.sms?.type) {
|
||||||
await loadTypeDefine(data.private.sms.type);
|
await loadTypeDefine(data.private.sms.type);
|
||||||
}
|
}
|
||||||
if (!settingsStore.isPlus) {
|
if (!settingsStore.isPlus) {
|
||||||
formState.public.userValidTimeEnabled = false;
|
formState.public.userValidTimeEnabled = false;
|
||||||
formState.public.emailRegisterEnabled = false;
|
formState.public.emailRegisterEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settingsStore.isComm) {
|
if (!settingsStore.isComm) {
|
||||||
formState.public.smsLoginEnabled = false;
|
formState.public.smsLoginEnabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveLoading = ref(false);
|
const saveLoading = ref(false);
|
||||||
loadSysSettings();
|
loadSysSettings();
|
||||||
const settingsStore = useSettingStore();
|
const settingsStore = useSettingStore();
|
||||||
const onFinish = async (form: any) => {
|
const onFinish = async (form: any) => {
|
||||||
try {
|
try {
|
||||||
saveLoading.value = true;
|
saveLoading.value = true;
|
||||||
await api.SysSettingsSave(form);
|
await api.SysSettingsSave(form);
|
||||||
await settingsStore.loadSysSettings();
|
await settingsStore.loadSysSettings();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "保存成功",
|
message: t('certd.saveSuccess'),
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
saveLoading.value = false;
|
saveLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.sys-settings-site {
|
.sys-settings-site {}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,54 +1,63 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sys-settings-form sys-settings-safe">
|
<div class="sys-settings-form sys-settings-safe">
|
||||||
<a-form ref="formRef" :model="formState" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off">
|
<a-form ref="formRef" :model="formState" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }"
|
||||||
<h2>站点隐藏</h2>
|
autocomplete="off">
|
||||||
<a-form-item label="启用站点隐藏" :name="['hidden', 'enabled']" :required="true">
|
<h2>{{ t('certd.siteHide') }}</h2>
|
||||||
<div class="flex">
|
<a-form-item :label="t('certd.enableSiteHide')" :name="['hidden', 'enabled']" :required="true">
|
||||||
<a-switch v-model:checked="formState.hidden.enabled" />
|
<div class="flex">
|
||||||
</div>
|
<a-switch v-model:checked="formState.hidden.enabled" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="helper">
|
<div class="helper">
|
||||||
可以在平时关闭站点的可访问性,需要时再打开,增强站点安全性
|
{{ t('certd.siteHideDescription') }}
|
||||||
<a href="https://certd.docmirror.cn/guide/feature/safe/hidden" class="flex items-center" target="_blank">
|
<a href="https://certd.docmirror.cn/guide/feature/safe/hidden" class="flex items-center"
|
||||||
<span>帮助说明</span>
|
target="_blank">
|
||||||
<fs-icon class="ml-1" icon="mingcute:question-line"></fs-icon
|
<span>{{ t('certd.helpDoc') }}</span>
|
||||||
></a>
|
<fs-icon class="ml-1" icon="mingcute:question-line"></fs-icon></a>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="formState.hidden.enabled" label="随机地址" :name="['hidden', 'openPath']" :required="true">
|
<a-form-item v-if="formState.hidden.enabled" :label="t('certd.randomAddress')"
|
||||||
<a-input-search v-model:value="formState.hidden.openPath" :allow-clear="true" @search="changeOpenPath">
|
:name="['hidden', 'openPath']" :required="true">
|
||||||
<template #enterButton>
|
<a-input-search v-model:value="formState.hidden.openPath" :allow-clear="true" @search="changeOpenPath">
|
||||||
<fs-icon icon="ion:refresh"></fs-icon>
|
<template #enterButton>
|
||||||
</template>
|
<fs-icon icon="ion:refresh"></fs-icon>
|
||||||
</a-input-search>
|
</template>
|
||||||
<div class="helper">站点被隐藏后,需要访问此URL解锁,才能正常访问</div>
|
</a-input-search>
|
||||||
</a-form-item>
|
<div class="helper">{{ t('certd.siteHideUrlHelper') }}</div>
|
||||||
<a-form-item v-if="formState.hidden.enabled" label="完整解除隐藏地址" :name="['hidden', 'openPath']" :required="true">
|
</a-form-item>
|
||||||
<div class="flex"><fs-copyable v-model="openUrl" class="flex-inline"></fs-copyable></div>
|
<a-form-item v-if="formState.hidden.enabled" :label="t('certd.fullUnlockUrl')"
|
||||||
<div class="helper red">请保存好此地址</div>
|
:name="['hidden', 'openPath']" :required="true">
|
||||||
</a-form-item>
|
<div class="flex"><fs-copyable v-model="openUrl" class="flex-inline"></fs-copyable></div>
|
||||||
<a-form-item v-if="formState.hidden.enabled" label="解除密码" :name="['hidden', 'openPassword']" :required="false">
|
<div class="helper red">{{ t('certd.saveThisUrl') }}</div>
|
||||||
<a-input-password v-model:value="formState.hidden.openPassword" :allow-clear="true" />
|
</a-form-item>
|
||||||
<div class="helper">解除隐藏时需要输入密码,第一次需要设置密码,填写则重置密码</div>
|
<a-form-item v-if="formState.hidden.enabled" :label="t('certd.unlockPassword')"
|
||||||
</a-form-item>
|
:name="['hidden', 'openPassword']" :required="false">
|
||||||
<a-form-item v-if="formState.hidden.enabled" label="自动隐藏时间" :name="['hidden', 'autoHiddenTimes']" :required="true">
|
<a-input-password v-model:value="formState.hidden.openPassword" :allow-clear="true" />
|
||||||
<a-input-number v-model:value="formState.hidden.autoHiddenTimes" :allow-clear="true" />
|
<div class="helper">{{ t('certd.unlockPasswordHelper') }}</div>
|
||||||
<div class="helper">多少分钟内无请求自动隐藏</div>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item v-if="formState.hidden.enabled" :label="t('certd.autoHideTime')"
|
||||||
<a-form-item v-if="formState.hidden.enabled" label="隐藏开放接口" :name="['hidden', 'hiddenOpenApi']" :required="true">
|
:name="['hidden', 'autoHiddenTimes']" :required="true">
|
||||||
<a-switch v-model:checked="formState.hidden.hiddenOpenApi" />
|
<a-input-number v-model:value="formState.hidden.autoHiddenTimes" :allow-clear="true" />
|
||||||
<div class="helper">是否隐藏开放接口,是否放开/api/v1开头的接口</div>
|
<div class="helper">{{ t('certd.autoHideTimeHelper') }}</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="formState.hidden.enabled" label="立即隐藏站点">
|
<a-form-item v-if="formState.hidden.enabled" :label="t('certd.hideOpenApi')"
|
||||||
<loading-button class="ml-1" type="primary" html-type="button" :click="doHiddenImmediate">立即隐藏</loading-button>
|
:name="['hidden', 'hiddenOpenApi']" :required="true">
|
||||||
</a-form-item>
|
<a-switch v-model:checked="formState.hidden.hiddenOpenApi" />
|
||||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
<div class="helper">{{ t('certd.hideOpenApiHelper') }}</div>
|
||||||
<loading-button type="primary" html-type="button" :click="onClick">保存</loading-button>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item v-if="formState.hidden.enabled" :label="t('certd.hideSiteImmediately')">
|
||||||
</a-form>
|
<loading-button class="ml-1" type="primary" html-type="button" :click="doHiddenImmediate">{{
|
||||||
</div>
|
t('certd.hideImmediately') }}</loading-button>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||||
|
<loading-button type="primary" html-type="button" :click="onClick">{{ t('certd.save')
|
||||||
|
}}</loading-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { computed, reactive, ref } from "vue";
|
import { computed, reactive, ref } from "vue";
|
||||||
import { merge } from "lodash-es";
|
import { merge } from "lodash-es";
|
||||||
@@ -56,106 +65,109 @@ import { Modal, notification } from "ant-design-vue";
|
|||||||
import { request } from "/@/api/service";
|
import { request } from "/@/api/service";
|
||||||
import { util, utils } from "/@/utils";
|
import { util, utils } from "/@/utils";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SettingSafe",
|
name: "SettingSafe",
|
||||||
});
|
});
|
||||||
const settingsStore = useSettingStore();
|
const settingsStore = useSettingStore();
|
||||||
const api = {
|
const api = {
|
||||||
async SettingGet() {
|
async SettingGet() {
|
||||||
return await request({
|
return await request({
|
||||||
url: "/sys/settings/safe/get",
|
url: "/sys/settings/safe/get",
|
||||||
method: "post",
|
method: "post",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async SettingSave(data: any) {
|
async SettingSave(data: any) {
|
||||||
return await request({
|
return await request({
|
||||||
url: "/sys/settings/safe/save",
|
url: "/sys/settings/safe/save",
|
||||||
method: "post",
|
method: "post",
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async HiddenImmediate() {
|
async HiddenImmediate() {
|
||||||
return await request({
|
return await request({
|
||||||
url: "/sys/settings/safe/hidden",
|
url: "/sys/settings/safe/hidden",
|
||||||
method: "post",
|
method: "post",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
hidden: {
|
hidden: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
autoHiddenTimes: 5,
|
autoHiddenTimes: 5,
|
||||||
hiddenOpenApi: false,
|
hiddenOpenApi: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const formRef = ref<any>(defaultState);
|
const formRef = ref<any>(defaultState);
|
||||||
type SiteHidden = {
|
type SiteHidden = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
openPath?: string;
|
openPath?: string;
|
||||||
autoHiddenTimes?: number;
|
autoHiddenTimes?: number;
|
||||||
openPassword?: string;
|
openPassword?: string;
|
||||||
hiddenOpenApi?: boolean;
|
hiddenOpenApi?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formState = reactive<
|
const formState = reactive<
|
||||||
Partial<{
|
Partial<{
|
||||||
hidden: SiteHidden;
|
hidden: SiteHidden;
|
||||||
}>
|
}>
|
||||||
>({
|
>({
|
||||||
hidden: { enabled: false },
|
hidden: { enabled: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
function changeOpenPath() {
|
function changeOpenPath() {
|
||||||
formState.hidden.openPath = util.randomString(16);
|
formState.hidden.openPath = util.randomString(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSettings() {
|
async function loadSettings() {
|
||||||
const data: any = await api.SettingGet();
|
const data: any = await api.SettingGet();
|
||||||
merge(formState, defaultState, formState, data);
|
merge(formState, defaultState, formState, data);
|
||||||
if (!formState.hidden.openPath) {
|
if (!formState.hidden.openPath) {
|
||||||
changeOpenPath();
|
changeOpenPath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
const openUrl = computed(() => {
|
const openUrl = computed(() => {
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
url.pathname = `/api/unhidden/${formState.hidden?.openPath || ""}`;
|
url.pathname = `/api/unhidden/${formState.hidden?.openPath || ""}`;
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
url.query = undefined;
|
url.query = undefined;
|
||||||
url.hash = "";
|
url.hash = "";
|
||||||
return url.href;
|
return url.href;
|
||||||
});
|
});
|
||||||
|
|
||||||
const onClick = async () => {
|
const onClick = async () => {
|
||||||
const form = await formRef.value.validateFields();
|
const form = await formRef.value.validateFields();
|
||||||
//密码md5
|
//密码md5
|
||||||
// if (form.hidden?.openPassword) {
|
// if (form.hidden?.openPassword) {
|
||||||
// form.hidden.openPassword = util.hash.md5(form.hidden.openPassword);
|
// form.hidden.openPassword = util.hash.md5(form.hidden.openPassword);
|
||||||
// }
|
// }
|
||||||
await api.SettingSave(form);
|
await api.SettingSave(form);
|
||||||
await loadSettings();
|
await loadSettings();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "保存成功",
|
message: t('certd.saveSuccess'),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
async function doHiddenImmediate() {
|
async function doHiddenImmediate() {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "确定要立即隐藏站点吗?",
|
title: t('certd.confirmHideSiteTitle'),
|
||||||
content: "隐藏后,将无法访问站点,请谨慎操作",
|
content: t('certd.confirmHideSiteContent'),
|
||||||
async onOk() {
|
async onOk() {
|
||||||
await api.HiddenImmediate();
|
await api.HiddenImmediate();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "站点已隐藏",
|
message: t('certd.siteHiddenSuccess'),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.sys-settings-base {
|
.sys-settings-base {}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user