Commit 64ac1237 authored by 罗超's avatar 罗超

新增,登录模拟和注册模拟,修复之前不合理的调用,新增一些组件

parent 883c93f6
<a name="lgzKP"></a> # Ocss App (ocss)
## 项目框架:
项目采用Quasar+Vue3.2+TS,构建。开发中需要严格遵守TS协议,语法,对象等标准。严厉杜绝直接使用JS的弱类型。严格遵守composition api,杜绝Option api。项目后期会加入Vite2作为打包工具,放弃Webpack。
> 项目弃用了ESLint语法约束,开发人员需要按照Prettier 格式化代码,并且处理语法不合规的内容,严禁关闭ESLint语法验证。
<br /> niuu bee
<a name="WJ7J1"></a>
## 第三方依赖:
> cnpm i -S @types/webpack-env 自动注入
> cnpm i -S vuex-persistedstate 持久化Store
> cnpm install --save @types/lockr LocalStorage管理工具
> cnpm install --save @types/lodash JS 原生库,封装了许多实用方法,项目使用初衷为全局组件注册的一致性
注意Store已加入自动化注入功能,请按照示例格式进行添加<br />store的持久化需要在index.ts文件中进行配置 ## Install the dependencies
```typescript ```bash
process.env.NODE_ENV !== 'production' yarn
? [
createLogger(),
createPersistedState({
paths: ['app', 'user','你的Store名称']
})
]
: [
createPersistedState({
paths: ['app', 'user','你的Store名称']
})
]
``` ```
<a name="GdhGb"></a>
## 项目基础架构说明: ### Start the app in development mode (hot-code reloading, error reporting, etc.)
```bash
quasar dev
```
### Lint the files
```bash
yarn run lint
``` ```
|-- 根目录
|-- quasar-cli文件,不需要做调整
|-- dist 项目 build 之后的文件夹
|-- public 项目静态资源,不经过 webpack,以及默认的模版,适合存放第三方压缩好的资源
|-- src 主要的开发目录
| |-- @types 项目共用的 type
| |-- App.vue 页面渲染根节点
| |-- main.ts 入口文件
| |-- shims-vue.d.ts vue 文件类型的 type
| |-- api 封装http请求相关(持久层)
| | |-- apiList.ts api接口列表(弃用,在各自的Service中维护)
| | |-- axios.ts 业务请求封装 (封装Request AOP)
| | |-- Common.ts 公共模块的的网络请求,所有通用 api 放在此处(例如请求枚举之类)
| | |-- user.ts 用户请求模块(示例)
| |-- assets 存放静态资源,这个文件夹下的文件会走 webpack 压缩流程
| |-- boot quasar全局组件封装目录
| | |-- axios.ts 弃用
| | |-- dict.ts 字典库(用于魔法值的封装,注意项目全局不能存在魔法值)
| | |-- i18n.ts 国际化
| | |-- permission.ts 路由权限封装
| |-- components
| | |-- index.ts 自动注册脚本
| | |-- global 自动注册的全局组件
| | |-- ...其他非全局注册的模块(按照实际需要创建目录管理组件,严禁直接在cmp根目录创建)
| |-- config 全局静态配置,不可更改项
| | |-- app.ts 全局配置信息
| | |-- color.ts 示例
| | |-- dictionary.ts 魔法值定义类,按照项目需要自行创建部分类
| |-- css 样式存放目录(注意遵从Sass或Scss创建,不允许使用原生CSS)
| |-- i18n 国际化配置
| |-- layout 页面页面骨架
| |-- module 遵从Vue3.x cmpApi对Setup进行拆分封装,按照领域划分文件夹,类名+Module
| | |-- user
| | | |-- loginModule.ts 示例:内部应该遵从usexxxxModule规范命名
| |-- pages 页面级组件
| |-- router 路由
| | |-- index.ts 路由入口
| |-- store vuex
| | |-- modules 多个模块
| | |-- index.ts 自动装载模块
| | |-- app app 模块
| |-- utils 常用函数以及其他有用工具
| | |-- auth.ts 身份验证中间件
| | |-- message.ts 消息组件封装
| | |-- tools.ts 工具类
| | |-- validate.ts 验证封装
|-- .czrc 提交规范选项设置
|-- .editorconfig vscode 编辑器 设置
|-- .env.development 开发环境配置
|-- .env.preview 测试环境配置
|-- .env.production 生产环境配置
|-- .eslintignore eslint 要忽略的文件夹
|-- .eslintrc.js eslint 规则配置
|-- .gitignore git 忽略的文件
|-- .prettierrc.js 格式化插件配置
|-- README.md 项目说明
|-- babel.config.js babel 设置
|-- package.json npm 配置
|-- tsconfig.json typescript 配置
|-- typedoc.json 文档配置文件
|-- quasar.conf.js quasar 脚手架配置文件
### Build the app for production
```bash
quasar build
``` ```
<br /> ### Customize the configuration
<br /><br /> See [Configuring quasar.conf.js](https://v2.quasar.dev/quasar-cli/quasar-conf-js).
...@@ -112,7 +112,7 @@ module.exports = configure(function (ctx) { ...@@ -112,7 +112,7 @@ module.exports = configure(function (ctx) {
directives: ['ClosePopup'], directives: ['ClosePopup'],
// Quasar plugins // Quasar plugins
plugins: [] plugins: ['Notify','SessionStorage','Dialog']
}, },
animations: 'all', // --- includes all animations animations: 'all', // --- includes all animations
......
export enum RoleType {
'超级管理员' = 1,
'子账号',
'团队超级管理员',
'团队管理员',
'团队成员',
'团队访客',
'项目管理员',
'项目成员',
'项目访客'
}
/**
* @description 常用异常结果常量定义
*/
export enum ResultType {
'Empty' = 1,
'Null' = 2,
'EmptyArray' = 3
}
export enum IndustryType {
'请选择' = 0,
'旅游行业' = 1,
'教育行业' = 2
}
export enum ScaleType {
'请选择' = 0,
'1-20人' = 1,
'21-50人' = 2,
'51-100人' = 3,
'100人以上' = 4
}
import { AppStateType } from '@/store/modules/app/state' import { AppStateType } from '@/store/modules/app/state'
import { UserStateType } from '@/store/modules/user/state' import { UserStateType } from '@/store/modules/user/state'
import { TeamStateType } from '@/store/modules/user/modules/team/state' import { TeamStateType } from '@/store/modules/user/modules/team/state'
import { RoleType } from './enumHelper'
// vuex state 的模块的类型 // vuex state 的模块的类型
type ModuleType = { type ModuleType = {
...@@ -27,14 +28,7 @@ export type IconType = 'icon' | 'iconfont' ...@@ -27,14 +28,7 @@ export type IconType = 'icon' | 'iconfont'
// 对话框打开类型 // 对话框打开类型
export type ModalOpenMode = 'edit' | 'add' | 'other' export type ModalOpenMode = 'edit' | 'add' | 'other'
/**
* @description 常用异常结果常量定义
*/
export enum ResultType {
'Empty' = 1,
'Null' = 2,
'EmptyArray' = 3
}
//TODO: 请根据实际情况调整定义的实体对象 //TODO: 请根据实际情况调整定义的实体对象
/** /**
* @description 模拟定义菜单信息 * @description 模拟定义菜单信息
...@@ -121,17 +115,6 @@ export interface TeamMemberType { ...@@ -121,17 +115,6 @@ export interface TeamMemberType {
cloudRole?: string cloudRole?: string
} }
export enum RoleType {
'超级管理员' = 1,
'子账号',
'团队超级管理员',
'团队管理员',
'团队成员',
'团队访客',
'项目管理员',
'项目成员',
'项目访客'
}
// 权限列表类型 // 权限列表类型
export interface RoleItemType { export interface RoleItemType {
......
import { ResultType, AuthMenuType } from '@/@types' import { AuthMenuType } from '@/@types'
import { ResultType } from '@/@types/enumHelper'
import router from '@/router/index' import router from '@/router/index'
import { getAuth, getUserAllMenu } from '@/utils/auth' import { getAuth, getUserAllMenu } from '@/utils/auth'
...@@ -11,7 +12,7 @@ LoadingBar.setDefaults({ ...@@ -11,7 +12,7 @@ LoadingBar.setDefaults({
let loadAsyncRouter = false let loadAsyncRouter = false
const whiteList = ['/auth/login'] const whiteList = ['/auth/login','/auth/regist']
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
localStorage.setItem('routerBefore', from.path) localStorage.setItem('routerBefore', from.path)
...@@ -20,15 +21,14 @@ router.beforeEach((to, from, next) => { ...@@ -20,15 +21,14 @@ router.beforeEach((to, from, next) => {
if (getAuth()) { if (getAuth()) {
//debugger; //debugger;
if (to.path === '/auth/login' || to.path === '/') { if (to.path === '/auth/login' || to.path === '/') {
next({ next('/index')
path: '/home'
})
LoadingBar.stop() LoadingBar.stop()
} else { } else {
if (!loadAsyncRouter) { if (!loadAsyncRouter) {
// 判断当前用户是否获取权限 // 判断当前用户是否获取权限
loadAsyncRouter = true loadAsyncRouter = true
const allAuth = getUserAllMenu() const allAuth = getUserAllMenu()
console.log(allAuth)
if (allAuth != ResultType.EmptyArray) { if (allAuth != ResultType.EmptyArray) {
//TODO: 动态生成并追加路由 //router.addRoutes(store.getters.addRouters); //TODO: 动态生成并追加路由 //router.addRoutes(store.getters.addRouters);
// if (to.path === '/404') { // if (to.path === '/404') {
...@@ -36,9 +36,12 @@ router.beforeEach((to, from, next) => { ...@@ -36,9 +36,12 @@ router.beforeEach((to, from, next) => {
// next(to.redirectedFrom || '/') // next(to.redirectedFrom || '/')
// } else { // } else {
//检查是否有权限访问 //检查是否有权限访问
const authMenu = allAuth.findIndex((x: AuthMenuType) => { const authMenu = allAuth.findIndex((x: AuthMenuType) => {
console.log(x.menuUrl,to.path)
return x.menuUrl == to.path return x.menuUrl == to.path
}) })
console.log(authMenu)
if (authMenu != -1) { if (authMenu != -1) {
next({ next({
...to, ...to,
......
<template>
<form autocorrect="off" autocapitalize="off" autocomplete="off" spellcheck="false">
<div class="row justify-between">
<div v-for="(x, i) in formatStyle.col" :key="i" class="override-verfity-ipt" style="width: 60px">
<q-input standout style="height: 60px" v-model="formatStyle.col[i]" @focus="codeFocus($event)" :ref="setRef" @keyup="changFocus($event, i)" maxlength="1" mask="#" dense />
</div>
<div class="q-my-md negative f12" v-if="hasError">{{ errorMsg }}</div>
</div>
</form>
</template>
<script>
import { reactive, ref } from 'vue'
export default {
props: {
/**
* @description 请传入能够被12整除的整数
*/
digit: Number,
/**
* @description 提示语
*/
hint: String,
/**
* @description 绑定值
*/
modelValue: [String, Number]
},
setup(props, context) {
const cols = reactive([])
const hasError = ref(false)
const errorMsg = ref('')
for (let i = 0; i < props.digit; i++) {
cols.push(ref(''))
}
const formatStyle = reactive({
col: reactive(cols),
colStyle: ref(`col-${12 / props.digit}`)
})
const myRef = ref([])
const setRef = el => {
myRef.value.push(el)
}
const changFocus = (event, index) => {
if (event.key == 'Backspace') {
if (index != 0) {
myRef.value[index - 1].focus()
}
} else if (index < props.digit) {
console.log(cols[index])
if (formatStyle.col[index] != '') {
myRef.value[index + 1].focus()
}
}
updateValue()
}
const updateValue = () => {
let tempValue = ref('')
formatStyle.col.forEach(x => {
tempValue.value += x.toString()
})
context.emit('update:modelValue', tempValue.value)
}
const codeFocus = e => {
e.target.select()
}
const validate = () => {
if (props.modelValue.length != formatStyle.col.length) {
errorMsg.value = '请输入正确的验证码'
hasError.value = true
} else {
errorMsg.value = ''
hasError.value = false
}
}
return { formatStyle, changFocus, setRef, codeFocus, validate, hasError, errorMsg }
}
}
</script>
<style>
.override-verfity-ipt .q-field--dense .q-field__control,
.override-verfity-ipt .q-field--dense .q-field__marginal {
height: 60px !important;
text-align: center;
font-family: din;
font-size: 32px;
}
.override-verfity-ipt .q-field--dense .q-field__control input,
.override-verfity-ipt .q-field--dense .q-field__marginal input {
text-align: center;
}
</style>
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
import { Notify } from 'quasar' import { Notify } from 'quasar'
const AppConfig = { const AppConfig = {
$message: Notify $message: Notify
} }
const StaticConfig = { const StaticConfig = {
MaxPageSize: 1000 MaxPageSize: 1000,
appsuffix: '大水豚渠道管理系统'
} }
export { AppConfig, StaticConfig } export { AppConfig, StaticConfig }
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
*/ */
const userDictionmary = { const userDictionmary = {
token: 'access_token', token: 'access_token',
serverTokenName: 'Token' serverTokenName: 'Token',
sendCodeTimeSpan:'scts'
} }
export { userDictionmary } export { userDictionmary }
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
@font-face @font-face
// 微软雅黑极细字体 // 微软雅黑极细字体
font-family: 'msl' font-family: 'msl'
src: url('../assets/fonts/PingFangBold.ttf') format('truetype') src: url('../assets/fonts/MicrosoftYaHeiUISemilight.ttf') format('truetype')
font-weight: normal font-weight: normal
font-style: normal font-style: normal
@font-face @font-face
...@@ -47,3 +47,9 @@ body ...@@ -47,3 +47,9 @@ body
color: var(--q-dark) color: var(--q-dark)
.change-a-primary:hover .change-a-primary:hover
color: var(--q-primary) color: var(--q-primary)
.f12
font-family: 'msl' !important
font-size: 13px
.mycard
box-shadow: 0 0.5rem 1.5rem 0.5rem rgba(0, 0, 0, 0.075) !important
border-radius: 0.475rem !important
import { StaticConfig } from '@/config/app'
import { useMeta } from 'quasar'
import { ref } from 'vue'
const userMetaTitleModule = () => {
const title = ref(StaticConfig.appsuffix)
useMeta(() => {
return {
title: title.value
}
})
const setTitle = (pageTitle: string, suffix = true) => {
if (suffix) {
title.value = `${pageTitle}-${StaticConfig.appsuffix}`
} else {
title.value = pageTitle
}
}
return { setTitle }
}
export default userMetaTitleModule
import { reactive } from 'vue'
const useScrollModule = () => {
const thumbStyle = reactive({
right: '2px',
borderRadius: '5px',
backgroundColor: '#80C5C5',
width: '5px',
opacity: 0.75
})
const barStyle = reactive({
right: '0px',
borderRadius: '9px',
backgroundColor: '#04c8c8',
width: '9px',
opacity: 0.2
})
const scrollStyle=reactive({
thumbStyle,
barStyle
})
return {scrollStyle}
}
export default useScrollModule
\ No newline at end of file
import { ResultType } from '@/@types/enumHelper'
import router from '@/router'
import { UserActionsType } from '@/store/modules/user/actions'
import { UserGetter } from '@/store/modules/user/getters'
import { dispatchAction, getStoreGetter, setStoreState } from '@/store/utils'
import message from '@/utils/message' import message from '@/utils/message'
import { reactive,ref } from 'vue' import { reactive, ref } from 'vue'
interface LoginParams { interface LoginParams {
username: string username: string
password: string password: string
...@@ -17,23 +22,63 @@ const userUserLoginModule = () => { ...@@ -17,23 +22,63 @@ const userUserLoginModule = () => {
usernameRule: [(val: any) => !!val || '请填写账户信息'], usernameRule: [(val: any) => !!val || '请填写账户信息'],
userpasswordRule: [(val: any) => !!val || '请填写密码信息'] userpasswordRule: [(val: any) => !!val || '请填写密码信息']
}) })
const stateManager = reactive({
const usernameRef=ref(null) subLogin: ref(false)
const passwordRef=ref(null) })
const usernameRef = ref(null)
const passwordRef = ref(null)
const loginSubmit = () => { const loginSubmit = () => {
const ur=(usernameRef as any) //断言任意类型 if (!stateManager.subLogin) {
const pr=(passwordRef as any) stateManager.subLogin = true
ur.value.validate() const ur = usernameRef as any //断言任意类型
pr.value.validate() const pr = passwordRef as any
if (ur.value.hasError || pr.value.hasError) { ur.value.validate()
message.warnMsg('请完善登录信息') pr.value.validate()
} else { if (ur.value.hasError || pr.value.hasError) {
message.successMsg('验证通过') message.warnMsg('请完善登录信息')
stateManager.subLogin = false
} else {
setTimeout(() => {
//#region 测试使用
const param = {
username: userModel.username,
password: userModel.password
}
dispatchAction<UserActionsType>('user', 'userLogin', param)
const menu:any = {
menuId: 1,
menuName: '首页',
menuUrl: '/index'
}
const menu2:any = {
menuId: 2,
menuName: '首页',
menuUrl: '/'
}
const menus: Array<any> = []
const auths = getStoreGetter<UserGetter>('user', 'getUserAllAuth')
if (auths != ResultType.EmptyArray) {
Object.assign(menus, ...auths)
}
menus.push(menu)
menus.push(menu2)
setStoreState('user', 'menuList', menus)
message.successMsg('登录成功')
stateManager.subLogin = false
router.push({
path: '/index'
})
//#endregion
}, 2000)
}
} }
} }
return { userModel, usernameRef, passwordRef, userValidateRule, loginSubmit } return { userModel, usernameRef, passwordRef, userValidateRule, loginSubmit, stateManager }
} }
export default userUserLoginModule export default userUserLoginModule
import { AuthMenuType } from '@/@types'
import { IndustryType, ResultType, ScaleType } from '@/@types/enumHelper'
import { userDictionmary } from '@/config/dictionary'
import router from '@/router'
import { UserGetter } from '@/store/modules/user/getters'
import { getStoreGetter, setStoreState } from '@/store/utils'
import message from '@/utils/message'
import { formatEnum } from '@/utils/tools'
import { SessionStorage } from 'quasar'
// import message from '@/utils/message'
import { nextTick, reactive, ref } from 'vue'
interface RegistParams {
mobile: string
verfityCode: string
companyName: string
username: string
industry: number
scale: number
password: string
confirmPwd: string
}
const useUserRegistModule = () => {
const userModel: RegistParams = reactive({
mobile: ref(''),
verfityCode: ref(''),
companyName: ref(''),
username: ref(''),
industry: ref(0),
scale: ref(0),
password: ref(''),
confirmPwd: ref('')
})
const userRule = reactive({
mobileRule: [(val: any) => !!/^1(3|4|5|6|7|8|9)\d{9}$/.test(val) || '请填写你的手机账号信息'],
verfityCodeRule: [(val: any) => val.length != 6 || '请填写验证码信息'],
passwordRule: [(val: any) => (val.length >= 6 && val.length <= 30) || '请输入6-30位密码'],
confirmPwdRule: [(val: any) => val == userModel.password || '两次密码不一致'],
companyNameRule: [(val: any) => !!val || '请您填写企业名称'],
usernameRule: [(val: any) => !!val || '请您填写联系人名称'],
industryRule: [(val: any) => val != 0 || '请选择企业所属行业'],
scaleRule: [(val: any) => val != 0 || '请选择']
})
const stateManager = reactive({
sendCodeStatus: ref(0),
validateRules: ref([]),
countDown: ref(0),
timeProvide: ref(),
canSubmitSetupOne: ref(true),
subCode: ref(false),
subSetupOne: ref(false),
isSended: ref(false),
verifyCodeLength: ref(6),
currentSetup: ref('1'),
induArray: ref(formatEnum(IndustryType)),
scaleArray: ref(formatEnum(ScaleType)),
subRegist: ref(false)
})
const validateSendStatus = () => {
if (stateManager.sendCodeStatus == 2) return
const mr = stateManager.validateRules[0] as any
nextTick(() => {
stateManager.sendCodeStatus = mr.hasError ? 0 : 1
})
}
const setValidateRules = (el: HTMLElement) => {
(stateManager.validateRules as Array<HTMLElement>).push(el)
}
const checkCodeStatus = () => {
if (SessionStorage.has(userDictionmary.sendCodeTimeSpan)) {
const oldSend = SessionStorage.getItem<number>(userDictionmary.sendCodeTimeSpan) ?? 0
const cnt = Math.abs(oldSend - Date.parse(new Date().toString())) / 1000
if (cnt >= 60) {
SessionStorage.remove(userDictionmary.sendCodeTimeSpan)
stateManager.countDown = 0
stateManager.sendCodeStatus = 0
validateSendStatus()
} else {
stateManager.countDown = 60 - cnt
stateManager.sendCodeStatus = 2
stateManager.timeProvide = setInterval(beginCutDown, 1000)
}
}
}
const sendCode = () => {
if (!stateManager.subCode && stateManager.countDown == 0 && stateManager.sendCodeStatus == 1) {
stateManager.subCode = true
//TODO: 完成接口调用
setTimeout(() => {
stateManager.isSended = true
stateManager.countDown = 60
stateManager.sendCodeStatus = 2
SessionStorage.set(userDictionmary.sendCodeTimeSpan, Date.parse(new Date().toString()))
stateManager.subCode = false
stateManager.timeProvide = setInterval(beginCutDown, 1000)
}, 3000)
}
}
const beginCutDown = () => {
stateManager.countDown--
if (stateManager.countDown == 0) {
SessionStorage.remove(userDictionmary.sendCodeTimeSpan)
stateManager.sendCodeStatus = 0
validateSendStatus()
if (stateManager.timeProvide) {
clearInterval(stateManager.timeProvide)
stateManager.timeProvide = null
}
}
}
const submitValidateCode = () => {
if (!stateManager.subSetupOne) {
stateManager.subSetupOne = true
let isError = false
for (let i = 0; i < stateManager.validateRules.length; i++) {
const mr = stateManager.validateRules[i] as any
if(mr){
mr.validate()
}
}
for (let i = 0; i < stateManager.validateRules.length; i++) {
const mr = stateManager.validateRules[i] as any
if (mr&&mr.hasError) {
isError = true
break
}
}
if (!isError && userModel.verfityCode.length == stateManager.verifyCodeLength && stateManager.isSended) {
setTimeout(() => {
stateManager.subSetupOne = false
if (userModel.verfityCode == '888888') {
stateManager.currentSetup = '2'
stateManager.isSended = false
} else {
message.warnMsg('验证码错误,请重新输入')
}
}, 3000)
} else {
stateManager.subSetupOne = false
}
}
}
const doRegist = () => {
if (!stateManager.subRegist) {
stateManager.subRegist = true
let isError = false
for (let i = 0; i < stateManager.validateRules.length; i++) {
const mr = stateManager.validateRules[i] as any
if(mr){
mr.validate()
}
}
for (let i = 0; i < stateManager.validateRules.length; i++) {
const mr = stateManager.validateRules[i] as any
if (mr&&mr.hasError) {
isError = true
break
}
}
if (isError) {
stateManager.subRegist = false
} else {
console.log(userModel)
//TODO: 调用注册接口
setTimeout(() => {
//注册成功应该实现自动登录,让后跳转到首页
const dissmiss = message.loadMsg('注册成功,正常初始化系统,马上就好')
setTimeout(() => {
dissmiss()
message.successMsg('初始化成功,正在进入系统')
//#region 测试使用
const menu: AuthMenuType = {
menuId: 1,
menuName: '首页',
menuUrl: '/index'
}
const menus = new Array<AuthMenuType>()
const auths = getStoreGetter<UserGetter>('user', 'getUserAllAuth')
if (auths != ResultType.EmptyArray) {
Object.assign(menus, ...auths)
}
menus.push(menu)
setStoreState('user', 'menuList', menus)
router.push({
path: '/index'
})
//#endregion
}, 2000)
}, 3000)
}
}
}
return { userModel, setValidateRules, userRule, stateManager, validateSendStatus, checkCodeStatus, sendCode, submitValidateCode, doRegist }
}
export default useUserRegistModule
<template> <template>
<q-page class="row items-center justify-evenly"> <q-page class="row items-center justify-evenly">
<example-component <example-component title="Example component" active :todos="todos" :meta="meta"></example-component>
title="Example component"
active
:todos="todos"
:meta="meta"
></example-component>
</q-page> </q-page>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Todo, Meta } from '@/components/models'; import { Todo, Meta } from '@/components/models'
import ExampleComponent from 'components/CompositionComponent.vue'; import ExampleComponent from 'components/CompositionComponent.vue'
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue'
export default defineComponent({ export default defineComponent({
name: 'PageIndex', name: 'PageIndex',
...@@ -39,11 +34,11 @@ export default defineComponent({ ...@@ -39,11 +34,11 @@ export default defineComponent({
id: 5, id: 5,
content: 'ct5' content: 'ct5'
} }
]); ])
const meta = ref<Meta>({ const meta = ref<Meta>({
totalCount: 1200 totalCount: 1200
}); })
return { todos, meta }; return { todos, meta }
} }
}); })
</script> </script>
...@@ -2,10 +2,13 @@ ...@@ -2,10 +2,13 @@
<div class="row"> <div class="row">
<div class="col window-height column"> <div class="col window-height column">
<div class="col q-pa-lg flex justify-center items-center"> <div class="col q-pa-lg flex justify-center items-center">
<div> <div class="relative-position">
<img src="../../assets/images/big-logo.png" style="height: 60px" class="q-mb-lg" alt="" /> <img src="../../assets/images/big-logo.png" style="height: 60px" class="q-mb-lg" alt="" />
<div class="text-h5 pfb q-mb-md">欢迎回来,亲爱的用户</div> <div class="text-h5 pfb q-mb-md">欢迎回来,亲爱的用户</div>
<div class="pfb text-grey-5 text-subtitle">通过配置您的推广渠道,实现对线索和客户的快速获取<br />并且及时的跟进</div> <div class="pfb text-grey-5 text-subtitle">通过配置您的推广渠道,实现对线索和客户的快速获取<br />并且及时的跟进</div>
<div class="absolute q-mt-md" style="top: 0; left: 180px">
<q-badge style="background: #cbcfe0" floating transparent>Alpha</q-badge>
</div>
</div> </div>
</div> </div>
<div class="col q-mb-lg login-bg"></div> <div class="col q-mb-lg login-bg"></div>
...@@ -29,34 +32,41 @@ ...@@ -29,34 +32,41 @@
</div> </div>
</div> </div>
<div class="q-mt-xs"> <div class="q-mt-xs">
<q-input standout v-model="userModel.password" dense ref="passwordRef" :rules="userValidateRule.userpasswordRule" /> <q-input standout v-model="userModel.password" type="password" dense ref="passwordRef" :rules="userValidateRule.userpasswordRule" />
</div> </div>
<div class="q-mt-md"> <div class="q-mt-md">
<q-checkbox dense v-model="userModel.remeber" label="30天免登录" color="accent" /> <q-checkbox dense v-model="userModel.remeber" label="30天免登录" color="accent" />
</div> </div>
<div class="q-mt-lg text-center"> <div class="q-mt-lg text-center">
<q-btn class="pfb" color="primary" unelevated label="登 录" style="width: 10rem" @click="loginSubmit"></q-btn> <q-btn class="pfb" color="primary" :loading="stateManager.subLogin" unelevated label="登 录" style="width: 10rem" @click="loginSubmit"></q-btn>
</div> </div>
</q-card> </q-card>
</div> </div>
<div class="q-my-lg q-mx-xl text-center w-450-only"> <div class="q-my-lg q-mx-xl text-center row w-450-only">
<router-link :to="{ path: '/auth/forget' }" class="pfb text-body no-underline change-a-primary q-mr-md">关于大水豚</router-link> <div class="f12 text-dark">© 2021-{{ dtNow }} 微途科技 版权所有</div>
<router-link :to="{ path: '/auth/forget' }" class="pfb text-body no-underline change-a-primary q-mr-md">联系我们</router-link> <div class="text-center col text-right">
<router-link :to="{ path: '/auth/forget' }" class="pfb text-body no-underline change-a-primary q-mr-md">技术支持</router-link> <router-link :to="{ path: '/auth/forget' }" class="f12 no-underline change-a-primary q-mr-md">关于大水豚</router-link>
<router-link :to="{ path: '/auth/forget' }" class="f12 no-underline change-a-primary q-mr-md">联系我们</router-link>
<router-link :to="{ path: '/auth/forget' }" class="f12 no-underline change-a-primary q-mr-md">技术支持</router-link>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { defineComponent, ref } from 'vue' import { defineComponent } from 'vue'
import useLgoinModule from '@/module/user/loginModule' import useLgoinModule from '@/module/user/loginModule'
import useMetaModule from '@/module/meta/metaModule'
export default defineComponent({ export default defineComponent({
setup() { setup() {
let { userModel, usernameRef, passwordRef, userValidateRule, loginSubmit } = useLgoinModule() //TODO: 缺陷,验证与提交应该使用Form表单来完成,不应该进行单个验证
let { userModel, usernameRef, passwordRef, userValidateRule, loginSubmit, stateManager } = useLgoinModule()
return { userModel, usernameRef, passwordRef, userValidateRule, loginSubmit } let { setTitle } = useMetaModule()
setTitle('登录')
const dtNow = new Date().getFullYear()
return { userModel, usernameRef, passwordRef, userValidateRule, loginSubmit, dtNow, stateManager }
} }
}) })
</script> </script>
...@@ -74,7 +84,7 @@ export default defineComponent({ ...@@ -74,7 +84,7 @@ export default defineComponent({
box-shadow: 0 0.5rem 1.5rem 0.5rem rgba(0, 0, 0, 0.075) !important; box-shadow: 0 0.5rem 1.5rem 0.5rem rgba(0, 0, 0, 0.075) !important;
border-radius: 0.475rem !important; border-radius: 0.475rem !important;
} }
.w-450-only{ .w-450-only {
width: 450px; width: 450px;
} }
</style> </style>
<template>
<div class="window-height regist-box column">
<q-scroll-area :thumb-style="scrollStyle.thumbStyle" :bar-style="scrollStyle.barStyle" class="full-height full-width">
<div class="w-600 col q-py-lg flex items-center">
<div class="form-box-diy col">
<div class="text-center">
<img src="@/assets/images/big-logo.png" height="60" alt="" />
</div>
<q-card class="mycard q-mt-lg q-pa-xl">
<q-tab-panels v-model="stateManager.currentSetup" animated>
<q-tab-panel name="1">
<div class="text-h5 pfb q-mb-md text-center">创建账号</div>
<div class="text-center q-mb-lg">
<span class="text-grey-5 pfb text-subtitle">已经有账号了?</span>
<router-link :to="{ path: '/auth/login' }" class="text-primary pfb text-subtitle">点击登录</router-link>
</div>
<div class="text-body pfb">手机号:</div>
<div class="q-mt-xs row">
<q-input standout v-model="userModel.mobile" mask="###########" class="col" @update:model-value="validateSendStatus" dense :ref="setValidateRules" :rules="userRule.mobileRule" />
<div class="q-ml-md">
<q-btn color="accent" :loading="stateManager.subCode" class="btn-fixed-width-102 ellipsis" :disable="stateManager.sendCodeStatus != 1" unelevated @click="sendCode">
{{ stateManager.sendCodeStatus != 2 ? '发送验证码' : `${stateManager.countDown}秒后重发` }}
</q-btn>
</div>
</div>
<div class="text-body pfb q-mt-md">验证码:</div>
<div class="q-mt-xs">
<verifyCodeBox :digit="stateManager.verifyCodeLength" v-model="userModel.verfityCode"></verifyCodeBox>
</div>
<div class="q-mt-md">
<div class="col text-body pfb">密码:</div>
</div>
<div class="q-mt-xs">
<q-input standout v-model="userModel.password" type="password" dense :ref="setValidateRules" :rules="userRule.passwordRule" />
</div>
<div class="q-mt-md">
<div class="col text-body pfb">确认密码:</div>
</div>
<div class="q-mt-xs">
<q-input standout v-model="userModel.confirmPwd" type="password" dense :ref="setValidateRules" :rules="userRule.confirmPwdRule" />
</div>
<div class="q-mt-md">
<q-btn :loading="stateManager.subSetupOne" :color="stateManager.isSended && userModel.verfityCode.length == stateManager.verifyCodeLength ? 'primary' : 'secondary'" class="full-width" :disable="!stateManager.isSended || userModel.verfityCode.length != 6" unelevated @click="submitValidateCode">下一步</q-btn>
</div>
</q-tab-panel>
<q-tab-panel name="2">
<div class="text-h5 pfb q-mb-md text-center">补充资料填写</div>
<div class="text-body pfb">注册账号:</div>
<div class="q-mt-xs din" style="font-size: 32px">
{{ userModel.mobile == '' ? '17308037817' : userModel.mobile }}
</div>
<div class="q-mt-md">
<div class="text-body pfb q-mt-md">企业名称:</div>
</div>
<div class="q-mt-xs">
<q-input standout v-model="userModel.companyName" dense :ref="setValidateRules" :rules="userRule.companyNameRule" />
</div>
<div class="text-body pfb q-mt-md">姓名:</div>
<div class="q-mt-xs">
<q-input standout v-model="userModel.username" dense :ref="setValidateRules" :rules="userRule.usernameRule" />
</div>
<div class="text-body pfb q-mt-md">联系人姓名:</div>
<div class="q-mt-xs">
<q-input standout v-model="userModel.username" dense :ref="setValidateRules" :rules="userRule.usernameRule" />
</div>
<div class="text-body pfb q-mt-md">所属行业</div>
<div class="q-mt-xs">
<q-select standout dense v-model="userModel.industry" :ref="setValidateRules" :options="stateManager.induArray" option-label="key" :rules="userRule.industryRule" behavior="menu" option-value="value" map-options></q-select>
</div>
<div class="text-body pfb q-mt-md">企业规模</div>
<div class="q-mt-xs">
<q-select standout dense v-model="userModel.scale" :ref="setValidateRules" :options="stateManager.scaleArray" option-label="key" :rules="userRule.scaleRule" behavior="menu" option-value="value" map-options></q-select>
</div>
<div class="q-mt-lg text-center">
<q-btn class="pfb" color="primary" :loading="stateManager.subRegist" unelevated label="立即注册" style="width: 10rem" @click="doRegist"></q-btn>
</div>
</q-tab-panel>
</q-tab-panels>
</q-card>
</div>
</div>
</q-scroll-area>
</div>
</template>
<script>
import { defineComponent, onMounted } from 'vue'
import useMetaModule from '@/module/meta/metaModule'
import useUserRegistModule from '@/module/user/registModule'
import verifyCodeBox from '@/components/common/verifyCode.vue'
import useScrollModule from '@/module/scrollbar/scrollModule'
export default defineComponent({
components: {
verifyCodeBox
},
setup() {
let { setTitle } = useMetaModule()
setTitle('创建账户')
let { userModel, setValidateRules, userRule, validateSendStatus, stateManager, sendCode, checkCodeStatus, submitValidateCode, doRegist } = useUserRegistModule()
let { scrollStyle } = useScrollModule()
onMounted(() => {
checkCodeStatus()
})
return { userModel, userRule, setValidateRules, validateSendStatus, stateManager, sendCode, submitValidateCode, scrollStyle, doRegist }
}
})
</script>
<style>
.regist-box {
background-image: url(../../assets/images/14.png);
background-attachment: fixed;
background-position-y: bottom;
background-repeat: no-repeat;
background-size: contain;
}
.btn-fixed-width-102 {
width: 102px;
padding: 7px 14px;
}
.w-600 {
width: 600px;
margin: 2rem auto;
}
</style>
...@@ -2,14 +2,18 @@ import { RouteRecordRaw } from 'vue-router' ...@@ -2,14 +2,18 @@ import { RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [ const routes: RouteRecordRaw[] = [
{ {
path: '/', path: '/index',
component: () => import('layouts/MainLayout.vue'), component: () => import('@/layouts/MainLayout.vue'),
children: [{ path: '', component: () => import('pages/Index.vue') }] children: [{ path: '', component: () => import('@/pages/Index.vue') }]
}, },
{ {
path:'/auth/login', path:'/auth/login',
component: () => import('@/pages/auth/login.vue') component: () => import('@/pages/auth/login.vue')
}, },
{
path:'/auth/regist',
component: () => import('@/pages/auth/regist.vue')
},
// Always leave this as last one, // Always leave this as last one,
// but you can also remove it // but you can also remove it
{ {
......
...@@ -7,23 +7,51 @@ import Store from '@/store' ...@@ -7,23 +7,51 @@ import Store from '@/store'
*/ */
const userActions = { const userActions = {
// 刷新令牌 // 刷新令牌
refreshToken() { refreshToken() {
return UserService.refreshToken({ return UserService.refreshToken({
// eslint-disable-next-line // eslint-disable-next-line
refresh_token: Store.state.user.token.refresh_token refresh_token: Store.state.user.token.refresh_token
}).then(res => { }).then(res => {
// token过期时间 // token过期时间
const expireTime = res.data.expires_in * 1000 + new Date().getTime() const expireTime = res.data.expires_in * 1000 + new Date().getTime()
setStoreState('user', 'token', { ...res.data, expireTime }) setStoreState('user', 'token', { ...res.data, expireTime })
}) })
}, },
// 获取用户信息
getUserDetail() { // 获取用户信息
return UserService.getUserDetail().then(res => { getUserDetail() {
setStoreState('user', 'userDetail', res.data.data) return UserService.getUserDetail().then(res => {
}) setStoreState('user', 'userDetail', res.data.data)
} })
},
userLogin(params: { username: string; password: string }) {
//TODO: 实现用户登录
// UserService.login(params).then(res=>{
// })
//测试伪造数据
const loginUser = {
email: 'alex9012@vip.qq.com',
type: 0, // 用户账号本身类型 0:主账号,1:子账号
userId: 1,
username: params.username,
description: '',
nickName: '罗超',
phone: '17308037817',
tenantId: 1,
roleId: 1
}
setStoreState('user', 'userDetail', loginUser)
const token = {
token_type: 'login_auth',
access_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
const expireTime = 60 * 72 * 1000 + new Date().getTime()
setStoreState('user', 'token', { ...token, expireTime })
}
} }
type UserActionsType = typeof userActions type UserActionsType = typeof userActions
......
import { ResultType } from '@/@types' import { ResultType } from '@/@types/enumHelper'
import store from '@/store' import store from '@/store'
const userGetter = { const userGetter = {
...@@ -7,7 +7,9 @@ const userGetter = { ...@@ -7,7 +7,9 @@ const userGetter = {
return token.access_token ?? ResultType.Empty return token.access_token ?? ResultType.Empty
}, },
getUserAllAuth() { getUserAllAuth() {
return store.state.user.menuList const menuList: any[] = store.state.user.menuList
console.log((menuList.length > 0 ? menuList : ResultType.EmptyArray),menuList.length,menuList)
return menuList.length > 0 ? menuList : ResultType.EmptyArray
} }
} }
......
import { AuthMenuType, StateType } from '@/@types' import { StateType } from '@/@types'
import { Module } from 'vuex' import { Module } from 'vuex'
import ModuleTeam from './modules/team/state' import ModuleTeam from './modules/team/state'
interface Token { interface Token {
...@@ -19,7 +19,7 @@ const state = { ...@@ -19,7 +19,7 @@ const state = {
}, },
currentTeamRoleId: 0, // 当前所选择的团队用户所具有的权限 currentTeamRoleId: 0, // 当前所选择的团队用户所具有的权限
currentProjectRoleId: 0, // 当前所选择的项目用户所具有的权限 currentProjectRoleId: 0, // 当前所选择的项目用户所具有的权限
menuList: [] as Array<AuthMenuType> menuList: []
} }
type UserStateType = typeof state type UserStateType = typeof state
......
import { getStoreGetter } from '@/store/utils' import { getStoreGetter } from '@/store/utils'
import { UserGetter } from '@/store/modules/user/getters' import { UserGetter } from '@/store/modules/user/getters'
import { ResultType } from '@/@types' import { ResultType } from '@/@types/enumHelper'
/** /**
* @description 获取授权信息 * @description 获取授权信息
...@@ -16,5 +16,6 @@ export function getAuth() { ...@@ -16,5 +16,6 @@ export function getAuth() {
*/ */
export function getUserAllMenu() { export function getUserAllMenu() {
const auths = getStoreGetter<UserGetter>('user', 'getUserAllAuth') const auths = getStoreGetter<UserGetter>('user', 'getUserAllAuth')
console.log(auths)
return auths.length > 0 ? auths : ResultType.EmptyArray return auths.length > 0 ? auths : ResultType.EmptyArray
} }
import { Notify } from 'quasar' import { Notify, QSpinnerIos } from 'quasar'
const message = { const message = {
warnMsg: (msg: string) => { warnMsg: (msg: string) => {
...@@ -6,16 +6,35 @@ const message = { ...@@ -6,16 +6,35 @@ const message = {
message: msg, message: msg,
color: 'warning', color: 'warning',
textColor: 'dark', textColor: 'dark',
position: 'top',
icon: 'announcement' icon: 'announcement'
}) })
}, },
errorMsg: (msg: string) => {
Notify.create({
message: msg,
type: 'negative',
position: 'top'
})
},
successMsg: (msg: string) => { successMsg: (msg: string) => {
Notify.create({ Notify.create({
message: msg, message: msg,
color: 'positive', color: 'positive',
position: 'top',
textColor: 'white', textColor: 'white',
icon: 'success' icon: 'check'
})
},
loadMsg: (msg: string): any => {
const loadDissmiss = Notify.create({
message: msg,
color: 'secondary',
position: 'center',
textColor: 'dark',
spinner: QSpinnerIos
}) })
return loadDissmiss
} }
} }
......
/** /**
* @description 按照需要写入 必要可以注入全局 * @description 按照需要写入 必要可以注入全局
*/ */
interface enumObj {
key: string
value: number
}
/**
* @description 将枚举转为数组
* @param objEnum 枚举对象
* @returns 转化后的数组
*/
export function formatEnum(objEnum: any): Array<enumObj> {
const array: Array<enumObj> = new Array<enumObj>()
for (const key in objEnum) {
if (!isNaNModified(key)) continue
const tempEnumObj: enumObj = {
key,
value: objEnum[key] as number
}
array.push(tempEnumObj)
}
return array
}
export function isNaNModified(inputStr: string) {
const numericRepr = parseFloat(inputStr)
return isNaN(numericRepr) || numericRepr.toString().length != inputStr.length
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment