初始化

This commit is contained in:
2026-02-22 18:56:10 +08:00
commit 26677972a6
3112 changed files with 255972 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
# \_core
此目录包含应用程序正常运行所需的基本视图。这些视图是应用程序布局中使用的视图。

View File

@@ -0,0 +1,39 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { About } from '@easyflow/common-ui';
import { api } from '#/api/request';
import { useDictStore } from '#/store';
defineOptions({ name: 'About' });
const dictStore = useDictStore();
onMounted(() => {
test();
dictStore.fetchDictionary('accountType');
});
const accountInfo = ref<any>();
function test() {
api
.get('/api/v1/sysAccount/myProfile')
.then((res) => {
accountInfo.value = res.data;
// console.log('res', res);
})
.catch((error) => {
console.error('error', error);
});
}
</script>
<template>
<div>
<div>{{ accountInfo?.loginName }}</div>
<div v-for="(item, index) in [0, 1, 99]" :key="index">
<div>
{{ dictStore.getDictLabel('accountType', item) }}
</div>
</div>
<About />
</div>
</template>

View File

@@ -0,0 +1,69 @@
<script lang="ts" setup>
import type { EasyFlowFormSchema } from '@easyflow/common-ui';
import type { Recordable } from '@easyflow/types';
import { computed, ref } from 'vue';
import { AuthenticationCodeLogin, z } from '@easyflow/common-ui';
import { $t } from '@easyflow/locales';
defineOptions({ name: 'CodeLogin' });
const loading = ref(false);
const CODE_LENGTH = 6;
const formSchema = computed((): EasyFlowFormSchema[] => {
return [
{
component: 'EasyFlowInput',
componentProps: {
placeholder: $t('authentication.mobile'),
},
fieldName: 'phoneNumber',
label: $t('authentication.mobile'),
rules: z
.string()
.min(1, { message: $t('authentication.mobileTip') })
.refine((v) => /^\d{11}$/.test(v), {
message: $t('authentication.mobileErrortip'),
}),
},
{
component: 'EasyFlowPinInput',
componentProps: {
codeLength: CODE_LENGTH,
createText: (countdown: number) => {
const text =
countdown > 0
? $t('authentication.sendText', [countdown])
: $t('authentication.sendCode');
return text;
},
placeholder: $t('authentication.code'),
},
fieldName: 'code',
label: $t('authentication.code'),
rules: z.string().length(CODE_LENGTH, {
message: $t('authentication.codeTip', [CODE_LENGTH]),
}),
},
];
});
/**
* 异步处理登录操作
* Asynchronously handle the login process
* @param values 登录表单数据
*/
async function handleLogin(values: Recordable<any>) {
// eslint-disable-next-line no-console
console.log(values);
}
</script>
<template>
<AuthenticationCodeLogin
:form-schema="formSchema"
:loading="loading"
@submit="handleLogin"
/>
</template>

View File

@@ -0,0 +1,43 @@
<script lang="ts" setup>
import type { EasyFlowFormSchema } from '@easyflow/common-ui';
import type { Recordable } from '@easyflow/types';
import { computed, ref } from 'vue';
import { AuthenticationForgetPassword, z } from '@easyflow/common-ui';
import { $t } from '@easyflow/locales';
defineOptions({ name: 'ForgetPassword' });
const loading = ref(false);
const formSchema = computed((): EasyFlowFormSchema[] => {
return [
{
component: 'EasyFlowInput',
componentProps: {
placeholder: 'example@example.com',
},
fieldName: 'email',
label: $t('authentication.email'),
rules: z
.string()
.min(1, { message: $t('authentication.emailTip') })
.email($t('authentication.emailValidErrorTip')),
},
];
});
function handleSubmit(value: Recordable<any>) {
// eslint-disable-next-line no-console
console.log('reset email:', value);
}
</script>
<template>
<AuthenticationForgetPassword
:form-schema="formSchema"
:loading="loading"
@submit="handleSubmit"
/>
</template>

View File

@@ -0,0 +1,129 @@
<script lang="ts" setup>
import type { EasyFlowFormSchema } from '@easyflow/common-ui';
import { computed, onMounted } from 'vue';
import { AuthenticationLogin, z } from '@easyflow/common-ui';
import { useAppConfig } from '@easyflow/hooks';
import { $t } from '@easyflow/locales';
import { preferences } from '@easyflow/preferences';
import { api } from '#/api/request';
import { useAuthStore } from '#/store';
defineOptions({ name: 'Login' });
onMounted(() => {});
const authStore = useAuthStore();
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
type PlatformType = 'ding_talk' | 'wx_web';
const title = computed(() => preferences.auth.welcomeBack);
const subTitle = computed(() => preferences.auth.loginSubtitle);
const formSchema = computed((): EasyFlowFormSchema[] => {
return [
{
component: 'EasyFlowInput',
componentProps: {
placeholder: $t('authentication.usernameTip'),
},
fieldName: 'account',
label: $t('authentication.username'),
rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
},
{
component: 'EasyFlowInputPassword',
componentProps: {
placeholder: $t('authentication.password'),
},
fieldName: 'password',
label: $t('authentication.password'),
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
},
];
});
function onSubmit(values: any) {
// config 对象为TAC验证码的一些配置和验证的回调
const config = {
// 生成接口 (必选项,必须配置, 要符合tianai-captcha默认验证码生成接口规范)
requestCaptchaDataUrl: `${apiURL}/api/v1/public/getCaptcha`,
// 验证接口 (必选项,必须配置, 要符合tianai-captcha默认验证码校验接口规范)
validCaptchaUrl: `${apiURL}/api/v1/public/check`,
// 验证码绑定的div块 (必选项,必须配置)
bindEl: '#captcha-box',
// 验证成功回调函数(必选项,必须配置)
validSuccess: (res: any, _: any, tac: any) => {
// 销毁验证码服务
tac.destroyWindow();
// 调用具体的login方法
values.validToken = res.data;
authStore.authLogin(values);
},
// 验证失败的回调函数(可忽略,如果不自定义 validFail 方法时,会使用默认的)
validFail: (_: any, __: any, tac: any) => {
// 验证失败后重新拉取验证码
tac.reloadCaptcha();
},
// 刷新按钮回调事件
btnRefreshFun: (_: any, tac: any) => {
tac.reloadCaptcha();
},
// 关闭按钮回调事件
btnCloseFun: (_: any, tac: any) => {
tac.destroyWindow();
},
};
const style = {
logoUrl: null, // 去除logo
// logoUrl: "/xx/xx/xxx.png" // 替换成自定义的logo
btnUrl: '/tac-btn.png',
};
window
// @ts-ignore
.initTAC('/tac', config, style)
.then((tac: any) => {
tac.init(); // 调用init则显示验证码
})
.catch((error: any) => {
console.error('初始化tac失败', error);
});
}
function getAuthUrl(platform: PlatformType) {
return api.get('/thirdAuth/getAuthUrl', {
params: {
platform,
},
});
}
</script>
<template>
<div>
<AuthenticationLogin
:form-schema="formSchema"
:loading="authStore.loginLoading"
:title="title"
:sub-title="subTitle"
@submit="onSubmit"
/>
<div id="captcha-box" class="captcha-div"></div>
</div>
</template>
<style scoped>
.captcha-div {
position: absolute;
top: 30vh;
left: 21vh;
}
.platform-icon {
width: 30px;
height: 30px;
cursor: pointer;
}
</style>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import { onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useAccessStore, useUserStore } from '@easyflow/stores';
import { getAccessCodesApi, getUserInfoApi } from '#/api';
const accessStore = useAccessStore();
const userStore = useUserStore();
const route = useRoute();
const token: any = route.query.token;
onMounted(() => {
redirect();
});
async function redirect() {
accessStore.setAccessToken(token);
const [fetchUserInfoResult, accessCodes] = await Promise.all([
getUserInfoApi(),
getAccessCodesApi(),
]);
userStore.setUserInfo(fetchUserInfoResult);
accessStore.setAccessCodes(accessCodes);
window.location.href = '/';
}
</script>
<template>
<div>redirecting...</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,10 @@
<script lang="ts" setup>
import { AuthenticationQrCodeLogin } from '@easyflow/common-ui';
import { LOGIN_PATH } from '@easyflow/constants';
defineOptions({ name: 'QrCodeLogin' });
</script>
<template>
<AuthenticationQrCodeLogin :login-path="LOGIN_PATH" />
</template>

View File

@@ -0,0 +1,96 @@
<script lang="ts" setup>
import type { EasyFlowFormSchema } from '@easyflow/common-ui';
import type { Recordable } from '@easyflow/types';
import { computed, h, ref } from 'vue';
import { AuthenticationRegister, z } from '@easyflow/common-ui';
import { $t } from '@easyflow/locales';
defineOptions({ name: 'Register' });
const loading = ref(false);
const formSchema = computed((): EasyFlowFormSchema[] => {
return [
{
component: 'EasyFlowInput',
componentProps: {
placeholder: $t('authentication.usernameTip'),
},
fieldName: 'username',
label: $t('authentication.username'),
rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
},
{
component: 'EasyFlowInputPassword',
componentProps: {
passwordStrength: true,
placeholder: $t('authentication.password'),
},
fieldName: 'password',
label: $t('authentication.password'),
renderComponentContent() {
return {
strengthText: () => $t('authentication.passwordStrength'),
};
},
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
},
{
component: 'EasyFlowInputPassword',
componentProps: {
placeholder: $t('authentication.confirmPassword'),
},
dependencies: {
rules(values) {
const { password } = values;
return z
.string({ required_error: $t('authentication.passwordTip') })
.min(1, { message: $t('authentication.passwordTip') })
.refine((value) => value === password, {
message: $t('authentication.confirmPasswordTip'),
});
},
triggerFields: ['password'],
},
fieldName: 'confirmPassword',
label: $t('authentication.confirmPassword'),
},
{
component: 'EasyFlowCheckbox',
fieldName: 'agreePolicy',
renderComponentContent: () => ({
default: () =>
h('span', [
$t('authentication.agree'),
h(
'a',
{
class: 'easyflow-link ml-1 ',
href: '',
},
`${$t('authentication.privacyPolicy')} & ${$t('authentication.terms')}`,
),
]),
}),
rules: z.boolean().refine((value) => !!value, {
message: $t('authentication.agreeTip'),
}),
},
];
});
function handleSubmit(value: Recordable<any>) {
// eslint-disable-next-line no-console
console.log('register submit:', value);
}
</script>
<template>
<AuthenticationRegister
:form-schema="formSchema"
:loading="loading"
@submit="handleSubmit"
/>
</template>

View File

@@ -0,0 +1,7 @@
<script lang="ts" setup>
import { Fallback } from '@easyflow/common-ui';
</script>
<template>
<Fallback status="coming-soon" />
</template>

View File

@@ -0,0 +1,9 @@
<script lang="ts" setup>
import { Fallback } from '@easyflow/common-ui';
defineOptions({ name: 'Fallback403Demo' });
</script>
<template>
<Fallback status="403" />
</template>

View File

@@ -0,0 +1,9 @@
<script lang="ts" setup>
import { Fallback } from '@easyflow/common-ui';
defineOptions({ name: 'Fallback500Demo' });
</script>
<template>
<Fallback status="500" />
</template>

View File

@@ -0,0 +1,9 @@
<script lang="ts" setup>
import { Fallback } from '@easyflow/common-ui';
defineOptions({ name: 'Fallback404Demo' });
</script>
<template>
<Fallback status="404" />
</template>

View File

@@ -0,0 +1,9 @@
<script lang="ts" setup>
import { Fallback } from '@easyflow/common-ui';
defineOptions({ name: 'FallbackOfflineDemo' });
</script>
<template>
<Fallback status="offline" />
</template>

View File

@@ -0,0 +1,77 @@
<script setup lang="ts">
import type { EasyFlowFormSchema } from '#/adapter/form';
import { computed, markRaw, onMounted, ref } from 'vue';
import { ProfileBaseSetting } from '@easyflow/common-ui';
import { ElMessage } from 'element-plus';
import { api } from '#/api/request';
import Cropper from '#/components/upload/Cropper.vue';
import { $t } from '#/locales';
import { useAuthStore } from '#/store';
const { fetchUserInfo } = useAuthStore();
const profileBaseSettingRef = ref();
const formSchema = computed((): EasyFlowFormSchema[] => {
return [
{
fieldName: 'avatar',
component: markRaw(Cropper),
componentProps: {
crop: true,
},
label: $t('sysAccount.avatar'),
},
{
fieldName: 'nickname',
component: 'Input',
label: $t('sysAccount.nickname'),
},
{
fieldName: 'mobile',
component: 'Input',
label: $t('sysAccount.mobile'),
},
{
fieldName: 'email',
component: 'Input',
label: $t('sysAccount.email'),
},
];
});
onMounted(async () => {
await getInfo();
});
async function getInfo() {
loading.value = true;
const data = await fetchUserInfo();
await profileBaseSettingRef.value.getFormApi().setValues(data);
loading.value = false;
}
const loading = ref(false);
const updateLoading = ref(false);
function handleSubmit(values: any) {
updateLoading.value = true;
api.post('/api/v1/sysAccount/updateProfile', values).then((res) => {
updateLoading.value = false;
if (res.errorCode === 0) {
ElMessage.success($t('message.success'));
getInfo();
}
});
}
</script>
<template>
<ProfileBaseSetting
v-loading="loading"
:button-loading="updateLoading"
ref="profileBaseSettingRef"
:form-schema="formSchema"
:button-text="$t('button.update')"
@submit="handleSubmit"
/>
</template>

