版本升级:

1. 修复(kinit-admin):页面缓存问题修复
2. 更新(kinit-api,kinit-admin):菜单管理新增是否缓存字段
3. 更新(kinit-admin):将缓存默认存储在localStorage中
4. 更新(kinit-api):将python-jose库更换为pyjwt库
5. 优化(kinit-admin,kinit-uni):退出登录方法优化
6. 优化(kinit-admin,kinit-uni):response拦截优化
7. 新增(kinit-api,kinit-admin,kinit-uni):jwt到期时间缩短,加入刷新token功能
8. (kinit-uni)切换到 vscode 开发 uniapp 项目
This commit is contained in:
ktianc 2023-03-13 14:34:26 +08:00
parent 06012fda6e
commit ff56a184ca
81 changed files with 2950 additions and 2508 deletions

View File

@ -5,6 +5,7 @@ import { useAuthStore } from '@/store/modules/auth'
import qs from 'qs'
import { config } from './config'
import { ElMessage } from 'element-plus'
import request from '@/config/axios'
const { result_code, unauthorized_code, request_timeout } = config
@ -62,33 +63,99 @@ service.interceptors.request.use(
// response 拦截器
service.interceptors.response.use(
(response: AxiosResponse<any>) => {
// 这个状态码是和后端约定好的
const code = response.data.code || unauthorized_code
const message = response.data.message || '后端接口无返回内容'
const refresh = response.data.refresh || false
if (response.config.responseType === 'blob') {
// 如果是文件流,直接过
return response
} else if (response.data.code === result_code) {
} else if (code === result_code) {
if (refresh) {
// 因token快过期刷新token
refreshToken().then((res) => {
const appStore = useAppStore()
wsCache.set(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`)
wsCache.set(appStore.getRefreshToken, res.data.refresh_token)
})
// .catch(() => {
// const authStore = useAuthStore()
// authStore.logout()
// ElMessage.error('未认证,请登录')
// })
}
return response.data
} else if (response.data.code === unauthorized_code) {
// 请重新登录
ElMessage.error(response.data.message)
const authStore = useAuthStore()
authStore.logout()
} else if (code === unauthorized_code) {
// 因token无效token过期导致
refreshToken().then((res) => {
const appStore = useAppStore()
wsCache.set(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`)
wsCache.set(appStore.getRefreshToken, res.data.refresh_token)
ElMessage.error('操作失败,请重试')
})
// .catch(() => {
// const authStore = useAuthStore()
// authStore.logout()
// ElMessage.error('未认证,请登录')
// })
} else {
ElMessage.error(response.data.message)
ElMessage.error(message)
}
},
(error: AxiosError) => {
console.log('err' + error)
let { message } = error
if (message == 'Network Error') {
message = '后端接口连接异常'
} else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substr(message.length - 3) + '异常'
const status = error.response?.status
switch (status) {
case 400:
message = '请求错误'
break
case 401:
// 强制要求重新登录因账号已冻结账号已过期手机号码错误刷新token无效等问题导致
const authStore = useAuthStore()
authStore.logout()
message = '未认证,请登录'
break
case 403:
message = '拒绝访问'
break
case 404:
message = `请求地址出错: ${error.response?.config.url}`
break
case 408:
message = '请求超时'
break
case 500:
message = '服务器内部错误'
break
case 501:
message = '服务未实现'
break
case 502:
message = '网关错误'
break
case 503:
message = '服务不可用'
break
case 504:
message = '网关超时'
break
case 505:
message = 'HTTP版本不受支持'
break
default:
break
}
ElMessage.error(message)
return Promise.reject(error)
}
)
// 刷新Token
const refreshToken = (): Promise<IResponse> => {
const appStore = useAppStore()
const data = wsCache.get(appStore.getRefreshToken)
return request.post({ url: '/auth/token/refresh/', data })
}
export { service }

View File

@ -1,12 +1,14 @@
/**
*
* sessionStorage
* localStorage
*/
import WebStorageCache from 'web-storage-cache'
type CacheType = 'sessionStorage' | 'localStorage'
export const useCache = (type: CacheType = 'sessionStorage') => {
export const useCache = (type: CacheType = 'localStorage') => {
const wsCache: WebStorageCache = new WebStorageCache({
storage: type
})

View File

@ -39,6 +39,7 @@ interface AppState {
footerContent: string
icpNumber: string
token: string
refreshToken: string
}
export const useAppStore = defineStore('app', {
@ -46,6 +47,7 @@ export const useAppStore = defineStore('app', {
return {
userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突
token: 'Token', // 存储Token字段
refreshToken: 'RefreshToken', // 存储刷新Token字段
sizeMap: ['default', 'large', 'small'],
mobile: false, // 是否是移动端
title: import.meta.env.VITE_APP_TITLE, // 标题
@ -170,6 +172,9 @@ export const useAppStore = defineStore('app', {
getToken(): string {
return this.token
},
getRefreshToken(): string {
return this.refreshToken
},
getIsDark(): boolean {
return this.isDark
},

View File

@ -7,6 +7,7 @@ import { useCache } from '@/hooks/web/useCache'
import { getCurrentAdminUserInfo } from '@/api/vadmin/auth/user'
import { resetRouter } from '@/router'
import { useTagsViewStore } from '@/store/modules/tagsView'
import router from '@/router'
const { wsCache } = useCache()
@ -48,6 +49,7 @@ export const useAuthStore = defineStore('auth', {
if (res) {
const appStore = useAppStore()
wsCache.set(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`)
wsCache.set(appStore.getRefreshToken, res.data.refresh_token)
// 存储用户信息
const auth = useAuthStore()
await auth.getUserInfo()
@ -61,7 +63,7 @@ export const useAuthStore = defineStore('auth', {
const tagsViewStore = useTagsViewStore()
tagsViewStore.delAllViews()
resetRouter()
window.location.href = '/login'
router.push('/login')
},
updateUser(data: UserState) {
this.user.gender = data.gender

View File

@ -24,6 +24,7 @@ const rules = reactive({
disabled: [required()],
hidden: [required()],
path: [required()],
noCache: [required()],
order: [required()]
})

View File

@ -44,6 +44,12 @@ export const columns = reactive<TableColumn[]>([
label: '组件路径',
show: true
},
{
field: 'noCache',
label: '页面缓存',
width: '120px',
show: true
},
{
field: 'hidden',
label: '显示状态',
@ -224,5 +230,31 @@ export const schema = reactive<FormSchema[]>([
span: 12
},
ifshow: (values) => values.menu_type === '2'
},
{
field: 'noCache',
label: '页面缓存',
colProps: {
span: 12
},
component: 'Radio',
componentProps: {
style: {
width: '100%'
},
options: [
{
label: '缓存',
value: false
},
{
label: '不缓存',
value: true
}
]
},
value: false,
ifshow: (values) => values.menu_type === '1',
labelMessage: '开启页面缓存,需要组件名称必须与xx.vue页面的name一致'
}
])

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'AuthMenu'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { RightToolbar } from '@/components/RightToolbar'
@ -213,12 +219,16 @@ watch(
<ElSwitch :value="!row.hidden" disabled />
</template>
<template #noCache="{ row }">
<ElSwitch :value="!row.noCache" disabled />
</template>
<template #disabled="{ row }">
<ElSwitch :value="!row.disabled" disabled />
</template>
</Table>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="700px">
<Dialog v-model="dialogVisible" :title="dialogTitle" width="800px">
<Write ref="writeRef" :current-row="tableObject.currentRow" :parent-id="parentId" />
<template #footer>

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'AuthRole'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Table } from '@/components/Table'

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'AuthUser'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Table } from '@/components/Table'

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'HelpIssueForm'
}
</script>
<script setup lang="ts">
import { Form } from '@/components/Form'
import { useForm } from '@/hooks/web/useForm'

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'HelpIssue'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Table } from '@/components/Table'

View File

@ -118,7 +118,7 @@ export const searchSchema = reactive<FormSchema[]>([
},
{
field: 'platform',
label: '展示平台',
label: '登录平台',
component: 'Select',
componentProps: {
style: {

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'HelpIssue'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Table } from '@/components/Table'

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'SystemDictDetail'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Table } from '@/components/Table'

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'SystemDict'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Table } from '@/components/Table'

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'SystemRecordLogin'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Table } from '@/components/Table'

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'SystemRecordOperation'
}
</script>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Table } from '@/components/Table'

View File

@ -1,3 +1,9 @@
<script lang="ts">
export default {
name: 'SystemSettings'
}
</script>
<script setup lang="ts">
import { ElTabs, ElTabPane } from 'element-plus'
import { ref } from 'vue'

View File

@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
"""
系统版本
"""
VERSION = "1.6.1"
VERSION = "1.6.2"
"""安全警告: 不要在生产中打开调试运行!"""
DEBUG = True
@ -21,6 +21,7 @@ DEMO = not DEBUG
"""演示功能白名单"""
DEMO_WHITE_LIST_PATH = [
"/auth/login/",
"/auth/token/refresh/",
"/auth/wx/login/",
"/vadmin/system/dict/types/details/",
"/vadmin/auth/user/export/query/list/to/excel/"
@ -49,8 +50,12 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login/", auto_error=False)
SECRET_KEY = 'vgb0tnl9d58+6n-6h-ea&u^1#s0ccp!794=kbvqacjq75vzps$'
"""用于设定 JWT 令牌签名算法"""
ALGORITHM = "HS256"
"""令牌过期时间9999分钟"""
ACCESS_TOKEN_EXPIRE_MINUTES = 9999
"""access_token 过期时间,一天"""
ACCESS_TOKEN_EXPIRE_MINUTES = 1440
"""refresh_token 过期时间用于刷新token使用两天"""
REFRESH_TOKEN_EXPIRE_MINUTES = 1440 * 2
"""access_token 缓存时间用于刷新token使用30分钟"""
ACCESS_TOKEN_CACHE_MINUTES = 30
"""
挂载临时文件目录并添加路由访问此路由不会在接口文档中显示

View File

@ -30,11 +30,11 @@ async def get_banners(auth: Auth = Depends(AllUserAuth())):
"id": 3, "image": "https://ktianc.oss-cn-beijing.aliyuncs.com/kinit/system/banner/2022-11-09/banner3.png"
},
]
return SuccessResponse(data)
return SuccessResponse(data, refresh=auth.refresh)
@app.get("/user/access/source/", summary="用户来源")
async def get_user_access_source():
async def get_user_access_source(auth: Auth = Depends(AllUserAuth())):
data = [
{"value": 1000, "name": 'analysis.directAccess'},
{"value": 310, "name": 'analysis.mailMarketing'},
@ -42,11 +42,11 @@ async def get_user_access_source():
{"value": 135, "name": 'analysis.videoAdvertising'},
{"value": 1548, "name": 'analysis.searchEngines'}
]
return SuccessResponse(data)
return SuccessResponse(data, refresh=auth.refresh)
@app.get("/weekly/user/activity/", summary="每周用户活跃量")
async def get_weekly_user_activity():
async def get_weekly_user_activity(auth: Auth = Depends(AllUserAuth())):
data = [
{"value": 13253, "name": 'analysis.monday'},
{"value": 34235, "name": 'analysis.tuesday'},
@ -56,11 +56,11 @@ async def get_weekly_user_activity():
{"value": 1322, "name": 'analysis.saturday'},
{"value": 1324, "name": 'analysis.sunday'}
]
return SuccessResponse(data)
return SuccessResponse(data, refresh=auth.refresh)
@app.get("/monthly/sales/", summary="每月销售额")
async def get_monthly_sales():
async def get_monthly_sales(auth: Auth = Depends(AllUserAuth())):
data = [
{"estimate": 100, "actual": 120, "name": 'analysis.january'},
{"estimate": 120, "actual": 82, "name": 'analysis.february'},
@ -75,4 +75,4 @@ async def get_monthly_sales():
{"estimate": 118, "actual": 99, "name": 'analysis.november'},
{"estimate": 123, "actual": 123, "name": 'analysis.december'}
]
return SuccessResponse(data)
return SuccessResponse(data, refresh=auth.refresh)

View File

@ -4,10 +4,9 @@
# @File : current.py
# @IDE : PyCharm
# @desc : 获取认证后的信息工具
from datetime import datetime, timedelta
from typing import List, Optional
from jose import jwt, JWTError
import jwt
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload
from apps.vadmin.auth.crud import UserDal
@ -43,7 +42,7 @@ class OpenAuth(AuthValidation):
"""
@classmethod
def validate_token(cls, token: str | None, db: AsyncSession) -> str | None:
def validate_token(cls, request: Request, token: str | None) -> str | None:
"""
验证用户 token没有则返回 None
"""
@ -52,9 +51,19 @@ class OpenAuth(AuthValidation):
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
telephone: str = payload.get("sub")
if telephone is None:
exp: int = payload.get("exp")
is_refresh: bool = payload.get("is_refresh")
if telephone is None or is_refresh:
return None
except JWTError:
# 计算当前时间 + 缓冲时间是否大于等于 JWT 过期时间
buffer_time = (datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_CACHE_MINUTES)).timestamp()
if buffer_time >= exp:
request.scope["refresh"] = True
else:
request.scope["refresh"] = False
except jwt.exceptions.InvalidSignatureError:
return None
except jwt.exceptions.ExpiredSignatureError:
return None
return telephone
@ -83,7 +92,7 @@ class OpenAuth(AuthValidation):
"""
每次调用依赖此类的接口会执行该方法
"""
telephone = self.validate_token(token, db)
telephone = self.validate_token(request, token)
if telephone:
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
return await self.validate_user(request, user, db)
@ -106,7 +115,9 @@ class AllUserAuth(AuthValidation):
"""
每次调用依赖此类的接口会执行该方法
"""
telephone = self.validate_token(token, db)
if not settings.OAUTH_ENABLE:
return Auth(db=db)
telephone = self.validate_token(request, token)
if isinstance(telephone, Auth):
return telephone
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
@ -136,7 +147,9 @@ class FullAdminAuth(AuthValidation):
"""
每次调用依赖此类的接口会执行该方法
"""
telephone = self.validate_token(token, db)
if not settings.OAUTH_ENABLE:
return Auth(db=db)
telephone = self.validate_token(request, token)
if isinstance(telephone, Auth):
return telephone
options = [joinedload(VadminUser.roles), joinedload("roles.menus")]

View File

@ -20,21 +20,20 @@ PassLib 是一个用于处理哈希密码的很棒的 Python 包。它支持许
"""
from datetime import timedelta
from fastapi import APIRouter, Depends, Request
import jwt
from fastapi import APIRouter, Depends, Request, Body
from sqlalchemy.ext.asyncio import AsyncSession
from core.database import db_getter
from utils import status
from utils.response import SuccessResponse, ErrorResponse
from application import settings
from utils.tools import generate_string
from .login_manage import LoginManage
from .validation import LoginForm, WXLoginForm
from apps.vadmin.record.models import VadminLoginRecord
from apps.vadmin.auth.crud import MenuDal, UserDal
from apps.vadmin.auth.schemas import UserIn
from .current import FullAdminAuth
from .validation.auth import Auth
from utils.wx.oauth import WXOAuth
from core.data_types import Telephone
app = APIRouter()
@ -57,10 +56,12 @@ async def login_for_access_token(
if not result.status:
raise ValueError(result.msg)
token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
token = LoginManage.create_access_token(data={"sub": result.user.telephone}, expires_delta=token_expires)
access_token = LoginManage.create_token({"sub": result.user.telephone, "is_refresh": False})
expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
refresh_token = LoginManage.create_token({"sub": result.user.telephone, "is_refresh": True}, expires=expires)
resp = {
"access_token": token,
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer",
"is_reset_password": result.user.is_reset_password,
"is_wx_server_openid": result.user.is_wx_server_openid
@ -95,10 +96,12 @@ async def wx_login_for_access_token(request: Request, data: WXLoginForm, db: Asy
await user.update_login_info(db, request.client.host)
# 登录成功创建 token
token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
token = LoginManage.create_access_token(data={"sub": user.telephone}, expires_delta=token_expires)
access_token = LoginManage.create_token({"sub": user.telephone, "is_refresh": False})
expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
refresh_token = LoginManage.create_token({"sub": user.telephone, "is_refresh": True}, expires=expires)
resp = {
"access_token": token,
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer",
"is_reset_password": user.is_reset_password,
"is_wx_server_openid": user.is_wx_server_openid
@ -110,3 +113,28 @@ async def wx_login_for_access_token(request: Request, data: WXLoginForm, db: Asy
@app.get("/getMenuList/", summary="获取当前用户菜单树")
async def get_menu_list(auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(await MenuDal(auth.db).get_routers(auth.user))
@app.post("/token/refresh/", summary="刷新Token")
async def token_refresh(refresh: str = Body(..., title="刷新Token")):
error_code = status.HTTP_401_UNAUTHORIZED
try:
payload = jwt.decode(refresh, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
telephone: str = payload.get("sub")
is_refresh: bool = payload.get("is_refresh")
if telephone is None or not is_refresh:
return ErrorResponse("未认证,请您重新登录", code=error_code, status=error_code)
except jwt.exceptions.InvalidSignatureError:
return ErrorResponse("无效认证,请您重新登录", code=error_code, status=error_code)
except jwt.exceptions.ExpiredSignatureError:
return ErrorResponse("登录已超时,请您重新登录", code=error_code, status=error_code)
access_token = LoginManage.create_token({"sub": telephone, "is_refresh": False})
expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
refresh_token = LoginManage.create_token({"sub": telephone, "is_refresh": True}, expires=expires)
resp = {
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer"
}
return SuccessResponse(resp)

View File

@ -7,10 +7,9 @@
# @desc : 简要说明
from datetime import datetime, timedelta
from typing import Optional
from fastapi import Request
from application import settings
from jose import jwt
import jwt
from apps.vadmin.auth import models
from .validation import LoginValidation, LoginForm, LoginResult
from utils.aliyun_sms import AliyunSMS
@ -44,15 +43,19 @@ class LoginManage:
return LoginResult(status=False, msg="验证码错误")
@staticmethod
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
def create_token(payload: dict, expires: timedelta = None):
"""
创建一个生成新的访问令牌的工具函数
pyjwthttps://github.com/jpadilla/pyjwt/blob/master/docs/usage.rst
jwt 博客https://geek-docs.com/python/python-tutorial/j_python-jwt.html
#TODO 传入的时间为UTC时间datetime.datetime类型但是在解码时获取到的是本机时间的时间戳
"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
if expires:
expire = datetime.utcnow() + expires
else:
expire = datetime.utcnow() + timedelta(minutes=60)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
payload.update({"exp": expire})
encoded_jwt = jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt

View File

@ -4,9 +4,9 @@
# @File : auth.py
# @IDE : PyCharm
# @desc : 用户凭证验证装饰器
from datetime import datetime, timedelta
from fastapi import Request
from jose import jwt, JWTError
import jwt
from pydantic import BaseModel
from application import settings
from sqlalchemy.ext.asyncio import AsyncSession
@ -17,6 +17,7 @@ from utils import status
class Auth(BaseModel):
user: models.VadminUser = None
refresh: bool = False
db: AsyncSession
class Config:
@ -29,22 +30,35 @@ class AuthValidation:
用于用户每次调用接口时验证用户提交的token是否正确并从token中获取用户信息
"""
error_code = status.HTTP_401_UNAUTHORIZED
@classmethod
def validate_token(cls, token: str, db: AsyncSession) -> str | Auth:
def validate_token(cls, request: Request, token: str) -> str:
"""
验证用户 token
"""
if not settings.OAUTH_ENABLE:
return Auth(db=db)
if not token:
raise CustomException(msg="请您先登录!", code=status.HTTP_ERROR)
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
telephone: str = payload.get("sub")
if telephone is None:
raise CustomException(msg="认证已过期,请您重新登陆", code=status.HTTP_401_UNAUTHORIZED)
except JWTError:
raise CustomException(msg="认证已过期,请您重新登陆", code=status.HTTP_401_UNAUTHORIZED)
exp: int = payload.get("exp")
is_refresh: bool = payload.get("is_refresh")
if telephone is None or is_refresh:
raise CustomException(msg="未认证,请您重新登录", code=cls.error_code)
# 计算当前时间 + 缓冲时间是否大于等于 JWT 过期时间
buffer_time = (datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_CACHE_MINUTES)).timestamp()
# print("过期时间", exp, datetime.fromtimestamp(exp))
# print("当前时间", buffer_time, datetime.fromtimestamp(buffer_time))
# print("剩余时间", exp - buffer_time)
if buffer_time >= exp:
request.scope["refresh"] = True
else:
request.scope["refresh"] = False
except jwt.exceptions.InvalidSignatureError:
raise CustomException(msg="无效认证,请您重新登录", code=cls.error_code)
except jwt.exceptions.ExpiredSignatureError:
raise CustomException(msg="认证已过期,请您重新登录", code=cls.error_code)
return telephone
@classmethod
@ -53,12 +67,13 @@ class AuthValidation:
验证用户信息
"""
if user is None:
raise CustomException(msg="认证已过期,请您重新登陆", code=status.HTTP_401_UNAUTHORIZED)
raise CustomException(msg="认证,请您重新登陆", code=cls.error_code, status_code=cls.error_code)
elif not user.is_active:
raise CustomException(msg="用户已被冻结!", code=status.HTTP_403_FORBIDDEN)
raise CustomException(msg="用户已被冻结!", code=cls.error_code, status_code=cls.error_code)
request.scope["telephone"] = user.telephone
try:
request.scope["body"] = await request.body()
except RuntimeError:
request.scope["body"] = "获取失败"
return Auth(user=user, db=db)
refresh = request.scope.get("refresh", False)
return Auth(user=user, db=db, refresh=refresh)

View File

@ -31,12 +31,12 @@ async def get_users(
schema = schemas.UserOut
datas = await crud.UserDal(auth.db).get_datas(**params.dict(), v_options=options, v_schema=schema)
count = await crud.UserDal(auth.db).get_count(**params.to_count())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
@app.post("/users/", summary="创建用户")
async def create_user(data: schemas.UserIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create"]))):
return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data))
return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data), refresh=auth.refresh)
@app.delete("/users/", summary="批量删除用户", description="软删除,删除后清空所关联的角色")
@ -46,7 +46,7 @@ async def delete_users(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu
elif 1 in ids.ids:
return ErrorResponse("不能删除超级管理员用户")
await crud.UserDal(auth.db).delete_datas(ids=ids.ids, v_soft=True, is_active=False)
return SuccessResponse("删除成功")
return SuccessResponse("删除成功", refresh=auth.refresh)
@app.put("/users/{data_id}/", summary="更新用户信息")
@ -55,7 +55,7 @@ async def put_user(
data: schemas.UserUpdate,
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.update"]))
):
return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data))
return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
@app.get("/users/{data_id}/", summary="获取用户信息")
@ -66,29 +66,32 @@ async def get_user(
model = models.VadminUser
options = [joinedload(model.roles)]
schema = schemas.UserOut
return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema))
return SuccessResponse(
await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema),
refresh=auth.refresh
)
@app.post("/user/current/reset/password/", summary="重置当前用户密码")
async def user_current_reset_password(data: schemas.ResetPwd, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data))
return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data), refresh=auth.refresh)
@app.post("/user/current/update/info/", summary="更新当前用户基本信息")
async def post_user_current_update_info(data: schemas.UserUpdateBaseInfo, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data))
return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data), refresh=auth.refresh)
@app.post("/user/current/update/avatar/", summary="更新当前用户头像")
async def post_user_current_update_avatar(file: UploadFile, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file))
return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file), refresh=auth.refresh)
@app.get("/user/admin/current/info/", summary="获取当前管理员信息")
async def get_user_admin_current_info(auth: Auth = Depends(FullAdminAuth())):
result = schemas.UserOut.from_orm(auth.user).dict()
result["permissions"] = list(get_user_permissions(auth.user))
return SuccessResponse(result)
return SuccessResponse(result, refresh=auth.refresh)
@app.post("/user/export/query/list/to/excel/", summary="导出用户查询列表为excel")
@ -97,17 +100,17 @@ async def post_user_export_query_list(
params: UserParams = Depends(),
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.export"]))
):
return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params))
return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params), refresh=auth.refresh)
@app.get("/user/download/import/template/", summary="下载最新批量导入用户模板")
async def get_user_download_new_import_template(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).download_import_template())
return SuccessResponse(await crud.UserDal(auth.db).download_import_template(), refresh=auth.refresh)
@app.post("/import/users/", summary="批量导入用户")
async def post_import_users(file: UploadFile, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.import"]))):
return SuccessResponse(await crud.UserDal(auth.db).import_users(file))
return SuccessResponse(await crud.UserDal(auth.db).import_users(file), refresh=auth.refresh)
@app.post("/users/init/password/send/sms/", summary="初始化所选用户密码并发送通知短信")
@ -116,13 +119,16 @@ async def post_users_init_password(
ids: IdList = Depends(),
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.reset"]))
):
return SuccessResponse(await crud.UserDal(auth.db).init_password_send_sms(ids.ids, request.app.state.redis))
return SuccessResponse(
await crud.UserDal(auth.db).init_password_send_sms(ids.ids, request.app.state.redis),
refresh=auth.refresh
)
@app.put("/users/wx/server/openid/", summary="更新当前用户服务端微信平台openid")
async def put_user_wx_server_openid(request: Request, code: str, auth: Auth = Depends(AllUserAuth())):
result = await crud.UserDal(auth.db).update_wx_server_openid(code, auth.user, request.app.state.redis)
return SuccessResponse(result)
return SuccessResponse(result, refresh=auth.refresh)
###########################################################
@ -135,12 +141,12 @@ async def get_roles(
):
datas = await crud.RoleDal(auth.db).get_datas(**params.dict())
count = await crud.RoleDal(auth.db).get_count(**params.to_count())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
@app.post("/roles/", summary="创建角色信息")
async def create_role(role: schemas.RoleIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create"]))):
return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role))
return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role), refresh=auth.refresh)
@app.delete("/roles/", summary="批量删除角色", description="硬删除, 如果存在用户关联则无法删除")
@ -148,7 +154,7 @@ async def delete_roles(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu
if 1 in ids.ids:
return ErrorResponse("不能删除管理员角色")
await crud.RoleDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功")
return SuccessResponse("删除成功", refresh=auth.refresh)
@app.put("/roles/{data_id}/", summary="更新角色信息")
@ -159,12 +165,12 @@ async def put_role(
):
if 1 == data_id:
return ErrorResponse("不能修改管理员角色")
return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data))
return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
@app.get("/roles/options/", summary="获取角色选择项")
async def get_role_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create", "auth.user.update"]))):
return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas())
return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas(), refresh=auth.refresh)
@app.get("/roles/{data_id}/", summary="获取角色信息")
@ -175,7 +181,10 @@ async def get_role(
model = models.VadminRole
options = [joinedload(model.menus)]
schema = schemas.RoleOut
return SuccessResponse(await crud.RoleDal(auth.db).get_data(data_id, options, v_schema=schema))
return SuccessResponse(
await crud.RoleDal(auth.db).get_data(data_id, options, v_schema=schema),
refresh=auth.refresh
)
###########################################################
@ -184,33 +193,33 @@ async def get_role(
@app.get("/menus/", summary="获取菜单列表")
async def get_menus(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.list"]))):
datas = await crud.MenuDal(auth.db).get_tree_list(mode=1)
return SuccessResponse(datas)
return SuccessResponse(datas, refresh=auth.refresh)
@app.get("/menus/tree/options/", summary="获取菜单树选择项,添加/修改菜单时使用")
async def get_menus_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create", "auth.menu.update"]))):
datas = await crud.MenuDal(auth.db).get_tree_list(mode=2)
return SuccessResponse(datas)
return SuccessResponse(datas, refresh=auth.refresh)
@app.get("/menus/role/tree/options/", summary="获取菜单列表树信息,角色权限使用")
async def get_menus_treeselect(
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))
):
return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3))
return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3), refresh=auth.refresh)
@app.post("/menus/", summary="创建菜单信息")
async def create_menu(menu: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create"]))):
if menu.parent_id:
menu.alwaysShow = False
return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu))
return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu), refresh=auth.refresh)
@app.delete("/menus/", summary="批量删除菜单", description="硬删除, 如果存在角色关联则无法删除")
async def delete_menus(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.delete"]))):
await crud.MenuDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功")
return SuccessResponse("删除成功", refresh=auth.refresh)
@app.put("/menus/{data_id}/", summary="更新菜单信息")
@ -218,7 +227,7 @@ async def put_menus(
data_id: int,
data: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.update"]))
):
return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data))
return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
@app.get("/menus/{data_id}/", summary="获取菜单信息")
@ -227,7 +236,7 @@ async def put_menus(
auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.view", "auth.menu.update"]))
):
schema = schemas.MenuSimpleOut
return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema))
return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema), refresh=auth.refresh)
@app.get("/role/menus/tree/{role_id}/", summary="获取菜单列表树信息以及角色菜单权限ID角色权限使用")
@ -237,4 +246,4 @@ async def get_role_menu_tree(
):
treeselect = await crud.MenuDal(auth.db).get_tree_list(mode=3)
role_menu_tree = await crud.RoleDal(auth.db).get_role_menu_tree(role_id)
return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": treeselect})
return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": treeselect}, refresh=auth.refresh)

View File

@ -29,36 +29,42 @@ async def get_issue_categorys(p: params.IssueCategoryParams = Depends(), auth: A
schema = schemas.IssueCategoryListOut
datas = await crud.IssueCategoryDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema)
count = await crud.IssueCategoryDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
@app.get("/issue/categorys/options/", summary="获取类别选择项")
async def get_issue_categorys_options(auth: Auth = Depends(AllUserAuth())):
schema = schemas.IssueCategoryOptionsOut
return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema))
return SuccessResponse(
await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema),
refresh=auth.refresh
)
@app.post("/issue/categorys/", summary="创建类别")
async def create_issue_category(data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())):
data.user_id = auth.user.id
return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data))
return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data), refresh=auth.refresh)
@app.delete("/issue/categorys/", summary="批量删除类别", description="硬删除")
async def delete_issue_categorys(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.IssueCategoryDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
return SuccessResponse("删除成功")
return SuccessResponse("删除成功", refresh=auth.refresh)
@app.put("/issue/categorys/{data_id}/", summary="更新类别信息")
async def put_issue_category(data_id: int, data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data))
return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
@app.get("/issue/categorys/{data_id}/", summary="获取类别信息")
async def get_issue_category(data_id: int, auth: Auth = Depends(AllUserAuth())):
schema = schemas.IssueCategorySimpleOut
return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema))
return SuccessResponse(
await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema),
refresh=auth.refresh
)
@app.get("/issue/categorys/platform/{platform}/", summary="获取平台中的常见问题类别列表")
@ -81,24 +87,24 @@ async def get_issues(p: params.IssueParams = Depends(), auth: Auth = Depends(All
schema = schemas.IssueListOut
datas = await crud.IssueDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema)
count = await crud.IssueDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
@app.post("/issues/", summary="创建问题")
async def create_issue(data: schemas.Issue, auth: Auth = Depends(AllUserAuth())):
data.user_id = auth.user.id
return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data))
return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data), refresh=auth.refresh)
@app.delete("/issues/", summary="批量删除问题", description="硬删除")
async def delete_issues(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.IssueDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
return SuccessResponse("删除成功")
return SuccessResponse("删除成功", refresh=auth.refresh)
@app.put("/issues/{data_id}/", summary="更新问题信息")
async def put_issue(data_id: int, data: schemas.Issue, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data))
return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
@app.get("/issues/{data_id}/", summary="获取问题信息")

View File

@ -23,7 +23,7 @@ app = APIRouter()
async def get_record_login(p: LoginParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.LoginRecordDal(auth.db).get_datas(**p.dict())
count = await crud.LoginRecordDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
@app.get("/operations/", summary="获取操作日志列表")
@ -31,14 +31,14 @@ async def get_record_operation(p: OperationParams = Depends(), db: DatabaseManag
auth: Auth = Depends(AllUserAuth())):
count = await db.get_count("operation_record", **p.to_count())
datas = await db.get_datas("operation_record", v_schema=schemas.OpertionRecordSimpleOut, **p.dict())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
@app.get("/sms/send/list/", summary="获取短信发送列表")
async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.SMSSendRecordDal(auth.db).get_datas(**p.dict())
count = await crud.SMSSendRecordDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
###########################################################
@ -46,4 +46,4 @@ async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUs
###########################################################
@app.get("/analysis/user/login/distribute/", summary="获取用户登录分布情况列表")
async def get_user_login_distribute(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.LoginRecordDal(auth.db).get_user_distribute())
return SuccessResponse(await crud.LoginRecordDal(auth.db).get_user_distribute(), refresh=auth.refresh)

View File

@ -31,18 +31,18 @@ app = APIRouter()
async def get_dict_types(p: DictTypeParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.DictTypeDal(auth.db).get_datas(**p.dict())
count = await crud.DictTypeDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
@app.post("/dict/types/", summary="创建字典类型")
async def create_dict_types(data: schemas.DictType, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).create_data(data=data))
return SuccessResponse(await crud.DictTypeDal(auth.db).create_data(data=data), refresh=auth.refresh)
@app.delete("/dict/types/", summary="批量删除字典类型")
async def delete_dict_types(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.DictTypeDal(auth.db).delete_datas(ids=ids.ids)
return SuccessResponse("删除成功")
return SuccessResponse("删除成功", refresh=auth.refresh)
@app.post("/dict/types/details/", summary="获取多个字典类型下的字典元素列表")
@ -51,23 +51,26 @@ async def post_dicts_details(
dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表")
):
datas = await crud.DictTypeDal(auth.db).get_dicts_details(dict_types)
return SuccessResponse(datas)
return SuccessResponse(datas, refresh=auth.refresh)
@app.get("/dict/types/options/", summary="获取字典类型选择项")
async def get_dicts_options(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas())
return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas(), refresh=auth.refresh)
@app.put("/dict/types/{data_id}/", summary="更新字典类型")
async def put_dict_types(data_id: int, data: schemas.DictType, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).put_data(data_id, data))
return SuccessResponse(await crud.DictTypeDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
@app.get("/dict/types/{data_id}/", summary="获取字典类型详细")
async def get_dict_type(data_id: int, auth: Auth = Depends(AllUserAuth())):
schema = schemas.DictTypeSimpleOut
return SuccessResponse(await crud.DictTypeDal(auth.db).get_data(data_id, None, v_schema=schema))
return SuccessResponse(
await crud.DictTypeDal(auth.db).get_data(data_id, None, v_schema=schema),
refresh=auth.refresh
)
###########################################################
@ -75,7 +78,7 @@ async def get_dict_type(data_id: int, auth: Auth = Depends(AllUserAuth())):
###########################################################
@app.post("/dict/details/", summary="创建字典元素")
async def create_dict_details(data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictDetailsDal(auth.db).create_data(data=data))
return SuccessResponse(await crud.DictDetailsDal(auth.db).create_data(data=data), refresh=auth.refresh)
@app.get("/dict/details/", summary="获取单个字典类型下的字典元素列表,分页")
@ -84,24 +87,27 @@ async def get_dict_details(params: DictDetailParams = Depends(), auth: Auth = De
return ErrorResponse(msg="未获取到字典类型!")
datas = await crud.DictDetailsDal(auth.db).get_datas(**params.dict())
count = await crud.DictDetailsDal(auth.db).get_count(**params.to_count())
return SuccessResponse(datas, count=count)
return SuccessResponse(datas, count=count, refresh=auth.refresh)
@app.delete("/dict/details/", summary="批量删除字典元素", description="硬删除")
async def delete_dict_details(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.DictDetailsDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功")
return SuccessResponse("删除成功", refresh=auth.refresh)
@app.put("/dict/details/{data_id}/", summary="更新字典元素")
async def put_dict_details(data_id: int, data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictDetailsDal(auth.db).put_data(data_id, data))
return SuccessResponse(await crud.DictDetailsDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
@app.get("/dict/details/{data_id}/", summary="获取字典元素详情")
async def get_dict_detail(data_id: int, auth: Auth = Depends(AllUserAuth())):
schema = schemas.DictDetailsSimpleOut
return SuccessResponse(await crud.DictDetailsDal(auth.db).get_data(data_id, None, v_schema=schema))
return SuccessResponse(
await crud.DictDetailsDal(auth.db).get_data(data_id, None, v_schema=schema),
refresh=auth.refresh
)
###########################################################
@ -136,17 +142,23 @@ async def sms_send(request: Request, telephone: str):
###########################################################
@app.get("/settings/tabs/", summary="获取系统配置标签列表")
async def get_settings_tabs(classify: str, auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=classify))
return SuccessResponse(
await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=classify),
refresh=auth.refresh
)
@app.get("/settings/tabs/values/", summary="获取系统配置标签下的信息")
async def get_settings_tabs_values(tab_id: int, auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id))
return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id), refresh=auth.refresh)
@app.put("/settings/tabs/values/", summary="更新系统配置信息")
async def put_settings_tabs_values(request: Request, datas: dict = Body(...), auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(await crud.SettingsDal(auth.db).update_datas(datas, request.app.state.redis))
return SuccessResponse(
await crud.SettingsDal(auth.db).update_datas(datas, request.app.state.redis),
refresh=auth.refresh
)
@app.get("/settings/base/config/", summary="获取系统基础配置", description="每次进入系统中时使用")
@ -156,9 +168,15 @@ async def get_setting_base_config(db: AsyncSession = Depends(db_getter)):
@app.get("/settings/privacy/", summary="获取隐私协议")
async def get_settings_privacy(auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value)
return SuccessResponse(
(await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value,
refresh=auth.refresh
)
@app.get("/settings/agreement/", summary="获取用户协议")
async def get_settings_agreement(auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value)
return SuccessResponse(
(await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value,
refresh=auth.refresh
)

View File

@ -21,6 +21,30 @@ def register_redis(app: FastAPI) -> None:
博客https://blog.csdn.net/wgPython/article/details/107668521
博客https://www.cnblogs.com/emunshe/p/15761597.html
官网https://aioredis.readthedocs.io/en/latest/getting-started/
Github: https://github.com/aio-libs/aioredis-py
aioredis.from_url(url, *, encoding=None, parser=None, decode_responses=False, db=None, password=None, ssl=None,
connection_cls=None, loop=None, **kwargs) 方法是 aioredis 库中用于从 Redis 连接 URL 创建 Redis 连接对象的方法
以下是该方法的参数说明
urlRedis 连接 URL例如 redis://localhost:6379/0
encoding可选参数Redis 编码格式默认为 utf-8
parser可选参数Redis 数据解析器默认为 None表示使用默认解析器
decode_responses可选参数是否将 Redis 响应解码为 Python 字符串默认为 False
db可选参数Redis 数据库编号默认为 None
password可选参数Redis 认证密码默认为 None表示无需认证
ssl可选参数是否使用 SSL/TLS 加密连接默认为 None
connection_cls可选参数Redis 连接类默认为 None表示使用默认连接类
loop可选参数用于创建连接对象的事件循环默认为 None表示使用默认事件循环
**kwargs可选参数其他连接参数用于传递给 Redis 连接类的构造函数
aioredis.from_url() 方法的主要作用是将 Redis 连接 URL 转换为 Redis 连接对象
除了 URL 参数外其他参数用于指定 Redis 连接的各种选项例如 Redis 数据库编号密码SSL/TLS 加密等等可以根据需要选择使用这些选项
health_check_interval aioredis.from_url() 方法中的一个可选参数用于设置 Redis 连接的健康检查间隔时间
健康检查是指在 Redis 连接池中使用的连接对象会定期向 Redis 服务器发送 PING 命令来检查连接是否仍然有效
该参数的默认值是 0表示不进行健康检查如果需要启用健康检查则可以将该参数设置为一个正整数表示检查间隔的秒数
例如如果需要每隔 5 秒对 Redis 连接进行一次健康检查则可以将 health_check_interval 设置为 5
:param app:
:return:
"""

View File

@ -16,9 +16,10 @@ from core.logger import logger
class CustomException(Exception):
def __init__(self, msg: str, code: int):
def __init__(self, msg: str, code: int, status_code: int = status.HTTP_200_OK):
self.msg = msg
self.code = code
self.status_code = status_code
def register_exception(app: FastAPI):

Binary file not shown.

37
kinit-uni/.eslintrc.js Normal file
View File

@ -0,0 +1,37 @@
module.exports = {
env: {
browser: true,
es6: true,
node: true
},
extends: [
'eslint:recommended',
'plugin:vue/recommended',
'prettier',
'plugin:prettier/recommended'
],
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module'
},
globals: {
uni: true,
wx: true,
ROUTES: true
},
plugins: ['vue', 'prettier'],
rules: {
'vue/multi-word-component-names': 'off',
'prettier/prettier': 'error',
'vue/html-indent': ['error', 2],
'vue/max-attributes-per-line': 'off',
'no-console': 'off',
'no-unused-vars': ['warn', { args: 'none' }],
'arrow-parens': ['error', 'always'],
'comma-dangle': ['error', 'never'],
quotes: ['error', 'single'],
semi: ['error', 'never'],
'object-curly-spacing': ['error', 'always'],
indent: ['error', 2, { SwitchCase: 1 }]
}
}

View File

@ -1,21 +1,18 @@
<script>
import config from './config'
import { getToken } from '@/common/utils/auth'
export default {
onLaunch: function() {
this.initApp()
},
methods: {
//
initApp() {
//
this.$store.dispatch('app/InitConfig')
}
export default {
onLaunch: function () {
this.initApp()
},
methods: {
//
initApp() {
//
this.$store.dispatch('app/InitConfig')
}
}
}
</script>
<style lang="scss">
@import '@/static/scss/index.scss';
@import '@/static/scss/index.scss';
</style>

View File

@ -47,3 +47,23 @@ RuoYi App 移动解决方案采用uniapp框架一份代码多终端适配
- 文档地址https://uviewui.com
uView UI是[uni-app](https://uniapp.dcloud.io/)全面兼容nvue的uni-app生态框架全面的组件和便捷的工具会让您信手拈来如鱼得水
## 开发工具
在此项目中我将开发`uni-app`的开发工具从 Hbuilder X 换到了 VSCode没有谁好谁坏只是本人更习惯使用 VSCode但是在运行项目时依然使用的是 Hbuilder XVSCode只是用来编写代码。当然使用 Hbuilder X 也是支持的,只做一个分享。
以下是我在VSCode中安装的几个插件
1. 名称: Chinese (Simplified) (简体中文) Language Pack for Visual Studio Code
2. 名称: ESLint
3. 名称: Image preview
4. 名称: Markdown Preview Enhanced
5. 名称: Path Intellisense
6. 名称: Prettier - Code formatter
7. 名称: Sass (.sass only)
8. 名称: SCSS IntelliSense
9. 名称: Stylelint
10. 名称: uni-app-schemas
11. 名称: uni-app-snippets
12. 名称: uni-create-view
13. 名称: Vetur

View File

@ -8,58 +8,57 @@ import { setUserOpenid } from '@/common/request/api/login.js'
import { toast } from '@/common/utils/common'
export const wxLoginMixins = {
computed: {
...mapGetters([
'isUserOpenid',
])
},
data () {
return {
}
},
methods: {
onGetPhoneNumber(e) {
return new Promise((resolve, reject) => {
// 获取手机号官方文档https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
if (e.detail.errMsg === "getPhoneNumber:fail user deny") {
// 用户拒绝授权
toast("已取消授权")
reject("已取消授权")
} else if (e.detail.errMsg === "getPhoneNumber:fail no permission") {
// 微信公众平台未认证或未使用企业认证
toast("微信公众平台未认证或未使用企业认证")
reject("微信公众平台未认证或未使用企业认证")
} else if (e.detail.errMsg === "getPhoneNumber:ok") {
// code换取用户手机号 每个code只能使用一次code的有效期为5min
this.$store.dispatch('auth/wxLogin', e.detail.code).then(res => {
this.setOpenid();
this.$store.dispatch('auth/GetInfo').then(result => {
resolve(result)
})
})
} else {
toast("授权失败")
reject("授权失败")
}
})
},
setOpenid() {
let self = this;
// uniapp 官方文档https://uniapp.dcloud.io/api/plugins/login.html#login
if (self.isUserOpenid) { return; };
uni.login({
provider: 'weixin',
success: function (loginRes) {
if (loginRes.code) {
setUserOpenid(loginRes.code).then(res => {
// console.log("更新openid成功", res)
self.$store.commit("auth/SET_IS_USER_OPENID", true);
})
} else {
console.log('登录失败获取code失败' + res.errMsg)
}
}
});
}
}
}
computed: {
...mapGetters(['isUserOpenid'])
},
data() {
return {}
},
methods: {
onGetPhoneNumber(e) {
return new Promise((resolve, reject) => {
// 获取手机号官方文档https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
if (e.detail.errMsg === 'getPhoneNumber:fail user deny') {
// 用户拒绝授权
toast('已取消授权')
reject('已取消授权')
} else if (e.detail.errMsg === 'getPhoneNumber:fail no permission') {
// 微信公众平台未认证或未使用企业认证
toast('微信公众平台未认证或未使用企业认证')
reject('微信公众平台未认证或未使用企业认证')
} else if (e.detail.errMsg === 'getPhoneNumber:ok') {
// code换取用户手机号 每个code只能使用一次code的有效期为5min
this.$store.dispatch('auth/wxLogin', e.detail.code).then((res) => {
this.setOpenid()
this.$store.dispatch('auth/GetInfo').then((result) => {
resolve(result)
})
})
} else {
toast('授权失败')
reject('授权失败')
}
})
},
setOpenid() {
let self = this
// uniapp 官方文档https://uniapp.dcloud.io/api/plugins/login.html#login
if (self.isUserOpenid) {
return
}
uni.login({
provider: 'weixin',
success: function (loginRes) {
if (loginRes.code) {
setUserOpenid(loginRes.code).then(() => {
// console.log("更新openid成功", res)
self.$store.commit('auth/SET_IS_USER_OPENID', true)
})
} else {
console.log('登录失败获取code失败' + loginRes.errMsg)
}
}
})
}
}
}

View File

@ -1,46 +1,46 @@
export const wxShareMixins = {
data() {
return {
share: {
title: "",
path: "",
imageUrl: ""
}
}
},
onLoad: function() {
wx.showShareMenu({
withShareTicket: true,
menus: ["shareAppMessage", "shareTimeline"]
})
},
onShareAppMessage(res) {
let that = this;
let imageUrl = that.share.imageUrl || '';
if (res.from === 'button') {
//这块需要传参不然链接地址进去获取不到数据
let path = `/` + that.$scope.route + `?item=` + that.$scope.options.item;
return {
title: '商品分享~',
path: path,
imageUrl: imageUrl
};
}
if (res.from === 'menu') {
return {
title: that.share.title,
path: that.share.path,
imageUrl: that.share.imageUrl
};
}
},
// 分享到朋友圈
onShareTimeline() {
return {
title: this.share.title,
path: this.share.path,
imageUrl: this.share.imageUrl
};
},
methods: {}
}
data() {
return {
share: {
title: '',
path: '',
imageUrl: ''
}
}
},
onLoad: function () {
wx.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline']
})
},
onShareAppMessage(res) {
let that = this
let imageUrl = that.share.imageUrl || ''
if (res.from === 'button') {
//这块需要传参不然链接地址进去获取不到数据
let path = '/' + that.$scope.route + '?item=' + that.$scope.options.item
return {
title: '商品分享~',
path: path,
imageUrl: imageUrl
}
}
if (res.from === 'menu') {
return {
title: that.share.title,
path: that.share.path,
imageUrl: that.share.imageUrl
}
}
},
// 分享到朋友圈
onShareTimeline() {
return {
title: this.share.title,
path: this.share.path,
imageUrl: this.share.imageUrl
}
},
methods: {}
}

View File

@ -5,29 +5,29 @@ export function login(telephone, password) {
const data = {
telephone,
password,
method: '0',
platform: '1'
method: '0',
platform: '1'
}
return request.post(`/auth/login/`, data)
return request.post('/auth/login/', data)
}
// 获取用户详细信息
export function getInfo() {
return request.get(`/vadmin/auth/user/admin/current/info/`)
return request.get('/vadmin/auth/user/admin/current/info/')
}
// 更新用户openid
export function setUserOpenid(code) {
const params = {code}
return request.put(`/vadmin/auth/users/wx/server/openid/`, {}, {params: params})
const params = { code }
return request.put('/vadmin/auth/users/wx/server/openid/', {}, { params: params })
}
// 使用微信一键登录
export function wxCodeLogin(code) {
const data = {
code,
method: '2',
platform: '1'
}
return request.post(`/auth/wx/login/`, data)
const data = {
code,
method: '2',
platform: '1'
}
return request.post('/auth/wx/login/', data)
}

View File

@ -2,15 +2,18 @@ import request from '@/common/request/request'
// 更新当前用户基本信息
export function updateCurrentUser(data) {
return request.post(`/vadmin/auth/user/current/update/info/`, data)
return request.post('/vadmin/auth/user/current/update/info/', data)
}
// 重置当前用户密码
export function postCurrentUserResetPassword(data) {
return request.post(`/vadmin/auth/user/current/reset/password/`, data)
return request.post('/vadmin/auth/user/current/reset/password/', data)
}
// 更新当前用户头像
export function postCurrentUserUploadAvatar(filePath) {
return request.upload(`/vadmin/auth/user/current/update/avatar/`, {filePath: filePath, name: 'file'})
}
return request.upload('/vadmin/auth/user/current/update/avatar/', {
filePath: filePath,
name: 'file'
})
}

View File

@ -2,15 +2,15 @@ import request from '@/common/request/request.js'
// 获取平台中的常见问题类别列表
export function getIssueCategoryList() {
return request.get(`/vadmin/help/issue/categorys/platform/1/`)
return request.get('/vadmin/help/issue/categorys/platform/1/')
}
// 获取问题详情
export function getIssue(dataId) {
return request.get(`/vadmin/help/issues/${dataId}/`)
return request.get(`/vadmin/help/issues/${dataId}/`)
}
// 更新常见问题查看次数+1
export function updateIssueAddViewNumber(dataId) {
return request.get(`/vadmin/help/issues/add/view/number/${dataId}/`)
}
return request.get(`/vadmin/help/issues/add/view/number/${dataId}/`)
}

View File

@ -2,5 +2,5 @@ import request from '@/common/request/request.js'
// 获取多个字典类型下的字典元素列表
export function getDictTypeDetailsApi(data) {
return request.post(`/vadmin/system/dict/types/details/`, data)
return request.post('/vadmin/system/dict/types/details/', data)
}

View File

@ -2,5 +2,5 @@ import request from '@/common/request/request'
// 获取系统基本配置
export function getSystemBaseConfigApi() {
return request.get(`/vadmin/system/settings/base/config/`)
}
return request.get('/vadmin/system/settings/base/config/')
}

View File

@ -1,6 +1,6 @@
export default {
"401": "认证失败,无法访问系统资源",
"403": "当前操作没有权限",
"404": "访问资源不存在",
"default": "系统未知错误,请反馈给管理员"
};
401: '认证失败,无法访问系统资源',
403: '当前操作没有权限',
404: '访问资源不存在',
default: '系统未知错误,请反馈给管理员'
}

View File

@ -1,78 +1,127 @@
import luchRequest from '@/components/luch-request' // 使用npm
import config from '@/config.js';
import errorCode from "@/common/request/errorCode";
import { getToken } from '@/common/utils/auth'
import { toast, showConfirm } from '@/common/utils/common'
import config from '@/config.js'
import errorCode from '@/common/request/errorCode'
import { getToken, getRefreshToken, setToken, setRefreshToken } from '@/common/utils/auth'
import { toast } from '@/common/utils/common'
import store from '@/store'
import request from '@/common/request/request.js'
// luch-request插件官网https://www.quanzhan.co/luch-request/guide/3.x/#%E5%85%A8%E5%B1%80%E8%AF%B7%E6%B1%82%E9%85%8D%E7%BD%AE
// 创建luchRequest实例
console.log(config.baseUrl)
const http = new luchRequest({
baseURL: config.baseUrl,
timeout: 20000, // 请求超时时间
dataType: 'json',
custom: {
loading: true
},
sslVerify: true,
header: {}
loading: true
}
})
// 请求拦截器
http.interceptors.request.use(
config => {
(config) => {
// 在发送请求之前
let token = getToken()
if (token) {
// 添加头信息token验证
config.header["Authorization"] = token
}
let token = getToken()
if (token) {
// 添加头信息token验证
config.header['Authorization'] = token
}
return config
},
error => {
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器
http.interceptors.response.use(res => {
// console.log("响应拦截器:", res)
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = res.data.message || errorCode[code] || errorCode["default"];
if (code === 500) {
toast(msg)
return Promise.reject(new Error(msg));
} else if (code === 401) {
showConfirm("登录状态已过期,您可以继续留在该页面,或者重新登录?").then(res => {
if (res.confirm) {
store.dispatch('auth/LogOut')
}
})
return Promise.reject("error");
} else if (code !== 200) {
toast(msg)
return Promise.reject("error");
} else {
return res.data;
}
},
error => {
console.log("请求状态码服务器直接报错", error);
let { errMsg } = error;
if (errMsg == "request:fail") {
errMsg = "接口连接异常";
} else if (errMsg == "request:fail timeout") {
errMsg = "接口连接超时";
} else {
errMsg = error.data.message;
}
toast(errMsg)
return Promise.reject(error);
}
);
http.interceptors.response.use(
(res) => {
// console.log("响应拦截器:", res)
// 未设置状态码则默认401状态
const code = res.data.code || 200
// 获取错误信息
const msg = res.data.message || errorCode[code] || errorCode['default']
// 是否刷新token
const refresh = res.data.refresh || false
if (code === 500) {
toast(msg)
return Promise.reject(new Error(msg))
} else if (code === 401) {
// 因token快过期刷新token
refreshToken().then((res) => {
setToken(`${res.data.token_type} ${res.data.access_token}`)
setRefreshToken(res.data.refresh_token)
})
toast('操作失败,请重试')
return Promise.reject('error')
} else if (code !== 200) {
toast(msg)
return Promise.reject('error')
} else if (code === 200) {
if (refresh) {
// 因token快过期刷新token
refreshToken().then((res) => {
setToken(`${res.data.token_type} ${res.data.access_token}`)
setRefreshToken(res.data.refresh_token)
})
}
return res.data
} else {
return res.data
}
},
(error) => {
console.log('err', error)
let message = error.data.message || error.errMsg
const status = error.statusCode
switch (status) {
case 400:
message = '请求错误'
break
case 401:
// 强制要求重新登录因账号已冻结账号已过期手机号码错误刷新token无效等问题导致
store.dispatch('auth/LogOut')
message = '未认证,请登录'
break
case 403:
message = '拒绝访问'
break
case 404:
message = '请求地址出错'
break
case 408:
message = '请求超时'
break
case 500:
message = '服务器内部错误'
break
case 501:
message = '服务未实现'
break
case 502:
message = '网关错误'
break
case 503:
message = '服务不可用'
break
case 504:
message = '网关超时'
break
case 505:
message = 'HTTP版本不受支持'
break
default:
break
}
toast(message)
return Promise.reject(error)
}
)
export default http
// 刷新 token
function refreshToken() {
const data = JSON.stringify(getRefreshToken())
return request.post('/auth/token/refresh/', data)
}
export default http

View File

@ -11,3 +11,17 @@ export function setToken(token) {
export function removeToken() {
return uni.removeStorageSync(TokenKey)
}
const RefreshTokenKey = 'Refresh-Token'
export function getRefreshToken() {
return uni.getStorageSync(RefreshTokenKey)
}
export function setRefreshToken(token) {
return uni.setStorageSync(RefreshTokenKey, token)
}
export function removeRefreshToken() {
return uni.removeStorageSync(RefreshTokenKey)
}

View File

@ -1,7 +1,7 @@
/**
* 显示消息提示框
* @param content 提示的标题
*/
* 显示消息提示框
* @param content 提示的标题
*/
export function toast(content) {
uni.showToast({
icon: 'none',
@ -10,9 +10,9 @@ export function toast(content) {
}
/**
* 显示模态弹窗
* @param content 提示的标题
*/
* 显示模态弹窗
* @param content 提示的标题
*/
export function showConfirm(content) {
return new Promise((resolve, reject) => {
uni.showModal({
@ -20,7 +20,7 @@ export function showConfirm(content) {
content: content,
cancelText: '取消',
confirmText: '确定',
success: function(res) {
success: function (res) {
resolve(res)
}
})
@ -28,27 +28,27 @@ export function showConfirm(content) {
}
/**
* 参数处理
* @param params 参数
*/
* 参数处理
* @param params 参数
*/
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName]
var part = encodeURIComponent(propName) + "="
if (value !== null && value !== "" && typeof (value) !== "undefined") {
var part = encodeURIComponent(propName) + '='
if (value !== null && value !== '' && typeof value !== 'undefined') {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
let params = propName + '[' + key + ']'
var subPart = encodeURIComponent(params) + "="
result += subPart + encodeURIComponent(value[key]) + "&"
var subPart = encodeURIComponent(params) + '='
result += subPart + encodeURIComponent(value[key]) + '&'
}
}
} else {
result += part + encodeURIComponent(value) + "&"
result += part + encodeURIComponent(value) + '&'
}
}
}
return result
}
}

View File

@ -1,11 +1,11 @@
export const auth = {
isUser: 'vuex_auth_isUser',
isUserOpenid: 'vuex_auth_isUserOpenid',
isResetPassword: 'vuex_auth_isResetPassword',
name: 'vuex_auth_name',
nickname: 'vuex_auth_nickname',
gender: 'vuex_auth_gender',
telephone: 'vuex_auth_telephone',
isUser: 'vuex_auth_isUser',
isUserOpenid: 'vuex_auth_isUserOpenid',
isResetPassword: 'vuex_auth_isResetPassword',
name: 'vuex_auth_name',
nickname: 'vuex_auth_nickname',
gender: 'vuex_auth_gender',
telephone: 'vuex_auth_telephone',
avatar: 'vuex_auth_avatar',
createDatetime: 'vuex_auth_createDatetime',
roles: 'vuex_auth_roles',

View File

@ -1,80 +0,0 @@
const TokenKey = 'Admin-Token'
// 获取客户端token
export function getToken() {
try {
const value = uni.getStorageSync(TokenKey);
if (value) {
return value;
}
return ""
} catch (e) {
// error
return ""
}
}
// 设置客户端token
export function setToken(token) {
uni.setStorage({
key: TokenKey,
data: token,
success: function (res) {
console.log('成功存储token');
},
fail:function(e){
console.log(e)
console.log("存储token失败");
}
});
}
// 删除客户端token
export function removeToken() {
uni.removeStorage({
key: TokenKey,
success: function (res) {
console.log('成功删除token');
}
});
}
// 获取客户端
export function getStorage(key) {
try {
const value = uni.getStorageSync(key);
if (value) {
// console.log("成功获取到 Storage", value);
return value;
}
return ""
} catch (e) {
// error
return ""
}
}
// 设置客户端 Storage
export function setStorage(key, value) {
uni.setStorage({
key: key,
data: value,
success: function (res) {
console.log('成功存储');
},
fail:function(e){
console.log(e)
console.log("存储失败");
}
});
}
// 删除客户端 Storage
export function removeStorage(key) {
uni.removeStorage({
key: key,
success: function (res) {
console.log('成功删除Storage');
}
});
}

View File

@ -17,14 +17,16 @@ module.exports = {
if (!log) return
log.error.apply(log, arguments)
},
setFilterMsg(msg) { // 从基础库2.7.3开始支持
setFilterMsg(msg) {
// 从基础库2.7.3开始支持
if (!log || !log.setFilterMsg) return
if (typeof msg !== 'string') return
log.setFilterMsg(msg)
},
addFilterMsg(msg) { // 从基础库2.8.1开始支持
addFilterMsg(msg) {
// 从基础库2.8.1开始支持
if (!log || !log.addFilterMsg) return
if (typeof msg !== 'string') return
log.addFilterMsg(msg)
}
}
}

View File

@ -9,9 +9,9 @@ export function checkPermi(value) {
if (value && value instanceof Array && value.length > 0) {
const permissions = store.getters && store.getters.permissions
const permissionDatas = value
const all_permission = "*:*:*"
const all_permission = '*:*:*'
const hasPermission = permissions.some(permission => {
const hasPermission = permissions.some((permission) => {
return all_permission === permission || permissionDatas.includes(permission)
})
@ -20,7 +20,7 @@ export function checkPermi(value) {
}
return true
} else {
console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
console.error('未获取到校验的字符权限!')
return false
}
}
@ -34,9 +34,9 @@ export function checkRole(value) {
if (value && value instanceof Array && value.length > 0) {
const roles = store.getters && store.getters.roles
const permissionRoles = value
const super_admin = "admin"
const super_admin = 'admin'
const hasRole = roles.some(role => {
const hasRole = roles.some((role) => {
return super_admin === role || permissionRoles.includes(role)
})
@ -45,7 +45,7 @@ export function checkRole(value) {
}
return true
} else {
console.error(`need roles! Like checkRole="['admin','editor']"`)
console.error('未获取到校验的角色!')
return false
}
}
}

View File

@ -5,34 +5,32 @@
博客https://www.jianshu.com/p/71ad2f45120c
*/
import config from '@/config.js'
import { getToken, removeToken } from '@/common/utils/cookies'
import { getToken } from '@/common/utils/auth'
// 单个文件上传
export function uploadFile(api, file, data={}) {
export function uploadFile(api, file, data = {}) {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: config.baseUrl + api,
filePath: file,
name: 'file',
timeout: 60000,
formData: data,
header: {
Authorization: getToken()
},
success: (res) => {
let data = JSON.parse(res.data);
if (data.code !== 200) {
reject(data);
}
resolve(data);
},
fail: (err) => {
console.log("上传失败", err);
reject(err);
}
});
uni.uploadFile({
url: config.baseUrl + api,
filePath: file,
name: 'file',
timeout: 60000,
formData: data,
header: {
Authorization: getToken()
},
success: (res) => {
let data = JSON.parse(res.data)
if (data.code !== 200) {
reject(data)
}
resolve(data)
},
fail: (err) => {
console.log('上传失败', err)
reject(err)
}
})
})
}

View File

@ -13,47 +13,47 @@
* getDate(new Date(), 3).fullDate # 三天后的日期
*/
export function getDate(date, AddDayCount = 0) {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null;
return null
}
const format = pattern || "{y}-{m}-{d} {h}:{i}:{s}";
let date;
if (typeof time === "object") {
date = time;
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === "string") && (/^[0-9]+$/.test(time))) {
time = parseInt(time);
} else if (typeof time === "string") {
time = time.replace(new RegExp(/-/gm), "/");
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/')
}
if ((typeof time === "number") && (time.toString().length === 10)) {
time = time * 1000;
if (typeof time === 'number' && time.toString().length === 10) {
time = time * 1000
}
date = new Date(time);
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
@ -63,129 +63,132 @@ export function parseTime(time, pattern) {
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
};
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key];
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === "a") { return ["", "", "", "", "", "", ""][value]; }
if (result.length > 0 && value < 10) {
value = "0" + value;
if (key === 'a') {
return ['', '', '', '', '', '', ''][value]
}
return value || 0;
});
return time_str;
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
// 表单重置
export function resetForm(refName) {
if (this.$refs[refName]) {
this.$refs[refName].resetFields();
this.$refs[refName].resetFields()
}
}
// 添加日期范围
export function addDateRange(params, dateRange, propName) {
const search = JSON.parse(JSON.stringify(params));
if (dateRange != null && dateRange !== "" && dateRange.length !== 0) {
search.as = JSON.stringify({ create_datetime__range: dateRange });
const search = JSON.parse(JSON.stringify(params))
if (dateRange != null && dateRange !== '' && dateRange.length !== 0) {
search.as = JSON.stringify({ create_datetime__range: dateRange })
}
return search;
return search
}
// 回显数据字典
export function selectDictLabel(datas, value) {
var actions = [];
var actions = []
Object.keys(datas).some((key) => {
if (String(datas[key].value) === ("" + String(value))) {
actions.push(datas[key].label);
return true;
if (String(datas[key].value) === '' + String(value)) {
actions.push(datas[key].label)
return true
}
});
return actions.join("");
})
return actions.join('')
}
// 获取字典默认值
export function selectDictDefault(datas) {
var actions = [];
var actions = []
Object.keys(datas).some((key) => {
if (datas[key].is_default === true) {
actions.push(datas[key].dictValue);
return true;
actions.push(datas[key].dictValue)
return true
}
});
})
if (!actions[0] && datas[0]) {
actions.push(datas[0].dictValue);
actions.push(datas[0].dictValue)
}
return actions.join("");
return actions.join('')
}
// 回显数据字典字符串数组
export function selectDictLabels(datas, value, separator) {
var actions = [];
var currentSeparator = undefined === separator ? "," : separator;
var temp = value.split(currentSeparator);
var actions = []
var currentSeparator = undefined === separator ? ',' : separator
var temp = value.split(currentSeparator)
Object.keys(value.split(currentSeparator)).some((val) => {
Object.keys(datas).some((key) => {
if (datas[key].dictValue == ("" + temp[val])) {
actions.push(datas[key].dictLabel + currentSeparator);
if (datas[key].dictValue == '' + temp[val]) {
actions.push(datas[key].dictLabel + currentSeparator)
}
});
});
return actions.join("").substring(0, actions.join("").length - 1);
})
})
return actions.join('').substring(0, actions.join('').length - 1)
}
// 转换字符串undefined,null等转化为""
export function praseStrEmpty(str) {
if (!str || str == "undefined" || str == "null") {
return "";
if (!str || str == 'undefined' || str == 'null') {
return ''
}
return str;
return str
}
// js模仿微信朋友圈计算时间显示几天/几小时/几分钟/刚刚
//datetime 格式为2019-11-22 12:23:59样式
export function timeConversion(datetime) { //dateTimeStamp是一个时间毫秒注意时间戳是秒的形式在这个毫秒的基础上除以1000就是十位数的时间戳13位数的都是时间毫秒
// var dateTimeStamp = new Date(datetime.replace(/ /, 'T')).getTime()-8 * 60 * 60 * 1000;//这里要减去中国的时区8小时
var dateTimeStamp = new Date(datetime.replace(/ /, 'T')).getTime();//这里不减去中国的时区8小时
var minute = 1000 * 60; //把分半个月一个月用毫秒表示
var hour = minute * 60;
var day = hour * 24;
var week = day * 7;
var halfamonth = day * 15;
var month = day * 30;
var now = new Date().getTime(); //获取当前时间毫秒
var diffValue = now - dateTimeStamp; //时间差
if (diffValue < 0) {
return '刚刚';
}
var minC = diffValue / minute; //计算时间差的分
var hourC = diffValue / hour;
var dayC = diffValue / day;
var weekC = diffValue / week;
var monthC = diffValue / month;
var result = "2";
if (monthC >= 1 && monthC <= 3) {
result = " " + parseInt(monthC) + "月前"
} else if (weekC >= 1 && weekC <= 3) {
result = " " + parseInt(weekC) + "周前"
} else if (dayC >= 1 && dayC <= 6) {
result = " " + parseInt(dayC) + "天前"
} else if (hourC >= 1 && hourC <= 23) {
result = " " + parseInt(hourC) + "小时前"
} else if (minC >= 1 && minC <= 59) {
result = " " + parseInt(minC) + "分钟前"
} else if (diffValue >= 0 && diffValue <= minute) {
result = "刚刚"
} else {
var datetime = new Date();
datetime.setTime(dateTimeStamp);
var Nyear = datetime.getFullYear(); {}
var Nmonth = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
var Ndate = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
var Nhour = datetime.getHours() < 10 ? "0" + datetime.getHours() : datetime.getHours();
var Nminute = datetime.getMinutes() < 10 ? "0" + datetime.getMinutes() : datetime.getMinutes();
var Nsecond = datetime.getSeconds() < 10 ? "0" + datetime.getSeconds() : datetime.getSeconds();
result = Nyear + "-" + Nmonth + "-" + Ndate
}
return result;
}
export function timeConversion(datetime) {
//dateTimeStamp是一个时间毫秒注意时间戳是秒的形式在这个毫秒的基础上除以1000就是十位数的时间戳13位数的都是时间毫秒
// var dateTimeStamp = new Date(datetime.replace(/ /, 'T')).getTime()-8 * 60 * 60 * 1000;//这里要减去中国的时区8小时
var dateTimeStamp = new Date(datetime.replace(/ /, 'T')).getTime() //这里不减去中国的时区8小时
var minute = 1000 * 60 //把分半个月一个月用毫秒表示
var hour = minute * 60
var day = hour * 24
var week = day * 7
var month = day * 30
var now = new Date().getTime() //获取当前时间毫秒
var diffValue = now - dateTimeStamp //时间差
if (diffValue < 0) {
return '刚刚'
}
var minC = diffValue / minute //计算时间差的分
var hourC = diffValue / hour
var dayC = diffValue / day
var weekC = diffValue / week
var monthC = diffValue / month
var result = '2'
if (monthC >= 1 && monthC <= 3) {
result = ' ' + parseInt(monthC) + '月前'
} else if (weekC >= 1 && weekC <= 3) {
result = ' ' + parseInt(weekC) + '周前'
} else if (dayC >= 1 && dayC <= 6) {
result = ' ' + parseInt(dayC) + '天前'
} else if (hourC >= 1 && hourC <= 23) {
result = ' ' + parseInt(hourC) + '小时前'
} else if (minC >= 1 && minC <= 59) {
result = ' ' + parseInt(minC) + '分钟前'
} else if (diffValue >= 0 && diffValue <= minute) {
result = '刚刚'
} else {
let datetime = new Date()
datetime.setTime(dateTimeStamp)
let Nyear = datetime.getFullYear()
var Nmonth =
datetime.getMonth() + 1 < 10 ? '0' + (datetime.getMonth() + 1) : datetime.getMonth() + 1
var Ndate = datetime.getDate() < 10 ? '0' + datetime.getDate() : datetime.getDate()
var Nhour = datetime.getHours() < 10 ? '0' + datetime.getHours() : datetime.getHours()
var Nminute = datetime.getMinutes() < 10 ? '0' + datetime.getMinutes() : datetime.getMinutes()
var Nsecond = datetime.getSeconds() < 10 ? '0' + datetime.getSeconds() : datetime.getSeconds()
result = Nyear + '-' + Nmonth + '-' + Ndate
}
return result
}

View File

@ -13,7 +13,7 @@ let storageNodeKeys = [...Object.values(auth)]
let storageData = uni.getStorageSync(storageKey) || {}
const storage = {
set: function(key, value) {
set: function (key, value) {
if (storageNodeKeys.indexOf(key) != -1) {
let tmp = uni.getStorageSync(storageKey)
tmp = tmp ? tmp : {}
@ -21,14 +21,14 @@ const storage = {
uni.setStorageSync(storageKey, tmp)
}
},
get: function(key) {
return storageData[key] || ""
get: function (key) {
return storageData[key] || ''
},
remove: function(key) {
remove: function (key) {
delete storageData[key]
uni.setStorageSync(storageKey, storageData)
},
clean: function() {
clean: function () {
uni.removeStorageSync(storageKey)
}
}

View File

@ -7,7 +7,7 @@ import { toast, showConfirm, tansParams } from '@/common/utils/common'
let timeout = 10000
const baseUrl = config.baseUrl
const upload = config => {
const upload = (config) => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
config.header = config.header || {}
@ -21,47 +21,47 @@ const upload = config => {
config.url = url
}
return new Promise((resolve, reject) => {
uni.uploadFile({
timeout: config.timeout || timeout,
url: baseUrl + config.url,
filePath: config.filePath,
name: config.name || 'file',
header: config.header,
formData: config.formData,
success: (res) => {
let result = JSON.parse(res.data)
const code = result.code || 200
const msg = errorCode[code] || result.msg || errorCode['default']
if (code === 200) {
resolve(result)
} else if (code == 401) {
showConfirm("登录状态已过期,您可以继续留在该页面,或者重新登录?").then(res => {
if (res.confirm) {
store.dispatch('LogOut')
}
})
reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
toast(msg)
reject('500')
} else if (code !== 200) {
toast(msg)
reject(code)
}
},
fail: (error) => {
let { message } = error
if (message == 'Network Error') {
message = '后端接口连接异常'
} else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substr(message.length - 3) + '异常'
}
toast(message)
reject(error)
uni.uploadFile({
timeout: config.timeout || timeout,
url: baseUrl + config.url,
filePath: config.filePath,
name: config.name || 'file',
header: config.header,
formData: config.formData,
success: (res) => {
let result = JSON.parse(res.data)
const code = result.code || 200
const msg = errorCode[code] || result.msg || errorCode['default']
if (code === 200) {
resolve(result)
} else if (code == 401) {
showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then((res) => {
if (res.confirm) {
store.dispatch('LogOut')
}
})
reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
toast(msg)
reject('500')
} else if (code !== 200) {
toast(msg)
reject(code)
}
})
},
fail: (error) => {
let { message } = error
if (message == 'Network Error') {
message = '后端接口连接异常'
} else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substr(message.length - 3) + '异常'
}
toast(message)
reject(error)
}
})
})
}

View File

@ -1,16 +1,14 @@
// 应用全局配置
module.exports = {
// 测试环境
// 测试环境
baseUrl: 'http://127.0.0.1:9000',
// 生产环境
// baseUrl: 'https://api.kinit.ktianc.top',
// 应用信息
appInfo: {
// 应用版本
version: "1.1.0",
// 隐私政策不支持本地路径
privacy: "http://kinit.ktianc.top/docs/privacy",
// 用户协议不支持本地路径
agreement: "http://kinit.ktianc.top/docs/agreement"
version: '1.2.0',
// 隐私政策不支持本地路径
privacy: 'http://kinit.ktianc.top/docs/privacy',
// 用户协议不支持本地路径
agreement: 'http://kinit.ktianc.top/docs/agreement'
}
}

8
kinit-uni/jsconfig.json Normal file
View File

@ -0,0 +1,8 @@
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}

View File

@ -2,8 +2,8 @@ import Vue from 'vue'
import App from './App'
import store from './store' // store
import plugins from './plugins' // plugins
import {router,RouterMount} from './permission.js' // 路由拦截
import uView from "uview-ui"
import { router, RouterMount } from './permission.js' // 路由拦截
import uView from 'uview-ui'
Vue.use(uView)
Vue.use(router)
@ -14,28 +14,28 @@ Vue.use(plugins)
// 配置后很多组件的默认尺寸就变了需要手动调整不熟悉不建议开启
// 需要在Vue.use(uView)之后执行
uni.$u.setConfig({
// 修改$u.config对象的属性
config: {
// 修改默认单位为rpx相当于执行 uni.$u.config.unit = 'rpx'
unit: 'rpx'
},
// 修改$u.props对象的属性
props: {
// 修改radio组件的size参数的默认值相当于执行 uni.$u.props.radio.size = 30
radio: {
size: 33,
labelSize: 30
},
button: {
loadingSize: 28
},
text: {
size: 30,
color: '#000'
}
// 其他组件属性配置
// ......
}
// 修改$u.config对象的属性
config: {
// 修改默认单位为rpx相当于执行 uni.$u.config.unit = 'rpx'
unit: 'rpx'
},
// 修改$u.props对象的属性
props: {
// 修改radio组件的size参数的默认值相当于执行 uni.$u.props.radio.size = 30
radio: {
size: 33,
labelSize: 30
},
button: {
loadingSize: 28
},
text: {
size: 30,
color: '#000'
}
// 其他组件属性配置
// ......
}
})
Vue.config.productionTip = false
@ -49,9 +49,9 @@ const app = new Vue({
//v1.3.5 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式
// #ifdef H5
RouterMount(app, router, '#app')
RouterMount(app, router, '#app')
// #endif
// #ifndef H5
app.$mount(); //为了兼容小程序及app端必须这样写才有效果
app.$mount() //为了兼容小程序及app端必须这样写才有效果
// #endif

View File

@ -3,5 +3,11 @@
"uni-read-pages": "^1.0.5",
"uni-simple-router": "^2.0.8-beta.3",
"uview-ui": "^2.0.34"
},
"devDependencies": {
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.9.0"
}
}

View File

@ -7,42 +7,42 @@
</template>
<script>
export default {
data() {
return {
title: '',
content: ''
}
},
onLoad(options) {
if (options.query) {
this.title = options.query.title
this.content = options.query.content
} else {
this.title = options.title
this.content = options.content
}
uni.setNavigationBarTitle({
title: options.title
})
export default {
data() {
return {
title: '',
content: ''
}
},
onLoad(options) {
if (options.query) {
this.title = options.query.title
this.content = options.query.content
} else {
this.title = options.title
this.content = options.content
}
uni.setNavigationBarTitle({
title: options.title
})
}
}
</script>
<style scoped>
page {
background-color: #ffffff;
}
page {
background-color: #ffffff;
}
.view-title {
font-weight: bold;
}
.view-title {
font-weight: bold;
}
.view-content {
font-size: 26rpx;
padding: 12px 5px 0;
color: #333;
line-height: 24px;
font-weight: normal;
}
.view-content {
font-size: 26rpx;
padding: 12px 5px 0;
color: #333;
line-height: 24px;
font-weight: normal;
}
</style>

View File

@ -1,35 +1,35 @@
<template>
<view v-if="params.url">
<!-- 不支持本地路径 -->
<!-- 不支持本地路径 -->
<web-view :webview-styles="webviewStyles" :src="`${params.url}`"></web-view>
</view>
</template>
<script>
export default {
data() {
return {
params: {},
webviewStyles: {
progress: {
color: "#FF3333"
}
export default {
props: {
src: {
type: [String],
default: null
}
},
data() {
return {
params: {},
webviewStyles: {
progress: {
color: '#FF3333'
}
}
},
props: {
src: {
type: [String],
default: null
}
},
onLoad(event) {
this.params = event
if (event.title) {
uni.setNavigationBarTitle({
title: event.title
})
}
}
},
onLoad(event) {
this.params = event
if (event.title) {
uni.setNavigationBarTitle({
title: event.title
})
}
}
}
</script>

View File

@ -8,48 +8,48 @@
</template>
<script>
import { wxShareMixins } from '@/common/mixins/share.js'
export default {
mixins: [wxShareMixins],
computed: {
name() {
return this.$store.state.auth.name
},
logo() {
return this.$store.state.app.logo
},
logoImage() {
return this.$store.state.app.logoImage
}
import { wxShareMixins } from '@/common/mixins/share.js'
export default {
mixins: [wxShareMixins],
computed: {
name() {
return this.$store.state.auth.name
},
logo() {
return this.$store.state.app.logo
},
logoImage() {
return this.$store.state.app.logoImage
}
}
}
</script>
<style lang="scss">
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
.title {
font-size: 36rpx;
color: #8f8f94;
}
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
.title {
font-size: 36rpx;
color: #8f8f94;
}
}
}
</style>

View File

@ -1,201 +1,212 @@
<template>
<view class="normal-login-container">
<view class="logo-content align-center justify-center flex">
<image v-if="logo" style="width: 100rpx;height: 100rpx;" :src="logoImage" mode="widthFix">
<image v-if="logo" style="width: 100rpx; height: 100rpx" :src="logoImage" mode="widthFix">
</image>
<text class="title">{{ title }}</text>
</view>
<view class="login-form-content">
<view class="input-item flex align-center">
<view class="iconfont icon-user icon"></view>
<input v-model="loginForm.telephone" class="input" type="text" placeholder="请输入手机号" maxlength="30" />
<input
v-model="loginForm.telephone"
class="input"
type="text"
placeholder="请输入手机号"
maxlength="30"
/>
</view>
<view class="input-item flex align-center">
<view class="iconfont icon-password icon"></view>
<input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
<input
v-model="loginForm.password"
type="password"
class="input"
placeholder="请输入密码"
maxlength="20"
/>
</view>
<view class="action-btn">
<!-- <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button> -->
<u-button type="primary" text="登录" @click="handleLogin" shape="circle"></u-button>
<u-button type="primary" text="登录" shape="circle" @click="handleLogin"></u-button>
</view>
</view>
<view class="xieyi text-center">
<text class="text-grey1">登录即代表同意</text>
<text @click="handleUserAgrement" class="text-blue">用户协议</text>
<text @click="handlePrivacy" class="text-blue">隐私协议</text>
<text class="text-blue" @click="handleUserAgrement">用户协议</text>
<text class="text-blue" @click="handlePrivacy">隐私协议</text>
</view>
<view class="footer text-center">
<u-button
type="primary"
text="微信一键登录"
shape="circle"
open-type="getPhoneNumber"
@getphonenumber="wxLogin"
></u-button>
</view>
<view class="footer text-center">
<u-button
type="primary"
text="微信一键登录"
shape="circle"
open-type="getPhoneNumber"
@getphonenumber="wxLogin"
></u-button>
</view>
</view>
</template>
<script>
import { wxLoginMixins } from '@/common/mixins/auth.js'
export default {
mixins: [wxLoginMixins],
data() {
return {
loginForm: {
telephone: "15020221010",
password: "kinit2022"
}
import { wxLoginMixins } from '@/common/mixins/auth.js'
export default {
mixins: [wxLoginMixins],
data() {
return {
loginForm: {
telephone: '15020221010',
password: 'kinit2022'
}
}
},
computed: {
title() {
return this.$store.state.app.title
},
logo() {
return this.$store.state.app.logo
},
logoImage() {
return this.$store.state.app.logoImage
},
privacy() {
return this.$store.state.app.privacy
},
agreement() {
return this.$store.state.app.agreement
},
isResetPassword() {
return this.$store.state.auth.isResetPassword
}
},
methods: {
//
handlePrivacy() {
const title = '隐私政策'
this.$tab.navigateTo(`/pages/common/webview/index?title=${title}&url=${this.privacy}`)
},
//
handleUserAgrement() {
const title = '用户协议'
this.$tab.navigateTo(`/pages/common/webview/index?title=${title}&url=${this.agreement}`)
},
//
async handleLogin() {
if (this.loginForm.telephone === '') {
this.$modal.msgError('请输入您的手机号')
} else if (this.loginForm.password === '') {
this.$modal.msgError('请输入您的密码')
} else {
this.$modal.loading('正在登录中...')
this.pwdLogin()
}
},
computed: {
title() {
return this.$store.state.app.title
},
logo() {
return this.$store.state.app.logo
},
logoImage() {
return this.$store.state.app.logoImage
},
privacy() {
return this.$store.state.app.privacy
},
agreement() {
return this.$store.state.app.agreement
},
isResetPassword() {
return this.$store.state.auth.isResetPassword
}
},
methods: {
//
handlePrivacy() {
const title = '隐私政策'
this.$tab.navigateTo(`/pages/common/webview/index?title=${title}&url=${this.privacy}`)
},
//
handleUserAgrement() {
const title = '用户协议'
this.$tab.navigateTo(`/pages/common/webview/index?title=${title}&url=${this.agreement}`)
},
//
async handleLogin() {
if (this.loginForm.telephone === "") {
this.$modal.msgError("请输入您的手机号")
} else if (this.loginForm.password === "") {
this.$modal.msgError("请输入您的密码")
}else {
this.$modal.loading("正在登录中...")
this.pwdLogin()
}
},
//
async pwdLogin() {
this.$store.dispatch('auth/Login', this.loginForm).then(() => {
this.$modal.closeLoading()
this.loginSuccess()
})
},
//
loginSuccess() {
if (this.isResetPassword) {
this.$tab.reLaunch('/pages/index')
} else {
this.$tab.reLaunch('/pages/mine/pwd/index')
}
},
//
wxLogin(detail) {
this.onGetPhoneNumber(detail).then(res => {
this.loginSuccess()
})
},
//
async pwdLogin() {
this.$store.dispatch('auth/Login', this.loginForm).then(() => {
this.$modal.closeLoading()
this.loginSuccess()
})
},
//
loginSuccess() {
if (this.isResetPassword) {
this.$tab.reLaunch('/pages/index')
} else {
this.$tab.reLaunch('/pages/mine/pwd/index')
}
},
//
wxLogin(detail) {
this.onGetPhoneNumber(detail).then((res) => {
this.loginSuccess()
})
}
}
}
</script>
<style lang="scss">
page {
background-color: #ffffff;
}
page {
background-color: #ffffff;
}
.normal-login-container {
.normal-login-container {
width: 100%;
height: 100vh;
position: relative;
.logo-content {
width: 100%;
height: 100vh;
position: relative;
font-size: 21px;
text-align: center;
padding-top: 15%;
.logo-content {
width: 100%;
font-size: 21px;
text-align: center;
padding-top: 15%;
image {
border-radius: 4px;
}
.title {
margin-left: 10px;
}
image {
border-radius: 4px;
}
.login-form-content {
text-align: center;
.title {
margin-left: 10px;
}
}
.login-form-content {
text-align: center;
margin: 20px auto;
margin-top: 15%;
width: 80%;
.input-item {
margin: 20px auto;
margin-top: 15%;
width: 80%;
.input-item {
margin: 20px auto;
background-color: #f5f6f7;
height: 45px;
border-radius: 20px;
.icon {
font-size: 38rpx;
margin-left: 10px;
color: #999;
}
.input {
width: 100%;
font-size: 14px;
line-height: 20px;
text-align: left;
padding-left: 15px;
}
background-color: #f5f6f7;
height: 45px;
border-radius: 20px;
.icon {
font-size: 38rpx;
margin-left: 10px;
color: #999;
}
.login-btn {
margin-top: 40px;
height: 45px;
}
.xieyi {
color: #333;
margin-top: 20px;
.input {
width: 100%;
font-size: 14px;
line-height: 20px;
text-align: left;
padding-left: 15px;
}
}
.easyinput {
width: 100%;
.login-btn {
margin-top: 40px;
height: 45px;
}
.xieyi {
color: #333;
margin-top: 20px;
}
.footer {
margin: 20px auto;
width: 80%;
position: absolute;
bottom: 30px;
left: 10%;
}
}
.login-code-img {
height: 45px;
.easyinput {
width: 100%;
}
.footer {
margin: 20px auto;
width: 80%;
position: absolute;
bottom: 30px;
left: 10%;
}
}
.login-code-img {
height: 45px;
}
</style>

View File

@ -1,8 +1,7 @@
<template>
<view class="about-container">
<view class="header-section text-center">
<image style="width: 150rpx;height: 150rpx;" :src="logoImage" mode="widthFix">
</image>
<image style="width: 150rpx; height: 150rpx" :src="logoImage" mode="widthFix"> </image>
<uni-title type="h2" :title="title"></uni-title>
</view>
@ -11,7 +10,7 @@
<view class="list-cell list-cell-arrow">
<view class="menu-item-box">
<view>版本信息</view>
<view class="text-right">v{{version}}</view>
<view class="text-right">v{{ version }}</view>
</view>
</view>
<view class="list-cell list-cell-arrow">
@ -30,7 +29,7 @@
<view class="menu-item-box">
<view>公司网站</view>
<view class="text-right">
<uni-link :href="siteUrl" :text="siteUrl" showUnderLine="false"></uni-link>
<uni-link :href="siteUrl" :text="siteUrl" show-under-line="false"></uni-link>
</view>
</view>
</view>
@ -44,52 +43,52 @@
</template>
<script>
export default {
data() {
return {}
export default {
data() {
return {}
},
computed: {
version() {
return this.$store.state.app.version
},
computed: {
version() {
return this.$store.state.app.version
},
title() {
return this.$store.state.app.title
},
logoImage() {
return this.$store.state.app.logoImage
},
siteUrl() {
return this.$store.state.app.siteUrl
},
WXEmail() {
return this.$store.state.app.WXEmail
},
WXPhone() {
return this.$store.state.app.WXPhone
},
footerContent() {
return this.$store.state.app.footerContent
}
},
title() {
return this.$store.state.app.title
},
logoImage() {
return this.$store.state.app.logoImage
},
siteUrl() {
return this.$store.state.app.siteUrl
},
WXEmail() {
return this.$store.state.app.WXEmail
},
WXPhone() {
return this.$store.state.app.WXPhone
},
footerContent() {
return this.$store.state.app.footerContent
}
}
}
</script>
<style lang="scss">
page {
background-color: #f8f8f8;
}
page {
background-color: #f8f8f8;
}
.copyright {
margin-top: 50rpx;
text-align: center;
line-height: 60rpx;
color: #999;
}
.copyright {
margin-top: 50rpx;
text-align: center;
line-height: 60rpx;
color: #999;
}
.header-section {
display: flex;
padding: 30rpx 0 0;
flex-direction: column;
align-items: center;
}
.header-section {
display: flex;
padding: 30rpx 0 0;
flex-direction: column;
align-items: center;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -5,10 +5,15 @@
<view>{{ item.name }}</view>
</view>
<view class="childList">
<view v-for="(issue, zindex) in item.issues" :key="zindex" class="question" hover-class="hover"
@click="handleText(issue)">
<view
v-for="(issue, zindex) in item.issues"
:key="zindex"
class="question"
hover-class="hover"
@click="handleText(issue)"
>
<view class="text-item">{{ issue.title }}</view>
<view class="line" v-if="zindex !== item.issues.length - 1"></view>
<view v-if="zindex !== item.issues.length - 1" class="line"></view>
</view>
</view>
</view>
@ -16,68 +21,68 @@
</template>
<script>
import { getIssueCategoryList } from '@/common/request/api/vadmin/help/issue.js'
export default {
data() {
return {
list: []
}
},
onLoad() {
getIssueCategoryList().then(res => {
this.list = res.data
})
},
methods: {
handleText(item) {
this.$tab.navigateTo(`/pages/mine/help/issue/info?id=${item.id}`)
}
import { getIssueCategoryList } from '@/common/request/api/vadmin/help/issue.js'
export default {
data() {
return {
list: []
}
},
onLoad() {
getIssueCategoryList().then((res) => {
this.list = res.data
})
},
methods: {
handleText(item) {
this.$tab.navigateTo(`/pages/mine/help/issue/info?id=${item.id}`)
}
}
}
</script>
<style lang="scss">
page {
background-color: #f8f8f8;
}
page {
background-color: #f8f8f8;
}
.help-container {
margin-bottom: 100rpx;
padding: 30rpx;
}
.help-container {
margin-bottom: 100rpx;
padding: 30rpx;
}
.list-title {
margin-bottom: 30rpx;
}
.list-title {
margin-bottom: 30rpx;
}
.childList {
background: #ffffff;
box-shadow: 0px 0px 10rpx rgba(193, 193, 193, 0.2);
border-radius: 16rpx;
margin-top: 10rpx;
}
.childList {
background: #ffffff;
box-shadow: 0px 0px 10rpx rgba(193, 193, 193, 0.2);
border-radius: 16rpx;
margin-top: 10rpx;
}
.line {
width: 100%;
height: 1rpx;
background-color: #F5F5F5;
}
.line {
width: 100%;
height: 1rpx;
background-color: #f5f5f5;
}
.text-title {
color: #303133;
font-size: 32rpx;
font-weight: bold;
margin-left: 10rpx;
}
.text-title {
color: #303133;
font-size: 32rpx;
font-weight: bold;
margin-left: 10rpx;
}
.text-item {
font-size: 28rpx;
padding: 24rpx;
}
.text-item {
font-size: 28rpx;
padding: 24rpx;
}
.question {
color: #606266;
font-size: 28rpx;
}
.question {
color: #606266;
font-size: 28rpx;
}
</style>

View File

@ -1,70 +1,71 @@
<template>
<view>
<uni-card v-if="isSuccess" class="view-title" :title="model.title">
<uni-card v-if="isSuccess" class="view-title">
<u--text :text="model.title" bold></u--text>
<rich-text class="uni-body view-content" :nodes="model.content"></rich-text>
</uni-card>
<u-empty
v-else
mode="data"
icon="https://cdn.uviewui.com/uview/empty/data.png"
:marginTop="100"
:width="300"
:height="300"
>
</u-empty>
<u-empty
v-else
mode="data"
icon="https://cdn.uviewui.com/uview/empty/data.png"
:margin-top="100"
:width="300"
:height="300"
>
</u-empty>
</view>
</template>
<script>
import { getIssue, updateIssueAddViewNumber } from '@/common/request/api/vadmin/help/issue.js'
export default {
data() {
return {
isSuccess: true,
model: {},
dataId: null
import { getIssue, updateIssueAddViewNumber } from '@/common/request/api/vadmin/help/issue.js'
export default {
data() {
return {
isSuccess: true,
model: {},
dataId: null
}
},
onLoad(options) {
if (options.query) {
this.dataId = options.query.id
} else if (options) {
this.dataId = options.id
}
this.getData()
},
methods: {
getData() {
if (!this.dataId) {
return
}
},
onLoad(options) {
if (options.query) {
this.dataId = options.query.id
} else if (options) {
this.dataId = options.id
}
this.getData()
},
methods: {
getData() {
if (!this.dataId) { return; }
getIssue(this.dataId).then(res => {
this.model = res.data;
uni.setNavigationBarTitle({
title: res.data.title
})
updateIssueAddViewNumber(this.dataId)
}).catch(() => {
this.isSuccess = false
})
}
}
getIssue(this.dataId)
.then((res) => {
this.model = res.data
uni.setNavigationBarTitle({
title: res.data.title
})
updateIssueAddViewNumber(this.dataId)
})
.catch(() => {
this.isSuccess = false
})
}
}
}
</script>
<style scoped>
page {
background-color: #ffffff;
}
page {
background-color: #ffffff;
}
.view-title {
font-weight: bold;
}
.view-content {
font-size: 26rpx;
padding: 12px 5px 0;
color: #333;
line-height: 24px;
font-weight: normal;
}
.view-content {
font-size: 26rpx;
padding: 12px 5px 0;
color: #333;
line-height: 24px;
font-weight: normal;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<view class="mine-container" :style="{height: `${windowHeight}px`}">
<view class="mine-container" :style="{ height: `${windowHeight}px` }">
<!--顶部个人信息栏-->
<view class="header-section">
<view class="flex padding justify-between">
@ -7,19 +7,23 @@
<view v-if="!avatar" class="cu-avatar xl round bg-white">
<view class="iconfont icon-people text-gray icon"></view>
</view>
<image v-if="avatar" @click="handleToAvatar" :src="avatar" class="cu-avatar xl round" mode="aspectFill">
<image
v-if="avatar"
:src="avatar"
class="cu-avatar xl round"
mode="aspectFill"
@click="handleToAvatar"
>
</image>
<view v-if="!name" @click="handleToLogin" class="login-tip">
点击登录
</view>
<view v-if="name" @click="handleToInfo" class="user-info">
<view v-if="!name" class="login-tip" @click="handleToLogin"> 点击登录 </view>
<view v-if="name" class="user-info" @click="handleToInfo">
<view class="u_title">
{{ name }}
</view>
</view>
</view>
<view @click="handleToInfo" class="flex align-center">
<text style="font-size: 30rpx;">个人信息</text>
<view class="flex align-center" @click="handleToInfo">
<text style="font-size: 30rpx">个人信息</text>
<view class="iconfont icon-right1"></view>
</view>
</view>
@ -41,7 +45,9 @@
</view>
<view class="action-item" @click="praiseMe">
<view class="iconfont icon-dianzan text-green icon"></view>
<view style="height: 0px;" :animation="animationData" class="praise-me animation-opacity"> +1 </view>
<view style="height: 0px" :animation="animationData" class="praise-me animation-opacity">
+1
</view>
<text class="text">点赞我们</text>
</view>
</view>
@ -72,171 +78,167 @@
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import storage from '@/common/utils/storage'
export default {
data() {
return {
animation: "",
animationData: {}
}
},
onLoad() {
// 1
this.animation = uni.createAnimation();
this.animationData = {};
},
onUnload() {
// 5
this.animationData = {};
},
computed: {
version() {
return this.$store.state.app.version
},
name() {
return this.$store.state.auth.name
},
avatar() {
return this.$store.state.auth.avatar
},
windowHeight() {
return uni.getSystemInfoSync().windowHeight - 50
}
export default {
data() {
return {
animation: '',
animationData: {}
}
},
onLoad() {
// 1
this.animation = uni.createAnimation()
this.animationData = {}
},
onUnload() {
// 5
this.animationData = {}
},
computed: {
version() {
return this.$store.state.app.version
},
methods: {
handleToInfo() {
this.$tab.navigateTo('/pages/mine/info/index')
},
handleToEditInfo() {
this.$tab.navigateTo('/pages/mine/info/edit')
},
handleToSetting() {
this.$tab.navigateTo('/pages/mine/setting/index')
},
handleToLogin() {
this.$tab.reLaunch('/pages/login/login')
},
handleToAvatar() {
this.$tab.navigateTo('/pages/mine/avatar/index')
},
handleLogout() {
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
this.$store.dispatch('LogOut').then(() => {
this.$tab.reLaunch('/pages/index')
})
name() {
return this.$store.state.auth.name
},
avatar() {
return this.$store.state.auth.avatar
},
windowHeight() {
return uni.getSystemInfoSync().windowHeight - 50
}
},
methods: {
handleToInfo() {
this.$tab.navigateTo('/pages/mine/info/index')
},
handleToEditInfo() {
this.$tab.navigateTo('/pages/mine/info/edit')
},
handleToSetting() {
this.$tab.navigateTo('/pages/mine/setting/index')
},
handleToLogin() {
this.$tab.reLaunch('/pages/login/login')
},
handleToAvatar() {
this.$tab.navigateTo('/pages/mine/avatar/index')
},
handleLogout() {
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
this.$store.dispatch('LogOut').then(() => {
this.$tab.reLaunch('/pages/index')
})
},
handleHelp() {
this.$tab.navigateTo('/pages/mine/help/issue/index')
},
handleAbout() {
this.$tab.navigateTo('/pages/mine/about/index')
},
handleJiaoLiuQun() {
this.$modal.showToast('模块建设中~')
},
handleBuilding() {
this.$modal.showToast('模块建设中~')
},
//
praiseMe() {
// 2 step()
this.animation.translateY(-90).opacity(1).step({
duration: 400
});
// 3 exportanimation
this.animationData = this.animation.export();
// 4
setTimeout(()=> {
this.animation.translateY(0).opacity(0).step({
duration: 0
});
this.animationData = this.animation.export();
}, 300)
},
})
},
handleHelp() {
this.$tab.navigateTo('/pages/mine/help/issue/index')
},
handleAbout() {
this.$tab.navigateTo('/pages/mine/about/index')
},
handleJiaoLiuQun() {
this.$modal.showToast('模块建设中~')
},
handleBuilding() {
this.$modal.showToast('模块建设中~')
},
//
praiseMe() {
// 2 step()
this.animation.translateY(-90).opacity(1).step({
duration: 400
})
// 3 exportanimation
this.animationData = this.animation.export()
// 4
setTimeout(() => {
this.animation.translateY(0).opacity(0).step({
duration: 0
})
this.animationData = this.animation.export()
}, 300)
}
}
}
</script>
<style lang="scss" scoped>
.praise-me {
font-size: 14px;
color: #feab2a;
}
.animation-opacity {
font-weight: bold;
opacity: 0;
}
page {
background-color: #f5f6f7;
.praise-me {
font-size: 14px;
color: #feab2a;
}
.animation-opacity {
font-weight: bold;
opacity: 0;
}
page {
background-color: #f5f6f7;
}
.mine-container {
width: 100%;
height: 100%;
.header-section {
padding: 15px 15px 45px 15px;
background-color: #3c96f3;
color: white;
.login-tip {
font-size: 18px;
margin-left: 10px;
}
.cu-avatar {
border: 2px solid #eaeaea;
.icon {
font-size: 40px;
}
}
.user-info {
margin-left: 15px;
.u_title {
font-size: 37rpx;
line-height: 30px;
}
}
}
.mine-container {
width: 100%;
height: 100%;
.content-section {
position: relative;
top: -50px;
.mine-actions {
margin: 15px 15px;
padding: 20px 0px;
border-radius: 8px;
background-color: white;
.header-section {
padding: 15px 15px 45px 15px;
background-color: #3c96f3;
color: white;
.login-tip {
font-size: 18px;
margin-left: 10px;
}
.cu-avatar {
border: 2px solid #eaeaea;
.action-item {
.icon {
font-size: 40px;
font-size: 28px;
}
}
.user-info {
margin-left: 15px;
.u_title {
font-size: 37rpx;
line-height: 30px;
}
}
}
.content-section {
position: relative;
top: -50px;
.mine-actions {
margin: 15px 15px;
padding: 20px 0px;
border-radius: 8px;
background-color: white;
.action-item {
.icon {
font-size: 28px;
}
.text {
display: block;
font-size: 13px;
margin: 8px 0px;
}
.text {
display: block;
font-size: 13px;
margin: 8px 0px;
}
}
}
}
}
</style>

View File

@ -1,168 +1,128 @@
<template>
<view class="container">
<view style="padding: 20px;">
<u--form
labelPosition="left"
labelWidth="100px"
:model="form"
:rules="rules"
ref="formRef"
>
<u-form-item
label="用户姓名"
prop="name"
borderBottom
:required="true"
>
<u--input
v-model="form.name"
placeholder="请输入用户姓名"
border="none"
></u--input>
</u-form-item>
<u-form-item
label="用户昵称"
prop="nickname"
borderBottom
:required="false"
>
<u--input
v-model="form.nickname"
placeholder="请输入用户昵称"
border="none"
></u--input>
</u-form-item>
<u-form-item
label="手机号码"
prop="telephone"
borderBottom
:required="true"
>
<u--input
v-model="form.telephone"
placeholder="请输入手机号码"
border="none"
></u--input>
</u-form-item>
<u-form-item
label="用户性别"
prop="gender"
borderBottom
:required="false"
>
<u-radio-group v-model="form.gender">
<u-radio
:customStyle="{marginRight: '16px'}"
v-for="(item, index) in genderOptions"
:key="index"
:label="item.label"
:name="item.value"
>
</u-radio>
</u-radio-group>
</u-form-item>
</u--form>
<view style="margin-top: 20px;">
<u-button
:loading="btnLoading"
type="primary"
@click="submit"
text="提交"
>
</u-button>
</view>
</view>
<view style="padding: 20px">
<u--form ref="formRef" label-position="left" label-width="100px" :model="form" :rules="rules">
<u-form-item label="用户姓名" prop="name" border-bottom :required="true">
<u--input v-model="form.name" placeholder="请输入用户姓名" border="none"></u--input>
</u-form-item>
<u-form-item label="用户昵称" prop="nickname" border-bottom :required="false">
<u--input v-model="form.nickname" placeholder="请输入用户昵称" border="none"></u--input>
</u-form-item>
<u-form-item label="手机号码" prop="telephone" border-bottom :required="true">
<u--input v-model="form.telephone" placeholder="请输入手机号码" border="none"></u--input>
</u-form-item>
<u-form-item label="用户性别" prop="gender" border-bottom :required="false">
<u-radio-group v-model="form.gender">
<u-radio
v-for="(item, index) in genderOptions"
:key="index"
:custom-style="{ marginRight: '16px' }"
:label="item.label"
:name="item.value"
>
</u-radio>
</u-radio-group>
</u-form-item>
</u--form>
<view style="margin-top: 20px">
<u-button :loading="btnLoading" type="primary" text="提交" @click="submit"> </u-button>
</view>
</view>
</view>
</template>
<script>
import { getInfo } from '@/common/request/api/login'
import { updateCurrentUser } from '@/common/request/api/vadmin/auth/user.js'
export default {
data() {
return {
btnLoading: false,
form: {
name: "",
nickname: "",
telephone: "",
gender: ""
},
rules: {
name: {
import { getInfo } from '@/common/request/api/login'
import { updateCurrentUser } from '@/common/request/api/vadmin/auth/user.js'
export default {
data() {
return {
btnLoading: false,
form: {
name: '',
nickname: '',
telephone: '',
gender: ''
},
rules: {
name: {
type: 'string',
required: true,
message: '请填写姓名',
trigger: ['blur', 'change']
},
telephone: [
{
type: 'string',
required: true,
message: '请填写姓名',
message: '请填写正确手机号',
trigger: ['blur', 'change']
},
telephone: [
{
type: 'string',
required: true,
message: '请填写正确手机号',
trigger: ['blur', 'change']
},
{
validator: (rule, value, callback) => {
// truefalse
// uni.$u.test.mobile()truefalse
return uni.$u.test.mobile(value);
},
message: '手机号码不正确',
// blurchange
trigger: ['change','blur'],
}
]
},
genderOptions: []
}
},
onLoad() {
this.$store.dispatch('dict/getDicts', ["sys_vadmin_gender"]).then(result => {
this.genderOptions = result.sys_vadmin_gender
})
// this.resetForm()
this.getUser()
},
onReady() {
//onReady uni-app
this.$refs.formRef.setRules(this.rules)
},
methods: {
resetForm() {
this.form = {
name: "",
nickname: "",
telephone: "",
gender: ""
}
},
getUser() {
this.$modal.loading("加载中")
getInfo().then(res => {
this.form = res.data
}).finally(() => {
this.$modal.closeLoading()
})
{
validator: (rule, value, callback) => {
// truefalse
// uni.$u.test.mobile()truefalse
return uni.$u.test.mobile(value)
},
message: '手机号码不正确',
// blurchange
trigger: ['change', 'blur']
}
]
},
submit(ref) {
this.$refs.formRef.validate().then(res => {
this.btnLoading = true
updateCurrentUser(this.form).then(res => {
this.$store.dispatch('auth/UpdateInfo', res.data)
this.$modal.msgSuccess("更新成功");
}).finally(() => {
this.btnLoading = false
})
})
genderOptions: []
}
},
onLoad() {
this.$store.dispatch('dict/getDicts', ['sys_vadmin_gender']).then((result) => {
this.genderOptions = result.sys_vadmin_gender
})
// this.resetForm()
this.getUser()
},
onReady() {
//onReady uni-app
this.$refs.formRef.setRules(this.rules)
},
methods: {
resetForm() {
this.form = {
name: '',
nickname: '',
telephone: '',
gender: ''
}
},
getUser() {
this.$modal.loading('加载中')
getInfo()
.then((res) => {
this.form = res.data
})
.finally(() => {
this.$modal.closeLoading()
})
},
submit(ref) {
this.$refs.formRef.validate().then((res) => {
this.btnLoading = true
updateCurrentUser(this.form)
.then((res) => {
this.$store.dispatch('auth/UpdateInfo', res.data)
this.$modal.msgSuccess('更新成功')
})
.finally(() => {
this.btnLoading = false
})
})
}
}
}
</script>
<style lang="scss">
page {
background-color: #ffffff;
}
page {
background-color: #ffffff;
}
</style>

View File

@ -1,49 +1,49 @@
<template>
<view class="container">
<u-cell-group>
<u-cell title="姓名" :value="name">
<u-icon slot="icon" class="iconfont icon-user"></u-icon>
</u-cell>
<u-cell title="昵称" :value="nickname">
<u-icon slot="icon" class="iconfont icon-user"></u-icon>
</u-cell>
<u-cell title="手机号码" :value="telephone">
<u-icon slot="icon" class="iconfont icon-dianhua"></u-icon>
</u-cell>
<u-cell title="角色" :value="roles.join(',')">
<u-icon slot="icon" class="iconfont icon-xitongjiaose"></u-icon>
</u-cell>
<u-cell title="创建日期" :value="createDatetime">
<u-icon slot="icon" class="iconfont icon-jiaofuriqi"></u-icon>
</u-cell>
</u-cell-group>
<u-cell-group>
<u-cell title="姓名" :value="name">
<u-icon slot="icon" class="iconfont icon-user"></u-icon>
</u-cell>
<u-cell title="昵称" :value="nickname">
<u-icon slot="icon" class="iconfont icon-user"></u-icon>
</u-cell>
<u-cell title="手机号码" :value="telephone">
<u-icon slot="icon" class="iconfont icon-dianhua"></u-icon>
</u-cell>
<u-cell title="角色" :value="roles.join(',')">
<u-icon slot="icon" class="iconfont icon-xitongjiaose"></u-icon>
</u-cell>
<u-cell title="创建日期" :value="createDatetime">
<u-icon slot="icon" class="iconfont icon-jiaofuriqi"></u-icon>
</u-cell>
</u-cell-group>
</view>
</template>
<script>
export default {
computed: {
name() {
return this.$store.state.auth.name
},
nickname() {
return this.$store.state.auth.nickname
},
telephone() {
return this.$store.state.auth.telephone
},
roles() {
return this.$store.state.auth.roles
},
createDatetime() {
return this.$store.state.auth.createDatetime
}
},
export default {
computed: {
name() {
return this.$store.state.auth.name
},
nickname() {
return this.$store.state.auth.nickname
},
telephone() {
return this.$store.state.auth.telephone
},
roles() {
return this.$store.state.auth.roles
},
createDatetime() {
return this.$store.state.auth.createDatetime
}
}
}
</script>
<style lang="scss">
page {
background-color: #ffffff;
}
page {
background-color: #ffffff;
}
</style>

View File

@ -1,104 +1,87 @@
<template>
<view class="pwd-retrieve-container">
<view class="header">
<u--text
v-if="!isResetPassword"
text="第一次进入系统,必须先重置密码。"
:size="33"
align="center"
>
</u--text>
</view>
<uni-forms ref="form" :value="form" labelWidth="80px">
<uni-forms ref="form" :value="form" label-width="80px">
<uni-forms-item name="newPassword" label="新密码">
<uni-easyinput type="password" v-model="form.password" placeholder="请输入新密码" />
<uni-easyinput v-model="form.password" type="password" placeholder="请输入新密码" />
</uni-forms-item>
<uni-forms-item name="confirmPassword" label="确认密码">
<uni-easyinput type="password" v-model="form.password_two" placeholder="请确认新密码" />
<uni-easyinput v-model="form.password_two" type="password" placeholder="请确认新密码" />
</uni-forms-item>
<u-button text="提交" @click="submit" type="primary"></u-button>
<u-button text="提交" color="#e09bc7" @click="submit"></u-button>
</uni-forms>
</view>
</template>
<script>
import { postCurrentUserResetPassword } from '@/common/request/api/vadmin/auth/user.js'
export default {
data() {
return {
form: {
password: undefined,
password_two: undefined
import { postCurrentUserResetPassword } from '@/common/request/api/vadmin/auth/user.js'
export default {
data() {
return {
form: {
password: undefined,
password_two: undefined
},
rules: {
password: {
rules: [
{
required: true,
errorMessage: '新密码不能为空'
},
{
minLength: 8,
maxLength: 20,
errorMessage: '长度在 8 到 20 个字符'
}
]
},
rules: {
password: {
rules: [{
required: true,
errorMessage: '新密码不能为空',
},
{
minLength: 8,
maxLength: 20,
errorMessage: '长度在 8 到 20 个字符'
}
]
},
password_two: {
rules: [{
required: true,
errorMessage: '确认密码不能为空'
}, {
validateFunction: (rule, value, data) => data.password === value,
errorMessage: '两次输入的密码不一致'
}
]
}
password_two: {
rules: [
{
required: true,
errorMessage: '确认密码不能为空'
},
{
validateFunction: (rule, value, data) => data.password === value,
errorMessage: '两次输入的密码不一致'
}
]
}
}
},
computed: {
isResetPassword() {
return this.$store.state.auth.isResetPassword
}
},
onReady() {
this.$refs.form.setRules(this.rules)
},
methods: {
submit() {
this.$refs.form.validate().then(res => {
this.$modal.loading("正在提交")
postCurrentUserResetPassword(this.form).then(response => {
this.form = {
password: "",
password_two: ""
}
this.$modal.msgSuccess("重置成功")
if (!this.isResetPassword) {
this.$store.commit('auth/SET_IS_RESET_PASSWORD', true)
this.$tab.reLaunch('/pages/index')
}
}).finally(() => {
this.$modal.closeLoading()
})
})
}
}
},
onReady() {
this.$refs.form.setRules(this.rules)
},
methods: {
submit() {
this.$refs.form.validate().then((res) => {
this.$modal.loading('正在提交')
postCurrentUserResetPassword(this.form)
.then((response) => {
this.form = {
password: '',
password_two: ''
}
this.$modal.msgSuccess('修改成功')
})
.finally(() => {
this.$modal.closeLoading()
})
})
}
}
}
</script>
<style lang="scss">
page {
background-color: #ffffff;
}
page {
background-color: #ffffff;
}
.pwd-retrieve-container {
padding-top: 36rpx;
padding: 15px;
.header {
padding-bottom: 36rpx;
}
}
.pwd-retrieve-container {
padding-top: 36rpx;
padding: 15px;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<view class="setting-container" :style="{height: `${windowHeight}px`}">
<view class="setting-container" :style="{ height: `${windowHeight}px` }">
<view class="menu-list">
<view class="list-cell list-cell-arrow" @click="handleToPwd">
<view class="menu-item-box">
@ -31,46 +31,46 @@
</template>
<script>
export default {
data() {
return {
windowHeight: uni.getSystemInfoSync().windowHeight
}
export default {
data() {
return {
windowHeight: uni.getSystemInfoSync().windowHeight
}
},
methods: {
handleToPwd() {
this.$tab.navigateTo('/pages/mine/pwd/index')
},
methods: {
handleToPwd() {
this.$tab.navigateTo('/pages/mine/pwd/index')
},
handleToUpgrade() {
this.$modal.showToast('模块建设中~')
},
handleCleanTmp() {
this.$modal.showToast('模块建设中~')
},
handleLogout() {
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
this.$store.dispatch('auth/LogOut')
})
}
handleToUpgrade() {
this.$modal.showToast('模块建设中~')
},
handleCleanTmp() {
this.$modal.showToast('模块建设中~')
},
handleLogout() {
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
this.$store.dispatch('auth/LogOut')
})
}
}
}
</script>
<style lang="scss">
.page {
background-color: #f8f8f8;
}
.page {
background-color: #f8f8f8;
}
.item-box {
background-color: #FFFFFF;
margin: 30rpx;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 10rpx;
border-radius: 8rpx;
color: #303133;
font-size: 32rpx;
}
.item-box {
background-color: #ffffff;
margin: 30rpx;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 10rpx;
border-radius: 8rpx;
color: #303133;
font-size: 32rpx;
}
</style>

View File

@ -2,99 +2,92 @@
<view class="work-container">
<!-- 轮播图 -->
<u-swiper
:list="images"
indicator
indicatorMode="line"
circular
:height="`${windowWidth / 2.5}px`"
></u-swiper>
:list="images"
indicator
indicator-mode="line"
circular
:height="`${windowWidth / 2.5}px`"
></u-swiper>
<!-- 宫格组件 -->
<view class="grid-body">
<u-grid
:border="false"
col="3"
@click="changeGrid"
>
<u-grid-item
v-for="(item, index) in baseList"
:key="index"
>
<view class="grid-item">
<view :class="'iconfont ' + item.icon + ' grid-icon'"></view>
<u--text :text="item.title" align="center" lineHeight="32px"></u--text>
</view>
</u-grid-item>
</u-grid>
<u-grid :border="false" col="3" @click="changeGrid">
<u-grid-item v-for="(item, index) in baseList" :key="index">
<view class="grid-item">
<view :class="'iconfont ' + item.icon + ' grid-icon'"></view>
<u--text :text="item.title" align="center" line-height="32px"></u--text>
</view>
</u-grid-item>
</u-grid>
</view>
</view>
</template>
<script>
export default {
data() {
return {
windowWidth: uni.getSystemInfoSync().windowWidth,
images: [
'https://ktianc.oss-cn-beijing.aliyuncs.com/kinit/system/banner/2022-11-14/1.jpg',
'/static/images/banner/banner03.jpg',
'/static/images/banner/banner03.jpg'
],
baseList: [
{
icon: 'icon-user1',
title: '用户管理'
},
{
icon: 'icon-users',
title: '角色管理'
},
{
icon: 'icon-caidan3',
title: '菜单管理'
},
{
icon: 'icon-shezhitianchong',
title: '系统配置'
},
{
icon: 'icon-changguizidian',
title: '字典管理'
},
{
icon: 'icon-rizhi',
title: '日志管理'
},
]
}
},
methods: {
changeGrid(e) {
this.$modal.showToast('模块建设中~')
}
export default {
data() {
return {
windowWidth: uni.getSystemInfoSync().windowWidth,
images: [
'https://ktianc.oss-cn-beijing.aliyuncs.com/kinit/system/banner/2022-11-14/1.jpg',
'/static/images/banner/banner03.jpg',
'/static/images/banner/banner03.jpg'
],
baseList: [
{
icon: 'icon-user1',
title: '用户管理'
},
{
icon: 'icon-users',
title: '角色管理'
},
{
icon: 'icon-caidan3',
title: '菜单管理'
},
{
icon: 'icon-shezhitianchong',
title: '系统配置'
},
{
icon: 'icon-changguizidian',
title: '字典管理'
},
{
icon: 'icon-rizhi',
title: '日志管理'
}
]
}
},
methods: {
changeGrid(e) {
this.$modal.showToast('模块建设中~')
}
}
}
</script>
<style lang="scss">
page {
background-color: #fff;
min-height: 100%;
height: auto;
}
page {
background-color: #fff;
min-height: 100%;
height: auto;
}
</style>
<style lang="scss" scoped>
.grid-body {
margin-top: 60rpx;
.grid-item {
margin-bottom: 30rpx;
text-align: center;
}
.grid-icon {
font-size: 40rpx;
}
}
.grid-body {
margin-top: 60rpx;
.grid-item {
margin-bottom: 30rpx;
text-align: center;
}
.grid-icon {
font-size: 40rpx;
}
}
</style>

View File

@ -1,54 +1,60 @@
import { getToken } from '@/common/utils/auth'
import store from '@/store'
import { RouterMount, createRouter } from 'uni-simple-router';
import { RouterMount, createRouter } from 'uni-simple-router'
// uni-simple-router 官方文档https://www.hhyang.cn/v2/start/cross/codeRoute.html
// 登录页面
const loginPage = "/pages/login/login"
const loginPage = '/pages/login/login'
// 首页
const indexPage = '/pages/index'
const router = createRouter({
platform: process.env.VUE_APP_PLATFORM,
routes: [...ROUTES]
});
platform: process.env.VUE_APP_PLATFORM,
detectBeforeLock: (router, to, navType) => {
if (navType === 'replaceAll' && (to.path === loginPage || to.path === indexPage)) {
router.$lockStatus = false // 取消跳转锁
}
},
routes: [...ROUTES] // ROUTES是通过webpack的defaultPlugin编译成全局变量
})
//全局路由前置守卫
router.beforeEach((to, from, next) => {
if (to.meta.loginAuth) {
// 如果跳转的路由需要登录权限则验证该权限
if (getToken()) {
if (!store.state.auth.isUser) {
store.dispatch('auth/GetInfo')
}
if (to.path === loginPage) {
next({
path: `/pages/index`,
NAVTYPE: 'replaceAll'
})
}
next();
} else {
next({
path: loginPage,
NAVTYPE: 'replaceAll'
})
}
} else if (to.path === loginPage && getToken()) {
// 如果跳转路由为登录页面并且存在token则跳转到首页
next({
path: `/pages/index`,
if (to.meta.loginAuth) {
// 如果跳转的路由需要登录权限则验证该权限
if (getToken()) {
if (!store.state.auth.isUser) {
store.dispatch('auth/GetInfo')
}
if (to.path === loginPage) {
next({
path: indexPage,
NAVTYPE: 'replaceAll'
})
}
next()
} else {
next({
path: loginPage,
NAVTYPE: 'replaceAll'
})
}
} else if (to.path === loginPage && getToken()) {
// 如果跳转路由为登录页面并且存在token则跳转到首页
next({
path: indexPage,
NAVTYPE: 'replaceAll'
})
} else {
// 不需要权限且不是登录页面则不进行验证
next();
}
});
} else {
// 不需要权限且不是登录页面则不进行验证
next()
}
})
// 全局路由后置守卫
router.afterEach((to, from) => {
// console.log('跳转结束')
// console.log('跳转结束')
})
export {
router,
RouterMount
}
export { router, RouterMount }

View File

@ -1,10 +1,10 @@
import store from '@/store'
function authPermission(permission) {
const all_permission = "*:*:*"
const all_permission = '*:*:*'
const permissions = store.getters && store.getters.permissions
if (permission && permission.length > 0) {
return permissions.some(v => {
return permissions.some((v) => {
return all_permission === v || v === permission
})
} else {
@ -13,10 +13,10 @@ function authPermission(permission) {
}
function authRole(role) {
const super_admin = "admin"
const super_admin = 'admin'
const roles = store.getters && store.getters.roles
if (role && role.length > 0) {
return roles.some(v => {
return roles.some((v) => {
return super_admin === v || v === role
})
} else {
@ -31,13 +31,13 @@ export default {
},
// 验证用户是否含有指定权限只需包含其中一个
hasPermiOr(permissions) {
return permissions.some(item => {
return permissions.some((item) => {
return authPermission(item)
})
},
// 验证用户是否含有指定权限必须全部拥有
hasPermiAnd(permissions) {
return permissions.every(item => {
return permissions.every((item) => {
return authPermission(item)
})
},
@ -47,13 +47,13 @@ export default {
},
// 验证用户是否含有指定角色只需包含其中一个
hasRoleOr(roles) {
return roles.some(item => {
return roles.some((item) => {
return authRole(item)
})
},
// 验证用户是否含有指定角色必须全部拥有
hasRoleAnd(roles) {
return roles.every(item => {
return roles.every((item) => {
return authRole(item)
})
}

View File

@ -40,7 +40,7 @@ export default {
content: content,
cancelText: '取消',
confirmText: '确定',
success: function(res) {
success: function (res) {
if (res.confirm) {
resolve(res.confirm)
}
@ -50,12 +50,12 @@ export default {
},
// 提示信息
showToast(option) {
if (typeof option === "object") {
if (typeof option === 'object') {
uni.showToast(option)
} else {
uni.showToast({
title: option,
icon: "none",
icon: 'none',
duration: 2500
})
}

View File

@ -0,0 +1,19 @@
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: false,
vueIndentScriptAndStyle: false,
singleQuote: true,
quoteProps: 'as-needed',
bracketSpacing: true,
trailingComma: 'none',
jsxSingleQuote: false,
arrowParens: 'always',
insertPragma: false,
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'auto',
rangeStart: 0
}

View File

@ -1,26 +1,26 @@
const getters = {
isUser: state => state.auth.isUser,
isUserOpenid: state => state.auth.isUserOpenid,
isResetPassword: state => state.auth.isResetPassword,
token: state => state.auth.token,
avatar: state => state.auth.avatar,
name: state => state.auth.name,
roles: state => state.auth.roles,
permissions: state => state.auth.permissions,
telephone: state => state.auth.telephone,
version: state => state.app.version,
title: state => state.app.title,
logo: state => state.app.logo,
logoImage: state => state.app.logoImage,
footer: state => state.app.footer,
footerContent: state => state.app.footerContent,
icpNumber: state => state.app.icpNumber,
privacy: state => state.app.privacy,
agreement: state => state.app.agreement,
siteUrl: state => state.app.siteUrl,
WXEmail: state => state.app.WXEmail,
WXPhone: state => state.app.WXPhone,
dictObj: state => state.dict.dictObj,
isUser: (state) => state.auth.isUser,
isUserOpenid: (state) => state.auth.isUserOpenid,
isResetPassword: (state) => state.auth.isResetPassword,
token: (state) => state.auth.token,
avatar: (state) => state.auth.avatar,
name: (state) => state.auth.name,
roles: (state) => state.auth.roles,
permissions: (state) => state.auth.permissions,
telephone: (state) => state.auth.telephone,
version: (state) => state.app.version,
title: (state) => state.app.title,
logo: (state) => state.app.logo,
logoImage: (state) => state.app.logoImage,
footer: (state) => state.app.footer,
footerContent: (state) => state.app.footerContent,
icpNumber: (state) => state.app.icpNumber,
privacy: (state) => state.app.privacy,
agreement: (state) => state.app.agreement,
siteUrl: (state) => state.app.siteUrl,
WXEmail: (state) => state.app.WXEmail,
WXPhone: (state) => state.app.WXPhone,
dictObj: (state) => state.dict.dictObj
}
export default getters

View File

@ -21,5 +21,5 @@ const store = new Vuex.Store({
modules,
getters
})
export default store
export default store

View File

@ -2,76 +2,77 @@ import config from '@/config.js'
import { getSystemBaseConfigApi } from '@/common/request/api/vadmin/system/settings.js'
const state = {
title: "", // 标题
logo: true, // 是否开启logo显示
logoImage: '', // logo图片
footer: true, // 显示页脚
footerContent: '', // 页脚内容
icpNumber: '', // 备案号
version: config.appInfo.version, // 版本
privacy: config.appInfo.privacy, // 隐私政策
agreement: config.appInfo.agreement, // 用户协议
siteUrl: "", // 源码地址
WXEmail: "", // 官方邮箱
WXPhone: "" // 服务热线
title: '', // 标题
logo: true, // 是否开启logo显示
logoImage: '', // logo图片
footer: true, // 显示页脚
footerContent: '', // 页脚内容
icpNumber: '', // 备案号
version: config.appInfo.version, // 版本
privacy: config.appInfo.privacy, // 隐私政策
agreement: config.appInfo.agreement, // 用户协议
siteUrl: '', // 源码地址
WXEmail: '', // 官方邮箱
WXPhone: '' // 服务热线
}
const mutations = {
SET_TITLE: (state, title) => {
state.title = title
},
SET_LOGO: (state, logo) => {
state.logo = logo
},
SET_LOGO_IMAGE: (state, logoImage) => {
state.logoImage = logoImage
},
SET_FOOTER: (state, footer) => {
state.footer = footer
},
SET_FOOTER_CONTENT: (state, footerContent) => {
state.footerContent = footerContent
},
SET_ICPNUMBER: (state, icpNumber) => {
state.icpNumber = icpNumber
},
SET_VERSION: (state, version) => {
state.version = version
},
SET_SITE_URL: (state, siteUrl) => {
state.siteUrl = siteUrl
},
SET_WX_EMAIL: (state, WXEmail) => {
state.WXEmail = WXEmail
},
SET_WX_PHONE: (state, WXPhone) => {
state.WXPhone = WXPhone
},
SET_TITLE: (state, title) => {
state.title = title
},
SET_LOGO: (state, logo) => {
state.logo = logo
},
SET_LOGO_IMAGE: (state, logoImage) => {
state.logoImage = logoImage
},
SET_FOOTER: (state, footer) => {
state.footer = footer
},
SET_FOOTER_CONTENT: (state, footerContent) => {
state.footerContent = footerContent
},
SET_ICPNUMBER: (state, icpNumber) => {
state.icpNumber = icpNumber
},
SET_VERSION: (state, version) => {
state.version = version
},
SET_SITE_URL: (state, siteUrl) => {
state.siteUrl = siteUrl
},
SET_WX_EMAIL: (state, WXEmail) => {
state.WXEmail = WXEmail
},
SET_WX_PHONE: (state, WXPhone) => {
state.WXPhone = WXPhone
}
}
const actions = {
// 初始化系统配置
InitConfig({ commit }) {
return new Promise((resolve, reject) => {
getSystemBaseConfigApi().then(res => {
commit('SET_TITLE', res.data.web_title || 'Kinit')
commit('SET_LOGO_IMAGE', config.baseUrl + (res.data.web_logo || '/media/system/logo.png'))
commit('SET_FOOTER_CONTENT', res.data.web_copyright || 'Copyright ©2022-present K')
commit('SET_ICPNUMBER', res.data.web_icp_number || '')
commit('SET_SITE_URL', res.data.wx_server_site || '')
commit('SET_WX_EMAIL', res.data.wx_server_email || '')
commit('SET_WX_PHONE', res.data.wx_server_phone || '')
resolve()
}).catch(error => {
reject(error)
})
})
}
// 初始化系统配置
InitConfig({ commit }) {
return new Promise((resolve, reject) => {
getSystemBaseConfigApi()
.then((res) => {
commit('SET_TITLE', res.data.web_title || 'Kinit')
commit('SET_LOGO_IMAGE', config.baseUrl + (res.data.web_logo || '/media/system/logo.png'))
commit('SET_FOOTER_CONTENT', res.data.web_copyright || 'Copyright ©2022-present K')
commit('SET_ICPNUMBER', res.data.web_icp_number || '')
commit('SET_SITE_URL', res.data.wx_server_site || '')
commit('SET_WX_EMAIL', res.data.wx_server_email || '')
commit('SET_WX_PHONE', res.data.wx_server_phone || '')
resolve()
})
.catch((error) => {
reject(error)
})
})
}
}
export default {
namespaced: true, // 使用命名空间去访问模块中属性user/login
namespaced: true, // 使用命名空间去访问模块中属性user/login
state,
mutations,
actions

View File

@ -1,164 +1,192 @@
import storage from '@/common/utils/storage'
import { auth } from '@/common/utils/constant'
import { getInfo, wxCodeLogin, login } from '@/common/request/api/login'
import { getToken, setToken, removeToken } from '@/common/utils/auth'
import {
getToken,
setToken,
removeToken,
getRefreshToken,
setRefreshToken,
removeRefreshToken
} from '@/common/utils/auth'
const state = {
token: getToken(),
isUser: storage.get(auth.isUser) || false,
isUserOpenid: storage.get(auth.isUserOpenid) || false,
isResetPassword: storage.get(auth.isResetPassword) || false,
name: storage.get(auth.name),
nickname: storage.get(auth.nickname),
gender: storage.get(auth.gender),
telephone: storage.get(auth.telephone),
avatar: storage.get(auth.avatar),
createDatetime: storage.get(auth.createDatetime),
roles: storage.get(auth.roles),
permissions: storage.get(auth.permissions)
token: getToken(),
refreshToken: getRefreshToken(),
isUser: storage.get(auth.isUser) || false,
isUserOpenid: storage.get(auth.isUserOpenid) || false,
isResetPassword: storage.get(auth.isResetPassword) || false,
name: storage.get(auth.name),
nickname: storage.get(auth.nickname),
gender: storage.get(auth.gender),
telephone: storage.get(auth.telephone),
avatar: storage.get(auth.avatar),
createDatetime: storage.get(auth.createDatetime),
roles: storage.get(auth.roles),
permissions: storage.get(auth.permissions)
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_IS_USER_OPENID: (state, isUserOpenid) => {
state.isUserOpenid = isUserOpenid
storage.set(auth.isUserOpenid, isUserOpenid)
},
SET_IS_RESET_PASSWORD: (state, isResetPassword) => {
state.isResetPassword = isResetPassword
storage.set(auth.isResetPassword, isResetPassword)
},
SET_NAME: (state, name) => {
state.name = name
storage.set(auth.name, name)
},
SET_GENDER: (state, gender) => {
state.gender = gender
storage.set(auth.gender, gender)
},
SET_NICKNAME: (state, nickname) => {
state.nickname = nickname
storage.set(auth.nickname, nickname)
},
SET_CREATE_DATETIME: (state, createDatetime) => {
state.createDatetime = createDatetime
storage.set(auth.createDatetime, createDatetime)
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
storage.set(auth.avatar, avatar)
},
SET_ROLES: (state, roles) => {
state.roles = roles
storage.set(auth.roles, roles)
},
SET_PERMISSIONS: (state, permissions) => {
state.permissions = permissions
storage.set(auth.permissions, permissions)
},
SET_TELEPHONE: (state, telephone) => {
state.telephone = telephone
storage.set(auth.telephone, telephone)
},
SET_ISUSER: (state, isUser) => {
state.isUser = isUser
storage.set(auth.isUser, isUser)
},
SET_TOKEN: (state, token) => {
state.token = token
},
SET_REFRESH_TOKEN: (state, refreshToken) => {
state.refreshToken = refreshToken
setRefreshToken(refreshToken)
},
SET_IS_USER_OPENID: (state, isUserOpenid) => {
state.isUserOpenid = isUserOpenid
storage.set(auth.isUserOpenid, isUserOpenid)
},
SET_IS_RESET_PASSWORD: (state, isResetPassword) => {
state.isResetPassword = isResetPassword
storage.set(auth.isResetPassword, isResetPassword)
},
SET_NAME: (state, name) => {
state.name = name
storage.set(auth.name, name)
},
SET_GENDER: (state, gender) => {
state.gender = gender
storage.set(auth.gender, gender)
},
SET_NICKNAME: (state, nickname) => {
state.nickname = nickname
storage.set(auth.nickname, nickname)
},
SET_CREATE_DATETIME: (state, createDatetime) => {
state.createDatetime = createDatetime
storage.set(auth.createDatetime, createDatetime)
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
storage.set(auth.avatar, avatar)
},
SET_ROLES: (state, roles) => {
state.roles = roles
storage.set(auth.roles, roles)
},
SET_PERMISSIONS: (state, permissions) => {
state.permissions = permissions
storage.set(auth.permissions, permissions)
},
SET_TELEPHONE: (state, telephone) => {
state.telephone = telephone
storage.set(auth.telephone, telephone)
},
SET_ISUSER: (state, isUser) => {
state.isUser = isUser
storage.set(auth.isUser, isUser)
}
}
const actions = {
// 手机号密码登录
Login({ commit }, userInfo) {
const telephone = userInfo.telephone.trim()
const password = userInfo.password
return new Promise((resolve, reject) => {
login(telephone, password).then(res => {
setToken(`${res.data.token_type} ${res.data.access_token}`)
commit('SET_TOKEN', `${res.data.token_type} ${res.data.access_token}`)
commit('SET_IS_USER_OPENID', res.data.is_wx_server_openid)
commit('SET_IS_RESET_PASSWORD', res.data.is_reset_password)
resolve(res)
}).catch(error => {
reject(error)
})
})
},
// 微信一键登录
// 微信文档https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html
wxLogin({ commit }, code) {
return new Promise((resolve, reject) => {
wxCodeLogin(code).then(res => {
setToken(`${res.data.token_type} ${res.data.access_token}`)
commit('SET_TOKEN', `${res.data.token_type} ${res.data.access_token}`)
commit('SET_IS_USER_OPENID', res.data.is_wx_server_openid)
commit('SET_IS_RESET_PASSWORD', res.data.is_reset_password)
resolve(res)
}).catch(error => {
reject(error)
})
})
},
// 手机号密码登录
Login({ commit }, userInfo) {
const telephone = userInfo.telephone.trim()
const password = userInfo.password
return new Promise((resolve, reject) => {
login(telephone, password)
.then((res) => {
setToken(`${res.data.token_type} ${res.data.access_token}`)
commit('SET_TOKEN', `${res.data.token_type} ${res.data.access_token}`)
commit('SET_REFRESH_TOKEN', res.data.refresh_token)
commit('SET_IS_USER_OPENID', res.data.is_wx_server_openid)
commit('SET_IS_RESET_PASSWORD', res.data.is_reset_password)
resolve(res)
})
.catch((error) => {
reject(error)
})
})
},
// 获取用户信息
GetInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo().then(res => {
const user = res.data
const avatar = (user == null || user.avatar == "" || user.avatar == null) ? "https://vv-reserve.oss-cn-hangzhou.aliyuncs.com/avatar/2023-01-27/1674820804e81e7631.png" : user.avatar
const name = (user == null || user.name == "" || user.name == null) ? "" : user.name
commit('SET_ROLES', user.roles.map((item) => item.name) || ['ROLE_DEFAULT'])
commit('SET_PERMISSIONS', user.permissions)
commit('SET_NAME', name)
commit('SET_NICKNAME', user.nickname)
commit('SET_GENDER', user.gender)
commit('SET_TELEPHONE', user.telephone)
commit('SET_AVATAR', avatar)
commit('SET_CREATE_DATETIME', user.create_datetime)
commit('SET_ISUSER', true)
resolve(res)
}).catch(error => {
reject(error)
})
})
},
// 更新用户基本信息
UpdateInfo({ commit }, user) {
commit('SET_NAME', user.name)
commit('SET_NICKNAME', user.nickname)
commit('SET_GENDER', user.gender)
commit('SET_TELEPHONE', user.telephone)
},
// 微信一键登录
// 微信文档https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html
wxLogin({ commit }, code) {
return new Promise((resolve, reject) => {
wxCodeLogin(code)
.then((res) => {
setToken(`${res.data.token_type} ${res.data.access_token}`)
commit('SET_TOKEN', `${res.data.token_type} ${res.data.access_token}`)
commit('SET_REFRESH_TOKEN', res.data.refresh_token)
commit('SET_IS_USER_OPENID', res.data.is_wx_server_openid)
commit('SET_IS_RESET_PASSWORD', res.data.is_reset_password)
resolve(res)
})
.catch((error) => {
reject(error)
})
})
},
// 退出系统
LogOut({ commit }) {
return new Promise((resolve, reject) => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_PERMISSIONS', [])
commit('SET_NAME', "")
commit('SET_NICKNAME', "")
commit('SET_GENDER', "")
commit('SET_TELEPHONE', "")
commit('SET_AVATAR', "")
commit('SET_CREATE_DATETIME', "")
commit('SET_IS_USER_OPENID', false)
commit('SET_IS_RESET_PASSWORD', false)
commit('SET_ISUSER', false)
removeToken()
storage.clean()
uni.reLaunch({ url: '/pages/login/login' })
resolve()
})
}
// 获取用户信息
GetInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo()
.then((res) => {
const user = res.data
const avatar =
user == null || user.avatar == '' || user.avatar == null
? 'https://vv-reserve.oss-cn-hangzhou.aliyuncs.com/avatar/2023-01-27/1674820804e81e7631.png'
: user.avatar
const name = user == null || user.name == '' || user.name == null ? '' : user.name
commit('SET_ROLES', user.roles.map((item) => item.name) || ['ROLE_DEFAULT'])
commit('SET_PERMISSIONS', user.permissions)
commit('SET_NAME', name)
commit('SET_NICKNAME', user.nickname)
commit('SET_GENDER', user.gender)
commit('SET_TELEPHONE', user.telephone)
commit('SET_AVATAR', avatar)
commit('SET_CREATE_DATETIME', user.create_datetime)
commit('SET_ISUSER', true)
resolve(res)
})
.catch((error) => {
reject(error)
})
})
},
// 更新用户基本信息
UpdateInfo({ commit }, user) {
commit('SET_NAME', user.name)
commit('SET_NICKNAME', user.nickname)
commit('SET_GENDER', user.gender)
commit('SET_TELEPHONE', user.telephone)
},
// 退出系统
LogOut({ commit }) {
return new Promise((resolve, reject) => {
commit('SET_TOKEN', '')
commit('SET_REFRESH_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_PERMISSIONS', [])
commit('SET_NAME', '')
commit('SET_NICKNAME', '')
commit('SET_GENDER', '')
commit('SET_TELEPHONE', '')
commit('SET_AVATAR', '')
commit('SET_CREATE_DATETIME', '')
commit('SET_IS_USER_OPENID', false)
commit('SET_IS_RESET_PASSWORD', false)
commit('SET_ISUSER', false)
removeToken()
removeRefreshToken()
storage.clean()
uni.reLaunch({
url: '/pages/login/login',
complete: () => {
resolve()
}
})
})
}
}
export default {
namespaced: true, // 使用命名空间去访问模块中属性user/login
namespaced: true, // 使用命名空间去访问模块中属性user/login
state,
mutations,
actions

View File

@ -1,51 +1,52 @@
import { getDictTypeDetailsApi } from '@/common/request/api/vadmin/system/dict.js'
const state = {
dictObj: {}, // 字典元素
dictObj: {} // 字典元素
}
const mutations = {
SET_DICT_OBJ: (state, dictObj) => {
state.dictObj = dictObj
}
SET_DICT_OBJ: (state, dictObj) => {
state.dictObj = dictObj
}
}
const actions = {
// 获取字典选项
getDicts({ commit, state }, dictTypes) {
return new Promise((resolve, reject) => {
const result = {}
const addList = []
const dictObj = JSON.parse(JSON.stringify(state.dictObj))
for (const item of dictTypes) {
if (item in dictObj) {
result[item] = dictObj[item]
} else {
result[item] = []
addList.push(item)
}
}
if (addList.length > 0) {
getDictTypeDetailsApi(addList).then(res => {
for (const item of addList) {
result[item] = res.data[item]
dictObj[item] = res.data[item]
}
commit('SET_DICT_OBJ', dictObj)
resolve(result)
}).catch(error => {
reject(error)
})
} else {
resolve(result)
}
})
}
// 获取字典选项
getDicts({ commit, state }, dictTypes) {
return new Promise((resolve, reject) => {
const result = {}
const addList = []
const dictObj = JSON.parse(JSON.stringify(state.dictObj))
for (const item of dictTypes) {
if (item in dictObj) {
result[item] = dictObj[item]
} else {
result[item] = []
addList.push(item)
}
}
if (addList.length > 0) {
getDictTypeDetailsApi(addList)
.then((res) => {
for (const item of addList) {
result[item] = res.data[item]
dictObj[item] = res.data[item]
}
commit('SET_DICT_OBJ', dictObj)
resolve(result)
})
.catch((error) => {
reject(error)
})
} else {
resolve(result)
}
})
}
}
export default {
namespaced: true, // 使用命名空间去访问模块中属性user/login
namespaced: true, // 使用命名空间去访问模块中属性user/login
state,
mutations,
actions

View File

@ -1,26 +1,26 @@
//vue.config.js
const TransformPages = require('uni-read-pages')
const {webpack} = new TransformPages()
const { webpack } = new TransformPages()
module.exports = {
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
ROUTES: webpack.DefinePlugin.runtimeValue(() => {
const tfPages = new TransformPages({
// includes 中包含的是router会读取pages路由中的字段名
// 后续如果有用到meta等路由信息可以在 includes 里增加 'meta'
// 在pages路由中写对应的数据router中就可以获取得到
includes: ['path', 'name', 'aliasPath', 'meta']
});
return JSON.stringify(tfPages.routes)
}, true )
})
]
},
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
ROUTES: webpack.DefinePlugin.runtimeValue(() => {
const tfPages = new TransformPages({
// includes 中包含的是router会读取pages路由中的字段名
// 后续如果有用到meta等路由信息可以在 includes 里增加 'meta'
// 在pages路由中写对应的数据router中就可以获取得到
includes: ['path', 'name', 'aliasPath', 'meta']
})
return JSON.stringify(tfPages.routes)
}, true)
})
]
},
devServer: {
port: 8080,
https: false,
disableHostCheck: true // 禁止访问本地host文件
port: 8080,
https: false,
disableHostCheck: true // 禁止访问本地host文件
}
}
}