Partially translate user settings

pull/436/head
Lorenzo 2025-06-26 00:56:43 +02:00
parent b50121ad0b
commit 0b3472d227
8 changed files with 881 additions and 767 deletions

View File

@ -300,4 +300,48 @@ export default {
nickName: "Nickname", nickName: "Nickname",
max50Chars: "Maximum 50 characters", max50Chars: "Maximum 50 characters",
myInfo: "My Information", myInfo: "My Information",
siteMonitorSettings: "Site Monitor Settings",
notificationChannel: "Notification Channel",
setNotificationChannel: "Set the notification channel",
retryTimes: "Retry Times",
monitorRetryTimes: "Number of retry attempts for monitoring requests",
monitorCronSetting: "Monitoring Schedule",
cronTrigger: "Scheduled trigger for monitoring",
save: "Save",
editSchedule: "Edit Schedule",
timerTrigger: "Timer Trigger",
schedule: "Schedule",
selectCron: "Please select a schedule Cron",
batchEditSchedule: "Batch Edit Schedule",
editTrigger: "Edit Trigger",
triggerName: "Trigger Name",
requiredField: "This field is required",
type: "Type",
enterName: "Please enter a name",
confirmDeleteTrigger: "Are you sure you want to delete this trigger?",
notificationType: "Notification Type",
selectNotificationType: "Please select a notification type",
notificationName: "Notification Name",
helperNotificationName: "Fill freely, helps to distinguish when multiple notifications of the same type exist",
isDefault: "Is Default",
yes: "Yes",
no: "No",
selectIsDefault: "Please select if default",
prompt: "Prompt",
confirmSetDefaultNotification: "Are you sure to set as default notification?",
test: "Test",
scope: "Scope",
scopeOpenApiOnly: "Open API Only",
scopeFullAccount: "Full Account Permissions",
required: "This field is required",
scopeHelper: "Open API only allows access to open APIs; full account permissions allow access to all APIs",
add: "Generate New Key",
gen: {
text: "API Test",
title: "x-certd-token",
okText: "Confirm",
contentPart1: "Test the x-certd-token below, you can use it within 3 minutes to test ",
openApi: "Open API",
contentPart2: " request testing",
},
}; };

View File

@ -306,4 +306,48 @@ export default {
nickName: "昵称", nickName: "昵称",
max50Chars: "最大50个字符", max50Chars: "最大50个字符",
myInfo: "我的信息", myInfo: "我的信息",
siteMonitorSettings: "站点监控设置",
notificationChannel: "通知渠道",
setNotificationChannel: "设置通知渠道",
retryTimes: "重试次数",
monitorRetryTimes: "监控请求重试次数",
monitorCronSetting: "监控定时设置",
cronTrigger: "定时触发监控",
save: "保存",
editSchedule: "修改定时",
timerTrigger: "定时触发",
schedule: "定时",
selectCron: "请选择定时Cron",
batchEditSchedule: "批量修改定时",
editTrigger: "编辑触发器",
triggerName: "触发器名称",
requiredField: "此项必填",
type: "类型",
enterName: "请输入名称",
confirmDeleteTrigger: "确定要删除此触发器吗?",
notificationType: "通知类型",
selectNotificationType: "请选择通知类型",
notificationName: "通知名称",
helperNotificationName: "随便填,当多个相同类型的通知时,便于区分",
isDefault: "是否默认",
yes: "是",
no: "否",
selectIsDefault: "请选择是否默认",
prompt: "提示",
confirmSetDefaultNotification: "确定设置为默认通知?",
test: "测试",
scope: "权限范围",
scopeOpenApiOnly: "仅开放接口",
scopeFullAccount: "账户所有权限",
required: "此项必填",
scopeHelper: "仅开放接口只可以访问开放接口,账户所有权限可以访问所有接口",
add: "生成新的Key",
gen: {
text: "接口测试",
title: "x-certd-token",
okText: "确定",
contentPart1: "测试x-certd-token如下您可以在3分钟内使用它进行",
openApi: "开放接口",
contentPart2: "请求测试",
},
}; };

View File

@ -1,37 +1,41 @@
<template> <template>
<fs-page class="page-user-settings page-site-monitor-setting"> <fs-page class="page-user-settings page-site-monitor-setting">
<template #header> <template #header>
<div class="title">站点监控设置</div> <div class="title">{{ t("certd.siteMonitorSettings") }}</div>
</template> </template>
<div class="user-settings-form settings-form"> <div class="user-settings-form settings-form">
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off"> <a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }"
<a-form-item label="通知渠道" :name="['notificationId']"> autocomplete="off">
<div class="flex"> <a-form-item :label="t('certd.notificationChannel')" :name="['notificationId']">
<NotificationSelector v-model="formState.notificationId" /> <div class="flex">
</div> <NotificationSelector v-model="formState.notificationId" />
<div class="helper">设置通知渠道</div> </div>
</a-form-item> <div class="helper">{{ t('certd.setNotificationChannel') }}</div>
<a-form-item label="重试次数" :name="['retryTimes']"> </a-form-item>
<div class="flex"> <a-form-item :label="t('certd.retryTimes')" :name="['retryTimes']">
<a-input-number v-model:value="formState.retryTimes" /> <div class="flex">
</div> <a-input-number v-model:value="formState.retryTimes" />
<div class="helper">监控请求重试次数</div> </div>
</a-form-item> <div class="helper">{{ t('certd.monitorRetryTimes') }}</div>
<a-form-item label="监控定时设置" :name="['cron']"> </a-form-item>
<div class="flex flex-baseline"> <a-form-item :label="t('certd.monitorCronSetting')" :name="['cron']">
<cron-editor v-model="formState.cron" :disabled="!settingsStore.isPlus" :allow-every-min="userStore.isAdmin" /> <div class="flex flex-baseline">
<vip-button class="ml-5" mode="button"></vip-button> <cron-editor v-model="formState.cron" :disabled="!settingsStore.isPlus"
</div> :allow-every-min="userStore.isAdmin" />
<div class="helper">定时触发监控</div> <vip-button class="ml-5" mode="button"></vip-button>
</a-form-item> </div>
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }"> <div class="helper">{{ t('certd.cronTrigger') }}</div>
<loading-button type="primary" html-type="button" :click="doSave">保存</loading-button> </a-form-item>
</a-form-item> <a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
</a-form> <loading-button type="primary" html-type="button" :click="doSave">{{ t('certd.save')
</div> }}</loading-button>
</fs-page> </a-form-item>
</a-form>
</div>
</fs-page>
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">
import { reactive } from "vue"; import { reactive } from "vue";
import * as api from "./api"; import * as api from "./api";
@ -41,38 +45,41 @@ import { merge } from "lodash-es";
import { useSettingStore } from "/src/store/settings"; import { useSettingStore } from "/src/store/settings";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue"; import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
import { useUserStore } from "/@/store/user"; import { useUserStore } from "/@/store/user";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const settingsStore = useSettingStore(); const settingsStore = useSettingStore();
const userStore = useUserStore(); const userStore = useUserStore();
defineOptions({ defineOptions({
name: "UserSecurity", name: "UserSecurity",
}); });
const formState = reactive<Partial<UserSiteMonitorSetting>>({ const formState = reactive<Partial<UserSiteMonitorSetting>>({
notificationId: 0, notificationId: 0,
}); });
async function loadUserSettings() { async function loadUserSettings() {
const data: any = await api.SiteMonitorSettingsGet(); const data: any = await api.SiteMonitorSettingsGet();
merge(formState, data); merge(formState, data);
} }
loadUserSettings(); loadUserSettings();
const doSave = async (form: any) => { const doSave = async (form: any) => {
await api.SiteMonitorSettingsSave({ await api.SiteMonitorSettingsSave({
...formState, ...formState,
}); });
notification.success({ notification.success({
message: "保存成功", message: t("certd.saveSuccess"),
}); });
}; };
</script> </script>
<style lang="less"> <style lang="less">
.page-user-settings { .page-user-settings {
.user-settings-form { .user-settings-form {
width: 700px; width: 700px;
margin: 20px; margin: 20px;
} }
} }
</style> </style>

View File

