From d67379f29725bbd6dcdefe97d101023c7fd11f03 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Mon, 31 Aug 2026 22:54:28 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=E7=94=B3=E8=AF=B7=E4=BB=BB=E5=8A=A1=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E6=B8=85=E7=A9=BAACME=E8=B4=A6=E5=8F=B7=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 这一版本新增了用户证书生成统计、流水线运行统计功能,优化了插件配置获取和更新流程: 1. 新增用户统计设置模型,统计单域名/多域名/泛域名证书生成数和流水线运行次数 2. 仪表盘新增累计证书数、分类统计和节省金额估算卡片 3. 重构插件配置相关接口,统一插件获取、配置保存逻辑 4. 修复远程选择组件初始值触发逻辑,新增emitImmediate配置项 5. 优化ACME账号切换逻辑,修复对象展开语法语法问题 6. 补充多语言国际化文案,调整部分前端样式细节 --- .../plugins/common/remote-select.vue | 4 +- .../locales/langs/en-US/certd/dashboard.ts | 10 +++ .../locales/langs/zh-CN/certd/dashboard.ts | 10 +++ .../ui/certd-client/src/store/plugin/index.ts | 79 +++++++++++-------- packages/ui/certd-client/src/style/certd.less | 2 + .../views/certd/pipeline/certd-form/use.tsx | 12 +-- .../pipeline/component/step-form/index.vue | 14 ++-- .../views/framework/home/dashboard/index.vue | 30 +++++++ .../home/dashboard/statistic-card.vue | 21 +++++ .../certd-client/src/views/sys/plugin/api.ts | 18 ++++- .../src/views/sys/plugin/config-common.vue | 4 +- .../src/views/sys/plugin/config-editor.vue | 38 +++------ .../src/views/sys/plugin/use-config.tsx | 3 +- .../sys/plugin/plugin-controller.ts | 14 +++- .../user/dashboard/statistic-controller.ts | 19 +++++ .../fix/common-eab-to-acme-account-fix.ts | 1 + .../modules/cert/service/domain-service.ts | 2 +- .../src/modules/mine/service/models.ts | 12 +++ .../mine/service/user-settings-service.ts | 42 ++++++++++ .../monitor/service/cert-info-service.ts | 12 ++- .../pipeline/service/pipeline-service.ts | 13 +++ .../plugin/service/plugin-config-getter.ts | 4 +- .../plugin/service/plugin-config-service.ts | 31 +++++--- .../modules/plugin/service/plugin-service.ts | 70 ++++++++-------- .../plugin-cert/access/acme-account-access.ts | 1 + .../plugin-cert/plugin/cert-plugin/apply.ts | 3 +- 26 files changed, 333 insertions(+), 136 deletions(-) diff --git a/packages/ui/certd-client/src/components/plugins/common/remote-select.vue b/packages/ui/certd-client/src/components/plugins/common/remote-select.vue index e3921dd92..49dfcb851 100644 --- a/packages/ui/certd-client/src/components/plugins/common/remote-select.vue +++ b/packages/ui/certd-client/src/components/plugins/common/remote-select.vue @@ -71,6 +71,7 @@ const props = defineProps< pager?: boolean; single?: boolean; pageSize?: number; + emitImmediate?: boolean; uploadCert?: UploadCertProps; } & ComponentPropsType >(); @@ -210,8 +211,9 @@ const getOptions = async () => { if (isFirst) { isFirst = false; - if (props.value) { + if (props.value && props.emitImmediate) { emitSelectedChange(props.value); + emit("update:value", props.value); } return; } diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd/dashboard.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd/dashboard.ts index afa6c340d..b2e0675f5 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd/dashboard.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd/dashboard.ts @@ -32,6 +32,16 @@ export default { recentRun: "Recent Run Statistics", runCount: "Run Count", expiringCerts: "Soon-to-Expire Certificates", + totalGeneratedCertCount: "Total Certificates Generated", + singleDomainCertShortCount: "Single-domain", + multiDomainCertShortCount: "Multi-domain", + wildcardCertShortCount: "Wildcard", + singleDomainCertCount: "Single-domain Certificates Generated", + multiDomainCertCount: "Multi-domain Certificates Generated", + wildcardCertCount: "Wildcard Certificates Generated", + savedAmount: "Estimated savings: ¥{amount}", + savedAmountRule: "Estimated at one quarter of Tencent Cloud's annual certificate prices: single-domain ¥532, multi-domain ¥912, and wildcard ¥2147. Actual savings may vary.", + totalPipelineRuns: "Total Pipeline Runs", supportedTasks: "Overview of Supported Deployment Tasks", changeLog: "Change Log", stableRelease: "Stable", diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd/dashboard.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd/dashboard.ts index dc56e367b..f2c746b7f 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd/dashboard.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd/dashboard.ts @@ -33,6 +33,16 @@ export default { recentRun: "最近运行统计", runCount: "运行次数", expiringCerts: "最快到期证书", + totalGeneratedCertCount: "累计生成证书数量", + singleDomainCertShortCount: "单域名", + multiDomainCertShortCount: "多域名", + wildcardCertShortCount: "泛域名", + singleDomainCertCount: "累计单域名证书数量", + multiDomainCertCount: "累计多域名证书数量", + wildcardCertCount: "累计泛域名证书数量", + savedAmount: "已为您累计节省 {amount} 元", + savedAmountRule: "按腾讯云证书平台一年期单价的 1/4 估算:单域名证书 532 元,多域名证书 912 元,泛域名证书 2147 元。实际节省金额仅供参考。", + totalPipelineRuns: "累计流水线运行次数", supportedTasks: "已支持的部署任务总览", changeLog: "更新日志", stableRelease: "稳定版", diff --git a/packages/ui/certd-client/src/store/plugin/index.ts b/packages/ui/certd-client/src/store/plugin/index.ts index 044ce758b..ace8e4a4e 100644 --- a/packages/ui/certd-client/src/store/plugin/index.ts +++ b/packages/ui/certd-client/src/store/plugin/index.ts @@ -1,13 +1,54 @@ +import { DynamicType, FormItemProps, useMerge } from "@fast-crud/fast-crud"; +import { merge, set } from "lodash-es"; import { defineStore } from "pinia"; import * as api from "./api.plugin"; -import { DynamicType, FormItemProps, useMerge } from "@fast-crud/fast-crud"; import { i18n } from "/src/locales/i18n"; -import { cloneDeep } from "lodash-es"; interface PluginState { group?: PluginGroups; originGroup?: PluginGroups; } +export function fillPluginDefine(plugin: any) { + const columns = plugin.input; + for (const key in columns) { + const column = columns[key]; + if (!column.component) { + column.component = { + name: "a-input", + vModel: "value", + }; + } else { + if (column.component.vModel == null && column.component.name?.startsWith("a-")) { + column.component.vModel = "value"; + } + } + } + + if (plugin.sysSetting?.metadata) { + merge(plugin.input, plugin.sysSetting.metadata?.input || {}); + // 应用选项映射 + for (const key of Object.keys(plugin.input)) { + const inputDef = plugin.input[key]; + if (inputDef.optionsMapping && inputDef.component?.options) { + const mapping = inputDef.optionsMapping; + for (const opt of inputDef.component.options) { + if (mapping[opt.value] !== undefined) { + opt.label = mapping[opt.value]; + } + } + } + } + } + if (plugin.sysSetting?.input) { + // 设置系统默认值 + for (const key in plugin.input) { + const sysValue = plugin.sysSetting?.input[key]; + if (sysValue != undefined) { + set(plugin, `input.${key}.value`, sysValue); + } + } + } +} export type PluginGroup = { key: string; title: string; @@ -35,10 +76,9 @@ export class PluginGroups { map!: { [key: string]: PluginDefine }; t: any; mergeSetting?: boolean; - constructor(groups: { [key: string]: PluginGroup }, opts?: { mergeSetting?: boolean }) { + constructor(groups: { [key: string]: PluginGroup }) { this.groups = groups; this.t = i18n.global.t; - this.mergeSetting = opts?.mergeSetting ?? false; this.initGroup(groups); this.initMap(); } @@ -53,26 +93,9 @@ export class PluginGroups { icon: "material-symbols:border-all-rounded", }; for (const key in groups) { - if (this.mergeSetting) { - for (const plugin of groups[key].plugins) { - if (plugin.sysSetting) { - merge(plugin.input, plugin.sysSetting.metadata?.input || {}); - // 应用选项映射 - for (const key of Object.keys(plugin.input)) { - const inputDef = plugin.input[key]; - if (inputDef.optionsMapping && inputDef.component?.options) { - const mapping = inputDef.optionsMapping; - for (const opt of inputDef.component.options) { - if (mapping[opt.value] !== undefined) { - opt.label = mapping[opt.value]; - } - } - } - } - } - } + for (const plugin of groups[key].plugins) { + fillPluginDefine(plugin); } - all.plugins.push(...groups[key].plugins); } this.groups = { @@ -173,16 +196,12 @@ export const usePluginStore = defineStore({ id: "app.plugin", state: (): PluginState => ({ group: null, - originGroup: null, }), actions: { async reload() { const groups = await api.GetGroups({}); - const installedGroups = filterInstalledPluginGroups(groups); - this.group = new PluginGroups(installedGroups, { mergeSetting: true }); - this.originGroup = new PluginGroups(cloneDeep(installedGroups)); + this.group = new PluginGroups(groups); console.log("group", this.group); - console.log("originGroup", this.originGroup); }, async init() { if (!this.group) { @@ -206,10 +225,6 @@ export const usePluginStore = defineStore({ await this.init(); return this.group.get(name); }, - async getPluginDefineFromOrigin(name: string): Promise { - await this.init(); - return this.originGroup.get(name); - }, async getPluginConfig(query: any) { return await api.GetPluginConfig(query); }, diff --git a/packages/ui/certd-client/src/style/certd.less b/packages/ui/certd-client/src/style/certd.less index cca9ad0fb..54cf27ca0 100644 --- a/packages/ui/certd-client/src/style/certd.less +++ b/packages/ui/certd-client/src/style/certd.less @@ -280,6 +280,8 @@ button.ant-btn.isPlus { align-items: center; img { + width: 16px; + height: 16px; &.font-size-16 { width: 16px; height: 16px; diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx index dc07d9a84..2b85c3898 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx @@ -556,12 +556,12 @@ export function useCertPipelineCreator({ formWrapperRef }: { formWrapperRef: Ref //设置系统初始值 const initialForm: any = { input: {} }; - const pluginSysConfig = await pluginStore.getPluginConfig({ name: req.pluginName, type: "builtIn" }); - if (pluginSysConfig.sysSetting?.input) { - for (const key in pluginSysConfig.sysSetting?.input) { - initialForm.input[key] = pluginSysConfig.sysSetting?.input[key]; - } - } + // const pluginSysConfig = await pluginStore.getPluginConfig({ name: req.pluginName, type: "builtIn" }); + // if (pluginSysConfig.sysSetting?.input) { + // for (const key in pluginSysConfig.sysSetting?.input) { + // initialForm.input[key] = pluginSysConfig.sysSetting?.input[key]; + // } + // } async function doSubmit({ form }: any) { // const certDetail = readCertDetail(form.cert.crt); // 添加certd pipeline diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/step-form/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/step-form/index.vue index 5c31294b1..080fb4ca6 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/step-form/index.vue +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/step-form/index.vue @@ -263,13 +263,13 @@ function useStepForm() { currentStep.input[key] = column.default ?? column.value; } } - //设置系统初始值 - const pluginSysConfig = await pluginStore.getPluginConfig({ name: pluginDefine.name, type: "builtIn" }); - if (pluginSysConfig.sysSetting?.input) { - for (const key in pluginSysConfig.sysSetting?.input) { - currentStep.input[key] = pluginSysConfig.sysSetting?.input[key]; - } - } + // //设置系统初始值 + // const pluginSysConfig = await pluginStore.getPluginConfig({ name: pluginDefine.name, type: pluginDefine.type }); + // if (pluginSysConfig.sysSetting?.input) { + // for (const key in pluginSysConfig.sysSetting?.input) { + // currentStep.input[key] = pluginSysConfig.sysSetting?.input[key]; + // } + // } }; const stepSave = async (e: any) => { diff --git a/packages/ui/certd-client/src/views/framework/home/dashboard/index.vue b/packages/ui/certd-client/src/views/framework/home/dashboard/index.vue index a41974ed1..4eb4ac7b8 100644 --- a/packages/ui/certd-client/src/views/framework/home/dashboard/index.vue +++ b/packages/ui/certd-client/src/views/framework/home/dashboard/index.vue @@ -125,6 +125,20 @@ + + + + + + @@ -275,6 +289,10 @@ function goPipeline() { } const count: any = ref({}); +function formatSavedAmount(amountValue: number) { + const amount = Number(amountValue) || 0; + return Number.isInteger(amount) ? amount.toString() : amount.toFixed(2); +} function transformStatusCount() { const data = count.value.pipelineStatusCount; const sorted = [ @@ -330,6 +348,18 @@ function transformStatusCount() { { name: t("certd.dashboard.domainNotExpiredCount"), value: domainCount.notExpired, color: "green" }, ]; count.value.domainCount = domainCount.total; + + const genCertCount = count.value.genCertCount || {}; + const singleDomainCertCount = Number(genCertCount.singleDomainCertCount) || 0; + const multiDomainCertCount = Number(genCertCount.multiDomainCertCount) || 0; + const wildcardCertCount = Number(genCertCount.wildcardCertCount) || 0; + count.value.totalGeneratedCertCount = singleDomainCertCount + multiDomainCertCount + wildcardCertCount; + count.value.generatedCertTypeCounts = [ + { name: t("certd.dashboard.singleDomainCertShortCount"), value: singleDomainCertCount, color: "blue" }, + { name: t("certd.dashboard.multiDomainCertShortCount"), value: multiDomainCertCount, color: "blue" }, + { name: t("certd.dashboard.wildcardCertShortCount"), value: wildcardCertCount, color: "blue" }, + ]; + count.value.totalSavedAmount = singleDomainCertCount * (532 / 4) + multiDomainCertCount * (912 / 4) + wildcardCertCount * (2147 / 4); } async function loadCount() { count.value = await GetStatisticCount(); diff --git a/packages/ui/certd-client/src/views/framework/home/dashboard/statistic-card.vue b/packages/ui/certd-client/src/views/framework/home/dashboard/statistic-card.vue index f7ba1a805..8ebe64116 100644 --- a/packages/ui/certd-client/src/views/framework/home/dashboard/statistic-card.vue +++ b/packages/ui/certd-client/src/views/framework/home/dashboard/statistic-card.vue @@ -15,6 +15,12 @@
{{ count ?? 0 }} {{ title }} + + {{ helperText }} + + + +
@@ -59,6 +65,8 @@ const props = defineProps<{ title?: string; link?: any; }[]; + helperText?: string; + helperTooltip?: string; }>(); const slots = defineSlots(); const router = useRouter(); @@ -175,6 +183,19 @@ function goDetail(link: any) { font-weight: 400; color: #626262; } + .helper-text { + color: #52a26a; + font-size: 12px; + font-weight: 500; + line-height: 20px; + + .helper-icon { + cursor: help; + font-size: 14px; + margin-left: 4px; + vertical-align: -2px; + } + } } .sub-item { diff --git a/packages/ui/certd-client/src/views/sys/plugin/api.ts b/packages/ui/certd-client/src/views/sys/plugin/api.ts index 914d33dd6..895b4f4e6 100644 --- a/packages/ui/certd-client/src/views/sys/plugin/api.ts +++ b/packages/ui/certd-client/src/views/sys/plugin/api.ts @@ -1,3 +1,4 @@ +import { PluginDefine } from "@certd/pipeline"; import { request } from "/src/api/service"; const apiPrefix = "/sys/plugin"; @@ -246,6 +247,7 @@ export type PluginConfigBean = { name: string; disabled: boolean; sysSetting: { + metadata?: Record; input?: Record; }; }; @@ -283,7 +285,7 @@ export async function SaveCommPluginConfigs(data: CommPluginConfig): Promise { +export async function savePluginSetting(req: { name: string; sysSetting: any; type: string }): Promise { return await request({ url: apiPrefix + "/saveSetting", method: "post", @@ -291,11 +293,11 @@ export async function savePluginSetting(req: { name: string; sysSetting: any }): }); } -export async function GetPluginByName(name: string): Promise { +export async function getPluginSetting(name: string, type: string = "builtIn"): Promise { return await request({ - url: apiPrefix + "/getPluginByName", + url: apiPrefix + "/getSetting", method: "post", - data: { name }, + data: { name, type }, }); } @@ -305,3 +307,11 @@ export async function ClearRuntimeDeps(): Promise { method: "post", }); } + +export async function getPluginDefine(name: string): Promise { + return await request({ + url: apiPrefix + "/getPluginDefine", + method: "post", + data: { name }, + }); +} diff --git a/packages/ui/certd-client/src/views/sys/plugin/config-common.vue b/packages/ui/certd-client/src/views/sys/plugin/config-common.vue index cdea0e28c..be5f5faa9 100644 --- a/packages/ui/certd-client/src/views/sys/plugin/config-common.vue +++ b/packages/ui/certd-client/src/views/sys/plugin/config-common.vue @@ -69,7 +69,7 @@