This commit is contained in:
xiaojunnuo
2025-09-11 00:19:38 +08:00
parent d2ecfe5491
commit 3635fb3910
26 changed files with 1368 additions and 4 deletions
@@ -46,6 +46,10 @@ export type SysPublicSetting = {
aiChatEnabled?: boolean;
showRunStrategy?: boolean;
captchaEnabled?: boolean;
captchaType?: number;
captchaAddonId?: number;
};
export type SuiteSetting = {
enabled?: boolean;
@@ -0,0 +1,117 @@
import { request } from "/src/api/service";
import { RequestHandleReq } from "/@/components/plugins/lib";
export function createAddonApi() {
const apiPrefix = "/addon";
return {
async GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query,
});
},
async AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj,
});
},
async UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj,
});
},
async DelObj(id: number) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id },
});
},
async GetObj(id: number) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id },
});
},
async GetOptions(id: number) {
return await request({
url: apiPrefix + "/options",
method: "post",
});
},
async SetDefault(id: number) {
return await request({
url: apiPrefix + "/setDefault",
method: "post",
params: { id },
});
},
async GetDefaultId() {
return await request({
url: apiPrefix + "/getDefaultId",
method: "post",
});
},
async GetSimpleInfo(id: number) {
return await request({
url: apiPrefix + "/simpleInfo",
method: "post",
params: { id },
});
},
async GetDefineTypes(addonType: string) {
return await request({
url: apiPrefix + `/getTypeDict?addonType=${addonType}`,
method: "post",
});
},
async GetProviderDefine(type: string) {
return await request({
url: apiPrefix + "/define",
method: "post",
params: { type },
});
},
async GetProviderDefineByType(type: string) {
return await request({
url: apiPrefix + "/defineByType",
method: "post",
params: { type },
});
},
async Handle(req: RequestHandleReq, opts: any = {}) {
const url = `/pi/handle/${req.type}`;
const { typeName, action, data, input } = req;
const res = await request({
url,
method: "post",
data: {
typeName,
action,
data,
input,
},
...opts,
});
return res;
},
};
}
@@ -0,0 +1,270 @@
import { ColumnCompositionProps, compute, dict } from "@fast-crud/fast-crud";
import { computed, provide, ref, toRef } from "vue";
import { useReference } from "/@/use/use-refrence";
import { forEach, get, merge, set } from "lodash-es";
import { Modal } from "ant-design-vue";
import { mitter } from "/@/utils/util.mitt";
import { useI18n } from "/src/locales";
export function addonProvide(api: any) {
provide("addonApi", api);
provide("get:plugin:type", () => {
return "addon";
});
}
export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
const { t } = useI18n();
const addonTypeTypeDictRef = dict({
data: [{ value: "captcha", label: "验证码" }],
});
const addonTypeDictRef = dict({
url: "/addon/getTypeDict?addonType=captcha",
});
const defaultPluginConfig = {
component: {
name: "a-input",
vModel: "value",
},
};
function buildDefineFields(define: any, form: any, mode: string) {
const formWrapperRef = crudExpose.getFormWrapperRef();
const columnsRef = toRef(formWrapperRef.formOptions, "columns");
for (const key in columnsRef.value) {
if (key.indexOf(".") >= 0) {
delete columnsRef.value[key];
}
}
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.rules) {
column.rules = [];
}
column.rules.push({ required: true, message: t("certd.requiredField") });
}
//设置默认值
if (column.value != null && get(form, key) == null) {
set(form, key, column.value);
}
//字段配置赋值
columnsRef.value[key] = column;
console.log("form", columnsRef.value, form);
});
}
const currentDefine = ref();
return {
id: {
title: "ID",
key: "id",
type: "number",
column: {
width: 100,
},
form: {
show: false,
},
},
addonType: {
title: "Addon类型",
type: "dict-select",
dict: addonTypeTypeDictRef,
search: {
show: false,
},
column: {
width: 200,
component: {
color: "auto",
},
},
form: {
onChange(ctx: { value: any }) {
addonTypeDictRef.url = `/addon/getTypeDict?addonType=${ctx.value}`;
},
},
editForm: {
component: {
disabled: false,
},
},
},
type: {
title: t("certd.notificationType"),
type: "dict-select",
dict: addonTypeDictRef,
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 (!immediate) {
form.body = {};
if (define.needPlus) {
mitter.emit("openVipModal");
}
}
if (!form.name || form.name === lastTitle) {
form.name = define.title;
}
buildDefineFields(define, form, mode);
},
},
helper: computed(() => {
const define = currentDefine.value;
if (define == null) {
return "";
}
return define.desc;
}),
},
} as ColumnCompositionProps,
name: {
title: t("certd.notificationName"),
search: {
show: true,
},
type: ["text"],
form: {
rules: [{ required: true, message: t("certd.enterName") }],
helper: t("certd.helperNotificationName"),
},
column: {
width: 200,
},
},
isDefault: {
title: t("certd.isDefault"),
type: "dict-switch",
dict: dict({
data: [
{ label: t("certd.yes"), value: true, color: "success" },
{ label: t("certd.no"), value: false, color: "default" },
],
}),
form: {
value: false,
rules: [{ required: true, message: t("certd.selectIsDefault") }],
order: 999,
},
column: {
align: "center",
width: 100,
component: {
name: "a-switch",
vModel: "checked",
disabled: compute(({ value }) => {
return value === true;
}),
on: {
change({ row }) {
Modal.confirm({
title: t("certd.prompt"),
content: t("certd.confirmSetDefaultNotification"),
onOk: async () => {
await api.SetDefault(row.id);
await crudExpose.doRefresh();
},
onCancel: async () => {
await crudExpose.doRefresh();
},
});
},
},
},
},
} as ColumnCompositionProps,
test: {
title: t("certd.test"),
form: {
show: compute(({ form }) => {
return !!form.type;
}),
component: {
name: "api-test",
action: "TestRequest",
},
order: 990,
col: {
span: 24,
},
},
column: {
show: false,
},
},
setting: {
column: { show: false },
form: {
show: false,
valueBuilder({ value, form }) {
form.body = {};
if (!value) {
return;
}
const setting = JSON.parse(value);
for (const key in setting) {
form.body[key] = setting[key];
}
},
valueResolve({ form }) {
const setting = form.body;
form.setting = JSON.stringify(setting);
},
},
} as ColumnCompositionProps,
};
}
@@ -0,0 +1,54 @@
import { ref } from "vue";
import { getCommonColumnDefine } from "./common";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { createAddonApi } from "/@/views/certd/addon/api";
const api = createAddonApi();
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async (req: EditReq) => {
const { form, row } = req;
form.id = row.id;
const res = await api.UpdateObj(form);
return res;
};
const delRequest = async (req: DelReq) => {
const { row } = req;
return await api.DelObj(row.id);
};
const addRequest = async (req: AddReq) => {
const { form } = req;
const res = await api.AddObj(form);
return res;
};
const typeRef = ref();
const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api);
return {
crudOptions: {
request: {
pageRequest,
addRequest,
editRequest,
delRequest,
},
form: {
labelCol: {
//固定label宽度
span: null,
style: {
width: "145px",
},
},
},
rowHandle: {
width: 200,
},
columns: {
...commonColumnsDefine,
},
},
};
}
@@ -0,0 +1,41 @@
<template>
<fs-page>
<template #header>
<div class="title">
通知管理
<span class="sub">管理通知配置</span>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page>
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { createNotificationApi } from "./api";
import { notificationProvide } from "/@/views/certd/notification/common";
export default defineComponent({
name: "NotificationManager",
setup() {
const api = createNotificationApi();
notificationProvide(api);
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } });
// 页面打开后获取列表数据
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
return {
crudBinding,
crudRef,
};
},
});
</script>
@@ -0,0 +1,46 @@
<template>
<div class="captcha"></div>
</template>
<script setup lang="ts">
import { doRequest } from "/@/components/plugins/lib";
import { createAddonApi } from "/src/api/modules/addon";
import { useSettingStore } from "/@/store/settings";
const props = defineProps<{
modelValue?: any;
}>();
const emit = defineEmits(["update:modelValue", "change"]);
const addonApi = createAddonApi();
const settingStore = useSettingStore();
async function getCaptchaAddonDefine() {
const type = settingStore.public.captchaType;
const define = addonApi.getDefineByType("captcha", type);
const res = await doRequest(
{
addonId: settingStore.public.captchaAddonId
type: "captcha",
typeName: type,
action: "onGetParams",
},
);
}
function init() {
// @ts-ignore
initGeetest4(
{
captchaId: "您的captchaId",
},
(captcha: any) => {
// captcha为验证码实例
captcha.appendTo(".captcha"); // 调用appendTo将验证码插入到页的某一个元素中,这个元素用户可以自定义
}
);
}
function onChange(value: string) {
emit("update:modelValue", value);
emit("change", value);
}
</script>
@@ -20,6 +20,10 @@
</template>
</a-input-password>
</a-form-item>
<a-form-item required name="captcha">
<captcha v-model:model-value="formState.captcha"></captcha>
</a-form-item>
</template>
</a-tab-pane>
<a-tab-pane v-if="sysPublicSettings.smsLoginEnabled === true" key="sms" :tab="t('authentication.smsTab')">
@@ -111,6 +115,7 @@ export default defineComponent({
imgCode: "",
smsCode: "",
randomStr: "",
captcha: {},
});
const rules = {