View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { Profile } from '@easyflow/common-ui';
import { useUserStore } from '@easyflow/stores';
import { $t } from '#/locales';
import ProfileBase from './base-setting.vue';
import ProfileNotificationSetting from './notification-setting.vue';
import ProfilePasswordSetting from './password-setting.vue';
import ProfileSecuritySetting from './security-setting.vue';
const route = useRoute();
const userStore = useUserStore();
const tabsValue = ref<string>('basic');
const tabs = [
{
label: $t('settingsConfig.basic'),
value: 'basic',
},
{
label: $t('settingsConfig.updatePwd'),
value: 'password',
},
];
onMounted(() => {
if (route.query.tab) {
tabsValue.value = route.query.tab as string;
}
});
</script>
<template>
<Profile
v-model:model-value="tabsValue"
:title="$t('page.auth.profile')"
:user-info="userStore.userInfo"
:tabs="tabs"
>
<template #content>
<ProfileBase v-if="tabsValue === 'basic'" />
<ProfileSecuritySetting v-if="tabsValue === 'security'" />
<ProfilePasswordSetting v-if="tabsValue === 'password'" />
<ProfileNotificationSetting v-if="tabsValue === 'notice'" />
</template>
</Profile>
</template>

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ProfileNotificationSetting } from '@easyflow/common-ui';
import { $t } from '#/locales';
const formSchema = computed(() => {
return [
{
value: true,
fieldName: 'accountPassword',
label: $t('page.auth.accountPassword'),
description: $t('page.description.accountPassword'),
},
{
value: true,
fieldName: 'systemMessage',
label: $t('page.auth.systemMessage'),
description: $t('page.description.systemMessage'),
},
{
value: true,
fieldName: 'todoTask',
label: $t('page.auth.todoTasks'),
description: $t('page.description.todoTasks'),
},
];
});
</script>
<template>
<ProfileNotificationSetting :form-schema="formSchema" />
</template>

View File

@@ -0,0 +1,78 @@
<script setup lang="ts">
import type { EasyFlowFormSchema } from '#/adapter/form';
import { computed, ref } from 'vue';
import { ProfilePasswordSetting, z } from '@easyflow/common-ui';
import { ElMessage } from 'element-plus';
import { api } from '#/api/request';
import { $t } from '#/locales';
const profilePasswordSettingRef = ref();
const formSchema = computed((): EasyFlowFormSchema[] => {
return [
{
fieldName: 'password',
label: $t('sysAccount.oldPwd'),
component: 'EasyFlowInputPassword',
componentProps: {
placeholder: $t('sysAccount.oldPwd') + $t('common.isRequired'),
},
},
{
fieldName: 'newPassword',
label: $t('sysAccount.newPwd'),
component: 'EasyFlowInputPassword',
componentProps: {
passwordStrength: true,
placeholder: $t('sysAccount.newPwd') + $t('common.isRequired'),
},
},
{
fieldName: 'confirmPassword',
label: $t('sysAccount.confirmPwd'),
component: 'EasyFlowInputPassword',
componentProps: {
passwordStrength: true,
placeholder: $t('sysAccount.repeatPwd'),
},
dependencies: {
rules(values) {
const { newPassword } = values;
return z
.string({ required_error: $t('sysAccount.repeatPwd') })
.min(1, { message: $t('sysAccount.repeatPwd') })
.refine((value) => value === newPassword, {
message: $t('sysAccount.notSamePwd'),
});
},
triggerFields: ['newPassword'],
},
},
];
});
const updateLoading = ref(false);
function handleSubmit(values: any) {
updateLoading.value = true;
api.post('/api/v1/sysAccount/updatePassword', values).then((res) => {
updateLoading.value = false;
if (res.errorCode === 0) {
ElMessage.success($t('message.success'));
}
});
}
</script>
<template>
<ProfilePasswordSetting
:button-loading="updateLoading"
:button-text="$t('button.update')"
ref="profilePasswordSettingRef"
class="w-1/3"
:form-schema="formSchema"
@submit="handleSubmit"
/>
</template>

View File

@@ -0,0 +1,43 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ProfileSecuritySetting } from '@easyflow/common-ui';
const formSchema = computed(() => {
return [
{
value: true,
fieldName: 'accountPassword',
label: '账户密码',
description: '当前密码强度:强',
},
{
value: true,
fieldName: 'securityPhone',
label: '密保手机',
description: '已绑定手机138****8293',
},
{
value: true,
fieldName: 'securityQuestion',
label: '密保问题',
description: '未设置密保问题,密保问题可有效保护账户安全',
},
{
value: true,
fieldName: 'securityEmail',
label: '备用邮箱',
description: '已绑定邮箱ant***sign.com',
},
{
value: false,
fieldName: 'securityMfa',
label: 'MFA 设备',
description: '未绑定 MFA 设备,绑定后,可以进行二次确认',
},
];
});
</script>
<template>
<ProfileSecuritySetting :form-schema="formSchema" />
</template>