feat(VInputPassword): Add VInputPassword Component
This commit is contained in:
parent
448ac5293e
commit
a1bf7e9b55
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"0 debug pnpm:scope": {
|
|
||||||
"selected": 1
|
|
||||||
},
|
|
||||||
"1 error pnpm": {
|
|
||||||
"errno": 1,
|
|
||||||
"code": "ELIFECYCLE",
|
|
||||||
"pkgid": "butterfly-admin@3.0.0",
|
|
||||||
"stage": "clean",
|
|
||||||
"script": "npx rimraf docs/node_modules && npx rimraf node_modules",
|
|
||||||
"pkgname": "butterfly-admin",
|
|
||||||
"err": {
|
|
||||||
"name": "pnpm",
|
|
||||||
"message": "butterfly-admin@3.0.0 clean: `npx rimraf docs/node_modules && npx rimraf node_modules`\nExit status 1",
|
|
||||||
"code": "ELIFECYCLE",
|
|
||||||
"stack": "pnpm: butterfly-admin@3.0.0 clean: `npx rimraf docs/node_modules && npx rimraf node_modules`\nExit status 1\n at EventEmitter.<anonymous> (C:\\Users\\Saber\\AppData\\Roaming\\nvm\\v16.0.0\\node_modules\\pnpm\\dist\\pnpm.cjs:103540:20)\n at EventEmitter.emit (node:events:365:28)\n at ChildProcess.<anonymous> (C:\\Users\\Saber\\AppData\\Roaming\\nvm\\v16.0.0\\node_modules\\pnpm\\dist\\pnpm.cjs:91469:18)\n at ChildProcess.emit (node:events:365:28)\n at maybeClose (node:internal/child_process:1067:16)\n at Process.ChildProcess._handle.onexit (node:internal/child_process:301:5)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,6 +39,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-loading .app-loading-logo {
|
.app-loading .app-loading-logo {
|
||||||
|
width: 100px;
|
||||||
margin: 0 auto 15px auto;
|
margin: 0 auto 15px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
53
package.json
53
package.json
|
@ -25,60 +25,61 @@
|
||||||
"postinstall": "husky install"
|
"postinstall": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vueuse/core": "^7.1.2",
|
"@vueuse/core": "^7.4.1",
|
||||||
|
"@zxcvbn-ts/core": "^1.2.0",
|
||||||
"element-plus": "1.2.0-beta.6",
|
"element-plus": "1.2.0-beta.6",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"pinia": "^2.0.6",
|
"pinia": "^2.0.9",
|
||||||
"vue": "^3.2.24",
|
"vue": "3.2.26",
|
||||||
"vue-i18n": "9.1.9",
|
"vue-i18n": "9.1.9",
|
||||||
"vue-router": "^4.0.12",
|
"vue-router": "^4.0.12",
|
||||||
"vue-types": "^4.1.1"
|
"vue-types": "^4.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^15.0.0",
|
"@commitlint/cli": "^16.0.1",
|
||||||
"@commitlint/config-conventional": "^15.0.0",
|
"@commitlint/config-conventional": "^16.0.0",
|
||||||
"@iconify/json": "^1.1.441",
|
"@iconify/json": "^1.1.448",
|
||||||
"@intlify/vite-plugin-vue-i18n": "^3.2.1",
|
"@intlify/vite-plugin-vue-i18n": "^3.2.1",
|
||||||
"@types/lodash-es": "^4.17.5",
|
"@types/lodash-es": "^4.17.5",
|
||||||
"@types/node": "^16.11.12",
|
"@types/node": "^17.0.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.6.0",
|
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
||||||
"@typescript-eslint/parser": "^5.6.0",
|
"@typescript-eslint/parser": "^5.8.1",
|
||||||
"@vitejs/plugin-vue": "^1.9.3",
|
"@vitejs/plugin-vue": "^2.0.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^1.3.0",
|
"@vitejs/plugin-vue-jsx": "^1.3.3",
|
||||||
"async-validator": "^4.0.7",
|
"async-validator": "^4.0.7",
|
||||||
"autoprefixer": "^10.4.0",
|
"autoprefixer": "^10.4.1",
|
||||||
"commitizen": "^4.2.4",
|
"commitizen": "^4.2.4",
|
||||||
"eslint": "^8.4.1",
|
"eslint": "^8.5.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-define-config": "^1.2.0",
|
"eslint-define-config": "^1.2.1",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^8.2.0",
|
"eslint-plugin-vue": "^8.2.0",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"less": "^4.1.2",
|
"less": "^4.1.2",
|
||||||
"lint-staged": "^12.1.2",
|
"lint-staged": "^12.1.4",
|
||||||
"postcss": "^8.4.4",
|
"postcss": "^8.4.5",
|
||||||
"postcss-html": "^1.3.0",
|
"postcss-html": "^1.3.0",
|
||||||
"postcss-less": "^5.0.0",
|
"postcss-less": "^5.0.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"pretty-quick": "^3.1.2",
|
"pretty-quick": "^3.1.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"stylelint": "^14.1.0",
|
"stylelint": "^14.2.0",
|
||||||
"stylelint-config-html": "^1.0.0",
|
"stylelint-config-html": "^1.0.0",
|
||||||
"stylelint-config-prettier": "^9.0.3",
|
"stylelint-config-prettier": "^9.0.3",
|
||||||
"stylelint-config-standard": "^24.0.0",
|
"stylelint-config-standard": "^24.0.0",
|
||||||
"stylelint-order": "^5.0.0",
|
"stylelint-order": "^5.0.0",
|
||||||
"typescript": "4.5.2",
|
"typescript": "4.5.4",
|
||||||
"unplugin-icons": "^0.12.23",
|
"unplugin-icons": "^0.13.0",
|
||||||
"vite": "2.6.14",
|
"vite": "2.7.9",
|
||||||
"vite-plugin-eslint": "^1.3.0",
|
"vite-plugin-eslint": "^1.3.0",
|
||||||
"vite-plugin-style-import": "^1.4.0",
|
"vite-plugin-style-import": "^1.4.1",
|
||||||
"vite-plugin-windicss": "^1.5.4",
|
"vite-plugin-windicss": "^1.6.1",
|
||||||
"vue-tsc": "^0.3.0",
|
"vue-tsc": "^0.30.1",
|
||||||
"windicss": "^3.2.1",
|
"windicss": "^3.4.1",
|
||||||
"windicss-analysis": "^0.3.5"
|
"windicss-analysis": "^0.3.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.0.0"
|
"node": ">= 14.0.0"
|
||||||
},
|
},
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
|
|
2735
pnpm-lock.yaml
2735
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 17 KiB |
BIN
public/logo.png
BIN
public/logo.png
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 7.0 KiB |
127
src/App.vue
127
src/App.vue
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, onMounted, markRaw } from 'vue'
|
import { reactive, ref, onMounted, markRaw } from 'vue'
|
||||||
import { ElConfigProvider, ElIcon } from 'element-plus'
|
import { ElConfigProvider, ElIcon } from 'element-plus'
|
||||||
|
import { VConfigGlobal } from '@/components/ConfigGlobal'
|
||||||
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
|
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
|
||||||
// import en from 'element-plus/lib/locale/lang/en'
|
// import en from 'element-plus/lib/locale/lang/en'
|
||||||
import { VFrom } from '@/components/Form'
|
import { VFrom } from '@/components/Form'
|
||||||
|
@ -1016,73 +1017,93 @@ const schema = reactive<VFormSchema[]>([
|
||||||
field: 'field63',
|
field: 'field63',
|
||||||
component: 'TimeSelect',
|
component: 'TimeSelect',
|
||||||
label: t('formDemo.default')
|
label: t('formDemo.default')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'field64',
|
||||||
|
component: 'Divider',
|
||||||
|
label: t('formDemo.inputPassword')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'field65',
|
||||||
|
component: 'InputPassword',
|
||||||
|
label: t('formDemo.default')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'field66',
|
||||||
|
component: 'InputPassword',
|
||||||
|
label: t('formDemo.passwordStrength'),
|
||||||
|
componentProps: {
|
||||||
|
strength: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElConfigProvider :locale="zhCn">
|
<VConfigGlobal>
|
||||||
<VFrom :schema="schema">
|
<ElConfigProvider :locale="zhCn">
|
||||||
<template #field4-prefix>
|
<VFrom :schema="schema">
|
||||||
<ElIcon class="el-input__icon"><Calendar /></ElIcon>
|
<template #field4-prefix>
|
||||||
</template>
|
<ElIcon class="el-input__icon"><Calendar /></ElIcon>
|
||||||
<template #field4-suffix>
|
</template>
|
||||||
<ElIcon class="el-input__icon"><Calendar /></ElIcon>
|
<template #field4-suffix>
|
||||||
</template>
|
<ElIcon class="el-input__icon"><Calendar /></ElIcon>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #field5-prepend> Http:// </template>
|
<template #field5-prepend> Http:// </template>
|
||||||
<template #field5-append> .com </template>
|
<template #field5-append> .com </template>
|
||||||
|
|
||||||
<template #field9-default="{ item }">
|
<template #field9-default="{ item }">
|
||||||
<div class="value">{{ item.value }}</div>
|
<div class="value">{{ item.value }}</div>
|
||||||
<span class="link">{{ item.link }}</span>
|
<span class="link">{{ item.link }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #field15-option="{ item }">
|
<template #field15-option="{ item }">
|
||||||
<span style="float: left">{{ item.label }}</span>
|
<span style="float: left">{{ item.label }}</span>
|
||||||
<span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">
|
<span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">
|
||||||
{{ item.value }}
|
{{ item.value }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #field17-option="{ item }">
|
<template #field17-option="{ item }">
|
||||||
<span style="float: left">{{ item.label }}</span>
|
<span style="float: left">{{ item.label }}</span>
|
||||||
<span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">
|
<span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">
|
||||||
{{ item.value }}
|
{{ item.value }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #field20-default="{ item }">
|
<template #field20-default="{ item }">
|
||||||
<span style="float: left">{{ item.label }}</span>
|
<span style="float: left">{{ item.label }}</span>
|
||||||
<span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">
|
<span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">
|
||||||
{{ item.value }}
|
{{ item.value }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #field22-default="{ item }">
|
<template #field22-default="{ item }">
|
||||||
<span style="float: left">{{ item.label }}</span>
|
<span style="float: left">{{ item.label }}</span>
|
||||||
<span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">
|
<span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">
|
||||||
{{ item.value }}
|
{{ item.value }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #field25-default="{ node, data }">
|
<template #field25-default="{ node, data }">
|
||||||
<span>{{ data.label }}</span>
|
<span>{{ data.label }}</span>
|
||||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #field36-default="{ option }">
|
<template #field36-default="{ option }">
|
||||||
<span>{{ option.value }} - {{ option.desc }}</span>
|
<span>{{ option.value }} - {{ option.desc }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #field55-default="cell">
|
<template #field55-default="cell">
|
||||||
<div class="cell" :class="{ current: cell.isCurrent }">
|
<div class="cell" :class="{ current: cell.isCurrent }">
|
||||||
<span class="text">{{ cell.text }}</span>
|
<span class="text">{{ cell.text }}</span>
|
||||||
<span v-if="isHoliday(cell)" class="holiday"></span>
|
<span v-if="isHoliday(cell)" class="holiday"></span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VFrom>
|
</VFrom>
|
||||||
</ElConfigProvider>
|
</ElConfigProvider>
|
||||||
|
</VConfigGlobal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import VConfigGlobal from './src/VConfigGlobal.vue'
|
||||||
|
|
||||||
|
export { VConfigGlobal }
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="tsx">
|
||||||
|
import { provide, defineComponent } from 'vue'
|
||||||
|
import { vConfigGlobalProps } from './props'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'VConfigGlobal',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: vConfigGlobalProps,
|
||||||
|
setup(props, { slots }) {
|
||||||
|
provide('configGlobal', props)
|
||||||
|
|
||||||
|
return () => slots.default?.()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
|
export const vConfigGlobalProps = {
|
||||||
|
size: propTypes.oneOf(['default', 'medium', 'small', 'mini']).def('default')
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="tsx">
|
<script lang="tsx">
|
||||||
import { PropType, defineComponent, ref, computed, unref, reactive, watch } from 'vue'
|
import { PropType, defineComponent, ref, computed, unref, watch } from 'vue'
|
||||||
import { ElForm, ElFormItem, ElRow, ElCol } from 'element-plus'
|
import { ElForm, ElFormItem, ElRow, ElCol } from 'element-plus'
|
||||||
import { componentMap } from './componentMap'
|
import { componentMap } from './componentMap'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
@ -9,7 +9,7 @@ import {
|
||||||
setGridProp,
|
setGridProp,
|
||||||
setComponentProps,
|
setComponentProps,
|
||||||
setItemComponentSlots,
|
setItemComponentSlots,
|
||||||
setModel
|
initModel
|
||||||
} from './helper'
|
} from './helper'
|
||||||
import { useRenderSelect } from './components/useRenderSelect'
|
import { useRenderSelect } from './components/useRenderSelect'
|
||||||
import { useRenderRadio } from './components/useRenderRadio'
|
import { useRenderRadio } from './components/useRenderRadio'
|
||||||
|
@ -44,13 +44,22 @@ export default defineComponent({
|
||||||
const getProps = computed(() => props)
|
const getProps = computed(() => props)
|
||||||
const { schema, isCol, isCustom, autoSetPlaceholder } = unref(getProps)
|
const { schema, isCol, isCustom, autoSetPlaceholder } = unref(getProps)
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formModel = reactive<Recordable>({})
|
const formModel = ref<Recordable>({})
|
||||||
|
watch(
|
||||||
|
() => formModel.value,
|
||||||
|
(formModel: Recordable) => {
|
||||||
|
console.log(formModel)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 监听表单结构化数组,重新生成formModel
|
// 监听表单结构化数组,重新生成formModel
|
||||||
watch(
|
watch(
|
||||||
() => schema,
|
() => schema,
|
||||||
(schema) => {
|
(schema) => {
|
||||||
setModel(schema, formModel)
|
formModel.value = initModel(schema, unref(formModel))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
@ -108,7 +117,7 @@ export default defineComponent({
|
||||||
const Com = componentMap[item.component as string] as ReturnType<typeof defineComponent>
|
const Com = componentMap[item.component as string] as ReturnType<typeof defineComponent>
|
||||||
return (
|
return (
|
||||||
<Com
|
<Com
|
||||||
vModel={formModel[item.field]}
|
vModel={formModel.value[item.field]}
|
||||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
||||||
{...setComponentProps(item)}
|
{...setComponentProps(item)}
|
||||||
{...(notRenderOptions.includes(item?.component as string) &&
|
{...(notRenderOptions.includes(item?.component as string) &&
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
ElAutocomplete,
|
ElAutocomplete,
|
||||||
ElDivider
|
ElDivider
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
|
import { VInputPassword } from '@/components/InputPassword'
|
||||||
|
|
||||||
const componentMap: Recordable<Component, ComponentName> = {
|
const componentMap: Recordable<Component, ComponentName> = {
|
||||||
Radio: ElRadioGroup,
|
Radio: ElRadioGroup,
|
||||||
|
@ -38,7 +39,8 @@ const componentMap: Recordable<Component, ComponentName> = {
|
||||||
Divider: ElDivider,
|
Divider: ElDivider,
|
||||||
TimeSelect: ElTimeSelect,
|
TimeSelect: ElTimeSelect,
|
||||||
SelectV2: ElSelectV2,
|
SelectV2: ElSelectV2,
|
||||||
RadioButton: ElRadioGroup
|
RadioButton: ElRadioGroup,
|
||||||
|
InputPassword: VInputPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
export { componentMap }
|
export { componentMap }
|
||||||
|
|
|
@ -17,7 +17,7 @@ interface PlaceholderMoel {
|
||||||
* @description 用于自动设置placeholder
|
* @description 用于自动设置placeholder
|
||||||
*/
|
*/
|
||||||
export function setTextPlaceholder(schema: VFormSchema): PlaceholderMoel {
|
export function setTextPlaceholder(schema: VFormSchema): PlaceholderMoel {
|
||||||
const textMap = ['Input', 'Autocomplete', 'InputNumber']
|
const textMap = ['Input', 'Autocomplete', 'InputNumber', 'InputPassword']
|
||||||
const selectMap = ['Select', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect']
|
const selectMap = ['Select', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect']
|
||||||
if (textMap.includes(schema?.component as string)) {
|
if (textMap.includes(schema?.component as string)) {
|
||||||
return {
|
return {
|
||||||
|
@ -114,17 +114,20 @@ export function setItemComponentSlots(
|
||||||
*
|
*
|
||||||
* @param schema Form表单结构化数组
|
* @param schema Form表单结构化数组
|
||||||
* @param formModel FormMoel
|
* @param formModel FormMoel
|
||||||
|
* @returns FormMoel
|
||||||
* @description 生成对应的formModel
|
* @description 生成对应的formModel
|
||||||
*/
|
*/
|
||||||
export function setModel(schema: VFormSchema[], formModel: Recordable) {
|
export function initModel(schema: VFormSchema[], formModel: Recordable) {
|
||||||
|
const model: Recordable = { ...formModel }
|
||||||
schema.map((v) => {
|
schema.map((v) => {
|
||||||
// 如果是hidden,就删除对应的值
|
// 如果是hidden,就删除对应的值
|
||||||
if (v.hidden) {
|
if (v.hidden) {
|
||||||
delete formModel[v.field]
|
delete model[v.field]
|
||||||
} else {
|
} else {
|
||||||
const hasField = Reflect.has(formModel, v.field)
|
const hasField = Reflect.has(model, v.field)
|
||||||
// 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值
|
// 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值
|
||||||
formModel[v.field] = hasField ? formModel[v.field] : v.value !== void 0 ? v.value : ''
|
model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
return model
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import VInputPassword from './src/VInputPassword.vue'
|
||||||
|
|
||||||
|
export { VInputPassword }
|
|
@ -0,0 +1,137 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, unref, computed, watch } from 'vue'
|
||||||
|
import { ElInput, ElIcon } from 'element-plus'
|
||||||
|
import EyeInvisibleOutlinedE from '~icons/ant-design/eyeInvisibleOutlined'
|
||||||
|
import EyeOutlined from '~icons/ant-design/eye-outlined'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
|
import { useConfigGlobal } from '@/hooks/web/useConfigGlobal'
|
||||||
|
const { configGlobal } = useConfigGlobal()
|
||||||
|
import { zxcvbn } from '@zxcvbn-ts/core'
|
||||||
|
import type { ZxcvbnResult } from '@zxcvbn-ts/core'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
// 是否显示密码强度
|
||||||
|
strength: propTypes.bool.def(false),
|
||||||
|
modelValue: propTypes.string.def('')
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
// 生成class前缀
|
||||||
|
const { getPrefixCls } = useDesign()
|
||||||
|
const prefixCls = ref(getPrefixCls('inputpassword'))
|
||||||
|
|
||||||
|
// 设置input的type属性
|
||||||
|
const textType = ref<'password' | 'text'>('password')
|
||||||
|
function changeTextType() {
|
||||||
|
textType.value = unref(textType) === 'text' ? 'password' : 'text'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入框的值
|
||||||
|
const valueRef = ref('')
|
||||||
|
// 监听
|
||||||
|
watch(
|
||||||
|
() => valueRef.value,
|
||||||
|
(val: string) => {
|
||||||
|
emit('update:modelValue', val)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 获取密码强度
|
||||||
|
const getPasswordStrength = computed(() => {
|
||||||
|
const value = unref(valueRef)
|
||||||
|
const zxcvbnRef = zxcvbn(unref(valueRef)) as ZxcvbnResult
|
||||||
|
return value ? zxcvbnRef.score : -1
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="[prefixCls, `${prefixCls}--${configGlobal?.size}`]">
|
||||||
|
<ElInput v-bind="$attrs" v-model="valueRef" :type="textType">
|
||||||
|
<template #suffix>
|
||||||
|
<ElIcon class="el-input__icon el-input__clear">
|
||||||
|
<EyeInvisibleOutlinedE v-if="textType === 'password'" @click="changeTextType" />
|
||||||
|
<EyeOutlined v-else @click="changeTextType" />
|
||||||
|
</ElIcon>
|
||||||
|
</template>
|
||||||
|
</ElInput>
|
||||||
|
<div
|
||||||
|
v-if="strength"
|
||||||
|
:class="`${prefixCls}__bar`"
|
||||||
|
class="relative h-6px mt-10px mb-6px mr-auto ml-auto"
|
||||||
|
>
|
||||||
|
<div :class="`${prefixCls}__bar--fill`" :data-score="getPasswordStrength"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@prefix-cls: ~'@{namespace}-inputpassword';
|
||||||
|
|
||||||
|
.@{prefix-cls} {
|
||||||
|
&__bar {
|
||||||
|
background-color: var(--el-text-color-disabled-base);
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
display: block;
|
||||||
|
width: 20%;
|
||||||
|
height: inherit;
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: var(--el-color-white);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 5px 0 5px;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
right: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--fill {
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: inherit;
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: inherit;
|
||||||
|
transition: width 0.5s ease-in-out, background 0.25s;
|
||||||
|
|
||||||
|
&[data-score='0'] {
|
||||||
|
width: 20%;
|
||||||
|
background-color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-score='1'] {
|
||||||
|
width: 40%;
|
||||||
|
background-color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-score='2'] {
|
||||||
|
width: 60%;
|
||||||
|
background-color: var(--el-color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-score='3'] {
|
||||||
|
width: 80%;
|
||||||
|
background-color: var(--el-color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-score='4'] {
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--el-color-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--mini > &__bar {
|
||||||
|
border-radius: var(--el-border-radius-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { inject } from 'vue'
|
||||||
|
|
||||||
|
export function useConfigGlobal() {
|
||||||
|
const configGlobal = inject('configGlobal', {}) as VConfigGlobalTypes
|
||||||
|
|
||||||
|
return {
|
||||||
|
configGlobal
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import variables from '@/styles/variables.module.less'
|
||||||
|
|
||||||
|
export function useDesign() {
|
||||||
|
const lessVariables = variables
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param scope 类名
|
||||||
|
* @returns 返回空间名-类名
|
||||||
|
*/
|
||||||
|
function getPrefixCls(scope: string) {
|
||||||
|
return `${lessVariables.namespace}-${scope}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
variables: lessVariables,
|
||||||
|
getPrefixCls
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,8 @@ export default {
|
||||||
dateTimePicker: 'DateTimePicker',
|
dateTimePicker: 'DateTimePicker',
|
||||||
dateTimerange: 'Datetime Range',
|
dateTimerange: 'Datetime Range',
|
||||||
timePicker: 'Time Picker',
|
timePicker: 'Time Picker',
|
||||||
timeSelect: 'Time Select'
|
timeSelect: 'Time Select',
|
||||||
|
inputPassword: 'input Password',
|
||||||
|
passwordStrength: 'Password Strength'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ export default {
|
||||||
dateTimePicker: '日期时间选择器',
|
dateTimePicker: '日期时间选择器',
|
||||||
dateTimerange: '日期时间范围',
|
dateTimerange: '日期时间范围',
|
||||||
timePicker: '时间选择器',
|
timePicker: '时间选择器',
|
||||||
timeSelect: '时间选择'
|
timeSelect: '时间选择',
|
||||||
|
inputPassword: '密码输入框',
|
||||||
|
passwordStrength: '密码强度'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
// 命名空间
|
|
||||||
@namespace: v;
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// 命名空间
|
||||||
|
@namespace: v;
|
||||||
|
|
||||||
|
// 导出变量
|
||||||
|
:export {
|
||||||
|
namespace: @namespace;
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ declare global {
|
||||||
| 'Divider'
|
| 'Divider'
|
||||||
| 'TimeSelect'
|
| 'TimeSelect'
|
||||||
| 'SelectV2'
|
| 'SelectV2'
|
||||||
|
| 'InputPassword'
|
||||||
|
|
||||||
declare type ColProps = {
|
declare type ColProps = {
|
||||||
span?: number
|
span?: number
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
declare interface VConfigGlobalTypes {
|
||||||
|
size?: ElememtPlusSzie
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ declare type Nullable<T> = T | null
|
||||||
|
|
||||||
declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>
|
declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>
|
||||||
|
|
||||||
declare type ElememtPlusSzie = 'medium' | 'small' | 'mini'
|
declare type ElememtPlusSzie = 'default' | 'medium' | 'small' | 'mini'
|
||||||
|
|
||||||
declare type ElementPlusInfoType = 'success' | 'info' | 'warning' | 'danger'
|
declare type ElementPlusInfoType = 'success' | 'info' | 'warning' | 'danger'
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
"types": [
|
"types": [
|
||||||
"@intlify/vite-plugin-vue-i18n/client",
|
"@intlify/vite-plugin-vue-i18n/client",
|
||||||
"vite/client",
|
"vite/client",
|
||||||
"./node_modules/element-plus/global",
|
"element-plus/global",
|
||||||
"unplugin-icons/types/vue"
|
"unplugin-icons/types/vue"
|
||||||
],
|
],
|
||||||
"typeRoots": ["./node_modules/@types/", "./src/types"]
|
"typeRoots": ["./node_modules/@types/", "./src/types"]
|
||||||
|
|
|
@ -3,11 +3,8 @@ import { loadEnv } from 'vite'
|
||||||
import type { UserConfig, ConfigEnv } from 'vite'
|
import type { UserConfig, ConfigEnv } from 'vite'
|
||||||
import Vue from '@vitejs/plugin-vue'
|
import Vue from '@vitejs/plugin-vue'
|
||||||
import WindiCSS from 'vite-plugin-windicss'
|
import WindiCSS from 'vite-plugin-windicss'
|
||||||
// import Components from 'unplugin-vue-components/vite'
|
|
||||||
// import AutoImport from 'unplugin-auto-import/vite'
|
|
||||||
import VueJsx from '@vitejs/plugin-vue-jsx'
|
import VueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
import EslintPlugin from 'vite-plugin-eslint'
|
import EslintPlugin from 'vite-plugin-eslint'
|
||||||
// import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
|
||||||
import VueI18n from '@intlify/vite-plugin-vue-i18n'
|
import VueI18n from '@intlify/vite-plugin-vue-i18n'
|
||||||
import Icons from 'unplugin-icons/vite'
|
import Icons from 'unplugin-icons/vite'
|
||||||
import StyleImport, { ElementPlusResolve } from 'vite-plugin-style-import'
|
import StyleImport, { ElementPlusResolve } from 'vite-plugin-style-import'
|
||||||
|
@ -23,12 +20,11 @@ function pathResolve(dir: string) {
|
||||||
export default ({ command, mode }: ConfigEnv): UserConfig => {
|
export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
let env = null
|
let env = null
|
||||||
if (command === 'serve') {
|
if (command === 'serve') {
|
||||||
env = loadEnv(process.argv[4], root)
|
env = loadEnv(process.argv[3], root)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
env = loadEnv(mode, root)
|
env = loadEnv(mode, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
base: env.VITE_BASE_PATH,
|
base: env.VITE_BASE_PATH,
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -45,33 +41,10 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}),
|
}),
|
||||||
// AutoImport({
|
|
||||||
// imports: [
|
|
||||||
// 'vue',
|
|
||||||
// 'vue-router',
|
|
||||||
// 'vue-i18n',
|
|
||||||
// '@vueuse/core'
|
|
||||||
// ],
|
|
||||||
// dts: 'src/types/auto-imports.d.ts'
|
|
||||||
// }),
|
|
||||||
Icons({
|
Icons({
|
||||||
compiler: 'vue3',
|
compiler: 'vue3',
|
||||||
autoInstall: true
|
autoInstall: true
|
||||||
}),
|
}),
|
||||||
// Components({
|
|
||||||
// dirs: ['src/components'],
|
|
||||||
// extensions: ['vue', 'md'],
|
|
||||||
// include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
|
|
||||||
// // custom resolvers
|
|
||||||
// resolvers: [
|
|
||||||
// ElementPlusResolver(),
|
|
||||||
// IconsResolver({
|
|
||||||
// prefix: false,
|
|
||||||
// enabledCollections : ['ep']
|
|
||||||
// })
|
|
||||||
// ],
|
|
||||||
// dts: 'src/types/components.d.ts'
|
|
||||||
// }),
|
|
||||||
EslintPlugin({
|
EslintPlugin({
|
||||||
cache: false,
|
cache: false,
|
||||||
include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
|
include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
|
||||||
|
@ -86,12 +59,13 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
css: {
|
css: {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
less: {
|
less: {
|
||||||
additionalData: '@import "./src/styles/variables.less";',
|
additionalData: '@import "./src/styles/variables.module.less";',
|
||||||
javascriptEnabled: true
|
javascriptEnabled: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.less', '.css'],
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
find: 'vue-i18n',
|
find: 'vue-i18n',
|
||||||
|
@ -105,7 +79,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
minify: 'terser',
|
minify: 'terser',
|
||||||
outDir: env.VITE_OUT_DIR,
|
outDir: env.VITE_OUT_DIR || 'dist',
|
||||||
sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
|
sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
|
||||||
brotliSize: false,
|
brotliSize: false,
|
||||||
terserOptions: {
|
terserOptions: {
|
||||||
|
@ -131,6 +105,9 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: path => path.replace(/^\/fallback/, '')
|
rewrite: path => path.replace(/^\/fallback/, '')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
hmr: {
|
||||||
|
overlay: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import { defineConfig } from 'windicss/helpers'
|
import { defineConfig } from 'windicss/helpers'
|
||||||
|
|
||||||
|
// function range(size, startAt = 1) {
|
||||||
|
// return Array.from(Array(size).keys()).map((i) => i + startAt)
|
||||||
|
// }
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
extract: {
|
extract: {
|
||||||
include: ['src/**/*.{vue,html,jsx,tsx}'],
|
include: ['src/**/*.{vue,html,jsx,tsx}'],
|
||||||
|
@ -16,5 +20,14 @@ export default defineConfig({
|
||||||
xl: '1920px'
|
xl: '1920px'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// height: {
|
||||||
|
// ...range(50).map((i) => `h-${i}px`)
|
||||||
|
// },
|
||||||
|
// margin: {
|
||||||
|
// // ...range(50).map((i) => `mt-${i}px`),
|
||||||
|
// // ...range(50).map((i) => `mr-${i}px`),
|
||||||
|
// // ...range(50).map((i) => `mb-${i}px`),
|
||||||
|
// // ...range(50).map((i) => `ml-${i}px`)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue