diff --git a/apps/daxpay-admin/src/api/system/security.api.ts b/apps/daxpay-admin/src/api/system/security.api.ts index 78f7caba7..b855ef094 100644 --- a/apps/daxpay-admin/src/api/system/security.api.ts +++ b/apps/daxpay-admin/src/api/system/security.api.ts @@ -98,36 +98,9 @@ export const SecurityApi = { }, }; -/** - * 密码策略配置 - */ -export interface PasswordPolicyConfig { - /** 是否启用 */ - enabled?: boolean; - /** 最小长度 */ - minLength?: number; - /** 最大长度 */ - maxLength?: number; - /** 是否要求大写字母 */ - requireUppercase?: boolean; - /** 是否要求小写字母 */ - requireLowercase?: boolean; - /** 是否要求数字 */ - requireDigit?: boolean; - /** 是否要求特殊字符 */ - requireSpecialChar?: boolean; - /** 特殊字符集合 */ - specialChars?: string; - /** 密码轮换天数 */ - rotationDays?: number; - /** 历史密码检查数量 */ - historyCount?: number; -} +import type { PasswordPolicyConfig, PasswordPolicyValidateConfig } from '@daxpay/ui-biz/types/password-policy'; -/** - * 密码策略校验配置(供前端校验使用) - */ -export type PasswordPolicyValidateConfig = PasswordPolicyConfig; +export type { PasswordPolicyConfig, PasswordPolicyValidateConfig }; /** * 登录安全配置 diff --git a/apps/daxpay-admin/src/components/channel/ChannelMerchantSelect.vue b/apps/daxpay-admin/src/components/channel/ChannelMerchantSelect.vue index bc55686d6..f8a8c9ee2 100644 --- a/apps/daxpay-admin/src/components/channel/ChannelMerchantSelect.vue +++ b/apps/daxpay-admin/src/components/channel/ChannelMerchantSelect.vue @@ -1,94 +1,13 @@ - - - - - - {{ option.data.label }} - + + + - - - - {{ selectedOption?.label ?? value }} - - - + diff --git a/apps/daxpay-admin/src/components/channel/index.ts b/apps/daxpay-admin/src/components/channel/index.ts index 86b564dcb..6b5020167 100644 --- a/apps/daxpay-admin/src/components/channel/index.ts +++ b/apps/daxpay-admin/src/components/channel/index.ts @@ -1 +1 @@ -export { ChannelLogo, resolveChannelLogoUrl } from '@daxpay/ui-biz/channel'; +export { ChannelLogo, ChannelMerchantSelect, resolveChannelLogoUrl } from '@daxpay/ui-biz/channel'; diff --git a/apps/daxpay-admin/src/components/input-password/PasswordStrength.vue b/apps/daxpay-admin/src/components/input-password/PasswordStrength.vue deleted file mode 100644 index 26d3c062c..000000000 --- a/apps/daxpay-admin/src/components/input-password/PasswordStrength.vue +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - {{ condition.label }} - - - - - - diff --git a/apps/daxpay-admin/src/components/input-password/index.ts b/apps/daxpay-admin/src/components/input-password/index.ts index 520961076..fdc6c9c01 100644 --- a/apps/daxpay-admin/src/components/input-password/index.ts +++ b/apps/daxpay-admin/src/components/input-password/index.ts @@ -1 +1 @@ -export { default as InputPassword } from './InputPassword.vue'; +export { InputPassword } from '@daxpay/ui-biz/components/input-password'; diff --git a/apps/daxpay-admin/src/components/route/RouteQueryMissingState.vue b/apps/daxpay-admin/src/components/route/RouteQueryMissingState.vue index 4d6acc66b..2218f9c2c 100644 --- a/apps/daxpay-admin/src/components/route/RouteQueryMissingState.vue +++ b/apps/daxpay-admin/src/components/route/RouteQueryMissingState.vue @@ -1,24 +1,13 @@ - - - - {{ backText }} - - - + + + + + diff --git a/apps/daxpay-admin/src/components/route/index.ts b/apps/daxpay-admin/src/components/route/index.ts new file mode 100644 index 000000000..108df857c --- /dev/null +++ b/apps/daxpay-admin/src/components/route/index.ts @@ -0,0 +1 @@ +export { RouteQueryMissingState } from '@daxpay/ui-biz/components/route'; diff --git a/apps/daxpay-admin/src/components/social/SocialLogo.vue b/apps/daxpay-admin/src/components/social/SocialLogo.vue index 3a3a31fb9..cba32acd3 100644 --- a/apps/daxpay-admin/src/components/social/SocialLogo.vue +++ b/apps/daxpay-admin/src/components/social/SocialLogo.vue @@ -1,95 +1,13 @@ - - - - {{ fallbackLetter }} - - + + + + + - - diff --git a/apps/daxpay-admin/src/components/social/index.ts b/apps/daxpay-admin/src/components/social/index.ts index 4039bf092..f0e4bbe1e 100644 --- a/apps/daxpay-admin/src/components/social/index.ts +++ b/apps/daxpay-admin/src/components/social/index.ts @@ -1 +1 @@ -export { default as SocialLogo } from './SocialLogo.vue'; +export { SocialLogo } from '@daxpay/ui-biz/components/social'; diff --git a/apps/daxpay-admin/src/utils/password-validator.ts b/apps/daxpay-admin/src/utils/password-validator.ts index f493ec4fe..f7fa1b61e 100644 --- a/apps/daxpay-admin/src/utils/password-validator.ts +++ b/apps/daxpay-admin/src/utils/password-validator.ts @@ -1,154 +1 @@ -import type { PasswordPolicyValidateConfig } from '#/api/system/security.api'; - -import { $t } from '@vben/locales'; - -/** - * 密码条件项 - */ -export interface PasswordCondition { - key: string; - label: string; - satisfied: boolean; -} - -/** - * 获取密码条件列表(用于显示状态) - */ -export function getPasswordConditions(password: string, config: PasswordPolicyValidateConfig): PasswordCondition[] { - const conditions: PasswordCondition[] = []; - - if (!config.enabled) { - return conditions; - } - - // 长度条件 - if (config.minLength && config.maxLength) { - conditions.push({ - key: 'length', - label: $t('iam.user.passwordPolicy.hintLength', { min: config.minLength, max: config.maxLength }), - satisfied: password.length >= config.minLength && password.length <= config.maxLength, - }); - } - - // 大写字母 - if (config.requireUppercase) { - conditions.push({ - key: 'uppercase', - label: $t('iam.user.passwordPolicy.hintUppercase'), - satisfied: /[A-Z]/.test(password), - }); - } - - // 小写字母 - if (config.requireLowercase) { - conditions.push({ - key: 'lowercase', - label: $t('iam.user.passwordPolicy.hintLowercase'), - satisfied: /[a-z]/.test(password), - }); - } - - // 数字 - if (config.requireDigit) { - conditions.push({ - key: 'digit', - label: $t('iam.user.passwordPolicy.hintDigit'), - satisfied: /\d/.test(password), - }); - } - - // 特殊字符 - if (config.requireSpecialChar && config.specialChars) { - conditions.push({ - key: 'specialChar', - label: $t('iam.user.passwordPolicy.hintSpecialChar', { chars: config.specialChars }), - satisfied: new RegExp(`[${escapeRegExp(config.specialChars)}]`).test(password), - }); - } - - return conditions; -} - -/** - * 计算密码强度(根据配置) - * 强度 = 满足的条件数 / 总条件数 * 5 - */ -export function calculatePasswordStrength(password: string, config: PasswordPolicyValidateConfig): number { - if (!password) return 0; - - const conditions = getPasswordConditions(password, config); - const satisfiedCount = conditions.filter((c) => c.satisfied).length; - const totalCount = conditions.length; - - if (totalCount === 0) return 0; - - // 按比例计算强度(1-5) - return Math.ceil((satisfiedCount / totalCount) * 5); -} - -/** - * 生成简化的表单校验规则 - */ -export function generatePasswordRules(config: PasswordPolicyValidateConfig) { - return [ - { required: true, message: $t('iam.user.validation.passwordRequired') }, - { - validator: (_rule: any, value: string) => { - if (!value) return Promise.resolve(); - // 密码不允许中文 - if (/[\u4E00-\u9FA5]/.test(value)) { - return Promise.reject($t('iam.user.validation.passwordNoChinese')); - } - if (!config.enabled) return Promise.resolve(); - const conditions = getPasswordConditions(value, config); - const allSatisfied = conditions.every((c) => c.satisfied); - if (!allSatisfied) { - return Promise.reject($t('iam.user.passwordPolicy.notMeetRequirements')); - } - return Promise.resolve(); - }, - trigger: 'change', - }, - ]; -} - -/** - * 账号验证规则(只允许字母、数字、下划线、中划线) - */ -export function generateAccountRules() { - return [ - { required: true, message: $t('common.pleaseInput') }, - { min: 6, max: 20, message: $t('iam.user.validation.accountLength') }, - { pattern: /^[a-zA-Z0-9_-]+$/, message: $t('iam.user.validation.accountFormat') }, - ]; -} - -/** - * 计算密码内在强度(固定规则,不依赖后台配置) - * 用于强度条的显示 - */ -export function calculateIntrinsicStrength(password: string): number { - if (!password) return 0; - - let strength = 0; - - // 长度 >= 8 - if (password.length >= 8) strength++; - // 包含小写字母 - if (/[a-z]/.test(password)) strength++; - // 包含大写字母 - if (/[A-Z]/.test(password)) strength++; - // 包含数字 - if (/\d/.test(password)) strength++; - // 包含特殊字符 - if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) strength++; - - return strength; -} - -/** - * 转义正则表达式特殊字符 - */ -function escapeRegExp(string: string): string { - return string.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`); -} +export * from '@daxpay/ui-biz/utils/password-validator'; diff --git a/apps/daxpay-merchant/src/api/system/security.api.ts b/apps/daxpay-merchant/src/api/system/security.api.ts index 78f7caba7..b855ef094 100644 --- a/apps/daxpay-merchant/src/api/system/security.api.ts +++ b/apps/daxpay-merchant/src/api/system/security.api.ts @@ -98,36 +98,9 @@ export const SecurityApi = { }, }; -/** - * 密码策略配置 - */ -export interface PasswordPolicyConfig { - /** 是否启用 */ - enabled?: boolean; - /** 最小长度 */ - minLength?: number; - /** 最大长度 */ - maxLength?: number; - /** 是否要求大写字母 */ - requireUppercase?: boolean; - /** 是否要求小写字母 */ - requireLowercase?: boolean; - /** 是否要求数字 */ - requireDigit?: boolean; - /** 是否要求特殊字符 */ - requireSpecialChar?: boolean; - /** 特殊字符集合 */ - specialChars?: string; - /** 密码轮换天数 */ - rotationDays?: number; - /** 历史密码检查数量 */ - historyCount?: number; -} +import type { PasswordPolicyConfig, PasswordPolicyValidateConfig } from '@daxpay/ui-biz/types/password-policy'; -/** - * 密码策略校验配置(供前端校验使用) - */ -export type PasswordPolicyValidateConfig = PasswordPolicyConfig; +export type { PasswordPolicyConfig, PasswordPolicyValidateConfig }; /** * 登录安全配置 diff --git a/apps/daxpay-merchant/src/assets/social/README.md b/apps/daxpay-merchant/src/assets/social/README.md deleted file mode 100644 index 8c4968c28..000000000 --- a/apps/daxpay-merchant/src/assets/social/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# 社交平台 SVG 图标 - -本目录存放第三方社交登录平台的品牌 Logo(SVG 矢量图),供 `SocialLogo.vue` 组件按 `source` 字段加载。 - -## 文件清单 - -| 文件名 | 平台 | source | 建议品牌色 | -|--------|------|--------|-----------| -| `weChat.svg` | 微信公众号 | `weChat` | #07c160 | -| `weCom.svg` | 企业微信 | `weCom` | #2f90ff | -| `qq.svg` | QQ | `qq` | #12b7f5 | -| `github.svg` | GitHub | `github` | #181717 | -| `gitee.svg` | Gitee | `gitee` | #c71d23 | -| `feishu.svg` | 飞书 | `feishu` | #3370ff | -| `dingTalk.svg` | 钉钉 | `dingTalk` | #1677ff | -| `douyin.svg` | 抖音 | `douyin` | #000000 | -| `alipay.svg` | 支付宝 | `alipay` | #1677FF | - -> 当前 SVG 为品牌识别用简化标, 拿到官方品牌 logo 后直接覆盖同名文件即可。 - -## 命名规则 - -文件名 = 后端 `SocialSource` 枚举的 `name` + `.svg`(与平台 source 字段一致,驼峰命名)。 - -## 素材来源建议 - -- 微信/QQ/钉钉/抖音:各平台开放平台官方品牌资源 -- GitHub:github.com/logos(官方品牌指南) -- Gitee/飞书:官网品牌资源页 -- 也可从 simple-icons.com(CC0 协议品牌图标库)下载对应 SVG - -## 缺失时的表现 - -`SocialLogo.vue` 组件在找不到对应 SVG 文件时会显示一个带品牌色背景的占位块(显示平台首字母),不影响页面功能。补充 SVG 文件后自动生效,无需改动代码。 diff --git a/apps/daxpay-merchant/src/assets/social/alipay.svg b/apps/daxpay-merchant/src/assets/social/alipay.svg deleted file mode 100644 index 87ced0a2b..000000000 --- a/apps/daxpay-merchant/src/assets/social/alipay.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/dingTalk.svg b/apps/daxpay-merchant/src/assets/social/dingTalk.svg deleted file mode 100644 index cfbfb30cd..000000000 --- a/apps/daxpay-merchant/src/assets/social/dingTalk.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/douyin.svg b/apps/daxpay-merchant/src/assets/social/douyin.svg deleted file mode 100644 index b7e145d37..000000000 --- a/apps/daxpay-merchant/src/assets/social/douyin.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/feishu.svg b/apps/daxpay-merchant/src/assets/social/feishu.svg deleted file mode 100644 index 54fe1277f..000000000 --- a/apps/daxpay-merchant/src/assets/social/feishu.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/gitee.svg b/apps/daxpay-merchant/src/assets/social/gitee.svg deleted file mode 100644 index 8878814a2..000000000 --- a/apps/daxpay-merchant/src/assets/social/gitee.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/github.svg b/apps/daxpay-merchant/src/assets/social/github.svg deleted file mode 100644 index 8769d6c02..000000000 --- a/apps/daxpay-merchant/src/assets/social/github.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/google.svg b/apps/daxpay-merchant/src/assets/social/google.svg deleted file mode 100644 index 4f88d26f6..000000000 --- a/apps/daxpay-merchant/src/assets/social/google.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/qq.svg b/apps/daxpay-merchant/src/assets/social/qq.svg deleted file mode 100644 index df9c8ad99..000000000 --- a/apps/daxpay-merchant/src/assets/social/qq.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/weChat.svg b/apps/daxpay-merchant/src/assets/social/weChat.svg deleted file mode 100644 index 1a05a9b77..000000000 --- a/apps/daxpay-merchant/src/assets/social/weChat.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/assets/social/weCom.svg b/apps/daxpay-merchant/src/assets/social/weCom.svg deleted file mode 100644 index 90411fa54..000000000 --- a/apps/daxpay-merchant/src/assets/social/weCom.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/daxpay-merchant/src/components/channel/ChannelMerchantSelect.vue b/apps/daxpay-merchant/src/components/channel/ChannelMerchantSelect.vue index bc55686d6..f8a8c9ee2 100644 --- a/apps/daxpay-merchant/src/components/channel/ChannelMerchantSelect.vue +++ b/apps/daxpay-merchant/src/components/channel/ChannelMerchantSelect.vue @@ -1,94 +1,13 @@ - - - - - - {{ option.data.label }} - + + + - - - - {{ selectedOption?.label ?? value }} - - - + diff --git a/apps/daxpay-merchant/src/components/channel/index.ts b/apps/daxpay-merchant/src/components/channel/index.ts index 86b564dcb..6b5020167 100644 --- a/apps/daxpay-merchant/src/components/channel/index.ts +++ b/apps/daxpay-merchant/src/components/channel/index.ts @@ -1 +1 @@ -export { ChannelLogo, resolveChannelLogoUrl } from '@daxpay/ui-biz/channel'; +export { ChannelLogo, ChannelMerchantSelect, resolveChannelLogoUrl } from '@daxpay/ui-biz/channel'; diff --git a/apps/daxpay-merchant/src/components/input-password/InputPassword.vue b/apps/daxpay-merchant/src/components/input-password/InputPassword.vue deleted file mode 100644 index ac1b9738f..000000000 --- a/apps/daxpay-merchant/src/components/input-password/InputPassword.vue +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/apps/daxpay-merchant/src/components/input-password/index.ts b/apps/daxpay-merchant/src/components/input-password/index.ts index 520961076..fdc6c9c01 100644 --- a/apps/daxpay-merchant/src/components/input-password/index.ts +++ b/apps/daxpay-merchant/src/components/input-password/index.ts @@ -1 +1 @@ -export { default as InputPassword } from './InputPassword.vue'; +export { InputPassword } from '@daxpay/ui-biz/components/input-password'; diff --git a/apps/daxpay-merchant/src/components/route/RouteQueryMissingState.vue b/apps/daxpay-merchant/src/components/route/RouteQueryMissingState.vue index 4d6acc66b..2218f9c2c 100644 --- a/apps/daxpay-merchant/src/components/route/RouteQueryMissingState.vue +++ b/apps/daxpay-merchant/src/components/route/RouteQueryMissingState.vue @@ -1,24 +1,13 @@ - - - - {{ backText }} - - - + + + + + diff --git a/apps/daxpay-merchant/src/components/route/index.ts b/apps/daxpay-merchant/src/components/route/index.ts new file mode 100644 index 000000000..108df857c --- /dev/null +++ b/apps/daxpay-merchant/src/components/route/index.ts @@ -0,0 +1 @@ +export { RouteQueryMissingState } from '@daxpay/ui-biz/components/route'; diff --git a/apps/daxpay-merchant/src/components/social/SocialLogo.vue b/apps/daxpay-merchant/src/components/social/SocialLogo.vue index 3a3a31fb9..cba32acd3 100644 --- a/apps/daxpay-merchant/src/components/social/SocialLogo.vue +++ b/apps/daxpay-merchant/src/components/social/SocialLogo.vue @@ -1,95 +1,13 @@ - - - - {{ fallbackLetter }} - - + + + + + - - diff --git a/apps/daxpay-merchant/src/components/social/index.ts b/apps/daxpay-merchant/src/components/social/index.ts index 4039bf092..f0e4bbe1e 100644 --- a/apps/daxpay-merchant/src/components/social/index.ts +++ b/apps/daxpay-merchant/src/components/social/index.ts @@ -1 +1 @@ -export { default as SocialLogo } from './SocialLogo.vue'; +export { SocialLogo } from '@daxpay/ui-biz/components/social'; diff --git a/apps/daxpay-merchant/src/utils/password-validator.ts b/apps/daxpay-merchant/src/utils/password-validator.ts index f493ec4fe..f7fa1b61e 100644 --- a/apps/daxpay-merchant/src/utils/password-validator.ts +++ b/apps/daxpay-merchant/src/utils/password-validator.ts @@ -1,154 +1 @@ -import type { PasswordPolicyValidateConfig } from '#/api/system/security.api'; - -import { $t } from '@vben/locales'; - -/** - * 密码条件项 - */ -export interface PasswordCondition { - key: string; - label: string; - satisfied: boolean; -} - -/** - * 获取密码条件列表(用于显示状态) - */ -export function getPasswordConditions(password: string, config: PasswordPolicyValidateConfig): PasswordCondition[] { - const conditions: PasswordCondition[] = []; - - if (!config.enabled) { - return conditions; - } - - // 长度条件 - if (config.minLength && config.maxLength) { - conditions.push({ - key: 'length', - label: $t('iam.user.passwordPolicy.hintLength', { min: config.minLength, max: config.maxLength }), - satisfied: password.length >= config.minLength && password.length <= config.maxLength, - }); - } - - // 大写字母 - if (config.requireUppercase) { - conditions.push({ - key: 'uppercase', - label: $t('iam.user.passwordPolicy.hintUppercase'), - satisfied: /[A-Z]/.test(password), - }); - } - - // 小写字母 - if (config.requireLowercase) { - conditions.push({ - key: 'lowercase', - label: $t('iam.user.passwordPolicy.hintLowercase'), - satisfied: /[a-z]/.test(password), - }); - } - - // 数字 - if (config.requireDigit) { - conditions.push({ - key: 'digit', - label: $t('iam.user.passwordPolicy.hintDigit'), - satisfied: /\d/.test(password), - }); - } - - // 特殊字符 - if (config.requireSpecialChar && config.specialChars) { - conditions.push({ - key: 'specialChar', - label: $t('iam.user.passwordPolicy.hintSpecialChar', { chars: config.specialChars }), - satisfied: new RegExp(`[${escapeRegExp(config.specialChars)}]`).test(password), - }); - } - - return conditions; -} - -/** - * 计算密码强度(根据配置) - * 强度 = 满足的条件数 / 总条件数 * 5 - */ -export function calculatePasswordStrength(password: string, config: PasswordPolicyValidateConfig): number { - if (!password) return 0; - - const conditions = getPasswordConditions(password, config); - const satisfiedCount = conditions.filter((c) => c.satisfied).length; - const totalCount = conditions.length; - - if (totalCount === 0) return 0; - - // 按比例计算强度(1-5) - return Math.ceil((satisfiedCount / totalCount) * 5); -} - -/** - * 生成简化的表单校验规则 - */ -export function generatePasswordRules(config: PasswordPolicyValidateConfig) { - return [ - { required: true, message: $t('iam.user.validation.passwordRequired') }, - { - validator: (_rule: any, value: string) => { - if (!value) return Promise.resolve(); - // 密码不允许中文 - if (/[\u4E00-\u9FA5]/.test(value)) { - return Promise.reject($t('iam.user.validation.passwordNoChinese')); - } - if (!config.enabled) return Promise.resolve(); - const conditions = getPasswordConditions(value, config); - const allSatisfied = conditions.every((c) => c.satisfied); - if (!allSatisfied) { - return Promise.reject($t('iam.user.passwordPolicy.notMeetRequirements')); - } - return Promise.resolve(); - }, - trigger: 'change', - }, - ]; -} - -/** - * 账号验证规则(只允许字母、数字、下划线、中划线) - */ -export function generateAccountRules() { - return [ - { required: true, message: $t('common.pleaseInput') }, - { min: 6, max: 20, message: $t('iam.user.validation.accountLength') }, - { pattern: /^[a-zA-Z0-9_-]+$/, message: $t('iam.user.validation.accountFormat') }, - ]; -} - -/** - * 计算密码内在强度(固定规则,不依赖后台配置) - * 用于强度条的显示 - */ -export function calculateIntrinsicStrength(password: string): number { - if (!password) return 0; - - let strength = 0; - - // 长度 >= 8 - if (password.length >= 8) strength++; - // 包含小写字母 - if (/[a-z]/.test(password)) strength++; - // 包含大写字母 - if (/[A-Z]/.test(password)) strength++; - // 包含数字 - if (/\d/.test(password)) strength++; - // 包含特殊字符 - if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) strength++; - - return strength; -} - -/** - * 转义正则表达式特殊字符 - */ -function escapeRegExp(string: string): string { - return string.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`); -} +export * from '@daxpay/ui-biz/utils/password-validator'; diff --git a/packages/business/ui-biz/package.json b/packages/business/ui-biz/package.json index 24aae8bc4..340da7273 100644 --- a/packages/business/ui-biz/package.json +++ b/packages/business/ui-biz/package.json @@ -109,10 +109,18 @@ "types": "./src/utils/timezone-data.ts", "default": "./src/utils/timezone-data.ts" }, + "./utils/password-validator": { + "types": "./src/utils/password-validator.ts", + "default": "./src/utils/password-validator.ts" + }, "./types/web": { "types": "./src/types/web.ts", "default": "./src/types/web.ts" }, + "./types/password-policy": { + "types": "./src/types/password-policy.ts", + "default": "./src/types/password-policy.ts" + }, "./components/charts": { "types": "./src/components/charts/index.ts", "default": "./src/components/charts/index.ts" @@ -145,6 +153,18 @@ "types": "./src/components/login-expired-modal/index.ts", "default": "./src/components/login-expired-modal/index.ts" }, + "./components/route": { + "types": "./src/components/route/index.ts", + "default": "./src/components/route/index.ts" + }, + "./components/input-password": { + "types": "./src/components/input-password/index.ts", + "default": "./src/components/input-password/index.ts" + }, + "./components/social": { + "types": "./src/components/social/index.ts", + "default": "./src/components/social/index.ts" + }, "./components/AntdAppProvider": { "types": "./src/components/AntdAppProvider.vue", "default": "./src/components/AntdAppProvider.vue" diff --git a/apps/daxpay-admin/src/assets/social/README.md b/packages/business/ui-biz/src/assets/social/README.md similarity index 100% rename from apps/daxpay-admin/src/assets/social/README.md rename to packages/business/ui-biz/src/assets/social/README.md diff --git a/apps/daxpay-admin/src/assets/social/alipay.svg b/packages/business/ui-biz/src/assets/social/alipay.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/alipay.svg rename to packages/business/ui-biz/src/assets/social/alipay.svg diff --git a/apps/daxpay-admin/src/assets/social/dingTalk.svg b/packages/business/ui-biz/src/assets/social/dingTalk.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/dingTalk.svg rename to packages/business/ui-biz/src/assets/social/dingTalk.svg diff --git a/apps/daxpay-admin/src/assets/social/douyin.svg b/packages/business/ui-biz/src/assets/social/douyin.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/douyin.svg rename to packages/business/ui-biz/src/assets/social/douyin.svg diff --git a/apps/daxpay-admin/src/assets/social/feishu.svg b/packages/business/ui-biz/src/assets/social/feishu.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/feishu.svg rename to packages/business/ui-biz/src/assets/social/feishu.svg diff --git a/apps/daxpay-admin/src/assets/social/gitee.svg b/packages/business/ui-biz/src/assets/social/gitee.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/gitee.svg rename to packages/business/ui-biz/src/assets/social/gitee.svg diff --git a/apps/daxpay-admin/src/assets/social/github.svg b/packages/business/ui-biz/src/assets/social/github.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/github.svg rename to packages/business/ui-biz/src/assets/social/github.svg diff --git a/apps/daxpay-admin/src/assets/social/google.svg b/packages/business/ui-biz/src/assets/social/google.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/google.svg rename to packages/business/ui-biz/src/assets/social/google.svg diff --git a/apps/daxpay-admin/src/assets/social/qq.svg b/packages/business/ui-biz/src/assets/social/qq.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/qq.svg rename to packages/business/ui-biz/src/assets/social/qq.svg diff --git a/apps/daxpay-admin/src/assets/social/weChat.svg b/packages/business/ui-biz/src/assets/social/weChat.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/weChat.svg rename to packages/business/ui-biz/src/assets/social/weChat.svg diff --git a/apps/daxpay-admin/src/assets/social/weCom.svg b/packages/business/ui-biz/src/assets/social/weCom.svg similarity index 100% rename from apps/daxpay-admin/src/assets/social/weCom.svg rename to packages/business/ui-biz/src/assets/social/weCom.svg diff --git a/packages/business/ui-biz/src/components/channel/ChannelMerchantSelect.vue b/packages/business/ui-biz/src/components/channel/ChannelMerchantSelect.vue new file mode 100644 index 000000000..99e8d81ef --- /dev/null +++ b/packages/business/ui-biz/src/components/channel/ChannelMerchantSelect.vue @@ -0,0 +1,94 @@ + + + + + + + + + {{ option.data.label }} + + + + + + {{ selectedOption?.label ?? value }} + + + + diff --git a/packages/business/ui-biz/src/components/channel/index.ts b/packages/business/ui-biz/src/components/channel/index.ts index a815e9fcf..6b836777e 100644 --- a/packages/business/ui-biz/src/components/channel/index.ts +++ b/packages/business/ui-biz/src/components/channel/index.ts @@ -1,2 +1,3 @@ export { default as ChannelLogo } from './ChannelLogo.vue'; +export { default as ChannelMerchantSelect } from './ChannelMerchantSelect.vue'; export { resolveChannelLogoUrl } from './resolveLogo'; diff --git a/apps/daxpay-admin/src/components/input-password/InputPassword.vue b/packages/business/ui-biz/src/components/input-password/InputPassword.vue similarity index 93% rename from apps/daxpay-admin/src/components/input-password/InputPassword.vue rename to packages/business/ui-biz/src/components/input-password/InputPassword.vue index ac1b9738f..9f71f8da4 100644 --- a/apps/daxpay-admin/src/components/input-password/InputPassword.vue +++ b/packages/business/ui-biz/src/components/input-password/InputPassword.vue @@ -1,5 +1,5 @@ + + + + + + {{ backText }} + + + + diff --git a/packages/business/ui-biz/src/components/route/index.ts b/packages/business/ui-biz/src/components/route/index.ts new file mode 100644 index 000000000..0301a1c7c --- /dev/null +++ b/packages/business/ui-biz/src/components/route/index.ts @@ -0,0 +1 @@ +export { default as RouteQueryMissingState } from './RouteQueryMissingState.vue'; diff --git a/packages/business/ui-biz/src/components/social/SocialLogo.vue b/packages/business/ui-biz/src/components/social/SocialLogo.vue new file mode 100644 index 000000000..dc8acad3f --- /dev/null +++ b/packages/business/ui-biz/src/components/social/SocialLogo.vue @@ -0,0 +1,98 @@ + + + + + + + {{ fallbackLetter }} + + + + + diff --git a/packages/business/ui-biz/src/components/social/index.ts b/packages/business/ui-biz/src/components/social/index.ts new file mode 100644 index 000000000..4039bf092 --- /dev/null +++ b/packages/business/ui-biz/src/components/social/index.ts @@ -0,0 +1 @@ +export { default as SocialLogo } from './SocialLogo.vue'; diff --git a/packages/business/ui-biz/src/types/password-policy.ts b/packages/business/ui-biz/src/types/password-policy.ts new file mode 100644 index 000000000..7f4be3d35 --- /dev/null +++ b/packages/business/ui-biz/src/types/password-policy.ts @@ -0,0 +1,30 @@ +/** + * 密码策略配置 + */ +export interface PasswordPolicyConfig { + /** 是否启用 */ + enabled?: boolean; + /** 最小长度 */ + minLength?: number; + /** 最大长度 */ + maxLength?: number; + /** 是否要求大写字母 */ + requireUppercase?: boolean; + /** 是否要求小写字母 */ + requireLowercase?: boolean; + /** 是否要求数字 */ + requireDigit?: boolean; + /** 是否要求特殊字符 */ + requireSpecialChar?: boolean; + /** 特殊字符集合 */ + specialChars?: string; + /** 密码轮换天数 */ + rotationDays?: number; + /** 历史密码检查数量 */ + historyCount?: number; +} + +/** + * 密码策略校验配置(供前端校验使用) + */ +export type PasswordPolicyValidateConfig = PasswordPolicyConfig; diff --git a/packages/business/ui-biz/src/utils/password-validator.ts b/packages/business/ui-biz/src/utils/password-validator.ts new file mode 100644 index 000000000..986f020c0 --- /dev/null +++ b/packages/business/ui-biz/src/utils/password-validator.ts @@ -0,0 +1,154 @@ +import type { PasswordPolicyValidateConfig } from '../types/password-policy'; + +import { $t } from '@vben/locales'; + +/** + * 密码条件项 + */ +export interface PasswordCondition { + key: string; + label: string; + satisfied: boolean; +} + +/** + * 获取密码条件列表(用于显示状态) + */ +export function getPasswordConditions(password: string, config: PasswordPolicyValidateConfig): PasswordCondition[] { + const conditions: PasswordCondition[] = []; + + if (!config.enabled) { + return conditions; + } + + // 长度条件 + if (config.minLength && config.maxLength) { + conditions.push({ + key: 'length', + label: $t('iam.user.passwordPolicy.hintLength', { min: config.minLength, max: config.maxLength }), + satisfied: password.length >= config.minLength && password.length <= config.maxLength, + }); + } + + // 大写字母 + if (config.requireUppercase) { + conditions.push({ + key: 'uppercase', + label: $t('iam.user.passwordPolicy.hintUppercase'), + satisfied: /[A-Z]/.test(password), + }); + } + + // 小写字母 + if (config.requireLowercase) { + conditions.push({ + key: 'lowercase', + label: $t('iam.user.passwordPolicy.hintLowercase'), + satisfied: /[a-z]/.test(password), + }); + } + + // 数字 + if (config.requireDigit) { + conditions.push({ + key: 'digit', + label: $t('iam.user.passwordPolicy.hintDigit'), + satisfied: /\d/.test(password), + }); + } + + // 特殊字符 + if (config.requireSpecialChar && config.specialChars) { + conditions.push({ + key: 'specialChar', + label: $t('iam.user.passwordPolicy.hintSpecialChar', { chars: config.specialChars }), + satisfied: new RegExp(`[${escapeRegExp(config.specialChars)}]`).test(password), + }); + } + + return conditions; +} + +/** + * 计算密码强度(根据配置) + * 强度 = 满足的条件数 / 总条件数 * 5 + */ +export function calculatePasswordStrength(password: string, config: PasswordPolicyValidateConfig): number { + if (!password) return 0; + + const conditions = getPasswordConditions(password, config); + const satisfiedCount = conditions.filter((c) => c.satisfied).length; + const totalCount = conditions.length; + + if (totalCount === 0) return 0; + + // 按比例计算强度(1-5) + return Math.ceil((satisfiedCount / totalCount) * 5); +} + +/** + * 生成简化的表单校验规则 + */ +export function generatePasswordRules(config: PasswordPolicyValidateConfig) { + return [ + { required: true, message: $t('iam.user.validation.passwordRequired') }, + { + validator: (_rule: any, value: string) => { + if (!value) return Promise.resolve(); + // 密码不允许中文 + if (/[\u4E00-\u9FA5]/.test(value)) { + return Promise.reject($t('iam.user.validation.passwordNoChinese')); + } + if (!config.enabled) return Promise.resolve(); + const conditions = getPasswordConditions(value, config); + const allSatisfied = conditions.every((c) => c.satisfied); + if (!allSatisfied) { + return Promise.reject($t('iam.user.passwordPolicy.notMeetRequirements')); + } + return Promise.resolve(); + }, + trigger: 'change', + }, + ]; +} + +/** + * 账号验证规则(只允许字母、数字、下划线、中划线) + */ +export function generateAccountRules() { + return [ + { required: true, message: $t('common.pleaseInput') }, + { min: 6, max: 20, message: $t('iam.user.validation.accountLength') }, + { pattern: /^[a-zA-Z0-9_-]+$/, message: $t('iam.user.validation.accountFormat') }, + ]; +} + +/** + * 计算密码内在强度(固定规则,不依赖后台配置) + * 用于强度条的显示 + */ +export function calculateIntrinsicStrength(password: string): number { + if (!password) return 0; + + let strength = 0; + + // 长度 >= 8 + if (password.length >= 8) strength++; + // 包含小写字母 + if (/[a-z]/.test(password)) strength++; + // 包含大写字母 + if (/[A-Z]/.test(password)) strength++; + // 包含数字 + if (/\d/.test(password)) strength++; + // 包含特殊字符 + if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) strength++; + + return strength; +} + +/** + * 转义正则表达式特殊字符 + */ +function escapeRegExp(string: string): string { + return string.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`); +}
- -