mirror of
https://github.com/Tencent/tdesign-vue-next-starter.git
synced 2024-11-10 07:28:24 +08:00
refactor: login page replace form component (#9)
Co-authored-by: pengYYYYY <pengyue970715@gmail.com>
This commit is contained in:
parent
c1d983ca1c
commit
a5b177e8e1
17
.eslintrc
17
.eslintrc
|
@ -32,17 +32,20 @@
|
|||
]
|
||||
},
|
||||
"rules": {
|
||||
"import/extensions": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"no-console": "off",
|
||||
"no-continue": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-shadow": "off",
|
||||
"guard-for-in": "off",
|
||||
|
||||
"import/extensions": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"no-plusplus": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-shadow": "off"
|
||||
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
}
|
||||
}
|
|
@ -2,9 +2,8 @@
|
|||
"name": "tdesign-vue-next-starter",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev:mock": "vite --open --mode mock",
|
||||
"dev": "vite --open --mode development",
|
||||
"dev:linux": "vite --mode developmenet",
|
||||
"start:mock": "vite --open --mode mock",
|
||||
"start": "vite --open --mode development",
|
||||
"build:test": "vite build --mode test",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"serve": "vite preview",
|
||||
|
@ -27,9 +26,9 @@
|
|||
"devDependencies": {
|
||||
"@commitlint/cli": "^13.1.0",
|
||||
"@commitlint/config-conventional": "^13.1.0",
|
||||
"@types/echarts": "^4.9.10",
|
||||
"@typescript-eslint/eslint-plugin": "^4.29.3",
|
||||
"@typescript-eslint/parser": "^4.29.3",
|
||||
"@types/echarts": "^4.9.10",
|
||||
"@vitejs/plugin-vue": "^1.3.0",
|
||||
"@vitejs/plugin-vue-jsx": "^1.1.7",
|
||||
"@vue/compiler-sfc": "^3.0.5",
|
||||
|
@ -47,7 +46,7 @@
|
|||
"less": "^4.1.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"prettier": "^2.4.1",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint": "^14.1.0",
|
||||
"stylelint-config-airbnb": "^0.0.0",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"stylelint-scss": "^3.20.0",
|
||||
|
|
41
src/assets/t-logo.svg
Normal file
41
src/assets/t-logo.svg
Normal file
|
@ -0,0 +1,41 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2557_23660)">
|
||||
<path d="M9.14038 8.25177H3.53919C3.45719 8.25173 3.37619 8.23372 3.30188 8.19901C3.22758 8.16431 3.16179 8.11375 3.10912 8.05089C3.05646 7.98803 3.01819 7.91439 2.99703 7.83516C2.97587 7.75593 2.97234 7.67303 2.98665 7.59228L3.87785 2.44561C3.90079 2.32052 3.96685 2.20742 4.06455 2.12601C4.16225 2.04459 4.2854 2.00001 4.41258 2.00001H10.2054L9.14038 8.25177Z" fill="url(#paint0_linear_2557_23660)"/>
|
||||
<path d="M13.0301 21.4727H6.77832L9.11772 8.2473H15.3695L13.0301 21.4727Z" fill="url(#paint1_linear_2557_23660)"/>
|
||||
<path d="M11.4707 27.7245H6.34183C6.26051 27.7236 6.18034 27.7051 6.10678 27.6704C6.03322 27.6358 5.96801 27.5856 5.91557 27.5235C5.86313 27.4613 5.82471 27.3886 5.80292 27.3102C5.78113 27.2319 5.77648 27.1497 5.78929 27.0694L6.78297 21.4861H13.0214L11.9965 27.2789C11.9738 27.4025 11.9091 27.5144 11.8132 27.5956C11.7174 27.6769 11.5963 27.7224 11.4707 27.7245Z" fill="url(#paint2_linear_2557_23660)"/>
|
||||
<path d="M26.666 8.25176H9.14062L10.2457 2.01337H27.5795C27.6613 2.01417 27.7419 2.03268 27.8158 2.06763C27.8898 2.10258 27.9552 2.15313 28.0077 2.21581C28.0603 2.27849 28.0986 2.35179 28.12 2.43069C28.1415 2.50959 28.1456 2.59221 28.1321 2.67285L27.2186 7.81953C27.1941 7.94607 27.1246 8.0595 27.0231 8.13892C26.9216 8.21834 26.7948 8.25841 26.666 8.25176V8.25176Z" fill="url(#paint3_linear_2557_23660)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_2557_23660" x1="2.71742" y1="5.12812" x2="10.0624" y2="4.98122" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0062FF"/>
|
||||
<stop offset="0.26" stop-color="#006AFF"/>
|
||||
<stop offset="0.68" stop-color="#0081FF"/>
|
||||
<stop offset="1" stop-color="#0097FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_2557_23660" x1="12.383" y1="7.62346" x2="9.12269" y2="22.0419" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0097FF"/>
|
||||
<stop offset="0.32" stop-color="#0081FF"/>
|
||||
<stop offset="0.74" stop-color="#006AFF"/>
|
||||
<stop offset="1" stop-color="#0062FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_2557_23660" x1="5.63057" y1="27.3635" x2="12.8582" y2="21.4727" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#009EFF"/>
|
||||
<stop offset="0.31" stop-color="#00A3FF"/>
|
||||
<stop offset="0.71" stop-color="#00B3FF"/>
|
||||
<stop offset="1" stop-color="#00C3FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_2557_23660" x1="8.84911" y1="5.12811" x2="27.9399" y2="4.74629" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.03" stop-color="#ECFFFE"/>
|
||||
<stop offset="0.19" stop-color="#AFF1D9"/>
|
||||
<stop offset="0.34" stop-color="#79E5B9"/>
|
||||
<stop offset="0.49" stop-color="#4EDB9F"/>
|
||||
<stop offset="0.63" stop-color="#2CD48A"/>
|
||||
<stop offset="0.77" stop-color="#14CE7C"/>
|
||||
<stop offset="0.89" stop-color="#05CB73"/>
|
||||
<stop offset="1" stop-color="#00CA70"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_2557_23660">
|
||||
<rect width="25.1407" height="25.72" fill="white" transform="translate(2.97754 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -158,7 +158,7 @@ export function getBrandColor(type: string): ColorToken {
|
|||
}
|
||||
|
||||
export function getColorList(colorArray: ColorToken[]): string[] {
|
||||
const pureColorList = [];
|
||||
const pureColorList: string[] = [];
|
||||
colorArray.map((colorToken) => Object.keys(colorToken).map((key) => pureColorList.push(colorToken[key])));
|
||||
|
||||
return pureColorList;
|
||||
|
|
|
@ -17,3 +17,7 @@ export type ModeType = 'dark' | 'light';
|
|||
export type SettingType = typeof STYLE_CONFIG;
|
||||
|
||||
export type ClassName = { [className: string]: any } | ClassName[] | string;
|
||||
|
||||
export type CommonObjType = {
|
||||
[key: string]: string | number
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import dayjs from 'dayjs';
|
||||
import * as echarts from 'echarts/core';
|
||||
import { getBrandColor } from '@/config/color';
|
||||
import store from '@/store';
|
||||
|
||||
import { CommonObjType } from '@/interface';
|
||||
const { state } = store;
|
||||
|
||||
/**
|
||||
|
@ -349,9 +349,9 @@ export function getSmoothLineDataSet(dateTime = []) {
|
|||
}
|
||||
|
||||
/** 折线图数据 */
|
||||
export function getFolderlineDataSet(dateTime?: []) {
|
||||
export function getFolderLineDataSet(dateTime?: string[]) {
|
||||
let dateArray: Array<string> = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
||||
if (dateTime.length > 0) {
|
||||
if (dateTime && dateTime.length > 0) {
|
||||
const devideNum = 7;
|
||||
dateArray = getDateArray(dateTime, devideNum);
|
||||
}
|
||||
|
@ -637,7 +637,7 @@ export function getSelftItemList(productName: string, devideNum: number): string
|
|||
* @export
|
||||
* @returns {}
|
||||
*/
|
||||
export function getScattlerDataSet(dateTime: Array<string> = []) {
|
||||
export function getScatterDataSet(dateTime: Array<string> = []) {
|
||||
const divideNum = 10;
|
||||
const timeArray = [];
|
||||
const inArray = [];
|
||||
|
@ -774,7 +774,7 @@ export function getAreaChartDataSet() {
|
|||
emphasis: {
|
||||
focus: 'series',
|
||||
},
|
||||
animationDelay(idx) {
|
||||
animationDelay(idx: number) {
|
||||
return idx * 10;
|
||||
},
|
||||
},
|
||||
|
@ -785,13 +785,13 @@ export function getAreaChartDataSet() {
|
|||
emphasis: {
|
||||
focus: 'series',
|
||||
},
|
||||
animationDelay(idx) {
|
||||
animationDelay(idx: number) {
|
||||
return idx * 10 + 100;
|
||||
},
|
||||
},
|
||||
],
|
||||
animationEasing: 'elasticOut',
|
||||
animationDelayUpdate(idx) {
|
||||
animationDelayUpdate(idx: number) {
|
||||
return idx * 5;
|
||||
},
|
||||
};
|
||||
|
@ -994,8 +994,7 @@ export function get2ColBarChartDataSet(isMonth = false) {
|
|||
barWidth: '30%',
|
||||
data: thisYearListCopy,
|
||||
itemStyle: {
|
||||
color: (params) => {
|
||||
// console.log('chartListColor', chartListColor());
|
||||
color: (params: CommonObjType) => {
|
||||
if (params.value >= 200) {
|
||||
return '#E34D59';
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
theme="primary"
|
||||
mode="date"
|
||||
range
|
||||
@change="onHappinesChange"
|
||||
@change="onSatisfyChange"
|
||||
/>
|
||||
<t-button class="card-date-button"> 导出数据 </t-button>
|
||||
</template>
|
||||
|
@ -63,7 +63,7 @@ import { LineChart, ScatterChart } from 'echarts/charts';
|
|||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import ProductCard from '@/pages/list/card/components/Card.vue';
|
||||
|
||||
import { changeChartsTheme, getFolderlineDataSet, getScattlerDataSet } from '../base/index';
|
||||
import { changeChartsTheme, getFolderLineDataSet, getScatterDataSet } from '../base/index';
|
||||
import { PANE_LIST_DATA, PRODUCT_LIST } from './constants';
|
||||
import { useChart } from '@/utils/hooks';
|
||||
import { LAST_7_DAYS } from '@/utils/date';
|
||||
|
@ -87,24 +87,24 @@ export default defineComponent({
|
|||
const store = useStore();
|
||||
watch(
|
||||
() => store.state.setting.brandTheme,
|
||||
(val) => {
|
||||
changeChartsTheme([lineChart.value, scatterChart.value], val);
|
||||
() => {
|
||||
changeChartsTheme([lineChart.value, scatterChart.value]);
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
lineChart.value.setOption(getFolderlineDataSet());
|
||||
scatterChart.value.setOption(getScattlerDataSet());
|
||||
lineChart.value.setOption(getFolderLineDataSet());
|
||||
scatterChart.value.setOption(getScatterDataSet());
|
||||
});
|
||||
return {
|
||||
LAST_7_DAYS,
|
||||
PRODUCT_LIST,
|
||||
PANE_LIST_DATA,
|
||||
onHappinesChange() {
|
||||
scatterChart.value.setOption(getScattlerDataSet());
|
||||
onSatisfyChange() {
|
||||
scatterChart.value.setOption(getScatterDataSet());
|
||||
},
|
||||
onMaterialChange(value) {
|
||||
lineChart.value.setOption(getFolderlineDataSet(value));
|
||||
onMaterialChange(value: string[]) {
|
||||
lineChart.value.setOption(getFolderLineDataSet(value));
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -135,7 +135,7 @@ export default defineComponent({
|
|||
|
||||
const updateCurrent = ref(0);
|
||||
|
||||
const setpUpdate = () => {
|
||||
const stepUpdate = () => {
|
||||
setInterval(() => {
|
||||
if (updateCurrent.value > 5) {
|
||||
updateCurrent.value = -1;
|
||||
|
@ -161,7 +161,7 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
onMounted(() => {
|
||||
setpUpdate();
|
||||
stepUpdate();
|
||||
fetchData();
|
||||
});
|
||||
|
||||
|
|
|
@ -149,8 +149,8 @@ export default defineComponent({
|
|||
const store = useStore();
|
||||
watch(
|
||||
() => store.state.setting.brandTheme,
|
||||
(val) => {
|
||||
changeChartsTheme([monitorChart.value, dataChart.value], val);
|
||||
() => {
|
||||
changeChartsTheme([monitorChart.value, dataChart.value]);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
<template>
|
||||
<!-- 密码登陆 -->
|
||||
<div v-if="type == 'pwd'" class="item-container login-pwd">
|
||||
<div class="input-container">
|
||||
<t-input v-model="userInfo.EngName" style="width: 400px" size="large" placeholder="请输入您的邮箱/手机号">
|
||||
<t-form
|
||||
ref="form"
|
||||
:class="['item-container', `login-${type}`]"
|
||||
:data="formData"
|
||||
:rules="FORM_RULES"
|
||||
label-width="0"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<template v-if="type == 'password'">
|
||||
<t-form-item name="account">
|
||||
<t-input v-model="formData.account" size="large" placeholder="请输入您的邮箱/手机号">
|
||||
<template #prefix-icon>
|
||||
<t-icon name="user" />
|
||||
</template>
|
||||
</t-input>
|
||||
<t-popup placement="right" trigger="focus" show-arrow>
|
||||
</t-form-item>
|
||||
|
||||
<t-form-item name="password">
|
||||
<t-input
|
||||
v-model="psw"
|
||||
style="width: 400px"
|
||||
v-model="formData.password"
|
||||
size="large"
|
||||
:type="showPsw ? 'text' : 'password'"
|
||||
clearablec
|
||||
placeholder="请输入密码"
|
||||
@keyup="checkPsw"
|
||||
clearable
|
||||
placeholder="请输入登录密码"
|
||||
>
|
||||
<template #prefix-icon>
|
||||
<t-icon name="lock-on" />
|
||||
|
@ -24,70 +32,51 @@
|
|||
<t-icon :name="showPsw ? 'browse' : 'browse-off'" @click="showPsw = !showPsw" />
|
||||
</template>
|
||||
</t-input>
|
||||
<template #content>
|
||||
<div>
|
||||
<div :class="['rex-check', { 'format-correct': check1 }]">
|
||||
<t-icon name="check-circle-filled" size="large" />
|
||||
<span>1-20个英文字符</span>
|
||||
</div>
|
||||
<div :class="['rex-check', { 'format-correct': check2 }]">
|
||||
<t-icon name="check-circle-filled" size="large" />
|
||||
<span>需包含下划线</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</t-popup>
|
||||
<div class="check-container">
|
||||
</t-form-item>
|
||||
|
||||
<div class="check-container remember-pwd">
|
||||
<t-checkbox>记住账号</t-checkbox>
|
||||
<span class="tip">忘记账号?</span>
|
||||
</div>
|
||||
<t-button class="button-container" style="width: 400px" size="large" @click="handleClickToLogIn"> 登录 </t-button>
|
||||
</div>
|
||||
<div class="bottom-container">
|
||||
<span class="tip" @click="switchType('qrcode')">使用微信扫码登录</span>
|
||||
<i>|</i>
|
||||
<span class="tip" @click="switchType('phone')">使用短信登录</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 扫码登陆 -->
|
||||
<div v-else-if="type == 'qrcode'" class="item-container login-qrcode">
|
||||
<div class="input-container">
|
||||
<template v-else-if="type == 'qrcode'">
|
||||
<div class="tip-container">
|
||||
<span class="tip1">请使用微信扫一扫登录</span>
|
||||
<span class="tip2 refresh">刷新 <t-icon name="refresh" color="#0052D9" /> </span>
|
||||
</div>
|
||||
<qrcode-vue value="" :size="192" level="H" />
|
||||
</div>
|
||||
<div class="bottom-container">
|
||||
<span class="tip" @click="switchType('pwd')">使用账号密码登录</span>
|
||||
<i>|</i>
|
||||
<span class="tip" @click="switchType('phone')">使用短信登录</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 手机号登陆 -->
|
||||
<div v-else class="item-container login-phone">
|
||||
<div class="input-container">
|
||||
<t-input v-model="userInfo.EngName" style="width: 400px" size="large" placeholder="请输入您的手机号">
|
||||
<template v-else>
|
||||
<t-form-item name="phone">
|
||||
<t-input v-model="formData.phone" size="large" placeholder="请输入您的手机号">
|
||||
<template #prefix-icon>
|
||||
<t-icon name="user" />
|
||||
</template>
|
||||
</t-input>
|
||||
<div class="verification-code">
|
||||
<t-input style="width: 282px" size="large" placeholder="请输入验证码" />
|
||||
</t-form-item>
|
||||
|
||||
<t-form-item class="verification-code" name="verifyCode">
|
||||
<t-input v-model="formData.verifyCode" size="large" placeholder="请输入验证码" />
|
||||
<t-button variant="outline" :disabled="countDown > 0" @click="handleCounter">
|
||||
{{ countDown == 0 ? '发送验证码' : `${countDown}秒后可重发` }}
|
||||
</t-button>
|
||||
</t-form-item>
|
||||
</template>
|
||||
|
||||
<t-form-item v-if="type !== 'qrcode'">
|
||||
<t-button block size="large" type="submit"> 登录 </t-button>
|
||||
</t-form-item>
|
||||
|
||||
<div class="switch-container">
|
||||
<span v-if="type !== 'password'" class="tip" @click="switchType('password')">使用账号密码登录</span>
|
||||
<span v-if="type !== 'qrcode'" class="tip" @click="switchType('qrcode')">使用微信扫码登录</span>
|
||||
<span v-if="type !== 'phone'" class="tip" @click="switchType('phone')">使用手机号登录</span>
|
||||
</div>
|
||||
<t-button class="button-container" style="width: 400px" size="large" @click="handleClickToLogIn"> 登录 </t-button>
|
||||
</div>
|
||||
<div class="bottom-container">
|
||||
<span class="tip" @click="switchType('pwd')">使用账号密码登录</span>
|
||||
<i>|</i>
|
||||
<span class="tip" @click="switchType('qrcode')">使用微信扫码登录</span>
|
||||
</div>
|
||||
</div>
|
||||
</t-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -98,37 +87,33 @@ import QrcodeVue from 'qrcode.vue';
|
|||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { useCounter } from '@/utils/hooks';
|
||||
|
||||
import { passwordValidator } from '../helper';
|
||||
|
||||
const INITIAL_DATA = {
|
||||
EngName: '',
|
||||
phone: '',
|
||||
account: '',
|
||||
password: '',
|
||||
verifyCode: '',
|
||||
checked: false,
|
||||
};
|
||||
|
||||
const FORM_RULES = {
|
||||
phone: [{ required: true, message: '手机号必填', type: 'error' }],
|
||||
account: [{ required: true, message: '账号必填', type: 'error' }],
|
||||
password: [{ required: true, message: '密码必填', type: 'error' }, { validator: passwordValidator }],
|
||||
verifyCode: [{ required: true, message: '验证码必填', type: 'error' }],
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: { QrcodeVue },
|
||||
setup() {
|
||||
const type = ref('pwd');
|
||||
const psw = ref('');
|
||||
const check1 = ref(false);
|
||||
const check2 = ref(false);
|
||||
const type = ref('password');
|
||||
|
||||
const userInfo = ref({ ...INITIAL_DATA });
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
const showPsw = ref(false);
|
||||
|
||||
const [countDown, handleCounter] = useCounter();
|
||||
|
||||
const checkPsw = () => {
|
||||
const regExp = /^[a-z0-9_]{1,20}$/;
|
||||
if (regExp.test(psw.value)) {
|
||||
check1.value = true;
|
||||
} else {
|
||||
check1.value = false;
|
||||
}
|
||||
if (psw.value.indexOf('_') !== -1) {
|
||||
check2.value = true;
|
||||
} else {
|
||||
check2.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const switchType = (val: string) => {
|
||||
type.value = val;
|
||||
};
|
||||
|
@ -136,28 +121,27 @@ export default defineComponent({
|
|||
const router = useRouter();
|
||||
const store = useStore();
|
||||
|
||||
const handleClickToLogIn = () => {
|
||||
store.commit('user/SET_USER_INFO', userInfo.value);
|
||||
const onSubmit = ({ validateResult }) => {
|
||||
if (validateResult === true) {
|
||||
store.commit('user/SET_USER_INFO', formData.value);
|
||||
|
||||
MessagePlugin.success('登录成功');
|
||||
|
||||
router.push({
|
||||
path: '/',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
FORM_RULES,
|
||||
formData,
|
||||
showPsw,
|
||||
psw,
|
||||
userInfo,
|
||||
checkPsw,
|
||||
check1,
|
||||
check2,
|
||||
type,
|
||||
switchType,
|
||||
countDown,
|
||||
handleCounter,
|
||||
handleClickToLogIn,
|
||||
onSubmit,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,11 +1,34 @@
|
|||
<template>
|
||||
<div v-if="type == 'phone'" class="item-container register-phone">
|
||||
<div class="input-container">
|
||||
<t-form
|
||||
ref="form"
|
||||
:class="['item-container', `register-${type}`]"
|
||||
:data="formData"
|
||||
:rules="FORM_RULES"
|
||||
label-width="0"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<template v-if="type == 'phone'">
|
||||
<t-form-item name="phone">
|
||||
<t-input
|
||||
v-model="formData.phone"
|
||||
:maxlength="11"
|
||||
style="width: 400px"
|
||||
size="large"
|
||||
placeholder="请输入您的手机号"
|
||||
>
|
||||
<template #prefix-icon>
|
||||
<t-icon name="user" />
|
||||
</template>
|
||||
</t-input>
|
||||
</t-form-item>
|
||||
</template>
|
||||
|
||||
<template v-if="type == 'email'">
|
||||
<t-form-item name="email">
|
||||
<t-select
|
||||
v-model="userInfo.EngName"
|
||||
v-model="formData.email"
|
||||
placeholder="请输入您的邮箱"
|
||||
filterable
|
||||
style="width: 400px"
|
||||
size="large"
|
||||
:empty="''"
|
||||
:options="emailOptions"
|
||||
|
@ -15,62 +38,16 @@
|
|||
<t-icon name="lock-on" />
|
||||
</template>
|
||||
</t-select>
|
||||
<t-popup placement="right" trigger="focus" show-arrow>
|
||||
<t-input
|
||||
v-model="psw"
|
||||
style="width: 400px"
|
||||
size="large"
|
||||
:type="showPsw ? 'text' : 'password'"
|
||||
clearablec
|
||||
placeholder="请输入登录密码"
|
||||
@keyup="checkPsw"
|
||||
>
|
||||
<template #prefix-icon>
|
||||
<t-icon name="lock-on" />
|
||||
</t-form-item>
|
||||
</template>
|
||||
<template #suffix-icon>
|
||||
<t-icon :name="showPsw ? 'browse' : 'browse-off'" @click="showPsw = !showPsw" />
|
||||
</template>
|
||||
</t-input>
|
||||
<template #content>
|
||||
<div>
|
||||
<div :class="['rex-check', { 'format-correct': check1 }]">
|
||||
<t-icon name="check-circle-filled" size="large" />
|
||||
<span>1-20个英文字符</span>
|
||||
</div>
|
||||
<div :class="['rex-check', { 'format-correct': check2 }]">
|
||||
<t-icon name="check-circle-filled" size="large" />
|
||||
<span>需包含下划线</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</t-popup>
|
||||
<div class="check-container">
|
||||
<t-checkbox>我已阅读并同意 </t-checkbox><span>TDesign服务协议</span> 和 <span>TDesign 隐私声明</span>
|
||||
</div>
|
||||
<t-button class="button-container" style="width: 400px" size="large" @click="handleRegister"> 注册 </t-button>
|
||||
</div>
|
||||
<div class="bottom-container">
|
||||
<span class="tip" @click="type = 'eamil'">使用手机号注册</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="item-container register-email">
|
||||
<div class="input-container">
|
||||
<t-input v-model="userInfo.EngName" style="width: 400px" size="large" placeholder="请输入您的手机号">
|
||||
<template #prefix-icon>
|
||||
<t-icon name="user" />
|
||||
</template>
|
||||
</t-input>
|
||||
<t-popup placement="right" trigger="focus" show-arrow>
|
||||
<t-form-item name="password">
|
||||
<t-input
|
||||
v-model="psw"
|
||||
style="width: 400px"
|
||||
v-model="formData.password"
|
||||
size="large"
|
||||
:type="showPsw ? 'text' : 'password'"
|
||||
clearablec
|
||||
clearable
|
||||
placeholder="请输入登录密码"
|
||||
@keyup="checkPsw"
|
||||
>
|
||||
<template #prefix-icon>
|
||||
<t-icon name="lock-on" />
|
||||
|
@ -79,78 +56,58 @@
|
|||
<t-icon :name="showPsw ? 'browse' : 'browse-off'" @click="showPsw = !showPsw" />
|
||||
</template>
|
||||
</t-input>
|
||||
<template #content>
|
||||
<div>
|
||||
<div :class="['rex-check', { 'format-correct': check1 }]">
|
||||
<t-icon name="check-circle-filled" size="large" />
|
||||
<span>1-20个英文字符</span>
|
||||
</div>
|
||||
<div :class="['rex-check', { 'format-correct': check2 }]">
|
||||
<t-icon name="check-circle-filled" size="large" />
|
||||
<span>需包含下划线</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</t-popup>
|
||||
<div class="verification-code">
|
||||
<t-input style="width: 282px" size="large" placeholder="请输入验证码" />
|
||||
</t-form-item>
|
||||
|
||||
<template v-if="type == 'phone'">
|
||||
<t-form-item class="verification-code" name="verifyCode">
|
||||
<t-input v-model="formData.verifyCode" size="large" placeholder="请输入验证码" />
|
||||
<t-button variant="outline" :disabled="countDown > 0" @click="handleCounter">
|
||||
{{ countDown == 0 ? '发送验证码' : `${countDown}秒后可重发` }}
|
||||
</t-button>
|
||||
</t-form-item>
|
||||
</template>
|
||||
|
||||
<t-form-item class="check-container" name="checked">
|
||||
<t-checkbox v-model="formData.checked">我已阅读并同意 </t-checkbox> <span>TDesign服务协议</span> 和
|
||||
<span>TDesign 隐私声明</span>
|
||||
</t-form-item>
|
||||
|
||||
<t-form-item>
|
||||
<t-button block size="large" type="submit"> 注册 </t-button>
|
||||
</t-form-item>
|
||||
|
||||
<div class="switch-container">
|
||||
<span class="tip" @click="switchType(type == 'phone' ? 'email' : 'phone')">{{
|
||||
type == 'phone' ? '使用邮箱注册' : '使用手机号注册'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="check-container">
|
||||
<t-checkbox>我已阅读并同意 </t-checkbox> <span>TDesign服务协议</span> 和 <span>TDesign 隐私声明</span>
|
||||
</div>
|
||||
<t-button class="button-container" style="width: 400px" size="large" @click="handleRegister"> 注册 </t-button>
|
||||
</div>
|
||||
<div class="bottom-container">
|
||||
<span class="tip" @click="type = 'phone'">使用邮箱注册</span>
|
||||
</div>
|
||||
</div>
|
||||
</t-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { useCounter } from '@/utils/hooks';
|
||||
|
||||
const getEmails = (search: string) => [
|
||||
{
|
||||
value: `${search}@qq.com`,
|
||||
label: `${search}@qq.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@gmail.com`,
|
||||
label: `${search}@gmail.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@126.com`,
|
||||
label: `${search}@126.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@163.com`,
|
||||
label: `${search}@163.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@hotmail.com`,
|
||||
label: `${search}@hotmail.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@21cn.com`,
|
||||
label: `${search}@21cn.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@yahoo.com`,
|
||||
label: `${search}@yahoo.com`,
|
||||
},
|
||||
];
|
||||
import { passwordValidator, getEmails } from '../helper';
|
||||
|
||||
const INITIAL_DATA = {
|
||||
EngName: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
password: '',
|
||||
verifyCode: '',
|
||||
checked: false,
|
||||
};
|
||||
|
||||
const FORM_RULES = {
|
||||
phone: [{ required: true, message: '手机号必填', type: 'error' }],
|
||||
email: [{ required: true, email: true, message: '邮箱必填', type: 'error' }],
|
||||
password: [{ required: true, message: '密码必填', type: 'error' }, { validator: passwordValidator }],
|
||||
verifyCode: [{ required: true, message: '验证码必填', type: 'error' }],
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
setup(props, ctx) {
|
||||
const type = ref('default');
|
||||
const type = ref('phone');
|
||||
const emailOptions = ref([]);
|
||||
const remoteMethod = (search: string) => {
|
||||
if (search && search.indexOf('@') === -1) {
|
||||
|
@ -158,47 +115,42 @@ export default defineComponent({
|
|||
}
|
||||
};
|
||||
|
||||
const psw = ref('');
|
||||
const check1 = ref(false);
|
||||
const check2 = ref(false);
|
||||
const userInfo = ref({ ...INITIAL_DATA });
|
||||
const form = ref();
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
|
||||
const showPsw = ref(false);
|
||||
|
||||
const [countDown, handleCounter] = useCounter();
|
||||
|
||||
const checkPsw = () => {
|
||||
const regExp = /^[a-z0-9_]{1,20}$/;
|
||||
if (regExp.test(psw.value)) {
|
||||
check1.value = true;
|
||||
} else {
|
||||
check1.value = false;
|
||||
const onSubmit = ({ validateResult }) => {
|
||||
if (validateResult === true) {
|
||||
if (!formData.value.checked) {
|
||||
MessagePlugin.error('请同意TDesign服务协议和TDesign 隐私声明');
|
||||
return;
|
||||
}
|
||||
if (psw.value.indexOf('_') !== -1) {
|
||||
check2.value = true;
|
||||
} else {
|
||||
check2.value = false;
|
||||
MessagePlugin.success('注册成功');
|
||||
const { emit } = ctx;
|
||||
emit('registerSuccess');
|
||||
}
|
||||
};
|
||||
|
||||
const handleRegister = () => {
|
||||
const { emit } = ctx;
|
||||
emit('registerSuccess');
|
||||
const switchType = (val) => {
|
||||
form.value.reset();
|
||||
type.value = val;
|
||||
};
|
||||
|
||||
return {
|
||||
FORM_RULES,
|
||||
formData,
|
||||
showPsw,
|
||||
psw,
|
||||
userInfo,
|
||||
checkPsw,
|
||||
check1,
|
||||
check2,
|
||||
form,
|
||||
type,
|
||||
emailOptions,
|
||||
remoteMethod,
|
||||
countDown,
|
||||
handleCounter,
|
||||
handleRegister,
|
||||
onSubmit,
|
||||
switchType,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
41
src/pages/login/helper.ts
Normal file
41
src/pages/login/helper.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
export const passwordValidator = (val) => {
|
||||
if (!/^[a-z0-9_]{1,20}$/.test(val)) {
|
||||
return { result: false, message: '需要为1-20个英文或数字字符', type: 'error' };
|
||||
}
|
||||
if (val && val.indexOf('_') === -1) {
|
||||
return { result: false, message: '需包含下划线_', type: 'warning' };
|
||||
}
|
||||
return { result: true };
|
||||
}
|
||||
|
||||
export const getEmails = (search) => [
|
||||
{
|
||||
value: `${search}@qq.com`,
|
||||
label: `${search}@qq.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@gmail.com`,
|
||||
label: `${search}@gmail.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@126.com`,
|
||||
label: `${search}@126.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@163.com`,
|
||||
label: `${search}@163.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@21cn.com`,
|
||||
label: `${search}@21cn.com`,
|
||||
},
|
||||
{
|
||||
value: `${search}@yahoo.com`,
|
||||
label: `${search}@yahoo.com`,
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
passwordValidator,
|
||||
getEmails
|
||||
}
|
|
@ -47,119 +47,11 @@
|
|||
}
|
||||
|
||||
.item-container {
|
||||
|
||||
.input-container {
|
||||
width: 400px;
|
||||
margin-top: 64px;
|
||||
|
||||
.tip-container {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.tip1 {
|
||||
font-size: 14px;
|
||||
color: rgba(0,0,0,.60);
|
||||
}
|
||||
|
||||
.tip2 {
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
color: @brand-color-8;
|
||||
|
||||
.t-icon {
|
||||
height: 20px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-container {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.check-container {
|
||||
font-size: 14px;
|
||||
color: rgba(0,0,0,.60);
|
||||
|
||||
.tip {
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
color: @brand-color-8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.t-input {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.check-container {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.t-checkbox__label {
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
|
||||
span {
|
||||
color: @brand-color-8;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.t-popup-reference {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.verification-code {
|
||||
margin-top: 24px;
|
||||
|
||||
.t-input {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 102px;
|
||||
height: 40px;
|
||||
margin-left: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-container {
|
||||
margin-top: 66px;
|
||||
|
||||
.tip {
|
||||
font-size: 14px;
|
||||
color: @brand-color-8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
i {
|
||||
font-style: normal;
|
||||
color: @gray-color-3;
|
||||
margin: 0 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.login-pwd {
|
||||
|
||||
.input-container {
|
||||
margin-top: 72px;
|
||||
}
|
||||
|
||||
.bottom-container {
|
||||
margin-top: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
&.login-qrcode {
|
||||
|
||||
.input-container {
|
||||
margin-top: 34px;
|
||||
}
|
||||
|
||||
.tip-container {
|
||||
width: 192px;
|
||||
|
@ -187,14 +79,110 @@
|
|||
}
|
||||
|
||||
&.register-phone {
|
||||
|
||||
.input-container {
|
||||
margin-top: 64px;
|
||||
|
||||
.t-select-popup-reference {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.check-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.remember-pwd {
|
||||
margin-bottom: 16px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.t-checkbox__label {
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
|
||||
span {
|
||||
color: @brand-color-8;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tip-container {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.tip1 {
|
||||
font-size: 14px;
|
||||
color: rgba(0,0,0,.60);
|
||||
}
|
||||
|
||||
.tip2 {
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
color: @brand-color-8;
|
||||
|
||||
.t-icon {
|
||||
height: 20px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.verification-code {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.t-form__controls {
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
flex-shrink: 0;
|
||||
width: 102px;
|
||||
height: 40px;
|
||||
margin-left: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.switch-container {
|
||||
margin-top: 66px;
|
||||
|
||||
.tip {
|
||||
font-size: 14px;
|
||||
color: @brand-color-8;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 14px;
|
||||
|
||||
&:last-child {
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 12px;
|
||||
background: @gray-color-3;
|
||||
margin-left: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.check-container {
|
||||
font-size: 14px;
|
||||
color: rgba(0,0,0,.60);
|
||||
|
||||
.tip {
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
color: @brand-color-8;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import Login from './components/Login.vue';
|
||||
import Register from './components/register.vue';
|
||||
import Register from './components/Register.vue';
|
||||
|
||||
/** 高级详情 */
|
||||
export default defineComponent({
|
||||
|
|
|
@ -96,7 +96,6 @@
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, watch } from 'vue';
|
||||
import { DateValue } from 'tdesign-vue-next';
|
||||
import { useStore } from 'vuex';
|
||||
|
||||
import * as echarts from 'echarts/core';
|
||||
|
@ -107,7 +106,7 @@ import { CanvasRenderer } from 'echarts/renderers';
|
|||
import { LAST_7_DAYS } from '@/utils/date';
|
||||
import { useChart } from '@/utils/hooks';
|
||||
import { USER_INFO_LIST, TEAM_MEMBERS, PRODUCT_LIST } from './constants';
|
||||
import { changeChartsTheme, getFolderlineDataSet } from '@/pages/dashboard/base/index';
|
||||
import { changeChartsTheme, getFolderLineDataSet } from '@/pages/dashboard/base/index';
|
||||
|
||||
echarts.use([GridComponent, TooltipComponent, LineChart, CanvasRenderer, LegendComponent]);
|
||||
|
||||
|
@ -115,8 +114,8 @@ export default defineComponent({
|
|||
setup() {
|
||||
const lineChart = useChart('lineContainer');
|
||||
|
||||
const onLineChange = (value: DateValue) => {
|
||||
lineChart.value.setOption(getFolderlineDataSet(value));
|
||||
const onLineChange = (value: string[]) => {
|
||||
lineChart.value.setOption(getFolderLineDataSet(value));
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -127,15 +126,15 @@ export default defineComponent({
|
|||
x2: 10, // 默认80px
|
||||
y2: 30, // 默认60px
|
||||
},
|
||||
...getFolderlineDataSet(),
|
||||
...getFolderLineDataSet(),
|
||||
});
|
||||
});
|
||||
|
||||
const store = useStore();
|
||||
watch(
|
||||
() => store.state.setting.brandTheme,
|
||||
(val) => {
|
||||
changeChartsTheme([lineChart.value], val);
|
||||
() => {
|
||||
changeChartsTheme([lineChart.value]);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 获取常用时间
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export const LAST_7_DAYS = [dayjs().subtract(7, 'day').format('YYYY-MM-DD'), dayjs().subtract(1, 'day').format('YYYY-MM-DD')];
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ export const useChart = (domId: string): Ref<echarts.ECharts> => {
|
|||
*/
|
||||
export const useCounter = (duration = 60): [Ref<number>, () => void] => {
|
||||
let intervalTimer: NodeJS.Timer;
|
||||
onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
clearInterval(intervalTimer);
|
||||
});
|
||||
const countDown = ref(0);
|
||||
|
|
|
@ -19,7 +19,7 @@ const instance = axios.create({
|
|||
|
||||
instance.interceptors.request.use((config) => config);
|
||||
|
||||
instance.defaults.retry = 3;
|
||||
instance.defaults.timeout = 5000;
|
||||
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
|
@ -46,7 +46,7 @@ instance.interceptors.response.use(
|
|||
|
||||
const backoff = new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
resolve(null);
|
||||
}, config.retryDelay || 1);
|
||||
});
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
|
|
Loading…
Reference in New Issue
Block a user