@ -5,238 +5,241 @@ import { forEach, get, merge, set } from "lodash-es";
import { Modal } from "ant-design-vue"; import { Modal } from "ant-design-vue";
import * as api from "/@/views/sys/cname/provider/api"; import * as api from "/@/views/sys/cname/provider/api";
import { mitter } from "/@/utils/util.mitt"; import { mitter } from "/@/utils/util.mitt";
import { useI18n } from "vue-i18n";
export function notificationProvide(api: any) { export function notificationProvide(api: any) {
provide("notificationApi", api); provide("notificationApi", api);
provide("get:plugin:type", () => { provide("get:plugin:type", () => {
return "notification"; return "notification";
}); });
} }
export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) { export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
const notificationTypeDictRef = dict({ const { t } = useI18n();
url: "/pi/notification/getTypeDict",
});
const defaultPluginConfig = {
component: {
name: "a-input",
vModel: "value",
},
};
function buildDefineFields(define: any, form: any, mode: string) { const notificationTypeDictRef = dict({
const formWrapperRef = crudExpose.getFormWrapperRef(); url: "/pi/notification/getTypeDict",
const columnsRef = toRef(formWrapperRef.formOptions, "columns"); });
const defaultPluginConfig = {
component: {
name: "a-input",
vModel: "value",
},
};
for (const key in columnsRef.value) { function buildDefineFields(define: any, form: any, mode: string) {
if (key.indexOf(".") >= 0) { const formWrapperRef = crudExpose.getFormWrapperRef();
delete columnsRef.value[key]; const columnsRef = toRef(formWrapperRef.formOptions, "columns");
}
}
console.log('crudBinding.value[mode + "Form"].columns', columnsRef.value);
forEach(define.input, (value: any, mapKey: any) => {
const key = "body." + mapKey;
const field = {
...value,
key,
};
const column = merge({ title: key }, defaultPluginConfig, field);
//eval
useReference(column);
if (column.required) { for (const key in columnsRef.value) {
if (!column.rules) { if (key.indexOf(".") >= 0) {
column.rules = []; delete columnsRef.value[key];
} }
column.rules.push({ required: true, message: "此项必填" }); }
} console.log('crudBinding.value[mode + "Form"].columns', columnsRef.value);
forEach(define.input, (value: any, mapKey: any) => {
const key = "body." + mapKey;
const field = {
...value,
key,
};
const column = merge({ title: key }, defaultPluginConfig, field);
//eval
useReference(column);
//设置默认值 if (column.required) {
if (column.value != null && get(form, key) == null) { if (!column.rules) {
set(form, key, column.value); column.rules = [];
} }
//字段配置赋值 column.rules.push({ required: true, message: t("certd.requiredField") });
columnsRef.value[key] = column; }
console.log("form", columnsRef.value, form);
});
}
const currentDefine = ref(); //设置默认值
if (column.value != null && get(form, key) == null) {
set(form, key, column.value);
}
//字段配置赋值
columnsRef.value[key] = column;
console.log("form", columnsRef.value, form);
});
}
return { const currentDefine = ref();
id: {
title: "ID",
key: "id",
type: "number",
column: {
width: 100,
},
form: {
show: false,
},
},
type: {
title: "通知类型",
type: "dict-select",
dict: notificationTypeDictRef,
search: {
show: false,
},
column: {
width: 200,
component: {
color: "auto",
},
},
editForm: {
component: {
disabled: false,
},
},
form: {
component: {
disabled: false,
showSearch: true,
filterOption: (input: string, option: any) => {
input = input?.toLowerCase();
return option.value.toLowerCase().indexOf(input) >= 0 || option.label.toLowerCase().indexOf(input) >= 0;
},
renderLabel(item: any) {
return (
<span class={"flex-o flex-between"}>
{item.label}
{item.needPlus && <fs-icon icon={"mingcute:vip-1-line"} className={"color-plus"}></fs-icon>}
</span>
);
},
},
rules: [{ required: true, message: "请选择通知类型" }],
valueChange: {
immediate: true,
async handle({ value, mode, form, immediate }) {
if (value == null) {
return;
}
const lastTitle = currentDefine.value?.title;
const define = await api.GetProviderDefine(value);
currentDefine.value = define;
console.log("define", define);
if (!immediate) { return {
form.body = {}; id: {
if (define.needPlus) { title: "ID",
mitter.emit("openVipModal"); key: "id",
} type: "number",
} column: {
width: 100,
},
form: {
show: false,
},
},
type: {
title: t("certd.notificationType"),
type: "dict-select",
dict: notificationTypeDictRef,
search: {
show: false,
},
column: {
width: 200,
component: {
color: "auto",
},
},
editForm: {
component: {
disabled: false,
},
},
form: {
component: {
disabled: false,
showSearch: true,
filterOption: (input: string, option: any) => {
input = input?.toLowerCase();
return option.value.toLowerCase().indexOf(input) >= 0 || option.label.toLowerCase().indexOf(input) >= 0;
},
renderLabel(item: any) {
return (
<span class={"flex-o flex-between"}>
{item.label}
{item.needPlus && <fs-icon icon={"mingcute:vip-1-line"} className={"color-plus"}></fs-icon>}
</span>
);
},
},
rules: [{ required: true, message: t("certd.selectNotificationType") }],
valueChange: {
immediate: true,
async handle({ value, mode, form, immediate }) {
if (value == null) {
return;
}
const lastTitle = currentDefine.value?.title;
const define = await api.GetProviderDefine(value);
currentDefine.value = define;
console.log("define", define);
if (!form.name || form.name === lastTitle) { if (!immediate) {
form.name = define.title; form.body = {};
} if (define.needPlus) {
buildDefineFields(define, form, mode); mitter.emit("openVipModal");
}, }
}, }
helper: computed(() => {
const define = currentDefine.value; if (!form.name || form.name === lastTitle) {
if (define == null) { form.name = define.title;
return ""; }
} buildDefineFields(define, form, mode);
return define.desc; },
}), },
}, helper: computed(() => {
} as ColumnCompositionProps, const define = currentDefine.value;
name: { if (define == null) {
title: "通知名称", return "";
search: { }
show: true, return define.desc;
}, }),
type: ["text"], },
form: { } as ColumnCompositionProps,
rules: [{ required: true, message: "请填写名称" }], name: {
helper: "随便填,当多个相同类型的通知时,便于区分", title: t("certd.notificationName"),
}, search: {
column: { show: true,
width: 200, },
}, type: ["text"],
}, form: {
isDefault: { rules: [{ required: true, message: t("certd.enterName") }],
title: "是否默认", helper: t("certd.helperNotificationName"),
type: "dict-switch", },
dict: dict({ column: {
data: [ width: 200,
{ label: "是", value: true, color: "success" }, },
{ label: "否", value: false, color: "default" }, },
], isDefault: {
}), title: t("certd.isDefault"),
form: { type: "dict-switch",
value: false, dict: dict({
rules: [{ required: true, message: "请选择是否默认" }], data: [
order: 999, { label: t("certd.yes"), value: true, color: "success" },
}, { label: t("certd.no"), value: false, color: "default" },
column: { ],
align: "center", }),
width: 100, form: {
component: { value: false,
name: "a-switch", rules: [{ required: true, message: t("certd.selectIsDefault") }],
vModel: "checked", order: 999,
disabled: compute(({ value }) => { },
return value === true; column: {
}), align: "center",
on: { width: 100,
change({ row }) { component: {
Modal.confirm({ name: "a-switch",
title: "提示", vModel: "checked",
content: "确定设置为默认通知?", disabled: compute(({ value }) => {
onOk: async () => { return value === true;
await api.SetDefault(row.id); }),
await crudExpose.doRefresh(); on: {
}, change({ row }) {
onCancel: async () => { Modal.confirm({
await crudExpose.doRefresh(); title: t("certd.prompt"),
}, content: t("certd.confirmSetDefaultNotification"),
}); onOk: async () => {
}, await api.SetDefault(row.id);
}, await crudExpose.doRefresh();
}, },
}, onCancel: async () => {
} as ColumnCompositionProps, await crudExpose.doRefresh();
test: { },
title: "测试", });
form: { },
show: compute(({ form }) => { },
return !!form.type; },
}), },
component: { } as ColumnCompositionProps,
name: "api-test", test: {
action: "TestRequest", title: t("certd.test"),
}, form: {
order: 990, show: compute(({ form }) => {
col: { return !!form.type;
span: 24, }),
}, component: {
}, name: "api-test",
column: { action: "TestRequest",
show: false, },
}, order: 990,
}, col: {
setting: { span: 24,
column: { show: false }, },
form: { },
show: false, column: {
valueBuilder({ value, form }) { show: false,
form.body = {}; },
if (!value) { },
return; setting: {
} column: { show: false },
const setting = JSON.parse(value); form: {
for (const key in setting) { show: false,
form.body[key] = setting[key]; valueBuilder({ value, form }) {
} form.body = {};
}, if (!value) {
valueResolve({ form }) { return;
const setting = form.body; }
form.setting = JSON.stringify(setting); const setting = JSON.parse(value);
}, for (const key in setting) {
}, form.body[key] = setting[key];
} as ColumnCompositionProps, }
}; },
valueResolve({ form }) {
const setting = form.body;
form.setting = JSON.stringify(setting);
},
},
} as ColumnCompositionProps,
};
} }

View File

@ -5,181 +5,182 @@ import { OPEN_API_DOC, openkeyApi } from "./api";
import { useModal } from "/@/use/use-modal"; import { useModal } from "/@/use/use-modal";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n(); const { t } = useI18n();
const api = openkeyApi; const api = openkeyApi;
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 (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;
const res = await api.AddObj(form); const res = await api.AddObj(form);
return res; return res;
}; };
const model = useModal(); const model = useModal();
return { return {
crudOptions: { crudOptions: {
request: { request: {
pageRequest, pageRequest,
addRequest, addRequest,
editRequest, editRequest,
delRequest, delRequest,
}, },
search: { search: {
show: false, show: false,
}, },
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: {
text: "生成新的Key", text: t("certd.actionbar.add"),
}, },
}, },
}, },
rowHandle: { rowHandle: {
width: 300, width: 300,
fixed: "right", fixed: "right",
buttons: { buttons: {
view: { show: true }, view: { show: true },
copy: { show: false }, copy: { show: false },
edit: { show: false }, edit: { show: false },
remove: { show: true }, remove: { show: true },
gen: { gen: {
text: "接口测试", text: t("certd.gen.text"),
size: "mini", size: "mini",
icon: "devicon-plain:vitest", icon: "devicon-plain:vitest",
type: "primary", type: "primary",
async click({ row }) { async click({ row }) {
const apiToken = await api.GetApiToken(row.id); const apiToken = await api.GetApiToken(row.id);
model.success({ model.success({
title: "x-certd-token", title: t("certd.gen.title"),
maskClosable: true, maskClosable: true,
okText: "确定", okText: t("certd.gen.okText"),
width: 600, width: 600,
content: () => { content: () => {
return ( return (
<div> <div>
<div class={"m-10 p-10"}> <div class={"m-10 p-10"}>
x-certd-token3使 {t("certd.gen.contentPart1")}
<a href={OPEN_API_DOC} target={"_blank"}> <a href={OPEN_API_DOC} target={"_blank"}>
{t("certd.gen.openApi")}
</a> </a>
{t("certd.gen.contentPart2")}
</div> </div>
<div class={"m-10 p-10"} style={{ border: "1px solid #333" }}> <div class={"m-10 p-10"} style={{ border: "1px solid #333" }}>
<fs-copyable model-value={apiToken}></fs-copyable> <fs-copyable model-value={apiToken}></fs-copyable>
</div> </div>
</div> </div>
); );
}, },
}); });
}, },
}, },
}, },
}, },
columns: { columns: {
id: { id: {
title: "ID", title: "ID",
key: "id", key: "id",
type: "number", type: "number",
search: { search: {
show: false, show: false,
}, },
column: { column: {
width: 100, width: 100,
editable: { editable: {
disabled: true, disabled: true,
}, },
}, },
form: { form: {
show: false, show: false,
}, },
}, },
keyId: { keyId: {
title: "KeyId", title: "KeyId",
type: ["text", "copyable"], type: ["text", "copyable"],
search: { search: {
show: true, show: true,
}, },
form: { form: {
show: false, show: false,
}, },
column: { column: {
width: 250, width: 250,
sorter: true, sorter: true,
}, },
}, },
keySecret: { keySecret: {
title: "KeySecret", title: "KeySecret",
type: ["text", "copyable"], type: ["text", "copyable"],
form: { form: {
show: false, show: false,
}, },
column: { column: {
width: 580, width: 580,
sorter: true, sorter: true,
}, },
}, },
scope: { scope: {
title: "权限范围", title: t("certd.scope"),
type: "dict-radio", type: "dict-radio",
dict: dict({ dict: dict({
data: [ data: [
{ label: "仅开放接口", value: "open", color: "blue" }, { label: t("certd.scopeOpenApiOnly"), value: "open", color: "blue" },
{ label: "账户所有权限", value: "user", color: "red" }, { label: t("certd.scopeFullAccount"), value: "user", color: "red" },
], ],
}), }),
form: { form: {
value: "open", value: "open",
show: true, show: true,
rules: [{ required: true, message: "此项必填" }], rules: [{ required: true, message: t("certd.required") }],
helper: "仅开放接口只可以访问开放接口,账户所有权限可以访问所有接口", helper: t("certd.scopeHelper"),
component: { component: {
vModel: "value", vModel: "value",
}, },
}, },
column: { column: {
width: 120, width: 120,
align: "center", align: "center",
sorter: true, sorter: true,
}, },
}, },
createTime: { createTime: {
title: "创建时间", title: t("certd.fields.createTime"),
type: "datetime", type: "datetime",
search: { search: {
show: false, show: false,
}, },
form: { form: {
show: false, show: false,
}, },
}, },
},
}, },
}; },
};
} }

View File

@ -1,62 +1,70 @@
<template> <template>
<fs-button icon="mdi:format-list-group" type="link" text="修改定时" @click="openFormDialog"></fs-button> <fs-button icon="mdi:format-list-group" type="link" :text="t('certd.editSchedule')"
@click="openFormDialog"></fs-button>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as api from "../api"; import * as api from "../api";
import { useFormWrapper } from "@fast-crud/fast-crud"; import { useFormWrapper } from "@fast-crud/fast-crud";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const props = defineProps<{ const props = defineProps<{
selectedRowKeys: any[]; selectedRowKeys: any[];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
change: any; change: any;
}>(); }>();
async function batchUpdateRequest(form: any) { async function batchUpdateRequest(form: any) {
await api.BatchUpdateTrigger(props.selectedRowKeys, { await api.BatchUpdateTrigger(props.selectedRowKeys, {
title: "定时触发", title: "定时触发",
type: "timer", type: "timer",
props: form.props, props: form.props,
}); });
emit("change"); emit("change");
} }
const { openCrudFormDialog } = useFormWrapper(); const { openCrudFormDialog } = useFormWrapper();
async function openFormDialog() { async function openFormDialog() {
const crudOptions: any = { const crudOptions: any = {
columns: { columns: {
"props.cron": { "props.cron": {
title: "定时", title: t("certd.schedule"),
form: { form: {
component: { component: {
name: "cron-editor", name: "cron-editor",
vModel: "modelValue", vModel: "modelValue",
}, },
rules: [{ required: true, message: "请选择定时Cron" }], rules: [{ required: true, message: t("certd.selectCron") }],
}, },
}, },
}, },
form: { form: {
mode: "edit", mode: "edit",
//@ts-ignore //@ts-ignore
async doSubmit({ form }) { async doSubmit({ form }) {
await batchUpdateRequest(form); await batchUpdateRequest(form);
}, },
col: { col: {
span: 22, span: 22,
}, },
labelCol: { labelCol: {
style: { style: {
width: "100px", width: "100px",
}, },
}, },
wrapper: { wrapper: {
title: "批量修改定时", title: t("certd.batchEditSchedule"),
width: 600, width: 600,
}, },
}, },
} as any;
await openCrudFormDialog({ crudOptions }); } as any;
await openCrudFormDialog({ crudOptions });
} }
</script> </script>

View File

@ -3,7 +3,7 @@
class="pi-trigger-form" @after-open-change="triggerDrawerOnAfterVisibleChange"> class="pi-trigger-form" @after-open-change="triggerDrawerOnAfterVisibleChange">
<template #title> <template #title>
<div> <div>
编辑触发器 {{ t("certd.editTrigger") }}
<a-button v-if="mode === 'edit'" @click="triggerDelete()"> <a-button v-if="mode === 'edit'" @click="triggerDelete()">
<template #icon> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
@ -16,27 +16,28 @@
<a-form ref="triggerFormRef" class="trigger-form" :model="currentTrigger" :label-col="labelCol" <a-form ref="triggerFormRef" class="trigger-form" :model="currentTrigger" :label-col="labelCol"
:wrapper-col="wrapperCol"> :wrapper-col="wrapperCol">
<fs-form-item v-model="currentTrigger.title" :item="{ <fs-form-item v-model="currentTrigger.title" :item="{
title: '触发器名称', title: t('certd.triggerName'),
key: 'title', key: 'title',
component: { component: {
name: 'a-input', name: 'a-input',
vModel: 'value', vModel: 'value',
disabled: !editMode, disabled: !editMode,
}, },
rules: [{ required: true, message: '此项必填' }], rules: [{ required: true, message: t('certd.requiredField') }],
}" /> }" />
<fs-form-item v-model="currentTrigger.type" :item="{ <fs-form-item v-model="currentTrigger.type" :item="{
title: '类型', title: t('certd.type'),
key: 'type', key: 'type',
value: 'timer', value: 'timer',
component: { component: {
name: 'a-select', name: 'a-select',
vModel: 'value', vModel: 'value',
disabled: !editMode, disabled: !editMode,
options: [{ value: 'timer', label: '定时' }], options: [{ value: 'timer', label: t('certd.schedule') }],
}, },
rules: [{ required: true, message: '此项必填' }], rules: [{ required: true, message: t('certd.requiredField') }],
}" /> }" />
<fs-form-item v-model="currentTrigger.props.cron" :item="{ <fs-form-item v-model="currentTrigger.props.cron" :item="{
@ -93,11 +94,12 @@ export default {
{ {
type: "string", type: "string",
required: true, required: true,
message: "请输入名称", message: t("certd.enterName"),
}, },
], ],
}); });
const triggerDrawerShow = () => { const triggerDrawerShow = () => {
triggerDrawerVisible.value = true; triggerDrawerVisible.value = true;
}; };
@ -118,10 +120,11 @@ export default {
const triggerAdd = emit => { const triggerAdd = emit => {
mode.value = "add"; mode.value = "add";
const trigger = { id: nanoid(), title: "定时触发", type: "timer", props: {} }; const trigger = { id: nanoid(), title: t("certd.timerTrigger"), type: "timer", props: {} };
triggerOpen(trigger, emit); triggerOpen(trigger, emit);
}; };
const triggerEdit = (trigger, emit) => { const triggerEdit = (trigger, emit) => {
mode.value = "edit"; mode.value = "edit";
triggerOpen(trigger, emit); triggerOpen(trigger, emit);
@ -147,8 +150,8 @@ export default {
const triggerDelete = () => { const triggerDelete = () => {
Modal.confirm({ Modal.confirm({
title: "确认", title: t("certd.confirm"),
content: `确定要删除此触发器吗?`, content: t("certd.confirmDeleteTrigger"),
async onOk() { async onOk() {
callback.value("delete"); callback.value("delete");
triggerDrawerClose(); triggerDrawerClose();
@ -156,6 +159,7 @@ export default {
}); });
}; };
const blankFn = () => { const blankFn = () => {
return {}; return {};
}; };

View File

@ -4,282 +4,285 @@ import { useUserStore } from "/@/store/user";
import { Modal, notification } from "ant-design-vue"; import { Modal, notification } from "ant-design-vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
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();
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);
}; };
const userStore = useUserStore(); const userStore = useUserStore();
const settingStore = useSettingStore(); const settingStore = useSettingStore();
const userValidTimeEnabled = compute(() => { const userValidTimeEnabled = compute(() => {
return settingStore.sysPublic.userValidTimeEnabled === true; return settingStore.sysPublic.userValidTimeEnabled === true;
}); });
return { return {
crudOptions: { crudOptions: {
request: { request: {
pageRequest, pageRequest,
addRequest, addRequest,
editRequest, editRequest,
delRequest, delRequest,
}, },
rowHandle: { rowHandle: {
fixed: "right", fixed: "right",
buttons: { buttons: {
unlock: { unlock: {
title: "解除登录锁定", title: "解除登录锁定",
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: "提示",
content: "确定要解除该用户的登录锁定吗?", content: "确定要解除该用户的登录锁定吗?",
onOk: async () => { onOk: async () => {
await api.Unlock(row.id); await api.Unlock(row.id);
notification.success({ notification.success({
message: "解除成功", message: "解除成功",
}); });
}, },
}); });
}, },
}, },
}, },
}, },
table: { table: {
scroll: { scroll: {
//使用固定列时需要设置此值,并且大于等于列宽度之和的值 //使用固定列时需要设置此值,并且大于等于列宽度之和的值
x: 1400, x: 1400,
}, },
}, },
columns: { columns: {
id: { id: {
title: "id", title: "id",
type: "text", type: "text",
form: { show: false }, // 表单配置 form: { show: false }, // 表单配置
column: { column: {
width: 100, width: 100,
sorter: true, sorter: true,
}, },
}, },
createTime: { createTime: {
title: "创建时间", title: "创建时间",
type: "datetime", type: "datetime",
form: { show: false }, // 表单配置 form: { show: false }, // 表单配置
column: { column: {
width: 180, width: 180,
sorter: true, sorter: true,
}, },
}, },
// updateTime: { // updateTime: {
// title: "修改时间", // title: "修改时间",
// type: "datetime", // type: "datetime",
// form: { show: false }, // 表单配置 // form: { show: false }, // 表单配置
// column: { // column: {
// sortable: "update_time", // sortable: "update_time",
// width: 180 // width: 180
// } // }
// }, // },
username: { username: {
title: "用户名", title: "用户名",
type: "text", type: "text",
search: { show: true }, // 开启查询 search: { show: true }, // 开启查询
form: { form: {
rules: [ rules: [
{ required: true, message: "请输入用户名" }, { required: true, message: "请输入用户名" },
{ max: 50, message: "最大50个字符" }, { max: 50, message: "最大50个字符" },
], ],
}, },
editForm: { component: { disabled: false } }, editForm: { component: { disabled: false } },
column: { column: {
sorter: true, sorter: true,
width: 200, width: 200,
}, },
}, },
password: { password: {
title: "密码", title: "密码",
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: "最大50个字符" }],
component: { component: {
showPassword: true, showPassword: true,
}, },
helper: "填写则修改密码", helper: "填写则修改密码",
}, },
}, },
nickName: { nickName: {
title: "昵称", title: t("certd.nickName"),
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,
}, },
}, },
email: { email: {
title: "邮箱", title: "邮箱",
type: "text", type: "text",
search: { show: true }, // 开启查询 search: { show: true }, // 开启查询
form: { form: {
rules: [{ max: 50, message: "最大50个字符" }], rules: [{ max: 50, message: "最大50个字符" }],
}, },
column: { column: {
sorter: true, sorter: true,
width: 160, width: 160,
}, },
}, },
mobile: { mobile: {
title: "手机号", title: "手机号",
type: "text", type: "text",
search: { show: true }, // 开启查询 search: { show: true }, // 开启查询
form: { form: {
rules: [{ max: 50, message: "最大50个字符" }], rules: [{ max: 50, message: "最大50个字符" }],
}, },
column: { column: {
sorter: true, sorter: true,
width: 130, width: 130,
}, },
}, },
avatar: { avatar: {
title: "头像", title: "头像",
type: "cropper-uploader", type: "cropper-uploader",
column: { column: {
width: 70, width: 70,
component: { component: {
//设置高度,修复操作列错位的问题 //设置高度,修复操作列错位的问题
style: { style: {
height: "30px", height: "30px",
width: "auto", width: "auto",
}, },
buildUrl(key: string) { buildUrl(key: string) {
return `api/basic/file/download?&key=` + key; return `api/basic/file/download?&key=` + key;
}, },
}, },
}, },
form: { form: {
component: { component: {
vModel: "modelValue", vModel: "modelValue",
valueType: "key", valueType: "key",
cropper: { cropper: {
aspectRatio: 1, aspectRatio: 1,
autoCropArea: 1, autoCropArea: 1,
viewMode: 0, viewMode: 0,
}, },
onReady: null, onReady: null,
uploader: { uploader: {
type: "form", type: "form",
action: "/basic/file/upload", action: "/basic/file/upload",
name: "file", name: "file",
headers: { headers: {
Authorization: "Bearer " + userStore.getToken, Authorization: "Bearer " + userStore.getToken,
}, },
successHandle(res: any) { successHandle(res: any) {
return res; return res;
}, },
}, },
buildUrl(key: string) { buildUrl(key: string) {
return `api/basic/file/download?&key=` + key; return `api/basic/file/download?&key=` + key;
}, },
}, },
}, },
}, },
status: { status: {
title: "状态", title: "状态",
type: "dict-switch", type: "dict-switch",
dict: dict({ dict: dict({
data: [ data: [
{ label: "启用", value: 1, color: "green" }, { label: "启用", value: 1, color: "green" },
{ label: "禁用", value: 0, color: "red" }, { label: "禁用", value: 0, color: "red" },
], ],
}), }),
column: { column: {
align: "center", align: "center",
sorter: true, sorter: true,
width: 100, width: 100,
}, },
}, },
validTime: { validTime: {
title: "有效期", title: "有效期",
type: "date", type: "date",
form: { form: {
show: userValidTimeEnabled, show: userValidTimeEnabled,
}, },
column: { column: {
align: "center", align: "center",
sorter: true, sorter: true,
width: 100, width: 100,
show: userValidTimeEnabled, show: userValidTimeEnabled,
cellRender({ value }) { cellRender({ value }) {
if (value == null || value === 0) { if (value == null || value === 0) {
return ""; return "";
} }
if (value < dayjs().valueOf()) { if (value < dayjs().valueOf()) {
return <a-tag color={"red"}></a-tag>; return <a-tag color={"red"}></a-tag>;
} }
const date = dayjs(value).format("YYYY-MM-DD"); const date = dayjs(value).format("YYYY-MM-DD");
return ( return (
<a-tag color={"green"} title={date}> <a-tag color={"green"} title={date}>
<fs-time-humanize modelValue={value} options={{ largest: 1, units: ["y", "d", "h"] }} useFormatGreater={30000000000} /> <fs-time-humanize modelValue={value} options={{ largest: 1, units: ["y", "d", "h"] }} useFormatGreater={30000000000} />
</a-tag> </a-tag>
); );
}, },
}, },
valueBuilder({ value, row, key }) { valueBuilder({ value, row, key }) {
if (value != null) { if (value != null) {
row[key] = dayjs(value); row[key] = dayjs(value);
} }
}, },
valueResolve({ value, row, key }) { valueResolve({ value, row, key }) {
if (value != null) { if (value != null) {
row[key] = value.valueOf(); row[key] = value.valueOf();
} }
}, },
}, },
remark: { remark: {
title: "备注", title: "备注",
type: "text", type: "text",
column: { column: {
sorter: true, sorter: true,
}, },
form: { form: {
rules: [{ max: 100, message: "最大100个字符" }], rules: [{ max: 100, message: "最大100个字符" }],
}, },
}, },
roles: { roles: {
title: "角色", title: "角色",
type: "dict-select", type: "dict-select",
dict: dict({ dict: dict({
url: "/sys/authority/role/list", url: "/sys/authority/role/list",
value: "id", value: "id",
label: "name", label: "name",
}), // 数据字典 }), // 数据字典
form: { form: {
component: { mode: "multiple" }, component: { mode: "multiple" },
}, },
column: { column: {
width: 250, width: 250,
sortable: true, sortable: true,
}, },
}, },
}, },
}, },
}; };
} }