新增加Uniapp移动端
This commit is contained in:
parent
d7a4b9819c
commit
417bfcde05
141
README.md
141
README.md
@ -20,7 +20,8 @@
|
||||
Kinit 是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
|
||||
|
||||
- 后端采用 Python 语言现代、快速(高性能) [FastAPI](https://fastapi.tiangolo.com/zh/) 异步框架 + [SQLAlchemy](https://www.sqlalchemy.org/) 异步操作 [MySQL](https://www.mysql.com/) 数据库。
|
||||
- 前端采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 、[Vue3](https://cn.vuejs.org/guide/introduction.html)、[Element Plus](https://element-plus.gitee.io/zh-CN/guide/design.html)、[TypeScript](https://www.tslang.cn/),等主流技术开发。
|
||||
- PC端采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 、[Vue3](https://cn.vuejs.org/guide/introduction.html)、[Element Plus](https://element-plus.gitee.io/zh-CN/guide/design.html)、[TypeScript](https://www.tslang.cn/)等主流技术开发。
|
||||
- 移动端采用 [uni-app](https://uniapp.dcloud.net.cn/component/),[Vue2](https://v2.cn.vuejs.org/v2/guide/),[uView 2](https://www.uviewui.com/components/intro.html)为主要技术开发
|
||||
- 新加入 [Typer](https://typer.tiangolo.com/) 命令行应用,简单化数据初始化,数据表模型迁移。
|
||||
- 权限认证使用[(哈希)密码和 JWT Bearer 令牌的 OAuth2](https://fastapi.tiangolo.com/zh/tutorial/security/oauth2-jwt/),支持多终端认证系统。
|
||||
- 支持加载动态权限菜单,多方式轻松权限控制,按钮级别权限控制。
|
||||
@ -32,15 +33,15 @@ Kinit 是一套全部开源的快速开发平台,毫无保留给个人及企
|
||||
|
||||
[vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin):一套基于vue3、element-plus、typescript4、vite3的后台集成方案
|
||||
|
||||
[django-vue-admin](https://gitee.com/liqianglog/django-vue-admin):基于RBAC模型的权限控制的一整套基础开发平台,前后端分离,后端采用 django+django-rest-framework,前端采用 vue+ElementUI。
|
||||
|
||||
[RuoYi 若依官方网站](http://www.ruoyi.vip/):RuoYi 是一个后台管理系统,基于经典技术组合(Spring Boot、Apache Shiro、MyBatis、Thymeleaf)主要目的让开发者注重专注业务,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量。
|
||||
|
||||
[django-vue-admin](https://gitee.com/liqianglog/django-vue-admin):基于RBAC模型的权限控制的一整套基础开发平台,前后端分离,后端采用 django+django-rest-framework,前端采用 vue+ElementUI。
|
||||
|
||||
[Ant Design Pro](https://preview.pro.ant.design/dashboard/analysis):开箱即用的中台前端/设计解决方案
|
||||
|
||||
[Gin-Vue-Admin](https://demo.gin-vue-admin.com):基于vite+vue3+gin搭建的开发基础平台(支持TS,JS混用),集成jwt鉴权,权限管理,动态路由,显隐可控组件,分页封装,多点登录拦截,资源权限,上传下载,代码生成器,表单生成器等开发必备功能。
|
||||
|
||||
[Vben Admin (vvbin.cn)](https://vvbin.cn/next):Vue Vben Admin 是一个免费开源的中后台模版。使用了最新的`vue3`,`vite2`,`TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考。
|
||||
[Vben Admin](https://doc.vvbin.cn/guide/introduction.html):Vue Vben Admin 是一个免费开源的中后台模版。使用了最新的`vue3`,`vite2`,`TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考。
|
||||
|
||||
[中华人民共和国行政区划 (github.com)](https://github.com/modood/Administrative-divisions-of-China):省级(省份)、 地级(城市)、 县级(区县)、 乡级(乡镇街道)、 村级(村委会居委会) ,中国省市区镇村二级三级四级五级联动地址数据。
|
||||
|
||||
@ -48,11 +49,20 @@ Kinit 是一套全部开源的快速开发平台,毫无保留给个人及企
|
||||
|
||||
[小诺开源技术 (xiaonuo.vip)](https://www.xiaonuo.vip/):国内首个国密前后端分离快速开发平台
|
||||
|
||||
[my-web:](https://gitee.com/newgateway/my-web):MyWeb 是一个企业级中后台前端/设计解决方案的的项目工程模板,它可以帮助你快速搭建企业级中后台产品原型
|
||||
[my-web](https://gitee.com/newgateway/my-web):MyWeb 是一个企业级中后台前端/设计解决方案的的项目工程模板,它可以帮助你快速搭建企业级中后台产品原型
|
||||
|
||||
## 在线体验
|
||||
|
||||
演示地址:http://kinit.ktianc.top/
|
||||
PC端演示地址:http://admin.kinit.top
|
||||
|
||||
移动端演示地址:http://h5.kinit.top
|
||||
|
||||
微信小程序端演示:
|
||||
|
||||
- 搜索:kinit
|
||||
- 扫码:
|
||||
|
||||
<img src="https://gitee.com/ktianc/kinit/raw/master/images/uni/gh_5566dcf85bf0_860.jpg" alt="image-20221010214526082" style="zoom:33%;" />
|
||||
|
||||
- 账号:15020221010
|
||||
- 密码:kinit2022
|
||||
@ -63,7 +73,7 @@ gitee地址(主推):https://gitee.com/ktianc/kinit
|
||||
|
||||
github地址:https://github.com/vvandk/kinit
|
||||
|
||||
## 内置功能
|
||||
## PC端内置功能
|
||||
|
||||
- [x] 菜单管理:配置系统菜单,操作权限,按钮权限标识、后端接口权限等。
|
||||
|
||||
@ -77,7 +87,7 @@ github地址:https://github.com/vvandk/kinit
|
||||
|
||||
- [x] 文件上传:对接阿里云OSS与本地存储。
|
||||
|
||||
- [x] 登录认证:目前支持用户使用手机号+密码方式登录。
|
||||
- [x] 登录认证:目前支持用户使用手机号+密码方式或者手机验证码登录。
|
||||
|
||||
说明:新建用户密码默认为手机号后六位;
|
||||
|
||||
@ -99,8 +109,6 @@ github地址:https://github.com/vvandk/kinit
|
||||
|
||||
- [x] 导入导出:灵活支持数据导入导出功能
|
||||
|
||||
- [x] 手机验证码登录功能
|
||||
|
||||
- [x] 简单适配手机端:
|
||||
|
||||
1. 工作台招呼语一行显示,多余显示省略号
|
||||
@ -109,10 +117,22 @@ github地址:https://github.com/vvandk/kinit
|
||||
4. 表格工具栏更新,手机端取消文字显示
|
||||
5. 表格操作按钮多的时候自动叠起
|
||||
|
||||
- [x] 已加入常见的`Redis`、`MYSQL`、`MongoDB`数据库异步操作。
|
||||
- [x] 已加入常见的`Redis`、`MySQL`、`MongoDB`数据库异步操作。
|
||||
|
||||
- [x] 命令行操作:新加入 `Typer` 命令行应用,简单化数据初始化,数据表模型迁移。
|
||||
|
||||
## 移动端内置功能
|
||||
|
||||
- [x] 登录认证:目前支持用户使用手机号+密码方式登录。
|
||||
|
||||
说明:新建用户密码默认为手机号后六位;
|
||||
|
||||
说明:用户在第一次登录时,必须修改当前用户密码。
|
||||
|
||||
- [x] 导航栏:首页、我的、工作台
|
||||
|
||||
- [x] 我的基础功能:编辑资料、头像修改、密码修改、常见问题、关于我们等
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] 考虑支持多机部署方案,如果接口使用多机,那么用户是否支持统一认证
|
||||
@ -123,8 +143,15 @@ github地址:https://github.com/vvandk/kinit
|
||||
|
||||
## 前序准备
|
||||
|
||||
- [FastAPI](https://fastapi.tiangolo.com/zh/) - 熟悉后台接口 Web 框架
|
||||
### 后端技术
|
||||
|
||||
- [Python3](https://www.python.org/downloads/windows/):熟悉 python3 基础语法
|
||||
- [FastAPI](https://fastapi.tiangolo.com/zh/) - 熟悉后台接口 Web 框架.
|
||||
- [Typer](https://typer.tiangolo.com/) - 熟悉命令行工具的使用
|
||||
- [MySQL](https://www.mysql.com/) 和 [MongoDB](https://www.mongodb.com/) - 熟悉数据存储数据库
|
||||
|
||||
### PC端
|
||||
|
||||
- [node](https://gitee.com/link?target=http%3A%2F%2Fnodejs.org%2F) 和 [git](https://gitee.com/link?target=https%3A%2F%2Fgit-scm.com%2F) - 项目开发环境
|
||||
- [Vite](https://gitee.com/link?target=https%3A%2F%2Fvitejs.dev%2F) - 熟悉 vite 特性
|
||||
- [Vue3](https://gitee.com/link?target=https%3A%2F%2Fv3.vuejs.org%2F) - 熟悉 Vue 基础语法
|
||||
@ -134,15 +161,26 @@ github地址:https://github.com/vvandk/kinit
|
||||
- [Element-Plus](https://gitee.com/link?target=https%3A%2F%2Felement-plus.org%2F) - element-plus 基本使用
|
||||
- [Mock.js](https://gitee.com/link?target=https%3A%2F%2Fgithub.com%2Fnuysoft%2FMock) - mockjs 基本语法
|
||||
|
||||
### 移动端
|
||||
|
||||
- [uni-app](https://uniapp.dcloud.net.cn/component/) - 熟悉 uni-app 基本语法
|
||||
- [Vue2](https://v2.cn.vuejs.org/v2/guide/) - 熟悉 Vue 基础语法
|
||||
- [uView UI 2](https://www.uviewui.com/components/intro.html):uView UI 组件的基本使用
|
||||
|
||||
### 依赖包
|
||||
|
||||
#### 前端
|
||||
#### PC端
|
||||
|
||||
- [vue3-json-viewer](https://gitee.com/isfive/vue3-json-viewer):简单易用的json内容展示组件,适配vue3和vite。
|
||||
- [vue3-slide-verify](https://github.com/monoplasty/vue3-slide-verify):滑块验证码插件 vue3 + typescript
|
||||
- [vue3-slide-verify](https://github.com/monoplasty/vue3-slide-verify):滑块验证码插件 vue3 + typescript。
|
||||
- [SortableJS/vue.draggable.next](https://github.com/SortableJS/vue.draggable.next):Vue 组件 (Vue.js 3.0) 允许拖放和与视图模型数组同步。
|
||||
- [高德地图API (amap.com)](https://lbs.amap.com/api/jsapi-v2/guide/webcli/map-vue1):地图 JSAPI 2.0 是高德开放平台免费提供的第四代 Web 地图渲染引擎, 以 WebGL 为主要绘图手段,本着“更轻、更快、更易用”的服务原则,广泛采用了各种前沿技术,交互体验、视觉体验大幅提升,同时提供了众多新增能力和特性。
|
||||
|
||||
#### 移动端
|
||||
|
||||
- [uni-read-pages](https://github.com/SilurianYang/uni-read-pages) :自动读取 `pages.json` 所有配置。
|
||||
- [uni-simple-router](https://hhyang.cn/v2/start/quickstart.html) :在uni-app中使用vue-router的方式进行跳转路由,路由拦截。
|
||||
|
||||
#### 后端
|
||||
|
||||
- [iP查询接口文档](https://user.ip138.com/ip/doc):IP查询第三方服务,有1000次的免费次数
|
||||
@ -169,11 +207,11 @@ Redis (推荐使用最新稳定版)
|
||||
|
||||
1. 安装依赖
|
||||
|
||||
```
|
||||
cd kinit-api
|
||||
|
||||
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||
```
|
||||
```
|
||||
cd kinit-api
|
||||
|
||||
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||
```
|
||||
|
||||
2. 修改项目数据库配置信息
|
||||
|
||||
@ -235,23 +273,32 @@ pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||
|
||||
```python
|
||||
# mysql+pymysql://数据库用户名:数据库密码@数据库地址:数据库端口/数据库名称
|
||||
sqlalchemy.url = mysql+pymysql://root:123456@127.0.0.1/kinit
|
||||
[dev]
|
||||
# 开发环境
|
||||
version_locations = %(here)s/alembic/versions_dev
|
||||
sqlalchemy.url = sqlalchemy.url = mysql+pymysql://root:123456@127.0.0.1/kinit
|
||||
|
||||
|
||||
[pro]
|
||||
# 生产环境
|
||||
version_locations = %(here)s/alembic/versions_pro
|
||||
sqlalchemy.url = sqlalchemy.url = mysql+pymysql://root:123456@127.0.0.1/kinit
|
||||
```
|
||||
|
||||
3. 创建数据库
|
||||
|
||||
```shell
|
||||
mysql> create database kinit; # 创建数据库
|
||||
mysql> use kinit; # 使用已创建的数据库
|
||||
mysql> set names utf8; # 设置编码
|
||||
```
|
||||
```
|
||||
mysql> create database kinit; # 创建数据库
|
||||
mysql> use kinit; # 使用已创建的数据库
|
||||
mysql> set names utf8; # 设置编码
|
||||
```
|
||||
|
||||
4. 初始化数据库数据
|
||||
|
||||
```shell
|
||||
# 进入项目根目录下执行
|
||||
python3 main.py init
|
||||
```
|
||||
```
|
||||
# 进入项目根目录下执行
|
||||
python3 main.py init
|
||||
```
|
||||
|
||||
5. 修改项目基本配置信息
|
||||
|
||||
@ -272,12 +319,12 @@ python3 main.py init
|
||||
|
||||
6. 启动
|
||||
|
||||
```shell
|
||||
# 进入项目根目录下执行
|
||||
python3 main.py run
|
||||
```
|
||||
```
|
||||
# 进入项目根目录下执行
|
||||
python3 main.py run
|
||||
```
|
||||
|
||||
### 前端
|
||||
### PC端
|
||||
|
||||
1. 安装依赖
|
||||
|
||||
@ -349,7 +396,7 @@ pnpm run build:pro
|
||||
|
||||
[MIT](https://gitee.com/kailong110120130/vue-element-plus-admin/blob/master/LICENSE)
|
||||
|
||||
## 演示图
|
||||
## PC端演示图
|
||||
|
||||

|
||||
|
||||
@ -357,12 +404,28 @@ pnpm run build:pro
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## 移动端演示图
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
@ -2,17 +2,12 @@
|
||||
|
||||
vue-element-plus-admin 是一个基于 `element-plus` 免费开源的中后台模版。使用了最新的`vue3`,`vite3`,`TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,可以用来作为项目的启动模版,也可用于学习参考。并且时刻关注着最新技术动向,尽可能的第一时间更新。
|
||||
|
||||
vue-element-plus-admin 的定位是后台集成方案,不太适合当基础模板来进行二次开发。因为集成了很多你可能用不到的功能,会造成不少的代码冗余。如果你的项目不关注这方面的问题,也可以直接基于它进行二次开发。
|
||||
|
||||
如需要基础模版,请切换到 `tempalte` 分支,`tempalte` 只简单集成了一些如:布局、动态菜单等常用布局功能,更适合开发者进行二次开发。
|
||||
|
||||
## 特性
|
||||
|
||||
- **最新技术栈**:使用 Vue3/vite3 等前端前沿技术开发
|
||||
- **TypeScript**: 应用程序级 JavaScript 的语言
|
||||
- **主题**: 可配置的主题
|
||||
- **国际化**:内置完善的国际化方案
|
||||
- **自定义数据** 内置 Mock 数据方案
|
||||
- **权限** 内置完善的动态路由权限生成方案
|
||||
- **组件** 二次封装了多个常用的组件
|
||||
- **示例** 内置丰富的示例
|
||||
@ -81,4 +76,22 @@ pnpm run build:pro
|
||||
|
||||
## 更新日志
|
||||
|
||||
[更新日志](./CHANGELOG.md)
|
||||
[更新日志](./CHANGELOG.md)
|
||||
|
||||
## 演示图
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
@ -26,7 +26,7 @@ const setSystemConfig = async () => {
|
||||
const res = await getSystemSettingsClassifysApi({ classify: 'web' })
|
||||
if (res) {
|
||||
appStore.setTitle(res.data.web_basic.web_title || import.meta.env.VITE_APP_TITLE)
|
||||
appStore.setLogoImage(res.data.web_basic.web_logo || '/static/system/logo.png')
|
||||
appStore.setLogoImage(res.data.web_basic.web_logo || '/media/system/logo.png')
|
||||
appStore.setFooterContent(res.data.web_basic.web_copyright || 'Copyright ©2022-present K')
|
||||
appStore.setIcpNumber(res.data.web_basic.web_icp_number || '')
|
||||
addMeta(
|
||||
|
@ -2,6 +2,7 @@ export type UserLoginType = {
|
||||
telephone: string
|
||||
password: string
|
||||
method: string
|
||||
platform?: string
|
||||
}
|
||||
|
||||
export type UserType = {
|
||||
|
@ -15,3 +15,7 @@ export const putSystemSettingsApi = (data: any): Promise<IResponse> => {
|
||||
export const getSystemSettingsClassifysApi = (params: any): Promise<IResponse> => {
|
||||
return request.get({ url: '/vadmin/system/settings/classifys/', params })
|
||||
}
|
||||
|
||||
export const getSystemSettingsConfigValueApi = (params: any): Promise<IResponse> => {
|
||||
return request.get({ url: '/vadmin/system/settings/config/value/', params })
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 229 KiB |
BIN
kinit-admin/src/assets/imgs/avatar_bak.jpg
Normal file
BIN
kinit-admin/src/assets/imgs/avatar_bak.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
@ -23,7 +23,6 @@ const loginOut = () => {
|
||||
})
|
||||
.then(() => {
|
||||
authStore.logout()
|
||||
replace('/login')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
const config: {
|
||||
result_code: number | string
|
||||
unauthorized_code: number | string
|
||||
default_headers: AxiosHeaders
|
||||
request_timeout: number
|
||||
} = {
|
||||
@ -7,6 +8,10 @@ const config: {
|
||||
* 接口成功返回状态码
|
||||
*/
|
||||
result_code: 200,
|
||||
/**
|
||||
* 接口TOKEN失效,返回状态码
|
||||
*/
|
||||
unauthorized_code: 401,
|
||||
|
||||
/**
|
||||
* 接口请求超时时间
|
||||
|
@ -1,16 +1,15 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import qs from 'qs'
|
||||
|
||||
import { config } from './config'
|
||||
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const { result_code, request_timeout } = config
|
||||
const { result_code, unauthorized_code, request_timeout } = config
|
||||
|
||||
const appStore = useAppStore()
|
||||
const authStore = useAuthStore()
|
||||
const { wsCache } = useCache()
|
||||
|
||||
// 创建axios实例
|
||||
@ -64,6 +63,10 @@ service.interceptors.response.use(
|
||||
return response
|
||||
} else if (response.data.code === result_code) {
|
||||
return response.data
|
||||
} else if (response.data.code === unauthorized_code) {
|
||||
// 请重新登录
|
||||
ElMessage.error(response.data.message)
|
||||
authStore.logout()
|
||||
} else {
|
||||
ElMessage.error(response.data.message)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ const { start, done } = useNProgress()
|
||||
|
||||
const { loadStart, loadDone } = usePageLoading()
|
||||
|
||||
const whiteList = ['/login'] // 不重定向白名单
|
||||
const whiteList = ['/login', '/docs/privacy', '/docs/agreement'] // 不重定向白名单
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
start()
|
||||
|
@ -47,6 +47,37 @@ export const constantRouterMap: AppRouteRecordRaw[] = [
|
||||
title: '404',
|
||||
noTagsView: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/docs',
|
||||
name: 'Docs',
|
||||
meta: {
|
||||
hidden: true,
|
||||
title: '在线文档',
|
||||
noTagsView: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'privacy',
|
||||
name: 'Privacy',
|
||||
component: () => import('@/views/vadmin/system/docs/privacy.vue'),
|
||||
meta: {
|
||||
hidden: true,
|
||||
title: '隐私政策',
|
||||
noTagsView: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'agreement',
|
||||
name: 'Agreement',
|
||||
component: () => import('@/views/vadmin/system/docs/agreement.vue'),
|
||||
meta: {
|
||||
hidden: true,
|
||||
title: '用户协议',
|
||||
noTagsView: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@ -61,7 +92,15 @@ const router = createRouter({
|
||||
})
|
||||
|
||||
export const resetRouter = (): void => {
|
||||
const resetWhiteNameList = ['Login', 'NoFind', 'Root', 'ResetPassword']
|
||||
const resetWhiteNameList = [
|
||||
'Login',
|
||||
'NoFind',
|
||||
'Root',
|
||||
'ResetPassword',
|
||||
'Docs',
|
||||
'Privacy',
|
||||
'Agreement'
|
||||
]
|
||||
router.getRoutes().forEach((route) => {
|
||||
// 切记 name 不能重复
|
||||
const { name } = route
|
||||
|
@ -49,6 +49,7 @@ export const useAuthStore = defineStore('auth', {
|
||||
},
|
||||
actions: {
|
||||
async login(formData: UserLoginType) {
|
||||
formData.platform = '0'
|
||||
const res = await loginApi(formData)
|
||||
if (res) {
|
||||
wsCache.set(appStore.getToken, `${res.data.token_type} ${res.data.access_token}`)
|
||||
@ -64,11 +65,13 @@ export const useAuthStore = defineStore('auth', {
|
||||
this.isUser = false
|
||||
tagsViewStore.delAllViews()
|
||||
resetRouter()
|
||||
window.location.href = '/login'
|
||||
},
|
||||
updateUser(data: UserState) {
|
||||
this.user.gender = data.gender
|
||||
this.user.name = data.name
|
||||
this.user.nickname = data.nickname
|
||||
this.user.telephone = data.telephone
|
||||
wsCache.set(appStore.getUserInfo, this.user)
|
||||
},
|
||||
async getUserInfo() {
|
||||
|
@ -50,6 +50,10 @@ const user = authStore.getUser
|
||||
<span class="pl-10px w-80px inline-block">性别:</span>
|
||||
<span class="pl-10px">{{ selectDictLabel(genderOptions, user.gender as string) }}</span>
|
||||
</div>
|
||||
<div class="leading-relaxed">
|
||||
<span class="pl-10px w-80px inline-block">角色:</span>
|
||||
<span class="pl-10px">{{ user.roles?.map((item) => item.name).join(',') }}</span>
|
||||
</div>
|
||||
<div class="leading-relaxed">
|
||||
<span class="pl-10px w-80px inline-block">创建时间:</span>
|
||||
<span class="pl-10px">{{ user.create_datetime }}</span>
|
||||
|
@ -7,6 +7,7 @@ import { postCurrentUserUpdateInfo } from '@/api/vadmin/auth/user'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { FormSchema } from '@/types/form'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
@ -44,6 +45,20 @@ const schema = reactive<FormSchema[]>([
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'telephone',
|
||||
label: '手机号',
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 24
|
||||
},
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '50%'
|
||||
},
|
||||
maxlength: 11
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'gender',
|
||||
label: '性别',
|
||||
|
@ -7,6 +7,7 @@ import { postCurrentUserResetPassword } from '@/api/vadmin/auth/user'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { FormSchema } from '@/types/form'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
@ -46,7 +47,7 @@ const schema = reactive<FormSchema[]>([
|
||||
},
|
||||
{
|
||||
field: 'password_two',
|
||||
label: '再次输入新密码',
|
||||
label: '确认密码',
|
||||
component: 'InputPassword',
|
||||
colProps: {
|
||||
span: 24
|
||||
|
@ -31,7 +31,6 @@ import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { selectDictLabel, DictDetail } from '@/utils/dict'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { useAuthStoreWithOut } from '@/store/modules/auth'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { RightToolbar } from '@/components/RightToolbar'
|
||||
import { Search } from '@/components/Search'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
@ -40,11 +39,9 @@ const appStore = useAppStore()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { replace } = useRouter()
|
||||
|
||||
const authStore = useAuthStoreWithOut()
|
||||
|
||||
let genderOptions = ref<DictDetail[]>([])
|
||||
const genderOptions = ref<DictDetail[]>([])
|
||||
|
||||
const getOptions = async () => {
|
||||
const dictStore = useDictStore()
|
||||
@ -130,7 +127,6 @@ const save = async () => {
|
||||
if (user.id === data.id && user.telephone !== data.telephone) {
|
||||
dialogVisible.value = false
|
||||
authStore.logout()
|
||||
replace('/login')
|
||||
return ElMessage.warning('认证已过期,请您重新登陆!')
|
||||
}
|
||||
}
|
||||
|
29
kinit-admin/src/views/vadmin/system/docs/agreement.vue
Normal file
29
kinit-admin/src/views/vadmin/system/docs/agreement.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { getSystemSettingsConfigValueApi } from '@/api/vadmin/system/settings'
|
||||
|
||||
const content = ref(null)
|
||||
|
||||
// 获取隐私协议内容
|
||||
const getSystemConfig = async () => {
|
||||
const res = await getSystemSettingsConfigValueApi({ config_key: 'web_agreement' })
|
||||
if (res) {
|
||||
content.value = res.data
|
||||
}
|
||||
}
|
||||
|
||||
getSystemConfig()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content-view" v-html="content"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.content-view {
|
||||
padding: 20px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
29
kinit-admin/src/views/vadmin/system/docs/privacy.vue
Normal file
29
kinit-admin/src/views/vadmin/system/docs/privacy.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { getSystemSettingsConfigValueApi } from '@/api/vadmin/system/settings'
|
||||
|
||||
const content = ref(null)
|
||||
|
||||
// 获取隐私协议内容
|
||||
const getSystemConfig = async () => {
|
||||
const res = await getSystemSettingsConfigValueApi({ config_key: 'web_privacy' })
|
||||
if (res) {
|
||||
content.value = res.data
|
||||
}
|
||||
}
|
||||
|
||||
getSystemConfig()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content-view" v-html="content"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.content-view {
|
||||
padding: 20px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -8,12 +8,13 @@ export const columns = reactive<TableColumn[]>([
|
||||
label: '编号',
|
||||
show: true,
|
||||
disabled: true,
|
||||
width: '150px',
|
||||
width: '120px',
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'telephone',
|
||||
label: '手机号',
|
||||
width: '150px',
|
||||
show: true,
|
||||
disabled: true,
|
||||
span: 24
|
||||
@ -21,6 +22,21 @@ export const columns = reactive<TableColumn[]>([
|
||||
{
|
||||
field: 'status',
|
||||
label: '登录状态',
|
||||
width: '100px',
|
||||
show: true,
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'platform',
|
||||
label: '登陆平台',
|
||||
width: '150px',
|
||||
show: true,
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'login_method',
|
||||
label: '认证方式',
|
||||
width: '120px',
|
||||
show: true,
|
||||
span: 24
|
||||
},
|
||||
@ -29,6 +45,7 @@ export const columns = reactive<TableColumn[]>([
|
||||
label: '登陆地址',
|
||||
show: true,
|
||||
disabled: true,
|
||||
width: '150px',
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
|
@ -10,6 +10,8 @@ import { RightToolbar } from '@/components/RightToolbar'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import Detail from './components/Detail.vue'
|
||||
import { Search } from '@/components/Search'
|
||||
import { selectDictLabel, DictDetail } from '@/utils/dict'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
|
||||
const { register, elTableRef, tableObject, methods } = useTable({
|
||||
getListApi: getRecordLoginListApi,
|
||||
@ -25,6 +27,17 @@ const { register, elTableRef, tableObject, methods } = useTable({
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const platformOptions = ref<DictDetail[]>([])
|
||||
const loginMethodOptions = ref<DictDetail[]>([])
|
||||
|
||||
const getOptions = async () => {
|
||||
const dictStore = useDictStore()
|
||||
const dictOptions = await dictStore.getDictObj(['sys_vadmin_platform', 'sys_vadmin_login_method'])
|
||||
platformOptions.value = dictOptions.sys_vadmin_platform
|
||||
loginMethodOptions.value = dictOptions.sys_vadmin_login_method
|
||||
}
|
||||
|
||||
getOptions()
|
||||
|
||||
const view = (row: any) => {
|
||||
dialogTitle.value = '登录详情'
|
||||
@ -86,6 +99,14 @@ getList()
|
||||
<template #status="{ row }">
|
||||
<ElSwitch :value="row.status" size="small" disabled />
|
||||
</template>
|
||||
|
||||
<template #platform="{ row }">
|
||||
{{ selectDictLabel(platformOptions, row.platform) }}
|
||||
</template>
|
||||
|
||||
<template #login_method="{ row }">
|
||||
{{ selectDictLabel(loginMethodOptions, row.login_method) }}
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="900px">
|
||||
|
@ -12,6 +12,10 @@ pydantic 数据模型代码生成器官方文档 (Json -> Pydantic):https:
|
||||
|
||||
SQLAlchemy-Utils:https://sqlalchemy-utils.readthedocs.io/en/latest/
|
||||
|
||||
alembic 中文文档:https://hellowac.github.io/alembic_doc/zh/_front_matter.html
|
||||
|
||||
Typer 官方文档:https://typer.tiangolo.com/
|
||||
|
||||
|
||||
## 项目结构
|
||||
|
||||
@ -94,9 +98,11 @@ git commit -m "clear cached"
|
||||
执行数据库迁移命令(终端执行)
|
||||
|
||||
```shell
|
||||
# 执行命令:
|
||||
|
||||
# 执行命令(生产环境):
|
||||
python main.py migrate
|
||||
|
||||
# 执行命令(测试环境):
|
||||
python main.py migrate --env dev
|
||||
```
|
||||
|
||||
生成迁移文件后,会在alembic迁移目录中的version目录中多个迁移文件
|
@ -1,6 +1,6 @@
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
[DEFAULT]
|
||||
# path to migration scripts
|
||||
script_location = alembic
|
||||
|
||||
@ -44,13 +44,21 @@ prepend_sys_path = .
|
||||
# version_path_separator = :
|
||||
# version_path_separator = ;
|
||||
# version_path_separator = space
|
||||
version_path_separator = os # default: use os.pathsep
|
||||
version_path_separator = os
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
# mysql+pymysql://username:password@host:post/name
|
||||
|
||||
[dev]
|
||||
version_locations = %(here)s/alembic/versions_dev
|
||||
sqlalchemy.url = mysql+pymysql://username:password@host:post/name
|
||||
|
||||
|
||||
[pro]
|
||||
version_locations = %(here)s/alembic/versions_pro
|
||||
sqlalchemy.url = mysql+pymysql://username:password@host:post/name
|
||||
|
||||
|
||||
|
0
kinit-api/alembic/versions_pro/.gitkeep
Normal file
0
kinit-api/alembic/versions_pro/.gitkeep
Normal file
@ -11,13 +11,13 @@ from fastapi.security import OAuth2PasswordBearer
|
||||
"""
|
||||
系统版本
|
||||
"""
|
||||
VERSION = "1.3.0"
|
||||
VERSION = "1.4.0"
|
||||
|
||||
"""安全警告: 不要在生产中打开调试运行!"""
|
||||
DEBUG = True
|
||||
DEBUG = False
|
||||
|
||||
"""是否开启演示功能:取消所有POST,DELETE,PUT操作权限"""
|
||||
DEMO = False
|
||||
DEMO = True
|
||||
"""演示功能白名单"""
|
||||
DEMO_WHITE_LIST_PATH = [
|
||||
"/auth/login/",
|
||||
@ -70,7 +70,7 @@ STATIC_ROOT:静态文件目录绝对路径
|
||||
官方文档:https://fastapi.tiangolo.com/tutorial/static-files/
|
||||
"""
|
||||
STATIC_ENABLE = True
|
||||
STATIC_URL = "/static"
|
||||
STATIC_URL = "/media"
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||
|
||||
|
||||
|
@ -16,9 +16,11 @@ from sqlalchemy import select
|
||||
from core.crud import DalBase
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.validator import vali_telephone
|
||||
from utils.aliyun_oss import AliyunOSS, BucketConf
|
||||
from utils.aliyun_sms import AliyunSMS
|
||||
from utils.excel.import_manage import ImportManage, FieldType
|
||||
from utils.excel.write_xlsx import WriteXlsx
|
||||
from utils.file_manage import FileManage
|
||||
from .params import UserParams
|
||||
from utils.tools import test_password
|
||||
from . import models, schemas
|
||||
@ -26,6 +28,7 @@ from application import settings
|
||||
from utils.excel.excel_manage import ExcelManage
|
||||
from apps.vadmin.system import crud as vadminSystemCRUD
|
||||
import copy
|
||||
from utils import status
|
||||
|
||||
|
||||
class UserDal(DalBase):
|
||||
@ -47,15 +50,13 @@ class UserDal(DalBase):
|
||||
"""
|
||||
unique = await self.get_data(telephone=data.telephone, v_return_none=True)
|
||||
if unique:
|
||||
raise ValueError("手机号已存在!")
|
||||
raise CustomException("手机号已存在!", code=status.HTTP_ERROR)
|
||||
password = data.telephone[5:12] if settings.DEFAULT_PASSWORD == "0" else settings.DEFAULT_PASSWORD
|
||||
data.password = self.model.get_password_hash(password)
|
||||
obj = self.model(**data.dict(exclude={'role_ids'}))
|
||||
for data_id in data.role_ids:
|
||||
obj.roles.append(await RoleDal(db=self.db).get_data(data_id=data_id))
|
||||
self.db.add(obj)
|
||||
await self.db.flush()
|
||||
await self.db.refresh(obj)
|
||||
await self.flush(obj)
|
||||
if options:
|
||||
obj = await self.get_data(obj.id, options=options)
|
||||
if return_obj:
|
||||
@ -75,21 +76,23 @@ class UserDal(DalBase):
|
||||
raise CustomException(msg=result, code=400)
|
||||
user.password = self.model.get_password_hash(data.password)
|
||||
user.is_reset_password = True
|
||||
self.db.add(user)
|
||||
await self.db.flush()
|
||||
await self.db.refresh(user)
|
||||
await self.flush(user)
|
||||
return True
|
||||
|
||||
async def update_current_info(self, user: models.VadminUser, data: schemas.UserUpdate):
|
||||
"""
|
||||
更新当前用户信息
|
||||
"""
|
||||
if data.telephone != user.telephone:
|
||||
unique = await self.get_data(telephone=data.telephone, v_return_none=True)
|
||||
if unique:
|
||||
raise CustomException("手机号已存在!", code=status.HTTP_ERROR)
|
||||
else:
|
||||
user.telephone = data.telephone
|
||||
user.name = data.name
|
||||
user.nickname = data.nickname
|
||||
user.gender = data.gender
|
||||
self.db.add(user)
|
||||
await self.db.flush()
|
||||
await self.db.refresh(user)
|
||||
await self.flush(user)
|
||||
return self.out_dict(user)
|
||||
|
||||
async def export_query_list(self, header: list, params: UserParams):
|
||||
@ -207,6 +210,18 @@ class UserDal(DalBase):
|
||||
user["send_sms_msg"] = e.msg
|
||||
return result
|
||||
|
||||
async def update_current_avatar(self, user: models.VadminUser, file: UploadFile):
|
||||
"""
|
||||
更新当前用户头像
|
||||
"""
|
||||
manage = FileManage(file, "avatar")
|
||||
result = await AliyunOSS(BucketConf(**settings.ALIYUN_OSS)).upload_image(manage.path, file)
|
||||
if not result:
|
||||
raise CustomException(msg="上传失败", code=status.HTTP_ERROR)
|
||||
user.avatar = result
|
||||
await self.flush(user)
|
||||
return result
|
||||
|
||||
|
||||
class RoleDal(DalBase):
|
||||
|
||||
@ -218,9 +233,7 @@ class RoleDal(DalBase):
|
||||
obj = self.model(**data.dict(exclude={'menu_ids'}))
|
||||
for data_id in data.menu_ids:
|
||||
obj.menus.append(await MenuDal(db=self.db).get_data(data_id=data_id))
|
||||
self.db.add(obj)
|
||||
await self.db.flush()
|
||||
await self.db.refresh(obj)
|
||||
await self.flush(obj)
|
||||
if options:
|
||||
obj = await self.get_data(obj.id, options=options)
|
||||
if return_obj:
|
||||
|
@ -52,6 +52,7 @@ class UserOut(UserSimpleOut):
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
name: str
|
||||
telephone: Telephone
|
||||
nickname: Optional[str] = None
|
||||
gender: Optional[str] = "0"
|
||||
|
||||
|
@ -45,9 +45,8 @@ async def login_for_access_token(request: Request, data: LoginForm, manage: Logi
|
||||
return ErrorResponse(msg="请使用正确的登录方式")
|
||||
if not result.status:
|
||||
resp = {"message": result.msg}
|
||||
telephone = data.telephone
|
||||
await VadminLoginRecord.\
|
||||
create_login_record(db, telephone, result.status, request, resp)
|
||||
create_login_record(db, data, result.status, request, resp)
|
||||
return ErrorResponse(msg=result.msg)
|
||||
|
||||
user = result.user
|
||||
@ -58,7 +57,7 @@ async def login_for_access_token(request: Request, data: LoginForm, manage: Logi
|
||||
"token_type": "bearer",
|
||||
"is_reset_password": user.is_reset_password
|
||||
}
|
||||
await VadminLoginRecord.create_login_record(db, user.telephone, result.status, request, resp)
|
||||
await VadminLoginRecord.create_login_record(db, data, result.status, request, resp)
|
||||
return SuccessResponse(resp)
|
||||
|
||||
|
||||
|
@ -38,17 +38,17 @@ class AuthValidation:
|
||||
if not settings.OAUTH_ENABLE:
|
||||
return Auth(db=db)
|
||||
if not token:
|
||||
raise CustomException(msg="请先登录!", code=status.HTTP_ERROR)
|
||||
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="无效 Token!", code=status.HTTP_403_FORBIDDEN)
|
||||
raise CustomException(msg="认证已过期,请您重新登陆", code=status.HTTP_401_UNAUTHORIZED)
|
||||
except JWTError:
|
||||
raise CustomException(msg="无效 Token!", code=status.HTTP_403_FORBIDDEN)
|
||||
raise CustomException(msg="认证已过期,请您重新登陆", code=status.HTTP_401_UNAUTHORIZED)
|
||||
user = await self.func(telephone, db)
|
||||
if user is None:
|
||||
raise CustomException(msg="用户不存在!", code=status.HTTP_404_NOT_FOUND)
|
||||
raise CustomException(msg="认证已过期,请您重新登陆", code=status.HTTP_401_UNAUTHORIZED)
|
||||
elif not user.is_active:
|
||||
raise CustomException(msg="用户已被冻结!", code=status.HTTP_403_FORBIDDEN)
|
||||
elif user.is_cancel:
|
||||
|
@ -19,6 +19,7 @@ class LoginForm(BaseModel):
|
||||
telephone: str
|
||||
password: str
|
||||
method: str = '0' # 认证方式,0:密码登录,1:短信登录
|
||||
platform: str = '0' # 登录平台,0:PC端管理系统,1:移动端管理系统
|
||||
|
||||
# validators
|
||||
_normalize_telephone = validator('telephone', allow_reuse=True)(vali_telephone)
|
||||
|
@ -64,18 +64,18 @@ async def post_user_current_update_info(data: schemas.UserUpdate, auth: Auth = D
|
||||
return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data))
|
||||
|
||||
|
||||
@app.post("/user/current/update/avatar/", summary="更新当前用户头像")
|
||||
async def post_user_current_update_avatar(file: UploadFile, auth: Auth = Depends(login_auth)):
|
||||
return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file))
|
||||
|
||||
|
||||
@app.get("/user/current/info/", summary="获取当前用户基本信息")
|
||||
async def get_user_current_info(auth: Auth = Depends(full_admin)):
|
||||
result = schemas.UserSimpleOut.from_orm(auth.user).dict()
|
||||
result = schemas.UserOut.from_orm(auth.user).dict()
|
||||
result["permissions"] = await get_user_permissions(auth.user)
|
||||
return SuccessResponse(result)
|
||||
|
||||
|
||||
@app.get("/user/current/info/", summary="获取当前用户基本信息")
|
||||
async def get_user_current_info(auth: Auth = Depends(login_auth)):
|
||||
return SuccessResponse(schemas.UserSimpleOut.from_orm(auth.user).dict())
|
||||
|
||||
|
||||
@app.post("/user/export/query/list/to/excel/", summary="导出用户查询列表为excel")
|
||||
async def post_user_export_query_list(header: list = Body(..., title="表头与对应字段"), params: UserParams = Depends(),
|
||||
auth: Auth = Depends(login_auth)):
|
||||
|
@ -8,6 +8,7 @@
|
||||
import json
|
||||
|
||||
from application.settings import LOGIN_LOG_RECORD
|
||||
from apps.vadmin.auth.utils.validation import LoginForm
|
||||
from utils.ip_manage import IPManage
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from db.db_base import BaseModel
|
||||
@ -22,6 +23,8 @@ class VadminLoginRecord(BaseModel):
|
||||
|
||||
telephone = Column(String(50), index=True, nullable=False, comment="手机号")
|
||||
status = Column(Boolean, default=True, comment="是否登录成功")
|
||||
platform = Column(String(8), comment="登陆平台")
|
||||
login_method = Column(String(8), comment="认证方式")
|
||||
ip = Column(String(50), comment="登陆地址")
|
||||
address = Column(String(255), comment="登陆地点")
|
||||
country = Column(String(255), comment="国家")
|
||||
@ -37,7 +40,7 @@ class VadminLoginRecord(BaseModel):
|
||||
request = Column(TEXT, comment="请求信息")
|
||||
|
||||
@classmethod
|
||||
async def create_login_record(cls, db: AsyncSession, telephone: str, status: bool, req: Request, resp: dict) -> None:
|
||||
async def create_login_record(cls, db: AsyncSession, data: LoginForm, status: bool, req: Request, resp: dict):
|
||||
"""
|
||||
创建登录记录
|
||||
@return:
|
||||
@ -54,7 +57,8 @@ class VadminLoginRecord(BaseModel):
|
||||
ip = IPManage(req.client.host)
|
||||
location = await ip.parse()
|
||||
params = json.dumps({"body": body, "headers": header})
|
||||
obj = VadminLoginRecord(**location.dict(), telephone=telephone, status=status, browser=browser,
|
||||
system=system, response=json.dumps(resp), request=params)
|
||||
obj = VadminLoginRecord(**location.dict(), telephone=data.telephone, status=status, browser=browser,
|
||||
system=system, response=json.dumps(resp), request=params, platform=data.platform,
|
||||
login_method=data.method)
|
||||
db.add(obj)
|
||||
await db.flush()
|
||||
|
@ -30,6 +30,8 @@ class LoginRecord(BaseModel):
|
||||
city: Optional[str] = None
|
||||
county: Optional[str] = None
|
||||
operator: Optional[str] = None
|
||||
platform: Optional[str] = None
|
||||
login_method: Optional[str] = None
|
||||
|
||||
|
||||
class LoginRecordSimpleOut(LoginRecord):
|
||||
|
@ -151,3 +151,8 @@ async def put_settings_tabs_values(datas: dict = Body(...), auth: Auth = Depends
|
||||
@app.get("/settings/classifys/", summary="获取系统配置分类下的所有显示标签信息")
|
||||
async def get_settings_classifys(classify: str, db: AsyncSession = Depends(db_getter)):
|
||||
return SuccessResponse(await crud.SettingsTabDal(db).get_classify_tab_values([classify]))
|
||||
|
||||
|
||||
@app.get("/settings/config/value/", summary="根据config_key获取到指定value")
|
||||
async def get_settings_config_value(config_key: str, db: AsyncSession = Depends(db_getter)):
|
||||
return SuccessResponse((await crud.SettingsDal(db).get_data(config_key=config_key)).config_value)
|
||||
|
@ -129,9 +129,7 @@ class DalBase:
|
||||
obj = self.model(**data)
|
||||
else:
|
||||
obj = self.model(**data.dict())
|
||||
self.db.add(obj)
|
||||
await self.db.flush()
|
||||
await self.db.refresh(obj)
|
||||
await self.flush(obj)
|
||||
if options:
|
||||
obj = await self.get_data(obj.id, options=options)
|
||||
if return_obj:
|
||||
@ -148,8 +146,7 @@ class DalBase:
|
||||
obj_dict = jsonable_encoder(data)
|
||||
for key, value in obj_dict.items():
|
||||
setattr(obj, key, value)
|
||||
await self.db.flush()
|
||||
await self.db.refresh(obj)
|
||||
await self.flush(obj)
|
||||
if return_obj:
|
||||
return obj
|
||||
if schema:
|
||||
@ -223,6 +220,16 @@ class DalBase:
|
||||
sql = sql.where(attr == value)
|
||||
return sql
|
||||
|
||||
async def flush(self, obj=None):
|
||||
"""
|
||||
刷新到数据库
|
||||
"""
|
||||
if obj:
|
||||
self.db.add(obj)
|
||||
await self.db.flush()
|
||||
if obj:
|
||||
await self.db.refresh(obj)
|
||||
|
||||
def out_dict(self, data):
|
||||
"""
|
||||
序列化
|
||||
|
@ -8,6 +8,7 @@
|
||||
"""
|
||||
FastApi 更新文档:https://github.com/tiangolo/fastapi/releases
|
||||
FastApi Github:https://github.com/tiangolo/fastapi
|
||||
Typer 官方文档:https://typer.tiangolo.com/
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI
|
||||
@ -20,7 +21,7 @@ import importlib
|
||||
from core.logger import logger
|
||||
from core.exception import register_exception
|
||||
import typer
|
||||
from scripts.initialize.initialize import InitializeData
|
||||
from scripts.initialize.initialize import InitializeData, Environment
|
||||
import asyncio
|
||||
|
||||
|
||||
@ -86,22 +87,26 @@ def run():
|
||||
|
||||
|
||||
@shell_app.command()
|
||||
def init():
|
||||
def init(env: Environment = Environment.pro):
|
||||
"""
|
||||
初始化数据
|
||||
|
||||
@params name: 数据库环境
|
||||
"""
|
||||
print("开始初始化数据")
|
||||
data = InitializeData()
|
||||
asyncio.run(data.run())
|
||||
asyncio.run(data.run(env))
|
||||
|
||||
|
||||
@shell_app.command()
|
||||
def migrate():
|
||||
def migrate(env: Environment = Environment.pro):
|
||||
"""
|
||||
将模型迁移到数据库,更新数据库表结构
|
||||
|
||||
@params name: 数据库环境
|
||||
"""
|
||||
print("开始更新数据库表")
|
||||
InitializeData().migrate_model()
|
||||
InitializeData().migrate_model(env)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Binary file not shown.
@ -5,6 +5,8 @@
|
||||
# @File : initialize.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from enum import Enum
|
||||
from core.database import db_getter
|
||||
from utils.excel.excel_manage import ExcelManage
|
||||
from application.settings import BASE_DIR, VERSION
|
||||
@ -15,6 +17,11 @@ from sqlalchemy.sql.schema import Table
|
||||
import subprocess
|
||||
|
||||
|
||||
class Environment(str, Enum):
|
||||
dev = "dev"
|
||||
pro = "pro"
|
||||
|
||||
|
||||
class InitializeData:
|
||||
"""
|
||||
初始化数据
|
||||
@ -36,13 +43,13 @@ class InitializeData:
|
||||
self.__get_sheet_data()
|
||||
|
||||
@classmethod
|
||||
def migrate_model(cls):
|
||||
def migrate_model(cls, env: Environment = Environment.pro):
|
||||
"""
|
||||
模型迁移映射到数据库
|
||||
"""
|
||||
subprocess.check_call(f'alembic revision --autogenerate -m "{VERSION}"', cwd=BASE_DIR)
|
||||
subprocess.check_call('alembic upgrade head', cwd=BASE_DIR)
|
||||
print(f"{VERSION} 数据库表迁移完成")
|
||||
subprocess.check_call(f'alembic --name {env.value} revision --autogenerate -m "{VERSION}"', cwd=BASE_DIR)
|
||||
subprocess.check_call(f'alembic --name {env.value} upgrade head', cwd=BASE_DIR)
|
||||
print(f"环境:{env} {VERSION} 数据库表迁移完成")
|
||||
|
||||
def __serializer_data(self):
|
||||
"""
|
||||
@ -132,11 +139,11 @@ class InitializeData:
|
||||
"""
|
||||
await self.__generate_data("vadmin_system_dict_details", system_models.VadminDictDetails)
|
||||
|
||||
async def run(self):
|
||||
async def run(self, env: Environment = Environment.pro):
|
||||
"""
|
||||
执行初始化工作
|
||||
"""
|
||||
self.migrate_model()
|
||||
self.migrate_model(env)
|
||||
await self.generate_menu()
|
||||
await self.generate_role()
|
||||
await self.generate_user()
|
||||
@ -145,4 +152,4 @@ class InitializeData:
|
||||
await self.generate_dict_type()
|
||||
await self.generate_system_config()
|
||||
await self.generate_dict_details()
|
||||
print(f"{VERSION} 数据已初始化完成")
|
||||
print(f"环境:{env} {VERSION} 数据已初始化完成")
|
||||
|
@ -9,5 +9,6 @@
|
||||
|
||||
HTTP_SUCCESS = 200
|
||||
HTTP_ERROR = 400
|
||||
HTTP_401_UNAUTHORIZED = 401
|
||||
HTTP_403_FORBIDDEN = 403
|
||||
HTTP_404_NOT_FOUND = 404
|
||||
|
14
kinit-uni/.gitignore
vendored
Normal file
14
kinit-uni/.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
######################################################################
|
||||
# Build Tools
|
||||
/node_modules/*
|
||||
|
||||
######################################################################
|
||||
# Development Tools
|
||||
|
||||
/.idea/*
|
||||
/.vscode/*
|
||||
/.hbuilderx/*
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
21
kinit-uni/App.vue
Normal file
21
kinit-uni/App.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script>
|
||||
import config from './config'
|
||||
import { getToken } from '@/common/utils/auth'
|
||||
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
this.initApp()
|
||||
},
|
||||
methods: {
|
||||
// 初始化应用
|
||||
initApp() {
|
||||
// 初始化应用配置
|
||||
this.$store.dispatch('InitConfig')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/static/scss/index.scss';
|
||||
</style>
|
21
kinit-uni/LICENSE
Normal file
21
kinit-uni/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 若依
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
49
kinit-uni/README.md
Normal file
49
kinit-uni/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
## 若依平台简介
|
||||
|
||||
RuoYi App 移动解决方案,采用uniapp框架,一份代码多终端适配,同时支持APP、小程序、H5!实现了与[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)、[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)完美对接的移动解决方案!目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。
|
||||
|
||||
* 应用框架基于[uniapp](https://uniapp.dcloud.net.cn/),支持小程序、H5、Android和IOS。
|
||||
* 前端组件加入了[uni-ui](https://github.com/dcloudio/uni-ui),全端兼容的高性能UI框架。
|
||||
|
||||
|
||||
## 若依技术文档
|
||||
|
||||
- 官网网站:[http://ruoyi.vip](http://ruoyi.vip)
|
||||
- 文档地址:[http://doc.ruoyi.vip](http://doc.ruoyi.vip)
|
||||
- H5页体验:[http://h5.ruoyi.vip](http://h5.ruoyi.vip)
|
||||
- 小程序体验
|
||||
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-26c76dc90b92acdbd9ac8cd5252f07c8ad9.jpg" alt="小程序演示"/>
|
||||
|
||||
## 二次开发
|
||||
|
||||
是的,KINIT-UNI 是在若依-移动端的基础上进行的二次开发,在此感谢若依团队!
|
||||
|
||||
二次开发中我们重新将接口请求改为了使用 `luch-request`,项目结构也有所改动,并且加入了 `uView UI` 组件,`uni-simple-router` 路由拦截。
|
||||
|
||||
|
||||
|
||||
开发环境:HBuilder X
|
||||
|
||||
## 依赖插件
|
||||
|
||||
- [uni-read-pages](https://github.com/SilurianYang/uni-read-pages) :自动读取 `pages.json` 所有配置。
|
||||
- [uni-simple-router](https://hhyang.cn/v2/start/quickstart.html) :在uni-app中使用vue-router的方式进行跳转路由,路由拦截。
|
||||
|
||||
## 依赖组件
|
||||
|
||||
### color UI
|
||||
|
||||
- 文档地址:http://docs.xzeu.com/
|
||||
- 源码地址:https://github.com/weilanwl/coloruicss
|
||||
- 微信小程序:#小程序://ColorUI组件库/0YmCxm5PUBuChYJ
|
||||
|
||||
该项目已在 2019 年停止维护,但因若依组件中有多处使用,所以在这里不做移除,新加入uView UI框架,两者不冲突
|
||||
|
||||
### uView UI
|
||||
|
||||
- 源码地址:https://github.com/umicro/uView2.0
|
||||
- 文档地址:https://uviewui.com
|
||||
|
||||
uView UI,是[uni-app](https://uniapp.dcloud.io/)全面兼容nvue的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水
|
17
kinit-uni/common/request/api/login.js
Normal file
17
kinit-uni/common/request/api/login.js
Normal file
@ -0,0 +1,17 @@
|
||||
import request from '@/common/request/request.js'
|
||||
|
||||
// 登录方法
|
||||
export function login(telephone, password, method) {
|
||||
const data = {
|
||||
telephone,
|
||||
password,
|
||||
method,
|
||||
platform: '1'
|
||||
}
|
||||
return request.post(`/auth/login/`, data)
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return request.get(`/vadmin/auth/user/current/info/`)
|
||||
}
|
16
kinit-uni/common/request/api/vadmin/auth/user.js
Normal file
16
kinit-uni/common/request/api/vadmin/auth/user.js
Normal file
@ -0,0 +1,16 @@
|
||||
import request from '@/common/request/request'
|
||||
|
||||
// 更新当前用户基本信息
|
||||
export function updateCurrentUser(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)
|
||||
}
|
||||
|
||||
// 更新当前用户头像
|
||||
export function postCurrentUserUploadAvatar(filePath) {
|
||||
return request.upload(`/vadmin/auth/user/current/update/avatar/`, {filePath: filePath, name: 'file'})
|
||||
}
|
6
kinit-uni/common/request/api/vadmin/system/dict.js
Normal file
6
kinit-uni/common/request/api/vadmin/system/dict.js
Normal file
@ -0,0 +1,6 @@
|
||||
import request from '@/common/request/request.js'
|
||||
|
||||
// 获取多个字典类型下的字典元素列表
|
||||
export function getDictTypeDetailsApi(data) {
|
||||
return request.post(`/vadmin/system/dict/types/details/`, data)
|
||||
}
|
6
kinit-uni/common/request/api/vadmin/system/settings.js
Normal file
6
kinit-uni/common/request/api/vadmin/system/settings.js
Normal file
@ -0,0 +1,6 @@
|
||||
import request from '@/common/request/request'
|
||||
|
||||
// 获取系统配置分类
|
||||
export function getSystemSettingsClassifysApi(params) {
|
||||
return request.get(`/vadmin/system/settings/classifys/`, {params: params})
|
||||
}
|
6
kinit-uni/common/request/errorCode.js
Normal file
6
kinit-uni/common/request/errorCode.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
"401": "认证失败,无法访问系统资源",
|
||||
"403": "当前操作没有权限",
|
||||
"404": "访问资源不存在",
|
||||
"default": "系统未知错误,请反馈给管理员"
|
||||
};
|
78
kinit-uni/common/request/request.js
Normal file
78
kinit-uni/common/request/request.js
Normal file
@ -0,0 +1,78 @@
|
||||
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 store from '@/store'
|
||||
|
||||
// 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: {}
|
||||
})
|
||||
|
||||
|
||||
// 请求拦截器
|
||||
http.interceptors.request.use(
|
||||
config => {
|
||||
// 在发送请求之前
|
||||
let token = getToken()
|
||||
if (token) {
|
||||
// 添加头信息,token验证
|
||||
config.header["Authorization"] = token
|
||||
}
|
||||
return config
|
||||
},
|
||||
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('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);
|
||||
}
|
||||
);
|
||||
|
||||
export default http
|
13
kinit-uni/common/utils/auth.js
Normal file
13
kinit-uni/common/utils/auth.js
Normal file
@ -0,0 +1,13 @@
|
||||
const TokenKey = 'App-Token'
|
||||
|
||||
export function getToken() {
|
||||
return uni.getStorageSync(TokenKey)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
return uni.setStorageSync(TokenKey, token)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return uni.removeStorageSync(TokenKey)
|
||||
}
|
54
kinit-uni/common/utils/common.js
Normal file
54
kinit-uni/common/utils/common.js
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 显示消息提示框
|
||||
* @param content 提示的标题
|
||||
*/
|
||||
export function toast(content) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: content
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示模态弹窗
|
||||
* @param content 提示的标题
|
||||
*/
|
||||
export function showConfirm(content) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: content,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: function(res) {
|
||||
resolve(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数处理
|
||||
* @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") {
|
||||
if (typeof value === 'object') {
|
||||
for (const key of Object.keys(value)) {
|
||||
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
||||
let params = propName + '[' + key + ']'
|
||||
var subPart = encodeURIComponent(params) + "="
|
||||
result += subPart + encodeURIComponent(value[key]) + "&"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += part + encodeURIComponent(value) + "&"
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
12
kinit-uni/common/utils/constant.js
Normal file
12
kinit-uni/common/utils/constant.js
Normal file
@ -0,0 +1,12 @@
|
||||
const constant = {
|
||||
avatar: 'vuex_avatar',
|
||||
name: 'vuex_name',
|
||||
nickname: 'vuex_nickname',
|
||||
telephone: 'vuex_telephone',
|
||||
isUser: 'vuex_isUser',
|
||||
roles: 'vuex_roles',
|
||||
create_datetime: 'vuex_createDatetime',
|
||||
permissions: 'vuex_permissions'
|
||||
}
|
||||
|
||||
export default constant
|
80
kinit-uni/common/utils/cookies.js
Normal file
80
kinit-uni/common/utils/cookies.js
Normal file
@ -0,0 +1,80 @@
|
||||
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');
|
||||
}
|
||||
});
|
||||
}
|
30
kinit-uni/common/utils/logs.js
Normal file
30
kinit-uni/common/utils/logs.js
Normal file
@ -0,0 +1,30 @@
|
||||
var log = wx.getRealtimeLogManager ? wx.getRealtimeLogManager() : null
|
||||
|
||||
module.exports = {
|
||||
debug() {
|
||||
if (!log) return
|
||||
log.debug.apply(log, arguments)
|
||||
},
|
||||
info() {
|
||||
if (!log) return
|
||||
log.info.apply(log, arguments)
|
||||
},
|
||||
warn() {
|
||||
if (!log) return
|
||||
log.warn.apply(log, arguments)
|
||||
},
|
||||
error() {
|
||||
if (!log) return
|
||||
log.error.apply(log, arguments)
|
||||
},
|
||||
setFilterMsg(msg) { // 从基础库2.7.3开始支持
|
||||
if (!log || !log.setFilterMsg) return
|
||||
if (typeof msg !== 'string') return
|
||||
log.setFilterMsg(msg)
|
||||
},
|
||||
addFilterMsg(msg) { // 从基础库2.8.1开始支持
|
||||
if (!log || !log.addFilterMsg) return
|
||||
if (typeof msg !== 'string') return
|
||||
log.addFilterMsg(msg)
|
||||
}
|
||||
}
|
51
kinit-uni/common/utils/permission.js
Normal file
51
kinit-uni/common/utils/permission.js
Normal file
@ -0,0 +1,51 @@
|
||||
import store from '@/store'
|
||||
|
||||
/**
|
||||
* 字符权限校验
|
||||
* @param {Array} value 校验值
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
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 hasPermission = permissions.some(permission => {
|
||||
return all_permission === permission || permissionDatas.includes(permission)
|
||||
})
|
||||
|
||||
if (!hasPermission) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色权限校验
|
||||
* @param {Array} value 校验值
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
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 hasRole = roles.some(role => {
|
||||
return super_admin === role || permissionRoles.includes(role)
|
||||
})
|
||||
|
||||
if (!hasRole) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
console.error(`need roles! Like checkRole="['admin','editor']"`)
|
||||
return false
|
||||
}
|
||||
}
|
39
kinit-uni/common/utils/postFile.js
Normal file
39
kinit-uni/common/utils/postFile.js
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
uniapp 上传文件到后台接口
|
||||
|
||||
官方文档:https://uniapp.dcloud.io/api/request/network-file.html#uploadfile
|
||||
博客:https://www.jianshu.com/p/71ad2f45120c
|
||||
*/
|
||||
|
||||
|
||||
import { API_BASE_URL } from '@/common/setting/index'
|
||||
import { getToken, removeToken, getStorage } from '@/common/utils/cookies'
|
||||
|
||||
|
||||
// 单个文件上传
|
||||
export function uploadFile(api, file, data={}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: API_BASE_URL + api,
|
||||
filePath: file,
|
||||
name: 'file',
|
||||
timeout: 60000,
|
||||
formData: data,
|
||||
header: {
|
||||
ossign: getStorage("ossign"),
|
||||
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);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
191
kinit-uni/common/utils/ruoyi.js
Normal file
191
kinit-uni/common/utils/ruoyi.js
Normal file
@ -0,0 +1,191 @@
|
||||
/**
|
||||
* 通用js方法封装处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取任意日期
|
||||
* date: 时间戳
|
||||
* AddDayCount: 在时间戳的基础上加减天数
|
||||
* 示例:
|
||||
* getDate(new Date(),-3).fullDate # 三天前的日期
|
||||
* getDate(new Date()).fullDate # 今天的日期
|
||||
* 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)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
// 日期格式化
|
||||
export function parseTime(time, pattern) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null;
|
||||
}
|
||||
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 === "number") && (time.toString().length === 10)) {
|
||||
time = time * 1000;
|
||||
}
|
||||
date = new Date(time);
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
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];
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === "a") { return ["日", "一", "二", "三", "四", "五", "六"][value]; }
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// 添加日期范围
|
||||
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 });
|
||||
}
|
||||
return search;
|
||||
}
|
||||
|
||||
// 回显数据字典
|
||||
export function selectDictLabel(datas, value) {
|
||||
var actions = [];
|
||||
Object.keys(datas).some((key) => {
|
||||
if (String(datas[key].dictValue) === ("" + String(value))) {
|
||||
actions.push(datas[key].dictLabel);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return actions.join("");
|
||||
}
|
||||
// 获取字典默认值
|
||||
export function selectDictDefault(datas) {
|
||||
var actions = [];
|
||||
Object.keys(datas).some((key) => {
|
||||
if (datas[key].is_default === true) {
|
||||
actions.push(datas[key].dictValue);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (!actions[0] && datas[0]) {
|
||||
actions.push(datas[0].dictValue);
|
||||
}
|
||||
return actions.join("");
|
||||
}
|
||||
|
||||
// 回显数据字典(字符串数组)
|
||||
export function selectDictLabels(datas, value, separator) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
return actions.join("").substring(0, actions.join("").length - 1);
|
||||
}
|
||||
|
||||
// 转换字符串,undefined,null等转化为""
|
||||
export function praseStrEmpty(str) {
|
||||
if (!str || str == "undefined" || str == "null") {
|
||||
return "";
|
||||
}
|
||||
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;
|
||||
}
|
33
kinit-uni/common/utils/storage.js
Normal file
33
kinit-uni/common/utils/storage.js
Normal file
@ -0,0 +1,33 @@
|
||||
import constant from './constant'
|
||||
|
||||
// 存储变量名
|
||||
let storageKey = 'storage_data'
|
||||
|
||||
// 存储节点变量名
|
||||
let storageNodeKeys = [constant.avatar, constant.name, constant.roles, constant.permissions]
|
||||
|
||||
// 存储的数据
|
||||
let storageData = uni.getStorageSync(storageKey) || {}
|
||||
|
||||
const storage = {
|
||||
set: function(key, value) {
|
||||
if (storageNodeKeys.indexOf(key) != -1) {
|
||||
let tmp = uni.getStorageSync(storageKey)
|
||||
tmp = tmp ? tmp : {}
|
||||
tmp[key] = value
|
||||
uni.setStorageSync(storageKey, tmp)
|
||||
}
|
||||
},
|
||||
get: function(key) {
|
||||
return storageData[key] || ""
|
||||
},
|
||||
remove: function(key) {
|
||||
delete storageData[key]
|
||||
uni.setStorageSync(storageKey, storageData)
|
||||
},
|
||||
clean: function() {
|
||||
uni.removeStorageSync(storageKey)
|
||||
}
|
||||
}
|
||||
|
||||
export default storage
|
68
kinit-uni/common/utils/upload.js
Normal file
68
kinit-uni/common/utils/upload.js
Normal file
@ -0,0 +1,68 @@
|
||||
import store from '@/store'
|
||||
import config from '@/config'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import { toast, showConfirm, tansParams } from '@/common/utils/common'
|
||||
|
||||
let timeout = 10000
|
||||
const baseUrl = config.baseUrl
|
||||
|
||||
const upload = config => {
|
||||
// 是否需要设置 token
|
||||
const isToken = (config.headers || {}).isToken === false
|
||||
config.header = config.header || {}
|
||||
if (getToken() && !isToken) {
|
||||
config.header['Authorization'] = 'Bearer ' + getToken()
|
||||
}
|
||||
// get请求映射params参数
|
||||
if (config.params) {
|
||||
let url = config.url + '?' + tansParams(config.params)
|
||||
url = url.slice(0, -1)
|
||||
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)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default upload
|
57
kinit-uni/components/luch-request/history.md
Normal file
57
kinit-uni/components/luch-request/history.md
Normal file
@ -0,0 +1,57 @@
|
||||
## 3.0.7 (2021-09-04)
|
||||
|
||||
1. Bug Fix: 修复通过 `Request.config` 设置全局参数,多个实例`config`存在共同引用bug
|
||||
|
||||
|
||||
## 3.0.6 (2021-05-10)
|
||||
|
||||
1. New Feature: APP端 增加`responseType`配置项
|
||||
|
||||
## 3.0.5 (2021-01-10)
|
||||
### Features
|
||||
|
||||
* [重要] APP不再支持`CONNECT`、`HEAD`、`TRACE`请求方式。[uni.request](https://uniapp.dcloud.io/api/request/request)
|
||||
* [重要]全局默认`timeout`由`30000`ms,改为`60000`ms
|
||||
* [重要]增加`index.d.ts`文件支持。感谢`Mr_Mao`的支持。github:`https://github.com/TuiMao233`
|
||||
* [重要]网络请求相关接口 uni.request、uni.uploadFile、uni.downloadFile 支持 timeout 参数。
|
||||
* [重要]返回结果response 增加`fullPath`参数。
|
||||
|
||||
## 3.0.4 (2020-07-05)
|
||||
|
||||
1. New Feature: request 方法增加 ` firstIpv4 `配置项
|
||||
1. New Feature: 增加 ` middleware `通用请求方法
|
||||
|
||||
## 3.0.3 (2020-06-16)
|
||||
|
||||
1. Bug Fix: 修复` params ` 选项对数组格式化错误bug
|
||||
|
||||
## 3.0.2 (2020-06-04)
|
||||
|
||||
1. Bug Fix: 修复文件上传和request 配置缺少字段bug
|
||||
|
||||
## 3.0.1 (2020-06-02)
|
||||
|
||||
1. Bug Fix: 请求方式都为` GET `的bug
|
||||
|
||||
## 3.0.0 (2020-06-01)
|
||||
|
||||
1. New Feature: 支持多拦截器
|
||||
1. New Feature: 支持局部配置自定义验证器
|
||||
|
||||
## 2.0.1 (2020-05-01)
|
||||
|
||||
1. Bug Fix: 修复多实例全局配置共用问题
|
||||
|
||||
## 2.0.0 (2020-04-24)
|
||||
|
||||
1. New Feature: 增加 request ` withCredentials `选项(仅h5端支持)
|
||||
1. New Feature: h5端 upload 增加 ` files ` ` file `选项。[uni.uploadFile](https://uniapp.dcloud.io/api/request/network-file?id=uploadfile "uni.uploadFile")
|
||||
1. Enhancement: ` params ` 选项参数格式化方法使用axios 格式化方法
|
||||
1. Bug Fix: 对upload 返回data 为空字符串的情况容错
|
||||
1. Change: 修改header与全局合并方式。当前:header = Object.assign(全局,局部)
|
||||
|
||||
## 0.0.0 (2019-05)
|
||||
|
||||
1. luch-request created
|
||||
|
||||
|
132
kinit-uni/components/luch-request/package.json
Normal file
132
kinit-uni/components/luch-request/package.json
Normal file
@ -0,0 +1,132 @@
|
||||
{
|
||||
"_from": "luch-request",
|
||||
"_id": "luch-request@3.0.7",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-rYTgAO0CiAMjuotvwhtEREQ6mh/qdgYO2a+cKDv2VPnww/MdNaHMGMpsPY6VvJCOtfH7kV9lDujjuihhlu1xmg==",
|
||||
"_location": "/luch-request",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "luch-request",
|
||||
"name": "luch-request",
|
||||
"escapedName": "luch-request",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/luch-request/-/luch-request-3.0.7.tgz",
|
||||
"_shasum": "5894fc3d3ff62622b042c84953c1192ccdeafaa5",
|
||||
"_spec": "luch-request",
|
||||
"_where": "E:\\ktianc\\base\\vvandk",
|
||||
"author": {
|
||||
"name": "luch"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/lei-mu/luch-request/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
},
|
||||
"ghooks": {
|
||||
"commit-msg": "validate-commit-msg"
|
||||
},
|
||||
"validate-commit-msg": {
|
||||
"types": [
|
||||
"feat",
|
||||
"fix",
|
||||
"docs",
|
||||
"style",
|
||||
"refactor",
|
||||
"perf",
|
||||
"test",
|
||||
"build",
|
||||
"ci",
|
||||
"chore",
|
||||
"revert"
|
||||
],
|
||||
"scope": {
|
||||
"required": false,
|
||||
"allowed": [
|
||||
"*"
|
||||
],
|
||||
"validate": false,
|
||||
"multiple": false
|
||||
},
|
||||
"warnOnFail": false,
|
||||
"maxSubjectLength": 100,
|
||||
"subjectPattern": ".+",
|
||||
"subjectPatternErrorMsg": "subject does not match subject pattern!",
|
||||
"helpMessage": "",
|
||||
"autoFix": false
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/types": "^2.0.16"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "基于Promise实现uni-app request 请求插件",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||
"@babel/plugin-transform-arrow-functions": "^7.8.3",
|
||||
"@babel/preset-env": "^7.9.5",
|
||||
"@vuepress/plugin-active-header-links": "^1.5.0",
|
||||
"@vuepress/plugin-pwa": "^1.5.2",
|
||||
"archiver": "^4.0.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"commitizen": "^4.2.4",
|
||||
"conventional-changelog-cli": "^2.1.1",
|
||||
"cz-conventional-changelog": "^3.2.0",
|
||||
"eslint": "^6.8.0",
|
||||
"grunt": "^1.1.0",
|
||||
"grunt-babel": "^8.0.0",
|
||||
"load-grunt-tasks": "^5.1.0",
|
||||
"node-zip": "^1.1.1",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-copy": "^3.3.0",
|
||||
"rollup-plugin-eslint": "^7.0.0",
|
||||
"rollup-plugin-json": "^4.0.0",
|
||||
"rollup-plugin-live-server": "^1.0.3",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-uglify": "^6.0.4",
|
||||
"rollup-plugin-zip": "^1.0.0",
|
||||
"validate-commit-msg": "^2.14.0",
|
||||
"vuepress": "^1.4.1"
|
||||
},
|
||||
"homepage": "https://www.quanzhan.co/luch-request/",
|
||||
"keywords": [
|
||||
"uni-app",
|
||||
"request",
|
||||
"Promise",
|
||||
"luch",
|
||||
"luch-request"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "src/lib/luch-request.js",
|
||||
"name": "luch-request",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lei-mu/luch-request.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rimraf DCloud && rollup -c --environment NODE_ENV:production",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0",
|
||||
"dev": "rollup -c",
|
||||
"docs": "vuepress dev docs",
|
||||
"docs:build": "vuepress build docs",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"watch": "rollup -c -w",
|
||||
"zipD": "node node/zipDCloudPlugin.js && node node/zipDCloudDemo.js"
|
||||
},
|
||||
"version": "3.0.7"
|
||||
}
|
260
kinit-uni/components/luch-request/readme.md
Normal file
260
kinit-uni/components/luch-request/readme.md
Normal file
@ -0,0 +1,260 @@
|
||||
# luch-request
|
||||
|
||||
[](https://www.npmjs.com/package/luch-request "npm")
|
||||
[](https://www.npmjs.com/package/luch-request "npm")
|
||||
[](https://github.com/lei-mu/luch-request "github")
|
||||
[](https://github.com/lei-mu/luch-request "github stars")
|
||||
[](https://github.com/lei-mu/luch-request "github forks")
|
||||
|
||||
- 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截
|
||||
- 支持全局挂载
|
||||
- 支持多个全局配置实例
|
||||
- 支持自定义验证器
|
||||
- 支持文件上传/下载
|
||||
- 支持task 操作
|
||||
- 支持自定义参数
|
||||
- 支持多拦截器
|
||||
- 对参数的处理比uni.request 更强
|
||||
|
||||
安装
|
||||
------------
|
||||
###### 使用npm
|
||||
|
||||
``` javascript
|
||||
npm i luch-request -S
|
||||
```
|
||||
使用npm前阅读[快速上手](https://www.quanzhan.co/luch-request/handbook/#npm "快速上手")
|
||||
|
||||
|
||||
###### github
|
||||
|
||||
[github](https://github.com/lei-mu/luch-request "github")
|
||||
安装依赖后 ` npm run build ` ,使用DCloud/luch-request 文件夹即可
|
||||
|
||||
|
||||
###### DCloud插件市场:
|
||||
|
||||
[DCloud插件市场](https://ext.dcloud.net.cn/plugin?id=392 "DCloud插件市场")
|
||||
|
||||
Example
|
||||
------------
|
||||
创建实例
|
||||
|
||||
``` javascript
|
||||
import Request from '@/utils/luch-request/index.js' // 下载的插件
|
||||
// import Request from 'luch-request' // 使用npm
|
||||
|
||||
const http = new Request();
|
||||
```
|
||||
|
||||
执行` GET `请求
|
||||
|
||||
``` javascript
|
||||
http.get('/user/login', {params: {userName: 'name', password: '123456'}}).then(res => {
|
||||
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
// 局部修改配置,局部配置优先级高于全局配置
|
||||
http.get('/user/login', {
|
||||
params: {userName: 'name', password: '123456'}, /* 会加在url上 */
|
||||
header: {}, /* 会与全局header合并,如有同名属性,局部覆盖全局 */
|
||||
dataType: 'json',
|
||||
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
|
||||
custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
|
||||
// #ifndef MP-ALIPAY
|
||||
responseType: 'text',
|
||||
// #endif
|
||||
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||
timeout: 60000, // H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
sslVerify: true, // 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+)
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
withCredentials: false, // 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+)
|
||||
// #endif
|
||||
// 返回当前请求的task, options。请勿在此处修改options。非必填
|
||||
getTask: (task, options) => {
|
||||
// setTimeout(() => {
|
||||
// task.abort()
|
||||
// }, 500)
|
||||
},
|
||||
// 自定义验证器。statusCode必存在。非必填
|
||||
validateStatus: function validateStatus(statusCode) {
|
||||
return statusCode >= 200 && statusCode < 300
|
||||
}
|
||||
}).then(res => {
|
||||
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
```
|
||||
执行` POST `请求
|
||||
|
||||
``` javascript
|
||||
http.post('/user/login', {userName: 'name', password: '123456'} ).then(res => {
|
||||
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
// 局部修改配置,局部配置优先级高于全局配置
|
||||
http.post('/user/login', {userName: 'name', password: '123456'}, {
|
||||
params: {}, /* 会加在url上 */
|
||||
header: {}, /* 会与全局header合并,如有同名属性,局部覆盖全局 */
|
||||
dataType: 'json',
|
||||
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
|
||||
custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
|
||||
// #ifndef MP-ALIPAY
|
||||
responseType: 'text',
|
||||
// #endif
|
||||
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||
timeout: 60000, // H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
sslVerify: true, // 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+)
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
withCredentials: false, // 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+)
|
||||
// #endif
|
||||
// 返回当前请求的task, options。请勿在此处修改options。非必填
|
||||
getTask: (task, options) => {
|
||||
// setTimeout(() => {
|
||||
// task.abort()
|
||||
// }, 500)
|
||||
},
|
||||
// 自定义验证器。statusCode必存在。非必填
|
||||
validateStatus: function validateStatus(statusCode) {
|
||||
return statusCode >= 200 && statusCode < 300
|
||||
}
|
||||
}).then(res => {
|
||||
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
```
|
||||
执行` upload `请求
|
||||
|
||||
``` javascript
|
||||
http.upload('api/upload/img', {
|
||||
params: {}, /* 会加在url上 */
|
||||
// #ifdef APP-PLUS || H5
|
||||
files: [], // 需要上传的文件列表。使用 files 时,filePath 和 name 不生效。App、H5( 2.6.15+)
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
fileType: 'image/video/audio', // 仅支付宝小程序,且必填。
|
||||
// #endif
|
||||
filePath: '', // 要上传文件资源的路径。
|
||||
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
|
||||
custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
|
||||
name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
|
||||
// #ifdef H5 || APP-PLUS
|
||||
timeout: 60000, // H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)
|
||||
// #endif
|
||||
header: {}, /* 会与全局header合并,如有同名属性,局部覆盖全局 */
|
||||
formData: {}, // HTTP 请求中其他额外的 form data
|
||||
// 返回当前请求的task, options。请勿在此处修改options。非必填
|
||||
getTask: (task, options) => {
|
||||
// task.onProgressUpdate((res) => {
|
||||
// console.log('上传进度' + res.progress);
|
||||
// console.log('已经上传的数据长度' + res.totalBytesSent);
|
||||
// console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);
|
||||
//
|
||||
// // 测试条件,取消上传任务。
|
||||
// if (res.progress > 50) {
|
||||
// uploadTask.abort();
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}).then(res => {
|
||||
// 返回的res.data 已经进行JSON.parse
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
```
|
||||
|
||||
luch-request Guide
|
||||
------------
|
||||
[luch-request 官网地址](https://www.quanzhan.co/luch-request/ "luch-request 官网地址")
|
||||
<br>
|
||||
[github](https://github.com/lei-mu/luch-request "github")
|
||||
|
||||
友情链接
|
||||
------------
|
||||
#### vue-admin-beautiful
|
||||
**vue-admin-beautiful** ——<a href="https://github.com/chuzhixin/vue-admin-beautiful" target="_blank">企业级、通用型中后台前端解决方案(基于vue/cli 4 最新版,同时支持电脑,手机,平板)</a>
|
||||
|
||||
**vue-admin-beautiful** ——<a href="http://beautiful.panm.cn/vue-admin-beautiful/#/index" target="_blank">在线演示</a>
|
||||
|
||||
#### uView
|
||||
<a href="https://uviewui.com" target="_blank">uView 文档</a> ——超棒的移动跨端框架,文档详细,上手容易
|
||||
|
||||
常见问题
|
||||
------------
|
||||
1. 为什么会请求两次?
|
||||
- 总有些小白问这些很那啥的问题,有两种可能,一种是‘post三次握手’,还有一种可能是`本地访问接口时跨域请求,所以浏览器会先发一个option 去预测能否成功,然后再发一个真正的请求`(自己观察请求头,Request Method,百度简单请求)。
|
||||
2. 如何跨域?
|
||||
- 问的人不少,可以先百度了解一下。<a href="https://ask.dcloud.net.cn/article/35267" target="_blank">如何跨域</a>
|
||||
3. post 怎么传不了数组的参数啊?
|
||||
- <a href="https://uniapp.dcloud.io/api/request/request" target="_blank">uni-request</a> <br>
|
||||
可以点击看一下uni-request 的api 文档,data支持的文件类型只有<code>Object/String/ArrayBuffer</code>这个真跟我没啥关系 0.0
|
||||
4. TypeError: undefined is not an object (evaluating 'this.$http.get')
|
||||
- 不知道为啥问的人这么多?太基础了,百度学习一下 export default 和export,头大。
|
||||
- `import { http } from '@/utils/luch-request/index.js'`
|
||||
5. 什么参数需要在` setConfig ` 设置?什么参数需要在` request ` 拦截器设置?
|
||||
- ` setConfig ` 适用于设置一些静态的/默认的参数;比如header 里的一些默认值、默认全局参数(全局请求配置)。` token ` 并不适合在这里设置。
|
||||
- ` interceptors.request ` 拦截器适用范围较广,但我仍然建议把一些静态的东西放在 ` setConfig ` 里。拦截器会在每次请求调用,而 ` setConfig ` 仅在调用时修改一遍。
|
||||
|
||||
tip
|
||||
------------
|
||||
- nvue 不支持全局挂载
|
||||
- 当前的hbuilderx 版本号:beat-3.0.4 alpha-3.0.4
|
||||
- 推荐使用下载插件的方式使用。如果本插件完全满足你的需求可直接使用 ` npm `安装
|
||||
- license: MIT
|
||||
|
||||
|
||||
issue
|
||||
------------
|
||||
- DCloud: 有任何问题或者建议可以=> <a href="https://ask.dcloud.net.cn/question/74922" target="_blank">issue提交</a>,先给个五星好评QAQ!!
|
||||
- github: [Issues](https://github.com/lei-mu/luch-request/issues "Issues")
|
||||
|
||||
|
||||
作者想说
|
||||
------------
|
||||
- 写代码很容易,为了让你们看懂写文档真的很lei 0.0
|
||||
- 最近发现有插件与我雷同,当初接触uni-app 就发现插件市场虽然有封装的不错的request库,但是都没有对多全局配置做处理,都是通过修改源码的方式配置。我首先推出通过class类,并仿照axios的api实现request请求库,并起名‘仿axios封装request网络请求库,支持拦截器全局配置’。他们虽然修改了部分代码,但是功能与性能并没有优化,反而使代码很冗余。希望能推出新的功能,和性能更加强悍的请求库。(2019-05)
|
||||
- 任何形式的‘参考’、‘借鉴’,请标明作者
|
||||
```javascript
|
||||
<a href="https://ext.dcloud.net.cn/plugin?id=392">luch-request</a>
|
||||
```
|
||||
- 关于问问题
|
||||
1. 首先请善于利用搜索引擎,不管百度,还是Google,遇到问题请先自己尝试解决。自己尝试过无法解决,再问。
|
||||
2. 不要问类似为什么我的xx无法使用这种问题。请仔细阅读文档,检查代码,或者说明运行环境,把相关代码贴至评论或者发送至我的邮箱,还可以点击上面的issue提交,在里面提问,可能我在里面已经回答了。
|
||||
3. 我的代码如果真的出现bug,或者你有好的建议、需求,可以提issue,我看到后会立即解决
|
||||
|
||||
- 如何问问题
|
||||
1. 问问题之前请换位思考,如果自己要解决这个问题,需要哪些信息
|
||||
2. 仔细阅读文档,检查代码
|
||||
3. 说明运行环境,比如:app端 ios、android 版本号、手机机型、普遍现象还是个别现象(越详细越好)
|
||||
4. 发出代码片段或者截图至邮箱(很重要)
|
||||
5. 或者可以在上方的'issue提交' 里发出详细的问题描述
|
||||
6. 以上都觉得解决不了你的问题,可以加QQ:`370306150`
|
||||
|
||||
个人网站
|
||||
------------
|
||||
- 欢迎大家都来踩一踩<a href="https://www.quanzhan.co/" target="_blank">luch的博客</a> 0.0
|
||||
|
||||
|
||||
|
||||
|
||||
土豪赞赏
|
||||
------------
|
||||
[](https://www.quanzhan.co/luch-request/acknowledgement/#前言 "wechat 打赏")
|
||||
[](https://www.quanzhan.co/luch-request/acknowledgement/#前言 "支付宝 打赏")
|
||||
|
||||
[打赏事宜具体说明](https://www.quanzhan.co/luch-request/acknowledgement/#前言 "打赏事宜具体说明")
|
||||
|
||||
|
||||
###### 您的鼓励是我更新的动力
|
||||
|
||||
#### 创作不易,五星好评你懂得!
|
99
kinit-uni/components/luch-request/src/lib/adapters/index.js
Normal file
99
kinit-uni/components/luch-request/src/lib/adapters/index.js
Normal file
@ -0,0 +1,99 @@
|
||||
import buildURL from '../helpers/buildURL'
|
||||
import buildFullPath from '../core/buildFullPath'
|
||||
import settle from '../core/settle'
|
||||
import { isUndefined } from "../utils"
|
||||
|
||||
/**
|
||||
* 返回可选值存在的配置
|
||||
* @param {Array} keys - 可选值数组
|
||||
* @param {Object} config2 - 配置
|
||||
* @return {{}} - 存在的配置项
|
||||
*/
|
||||
const mergeKeys = (keys, config2) => {
|
||||
let config = {}
|
||||
keys.forEach(prop => {
|
||||
if (!isUndefined(config2[prop])) {
|
||||
config[prop] = config2[prop]
|
||||
}
|
||||
})
|
||||
return config
|
||||
}
|
||||
export default (config) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params)
|
||||
const _config = {
|
||||
url: fullPath,
|
||||
header: config.header,
|
||||
complete: (response) => {
|
||||
config.fullPath = fullPath
|
||||
response.config = config
|
||||
try {
|
||||
// 对可能字符串不是json 的情况容错
|
||||
if (typeof response.data === 'string') {
|
||||
response.data = JSON.parse(response.data)
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {
|
||||
}
|
||||
settle(resolve, reject, response)
|
||||
}
|
||||
}
|
||||
let requestTask
|
||||
if (config.method === 'UPLOAD') {
|
||||
delete _config.header['content-type']
|
||||
delete _config.header['Content-Type']
|
||||
let otherConfig = {
|
||||
// #ifdef MP-ALIPAY
|
||||
fileType: config.fileType,
|
||||
// #endif
|
||||
filePath: config.filePath,
|
||||
name: config.name
|
||||
}
|
||||
const optionalKeys = [
|
||||
// #ifdef APP-PLUS || H5
|
||||
'files',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
'file',
|
||||
// #endif
|
||||
// #ifdef H5 || APP-PLUS
|
||||
'timeout',
|
||||
// #endif
|
||||
'formData'
|
||||
]
|
||||
requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
|
||||
} else if (config.method === 'DOWNLOAD') {
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (!isUndefined(config['timeout'])) {
|
||||
_config['timeout'] = config['timeout']
|
||||
}
|
||||
// #endif
|
||||
requestTask = uni.downloadFile(_config)
|
||||
} else {
|
||||
const optionalKeys = [
|
||||
'data',
|
||||
'method',
|
||||
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||
'timeout',
|
||||
// #endif
|
||||
'dataType',
|
||||
// #ifndef MP-ALIPAY
|
||||
'responseType',
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
'sslVerify',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
'withCredentials',
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
'firstIpv4',
|
||||
// #endif
|
||||
]
|
||||
requestTask = uni.request({..._config,...mergeKeys(optionalKeys, config)})
|
||||
}
|
||||
if (config.getTask) {
|
||||
config.getTask(requestTask, config)
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
'use strict'
|
||||
|
||||
|
||||
function InterceptorManager() {
|
||||
this.handlers = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new interceptor to the stack
|
||||
*
|
||||
* @param {Function} fulfilled The function to handle `then` for a `Promise`
|
||||
* @param {Function} rejected The function to handle `reject` for a `Promise`
|
||||
*
|
||||
* @return {Number} An ID used to remove interceptor later
|
||||
*/
|
||||
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
|
||||
this.handlers.push({
|
||||
fulfilled: fulfilled,
|
||||
rejected: rejected
|
||||
})
|
||||
return this.handlers.length - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an interceptor from the stack
|
||||
*
|
||||
* @param {Number} id The ID that was returned by `use`
|
||||
*/
|
||||
InterceptorManager.prototype.eject = function eject(id) {
|
||||
if (this.handlers[id]) {
|
||||
this.handlers[id] = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all the registered interceptors
|
||||
*
|
||||
* This method is particularly useful for skipping over any
|
||||
* interceptors that may have become `null` calling `eject`.
|
||||
*
|
||||
* @param {Function} fn The function to call for each interceptor
|
||||
*/
|
||||
InterceptorManager.prototype.forEach = function forEach(fn) {
|
||||
this.handlers.forEach(h => {
|
||||
if (h !== null) {
|
||||
fn(h)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default InterceptorManager
|
200
kinit-uni/components/luch-request/src/lib/core/Request.js
Normal file
200
kinit-uni/components/luch-request/src/lib/core/Request.js
Normal file
@ -0,0 +1,200 @@
|
||||
/**
|
||||
* @Class Request
|
||||
* @description luch-request http请求插件
|
||||
* @version 3.0.7
|
||||
* @Author lu-ch
|
||||
* @Date 2021-09-04
|
||||
* @Email webwork.s@qq.com
|
||||
* 文档: https://www.quanzhan.co/luch-request/
|
||||
* github: https://github.com/lei-mu/luch-request
|
||||
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
|
||||
* HBuilderX: beat-3.0.4 alpha-3.0.4
|
||||
*/
|
||||
|
||||
|
||||
import dispatchRequest from './dispatchRequest'
|
||||
import InterceptorManager from './InterceptorManager'
|
||||
import mergeConfig from './mergeConfig'
|
||||
import defaults from './defaults'
|
||||
import { isPlainObject } from '../utils'
|
||||
import clone from '../utils/clone'
|
||||
|
||||
export default class Request {
|
||||
/**
|
||||
* @param {Object} arg - 全局配置
|
||||
* @param {String} arg.baseURL - 全局根路径
|
||||
* @param {Object} arg.header - 全局header
|
||||
* @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
|
||||
* @param {String} arg.dataType = [json] - 全局默认的dataType
|
||||
* @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持
|
||||
* @param {Object} arg.custom - 全局默认的自定义参数
|
||||
* @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
|
||||
* @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+)
|
||||
* @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+)
|
||||
* @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+)
|
||||
* @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300
|
||||
*/
|
||||
constructor(arg = {}) {
|
||||
if (!isPlainObject(arg)) {
|
||||
arg = {}
|
||||
console.warn('设置全局参数必须接收一个Object')
|
||||
}
|
||||
this.config = clone({...defaults, ...arg})
|
||||
this.interceptors = {
|
||||
request: new InterceptorManager(),
|
||||
response: new InterceptorManager()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Function
|
||||
* @param {Request~setConfigCallback} f - 设置全局默认配置
|
||||
*/
|
||||
setConfig(f) {
|
||||
this.config = f(this.config)
|
||||
}
|
||||
|
||||
middleware(config) {
|
||||
config = mergeConfig(this.config, config)
|
||||
let chain = [dispatchRequest, undefined]
|
||||
let promise = Promise.resolve(config)
|
||||
|
||||
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
|
||||
chain.unshift(interceptor.fulfilled, interceptor.rejected)
|
||||
})
|
||||
|
||||
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
|
||||
chain.push(interceptor.fulfilled, interceptor.rejected)
|
||||
})
|
||||
|
||||
while (chain.length) {
|
||||
promise = promise.then(chain.shift(), chain.shift())
|
||||
}
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
/**
|
||||
* @Function
|
||||
* @param {Object} config - 请求配置项
|
||||
* @prop {String} options.url - 请求路径
|
||||
* @prop {Object} options.data - 请求参数
|
||||
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
|
||||
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
|
||||
* @prop {Object} [options.header = config.header] - 请求header
|
||||
* @prop {Object} [options.method = config.method] - 请求方法
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
request(config = {}) {
|
||||
return this.middleware(config)
|
||||
}
|
||||
|
||||
get(url, options = {}) {
|
||||
return this.middleware({
|
||||
url,
|
||||
method: 'GET',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
post(url, data, options = {}) {
|
||||
return this.middleware({
|
||||
url,
|
||||
data,
|
||||
method: 'POST',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
// #ifndef MP-ALIPAY
|
||||
put(url, data, options = {}) {
|
||||
return this.middleware({
|
||||
url,
|
||||
data,
|
||||
method: 'PUT',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
|
||||
delete(url, data, options = {}) {
|
||||
return this.middleware({
|
||||
url,
|
||||
data,
|
||||
method: 'DELETE',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef H5 || MP-WEIXIN
|
||||
connect(url, data, options = {}) {
|
||||
return this.middleware({
|
||||
url,
|
||||
data,
|
||||
method: 'CONNECT',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef H5 || MP-WEIXIN || MP-BAIDU
|
||||
head(url, data, options = {}) {
|
||||
return this.middleware({
|
||||
url,
|
||||
data,
|
||||
method: 'HEAD',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
|
||||
options(url, data, options = {}) {
|
||||
return this.middleware({
|
||||
url,
|
||||
data,
|
||||
method: 'OPTIONS',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef H5 || MP-WEIXIN
|
||||
trace(url, data, options = {}) {
|
||||
return this.middleware({
|
||||
url,
|
||||
data,
|
||||
method: 'TRACE',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
upload(url, config = {}) {
|
||||
config.url = url
|
||||
config.method = 'UPLOAD'
|
||||
return this.middleware(config)
|
||||
}
|
||||
|
||||
download(url, config = {}) {
|
||||
config.url = url
|
||||
config.method = 'DOWNLOAD'
|
||||
return this.middleware(config)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* setConfig回调
|
||||
* @return {Object} - 返回操作后的config
|
||||
* @callback Request~setConfigCallback
|
||||
* @param {Object} config - 全局默认config
|
||||
*/
|
@ -0,0 +1,20 @@
|
||||
'use strict'
|
||||
|
||||
import isAbsoluteURL from '../helpers/isAbsoluteURL'
|
||||
import combineURLs from '../helpers/combineURLs'
|
||||
|
||||
/**
|
||||
* Creates a new URL by combining the baseURL with the requestedURL,
|
||||
* only when the requestedURL is not already an absolute URL.
|
||||
* If the requestURL is absolute, this function returns the requestedURL untouched.
|
||||
*
|
||||
* @param {string} baseURL The base URL
|
||||
* @param {string} requestedURL Absolute or relative URL to combine
|
||||
* @returns {string} The combined full path
|
||||
*/
|
||||
export default function buildFullPath(baseURL, requestedURL) {
|
||||
if (baseURL && !isAbsoluteURL(requestedURL)) {
|
||||
return combineURLs(baseURL, requestedURL)
|
||||
}
|
||||
return requestedURL
|
||||
}
|
30
kinit-uni/components/luch-request/src/lib/core/defaults.js
Normal file
30
kinit-uni/components/luch-request/src/lib/core/defaults.js
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 默认的全局配置
|
||||
*/
|
||||
|
||||
|
||||
export default {
|
||||
baseURL: '',
|
||||
header: {},
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
// #ifndef MP-ALIPAY
|
||||
responseType: 'text',
|
||||
// #endif
|
||||
custom: {},
|
||||
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||
timeout: 60000,
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
sslVerify: true,
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
withCredentials: false,
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
firstIpv4: false,
|
||||
// #endif
|
||||
validateStatus: function validateStatus(status) {
|
||||
return status >= 200 && status < 300
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import adapter from '../adapters/index'
|
||||
|
||||
|
||||
export default (config) => {
|
||||
return adapter(config)
|
||||
}
|
103
kinit-uni/components/luch-request/src/lib/core/mergeConfig.js
Normal file
103
kinit-uni/components/luch-request/src/lib/core/mergeConfig.js
Normal file
@ -0,0 +1,103 @@
|
||||
import {deepMerge, isUndefined} from '../utils'
|
||||
|
||||
/**
|
||||
* 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局
|
||||
* @param {Array} keys - 配置项
|
||||
* @param {Object} globalsConfig - 当前的全局配置
|
||||
* @param {Object} config2 - 局部配置
|
||||
* @return {{}}
|
||||
*/
|
||||
const mergeKeys = (keys, globalsConfig, config2) => {
|
||||
let config = {}
|
||||
keys.forEach(prop => {
|
||||
if (!isUndefined(config2[prop])) {
|
||||
config[prop] = config2[prop]
|
||||
} else if (!isUndefined(globalsConfig[prop])) {
|
||||
config[prop] = globalsConfig[prop]
|
||||
}
|
||||
})
|
||||
return config
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param globalsConfig - 当前实例的全局配置
|
||||
* @param config2 - 当前的局部配置
|
||||
* @return - 合并后的配置
|
||||
*/
|
||||
export default (globalsConfig, config2 = {}) => {
|
||||
const method = config2.method || globalsConfig.method || 'GET'
|
||||
let config = {
|
||||
baseURL: globalsConfig.baseURL || '',
|
||||
method: method,
|
||||
url: config2.url || '',
|
||||
params: config2.params || {},
|
||||
custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
|
||||
header: deepMerge(globalsConfig.header || {}, config2.header || {})
|
||||
}
|
||||
const defaultToConfig2Keys = ['getTask', 'validateStatus']
|
||||
config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
|
||||
|
||||
// eslint-disable-next-line no-empty
|
||||
if (method === 'DOWNLOAD') {
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (!isUndefined(config2.timeout)) {
|
||||
config['timeout'] = config2['timeout']
|
||||
} else if (!isUndefined(globalsConfig.timeout)) {
|
||||
config['timeout'] = globalsConfig['timeout']
|
||||
}
|
||||
// #endif
|
||||
} else if (method === 'UPLOAD') {
|
||||
delete config.header['content-type']
|
||||
delete config.header['Content-Type']
|
||||
const uploadKeys = [
|
||||
// #ifdef APP-PLUS || H5
|
||||
'files',
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
'fileType',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
'file',
|
||||
// #endif
|
||||
'filePath',
|
||||
'name',
|
||||
// #ifdef H5 || APP-PLUS
|
||||
'timeout',
|
||||
// #endif
|
||||
'formData',
|
||||
]
|
||||
uploadKeys.forEach(prop => {
|
||||
if (!isUndefined(config2[prop])) {
|
||||
config[prop] = config2[prop]
|
||||
}
|
||||
})
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
|
||||
config['timeout'] = globalsConfig['timeout']
|
||||
}
|
||||
// #endif
|
||||
} else {
|
||||
const defaultsKeys = [
|
||||
'data',
|
||||
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||
'timeout',
|
||||
// #endif
|
||||
'dataType',
|
||||
// #ifndef MP-ALIPAY
|
||||
'responseType',
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
'sslVerify',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
'withCredentials',
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
'firstIpv4',
|
||||
// #endif
|
||||
]
|
||||
config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
16
kinit-uni/components/luch-request/src/lib/core/settle.js
Normal file
16
kinit-uni/components/luch-request/src/lib/core/settle.js
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Resolve or reject a Promise based on response status.
|
||||
*
|
||||
* @param {Function} resolve A function that resolves the promise.
|
||||
* @param {Function} reject A function that rejects the promise.
|
||||
* @param {object} response The response.
|
||||
*/
|
||||
export default function settle(resolve, reject, response) {
|
||||
const validateStatus = response.config.validateStatus
|
||||
const status = response.statusCode
|
||||
if (status && (!validateStatus || validateStatus(status))) {
|
||||
resolve(response)
|
||||
} else {
|
||||
reject(response)
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
'use strict'
|
||||
|
||||
import * as utils from './../utils'
|
||||
|
||||
function encode(val) {
|
||||
return encodeURIComponent(val).
|
||||
replace(/%40/gi, '@').
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace(/%20/g, '+').
|
||||
replace(/%5B/gi, '[').
|
||||
replace(/%5D/gi, ']')
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a URL by appending params to the end
|
||||
*
|
||||
* @param {string} url The base of the url (e.g., http://www.google.com)
|
||||
* @param {object} [params] The params to be appended
|
||||
* @returns {string} The formatted url
|
||||
*/
|
||||
export default function buildURL(url, params) {
|
||||
/*eslint no-param-reassign:0*/
|
||||
if (!params) {
|
||||
return url
|
||||
}
|
||||
|
||||
var serializedParams
|
||||
if (utils.isURLSearchParams(params)) {
|
||||
serializedParams = params.toString()
|
||||
} else {
|
||||
var parts = []
|
||||
|
||||
utils.forEach(params, function serialize(val, key) {
|
||||
if (val === null || typeof val === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
if (utils.isArray(val)) {
|
||||
key = key + '[]'
|
||||
} else {
|
||||
val = [val]
|
||||
}
|
||||
|
||||
utils.forEach(val, function parseValue(v) {
|
||||
if (utils.isDate(v)) {
|
||||
v = v.toISOString()
|
||||
} else if (utils.isObject(v)) {
|
||||
v = JSON.stringify(v)
|
||||
}
|
||||
parts.push(encode(key) + '=' + encode(v))
|
||||
})
|
||||
})
|
||||
|
||||
serializedParams = parts.join('&')
|
||||
}
|
||||
|
||||
if (serializedParams) {
|
||||
var hashmarkIndex = url.indexOf('#')
|
||||
if (hashmarkIndex !== -1) {
|
||||
url = url.slice(0, hashmarkIndex)
|
||||
}
|
||||
|
||||
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Creates a new URL by combining the specified URLs
|
||||
*
|
||||
* @param {string} baseURL The base URL
|
||||
* @param {string} relativeURL The relative URL
|
||||
* @returns {string} The combined URL
|
||||
*/
|
||||
export default function combineURLs(baseURL, relativeURL) {
|
||||
return relativeURL
|
||||
? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
|
||||
: baseURL
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Determines whether the specified URL is absolute
|
||||
*
|
||||
* @param {string} url The URL to test
|
||||
* @returns {boolean} True if the specified URL is absolute, otherwise false
|
||||
*/
|
||||
export default function isAbsoluteURL(url) {
|
||||
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
|
||||
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
|
||||
// by any combination of letters, digits, plus, period, or hyphen.
|
||||
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
|
||||
}
|
116
kinit-uni/components/luch-request/src/lib/luch-request.d.ts
vendored
Normal file
116
kinit-uni/components/luch-request/src/lib/luch-request.d.ts
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
type AnyObject = Record<string | number | symbol, any>
|
||||
type HttpPromise<T> = Promise<HttpResponse<T>>;
|
||||
type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask
|
||||
export interface RequestTask {
|
||||
abort: () => void;
|
||||
offHeadersReceived: () => void;
|
||||
onHeadersReceived: () => void;
|
||||
}
|
||||
export interface HttpRequestConfig<T = Tasks> {
|
||||
/** 请求基地址 */
|
||||
baseURL?: string;
|
||||
/** 请求服务器接口地址 */
|
||||
url?: string;
|
||||
|
||||
/** 请求查询参数,自动拼接为查询字符串 */
|
||||
params?: AnyObject;
|
||||
/** 请求体参数 */
|
||||
data?: AnyObject;
|
||||
|
||||
/** 文件对应的 key */
|
||||
name?: string;
|
||||
/** HTTP 请求中其他额外的 form data */
|
||||
formData?: AnyObject;
|
||||
/** 要上传文件资源的路径。 */
|
||||
filePath?: string;
|
||||
/** 需要上传的文件列表。使用 files 时,filePath 和 name 不生效,App、H5( 2.6.15+) */
|
||||
files?: Array<{
|
||||
name?: string;
|
||||
file?: File;
|
||||
uri: string;
|
||||
}>;
|
||||
/** 要上传的文件对象,仅H5(2.6.15+)支持 */
|
||||
file?: File;
|
||||
|
||||
/** 请求头信息 */
|
||||
header?: AnyObject;
|
||||
/** 请求方式 */
|
||||
method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD";
|
||||
/** 如果设为 json,会尝试对返回的数据做一次 JSON.parse */
|
||||
dataType?: string;
|
||||
/** 设置响应的数据类型,支付宝小程序不支持 */
|
||||
responseType?: "text" | "arraybuffer";
|
||||
/** 自定义参数 */
|
||||
custom?: AnyObject;
|
||||
/** 超时时间,仅微信小程序(2.10.0)、支付宝小程序支持 */
|
||||
timeout?: number;
|
||||
/** DNS解析时优先使用ipv4,仅 App-Android 支持 (HBuilderX 2.8.0+) */
|
||||
firstIpv4?: boolean;
|
||||
/** 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+) */
|
||||
sslVerify?: boolean;
|
||||
/** 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) */
|
||||
withCredentials?: boolean;
|
||||
|
||||
/** 返回当前请求的task, options。请勿在此处修改options。 */
|
||||
getTask?: (task: T, options: HttpRequestConfig<T>) => void;
|
||||
/** 全局自定义验证器 */
|
||||
validateStatus?: (statusCode: number) => boolean | void;
|
||||
}
|
||||
export interface HttpResponse<T = any> {
|
||||
config: HttpRequestConfig;
|
||||
statusCode: number;
|
||||
cookies: Array<string>;
|
||||
data: T;
|
||||
errMsg: string;
|
||||
header: AnyObject;
|
||||
}
|
||||
export interface HttpUploadResponse<T = any> {
|
||||
config: HttpRequestConfig;
|
||||
statusCode: number;
|
||||
data: T;
|
||||
errMsg: string;
|
||||
}
|
||||
export interface HttpDownloadResponse extends HttpResponse {
|
||||
tempFilePath: string;
|
||||
}
|
||||
export interface HttpError {
|
||||
config: HttpRequestConfig;
|
||||
statusCode?: number;
|
||||
cookies?: Array<string>;
|
||||
data?: any;
|
||||
errMsg: string;
|
||||
header?: AnyObject;
|
||||
}
|
||||
export interface HttpInterceptorManager<V, E = V> {
|
||||
use(
|
||||
onFulfilled?: (config: V) => Promise<V> | V,
|
||||
onRejected?: (config: E) => Promise<E> | E
|
||||
): void;
|
||||
eject(id: number): void;
|
||||
}
|
||||
export abstract class HttpRequestAbstract {
|
||||
constructor(config?: HttpRequestConfig);
|
||||
config: HttpRequestConfig;
|
||||
interceptors: {
|
||||
request: HttpInterceptorManager<HttpRequestConfig, HttpRequestConfig>;
|
||||
response: HttpInterceptorManager<HttpResponse, HttpError>;
|
||||
}
|
||||
middleware<T = any>(config: HttpRequestConfig): HttpPromise<T>;
|
||||
request<T = any>(config: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
get<T = any>(url: string, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
upload<T = any>(url: string, config?: HttpRequestConfig<UniApp.UploadTask>): HttpPromise<T>;
|
||||
delete<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
head<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
post<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
put<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
connect<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
options<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
trace<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
|
||||
|
||||
download(url: string, config?: HttpRequestConfig<UniApp.DownloadTask>): Promise<HttpDownloadResponse>;
|
||||
|
||||
setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void;
|
||||
}
|
||||
|
||||
declare class HttpRequest extends HttpRequestAbstract { }
|
||||
export default HttpRequest;
|
@ -0,0 +1,2 @@
|
||||
import Request from './core/Request'
|
||||
export default Request
|
135
kinit-uni/components/luch-request/src/lib/utils.js
Normal file
135
kinit-uni/components/luch-request/src/lib/utils.js
Normal file
@ -0,0 +1,135 @@
|
||||
'use strict'
|
||||
|
||||
// utils is a library of generic helper functions non-specific to axios
|
||||
|
||||
var toString = Object.prototype.toString
|
||||
|
||||
/**
|
||||
* Determine if a value is an Array
|
||||
*
|
||||
* @param {Object} val The value to test
|
||||
* @returns {boolean} True if value is an Array, otherwise false
|
||||
*/
|
||||
export function isArray (val) {
|
||||
return toString.call(val) === '[object Array]'
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a value is an Object
|
||||
*
|
||||
* @param {Object} val The value to test
|
||||
* @returns {boolean} True if value is an Object, otherwise false
|
||||
*/
|
||||
export function isObject (val) {
|
||||
return val !== null && typeof val === 'object'
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a value is a Date
|
||||
*
|
||||
* @param {Object} val The value to test
|
||||
* @returns {boolean} True if value is a Date, otherwise false
|
||||
*/
|
||||
export function isDate (val) {
|
||||
return toString.call(val) === '[object Date]'
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a value is a URLSearchParams object
|
||||
*
|
||||
* @param {Object} val The value to test
|
||||
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
|
||||
*/
|
||||
export function isURLSearchParams (val) {
|
||||
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Iterate over an Array or an Object invoking a function for each item.
|
||||
*
|
||||
* If `obj` is an Array callback will be called passing
|
||||
* the value, index, and complete array for each item.
|
||||
*
|
||||
* If 'obj' is an Object callback will be called passing
|
||||
* the value, key, and complete object for each property.
|
||||
*
|
||||
* @param {Object|Array} obj The object to iterate
|
||||
* @param {Function} fn The callback to invoke for each item
|
||||
*/
|
||||
export function forEach (obj, fn) {
|
||||
// Don't bother if no value provided
|
||||
if (obj === null || typeof obj === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
// Force an array if not already something iterable
|
||||
if (typeof obj !== 'object') {
|
||||
/*eslint no-param-reassign:0*/
|
||||
obj = [obj]
|
||||
}
|
||||
|
||||
if (isArray(obj)) {
|
||||
// Iterate over array values
|
||||
for (var i = 0, l = obj.length; i < l; i++) {
|
||||
fn.call(null, obj[i], i, obj)
|
||||
}
|
||||
} else {
|
||||
// Iterate over object keys
|
||||
for (var key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
fn.call(null, obj[key], key, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为boolean 值
|
||||
* @param val
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isBoolean(val) {
|
||||
return typeof val === 'boolean'
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为真正的对象{} new Object
|
||||
* @param {any} obj - 检测的对象
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isPlainObject(obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Object]'
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Function equal to merge with the difference being that no reference
|
||||
* to original objects is kept.
|
||||
*
|
||||
* @see merge
|
||||
* @param {Object} obj1 Object to merge
|
||||
* @returns {Object} Result of all merge properties
|
||||
*/
|
||||
export function deepMerge(/* obj1, obj2, obj3, ... */) {
|
||||
let result = {}
|
||||
function assignValue(val, key) {
|
||||
if (typeof result[key] === 'object' && typeof val === 'object') {
|
||||
result[key] = deepMerge(result[key], val)
|
||||
} else if (typeof val === 'object') {
|
||||
result[key] = deepMerge({}, val)
|
||||
} else {
|
||||
result[key] = val
|
||||
}
|
||||
}
|
||||
for (let i = 0, l = arguments.length; i < l; i++) {
|
||||
forEach(arguments[i], assignValue)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function isUndefined (val) {
|
||||
return typeof val === 'undefined'
|
||||
}
|
264
kinit-uni/components/luch-request/src/lib/utils/clone.js
Normal file
264
kinit-uni/components/luch-request/src/lib/utils/clone.js
Normal file
@ -0,0 +1,264 @@
|
||||
/* eslint-disable */
|
||||
var clone = (function() {
|
||||
'use strict';
|
||||
|
||||
function _instanceof(obj, type) {
|
||||
return type != null && obj instanceof type;
|
||||
}
|
||||
|
||||
var nativeMap;
|
||||
try {
|
||||
nativeMap = Map;
|
||||
} catch(_) {
|
||||
// maybe a reference error because no `Map`. Give it a dummy value that no
|
||||
// value will ever be an instanceof.
|
||||
nativeMap = function() {};
|
||||
}
|
||||
|
||||
var nativeSet;
|
||||
try {
|
||||
nativeSet = Set;
|
||||
} catch(_) {
|
||||
nativeSet = function() {};
|
||||
}
|
||||
|
||||
var nativePromise;
|
||||
try {
|
||||
nativePromise = Promise;
|
||||
} catch(_) {
|
||||
nativePromise = function() {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones (copies) an Object using deep copying.
|
||||
*
|
||||
* This function supports circular references by default, but if you are certain
|
||||
* there are no circular references in your object, you can save some CPU time
|
||||
* by calling clone(obj, false).
|
||||
*
|
||||
* Caution: if `circular` is false and `parent` contains circular references,
|
||||
* your program may enter an infinite loop and crash.
|
||||
*
|
||||
* @param `parent` - the object to be cloned
|
||||
* @param `circular` - set to true if the object to be cloned may contain
|
||||
* circular references. (optional - true by default)
|
||||
* @param `depth` - set to a number if the object is only to be cloned to
|
||||
* a particular depth. (optional - defaults to Infinity)
|
||||
* @param `prototype` - sets the prototype to be used when cloning an object.
|
||||
* (optional - defaults to parent prototype).
|
||||
* @param `includeNonEnumerable` - set to true if the non-enumerable properties
|
||||
* should be cloned as well. Non-enumerable properties on the prototype
|
||||
* chain will be ignored. (optional - false by default)
|
||||
*/
|
||||
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
|
||||
if (typeof circular === 'object') {
|
||||
depth = circular.depth;
|
||||
prototype = circular.prototype;
|
||||
includeNonEnumerable = circular.includeNonEnumerable;
|
||||
circular = circular.circular;
|
||||
}
|
||||
// maintain two arrays for circular references, where corresponding parents
|
||||
// and children have the same index
|
||||
var allParents = [];
|
||||
var allChildren = [];
|
||||
|
||||
var useBuffer = typeof Buffer != 'undefined';
|
||||
|
||||
if (typeof circular == 'undefined')
|
||||
circular = true;
|
||||
|
||||
if (typeof depth == 'undefined')
|
||||
depth = Infinity;
|
||||
|
||||
// recurse this function so we don't reset allParents and allChildren
|
||||
function _clone(parent, depth) {
|
||||
// cloning null always returns null
|
||||
if (parent === null)
|
||||
return null;
|
||||
|
||||
if (depth === 0)
|
||||
return parent;
|
||||
|
||||
var child;
|
||||
var proto;
|
||||
if (typeof parent != 'object') {
|
||||
return parent;
|
||||
}
|
||||
|
||||
if (_instanceof(parent, nativeMap)) {
|
||||
child = new nativeMap();
|
||||
} else if (_instanceof(parent, nativeSet)) {
|
||||
child = new nativeSet();
|
||||
} else if (_instanceof(parent, nativePromise)) {
|
||||
child = new nativePromise(function (resolve, reject) {
|
||||
parent.then(function(value) {
|
||||
resolve(_clone(value, depth - 1));
|
||||
}, function(err) {
|
||||
reject(_clone(err, depth - 1));
|
||||
});
|
||||
});
|
||||
} else if (clone.__isArray(parent)) {
|
||||
child = [];
|
||||
} else if (clone.__isRegExp(parent)) {
|
||||
child = new RegExp(parent.source, __getRegExpFlags(parent));
|
||||
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
|
||||
} else if (clone.__isDate(parent)) {
|
||||
child = new Date(parent.getTime());
|
||||
} else if (useBuffer && Buffer.isBuffer(parent)) {
|
||||
if (Buffer.from) {
|
||||
// Node.js >= 5.10.0
|
||||
child = Buffer.from(parent);
|
||||
} else {
|
||||
// Older Node.js versions
|
||||
child = new Buffer(parent.length);
|
||||
parent.copy(child);
|
||||
}
|
||||
return child;
|
||||
} else if (_instanceof(parent, Error)) {
|
||||
child = Object.create(parent);
|
||||
} else {
|
||||
if (typeof prototype == 'undefined') {
|
||||
proto = Object.getPrototypeOf(parent);
|
||||
child = Object.create(proto);
|
||||
}
|
||||
else {
|
||||
child = Object.create(prototype);
|
||||
proto = prototype;
|
||||
}
|
||||
}
|
||||
|
||||
if (circular) {
|
||||
var index = allParents.indexOf(parent);
|
||||
|
||||
if (index != -1) {
|
||||
return allChildren[index];
|
||||
}
|
||||
allParents.push(parent);
|
||||
allChildren.push(child);
|
||||
}
|
||||
|
||||
if (_instanceof(parent, nativeMap)) {
|
||||
parent.forEach(function(value, key) {
|
||||
var keyChild = _clone(key, depth - 1);
|
||||
var valueChild = _clone(value, depth - 1);
|
||||
child.set(keyChild, valueChild);
|
||||
});
|
||||
}
|
||||
if (_instanceof(parent, nativeSet)) {
|
||||
parent.forEach(function(value) {
|
||||
var entryChild = _clone(value, depth - 1);
|
||||
child.add(entryChild);
|
||||
});
|
||||
}
|
||||
|
||||
for (var i in parent) {
|
||||
var attrs = Object.getOwnPropertyDescriptor(parent, i);
|
||||
if (attrs) {
|
||||
child[i] = _clone(parent[i], depth - 1);
|
||||
}
|
||||
|
||||
try {
|
||||
var objProperty = Object.getOwnPropertyDescriptor(parent, i);
|
||||
if (objProperty.set === 'undefined') {
|
||||
// no setter defined. Skip cloning this property
|
||||
continue;
|
||||
}
|
||||
child[i] = _clone(parent[i], depth - 1);
|
||||
} catch(e){
|
||||
if (e instanceof TypeError) {
|
||||
// when in strict mode, TypeError will be thrown if child[i] property only has a getter
|
||||
// we can't do anything about this, other than inform the user that this property cannot be set.
|
||||
continue
|
||||
} else if (e instanceof ReferenceError) {
|
||||
//this may happen in non strict mode
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
var symbols = Object.getOwnPropertySymbols(parent);
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
// Don't need to worry about cloning a symbol because it is a primitive,
|
||||
// like a number or string.
|
||||
var symbol = symbols[i];
|
||||
var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
|
||||
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
|
||||
continue;
|
||||
}
|
||||
child[symbol] = _clone(parent[symbol], depth - 1);
|
||||
Object.defineProperty(child, symbol, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (includeNonEnumerable) {
|
||||
var allPropertyNames = Object.getOwnPropertyNames(parent);
|
||||
for (var i = 0; i < allPropertyNames.length; i++) {
|
||||
var propertyName = allPropertyNames[i];
|
||||
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
|
||||
if (descriptor && descriptor.enumerable) {
|
||||
continue;
|
||||
}
|
||||
child[propertyName] = _clone(parent[propertyName], depth - 1);
|
||||
Object.defineProperty(child, propertyName, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
return _clone(parent, depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple flat clone using prototype, accepts only objects, usefull for property
|
||||
* override on FLAT configuration object (no nested props).
|
||||
*
|
||||
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
|
||||
* works.
|
||||
*/
|
||||
clone.clonePrototype = function clonePrototype(parent) {
|
||||
if (parent === null)
|
||||
return null;
|
||||
|
||||
var c = function () {};
|
||||
c.prototype = parent;
|
||||
return new c();
|
||||
};
|
||||
|
||||
// private utility functions
|
||||
|
||||
function __objToStr(o) {
|
||||
return Object.prototype.toString.call(o);
|
||||
}
|
||||
clone.__objToStr = __objToStr;
|
||||
|
||||
function __isDate(o) {
|
||||
return typeof o === 'object' && __objToStr(o) === '[object Date]';
|
||||
}
|
||||
clone.__isDate = __isDate;
|
||||
|
||||
function __isArray(o) {
|
||||
return typeof o === 'object' && __objToStr(o) === '[object Array]';
|
||||
}
|
||||
clone.__isArray = __isArray;
|
||||
|
||||
function __isRegExp(o) {
|
||||
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
|
||||
}
|
||||
clone.__isRegExp = __isRegExp;
|
||||
|
||||
function __getRegExpFlags(re) {
|
||||
var flags = '';
|
||||
if (re.global) flags += 'g';
|
||||
if (re.ignoreCase) flags += 'i';
|
||||
if (re.multiline) flags += 'm';
|
||||
return flags;
|
||||
}
|
||||
clone.__getRegExpFlags = __getRegExpFlags;
|
||||
|
||||
return clone;
|
||||
})();
|
||||
|
||||
export default clone
|
167
kinit-uni/components/uni-section/uni-section.vue
Normal file
167
kinit-uni/components/uni-section/uni-section.vue
Normal file
@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<view class="uni-section">
|
||||
<view class="uni-section-header" @click="onClick">
|
||||
<view class="uni-section-header__decoration" v-if="type" :class="type" />
|
||||
<slot v-else name="decoration"></slot>
|
||||
|
||||
<view class="uni-section-header__content">
|
||||
<text :style="{'font-size':titleFontSize,'color':titleColor}" class="uni-section__content-title" :class="{'distraction':!subTitle}">{{ title }}</text>
|
||||
<text v-if="subTitle" :style="{'font-size':subTitleFontSize,'color':subTitleColor}" class="uni-section-header__content-sub">{{ subTitle }}</text>
|
||||
</view>
|
||||
|
||||
<view class="uni-section-header__slot-right">
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="uni-section-content" :style="{padding: _padding}">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
/**
|
||||
* Section 标题栏
|
||||
* @description 标题栏
|
||||
* @property {String} type = [line|circle|square] 标题装饰类型
|
||||
* @value line 竖线
|
||||
* @value circle 圆形
|
||||
* @value square 正方形
|
||||
* @property {String} title 主标题
|
||||
* @property {String} titleFontSize 主标题字体大小
|
||||
* @property {String} titleColor 主标题字体颜色
|
||||
* @property {String} subTitle 副标题
|
||||
* @property {String} subTitleFontSize 副标题字体大小
|
||||
* @property {String} subTitleColor 副标题字体颜色
|
||||
* @property {String} padding 默认插槽 padding
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'UniSection',
|
||||
emits:['click'],
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
titleFontSize: {
|
||||
type: String,
|
||||
default: '14px'
|
||||
},
|
||||
titleColor:{
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
subTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
subTitleFontSize: {
|
||||
type: String,
|
||||
default: '12px'
|
||||
},
|
||||
subTitleColor: {
|
||||
type: String,
|
||||
default: '#999'
|
||||
},
|
||||
padding: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
_padding(){
|
||||
if(typeof this.padding === 'string'){
|
||||
return this.padding
|
||||
}
|
||||
|
||||
return this.padding?'10px':''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
title(newVal) {
|
||||
if (uni.report && newVal !== '') {
|
||||
uni.report('title', newVal)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" >
|
||||
$uni-primary: #2979ff !default;
|
||||
|
||||
.uni-section {
|
||||
background-color: #fff;
|
||||
.uni-section-header {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 12px 10px;
|
||||
font-weight: normal;
|
||||
|
||||
&__decoration{
|
||||
margin-right: 6px;
|
||||
background-color: $uni-primary;
|
||||
&.line {
|
||||
width: 4px;
|
||||
height: 12px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&.circle {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-top-right-radius: 50px;
|
||||
border-top-left-radius: 50px;
|
||||
border-bottom-left-radius: 50px;
|
||||
border-bottom-right-radius: 50px;
|
||||
}
|
||||
|
||||
&.square {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
color: #333;
|
||||
|
||||
.distraction {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
&-sub {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&__slot-right{
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.uni-section-content{
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
18
kinit-uni/config.js
Normal file
18
kinit-uni/config.js
Normal file
@ -0,0 +1,18 @@
|
||||
// 应用全局配置
|
||||
module.exports = {
|
||||
// 测试环境
|
||||
// baseUrl: 'http://127.0.0.1:9000',
|
||||
// 生产环境
|
||||
baseUrl: 'https://api.kinit.ktianc.top',
|
||||
// 应用信息
|
||||
appInfo: {
|
||||
// 应用版本
|
||||
version: "1.0.0",
|
||||
// 官方网站
|
||||
siteUrl: "https://gitee.com/ktianc/kinit",
|
||||
// 隐私政策,不支持本地路径
|
||||
privacy: "http://kinit.ktianc.top/docs/privacy",
|
||||
// 用户协议,不支持本地路径
|
||||
agreement: "http://kinit.ktianc.top/docs/agreement"
|
||||
}
|
||||
}
|
57
kinit-uni/main.js
Normal file
57
kinit-uni/main.js
Normal file
@ -0,0 +1,57 @@
|
||||
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"
|
||||
|
||||
Vue.use(uView)
|
||||
Vue.use(router)
|
||||
Vue.use(plugins)
|
||||
|
||||
// 调用setConfig方法,方法内部会进行对象属性深度合并,可以放心嵌套配置
|
||||
// 文档:https://www.uviewui.com/components/setting.html
|
||||
// 配置后,很多组件的默认尺寸就变了,需要手动调整,不熟悉不建议开启
|
||||
// 需要在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'
|
||||
}
|
||||
// 其他组件属性配置
|
||||
// ......
|
||||
}
|
||||
})
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$store = store
|
||||
|
||||
App.mpType = 'app'
|
||||
|
||||
const app = new Vue({
|
||||
...App
|
||||
})
|
||||
|
||||
//v1.3.5起 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式
|
||||
// #ifdef H5
|
||||
RouterMount(app, router, '#app')
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
app.$mount(); //为了兼容小程序及app端必须这样写才有效果
|
||||
// #endif
|
65
kinit-uni/manifest.json
Normal file
65
kinit-uni/manifest.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"name" : "kinit-uni",
|
||||
"appid" : "__UNI__9D006F9",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueCompiler" : "uni-app",
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
"modules" : {},
|
||||
"distribute" : {
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
"ios" : {},
|
||||
"sdkConfigs" : {}
|
||||
}
|
||||
},
|
||||
"quickapp" : {},
|
||||
"mp-weixin" : {
|
||||
"appid" : "wxa510d7a34a124349",
|
||||
"setting" : {
|
||||
"urlCheck" : false,
|
||||
"es6" : false,
|
||||
"minified" : true,
|
||||
"postcss" : true
|
||||
},
|
||||
"optimization" : {
|
||||
"subPackages" : true
|
||||
},
|
||||
"usingComponents" : true
|
||||
},
|
||||
"vueVersion" : "2",
|
||||
"h5" : {
|
||||
"template" : "static/index.html",
|
||||
"title" : "Kinit",
|
||||
"router" : {
|
||||
"mode" : "hash",
|
||||
"base" : "./"
|
||||
}
|
||||
}
|
||||
}
|
BIN
kinit-uni/node_modules.zip
Normal file
BIN
kinit-uni/node_modules.zip
Normal file
Binary file not shown.
7
kinit-uni/package.json
Normal file
7
kinit-uni/package.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"uni-read-pages": "^1.0.5",
|
||||
"uni-simple-router": "^2.0.8-beta.3",
|
||||
"uview-ui": "^2.0.34"
|
||||
}
|
||||
}
|
144
kinit-uni/pages.json
Normal file
144
kinit-uni/pages.json
Normal file
@ -0,0 +1,144 @@
|
||||
{
|
||||
"easycom": {
|
||||
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/login",
|
||||
"meta":{
|
||||
"loginAuth": false
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "Kinit 移动端框架"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/work/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "工作台"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/avatar/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改头像"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/info/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人信息"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/info/edit",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "编辑资料"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/pwd/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改密码"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/setting/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "应用设置"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/help/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "常见问题"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/about/index",
|
||||
"meta":{
|
||||
"loginAuth": true
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "关于我们"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/common/webview/index",
|
||||
"meta":{
|
||||
"loginAuth": false
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "浏览网页"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/common/textview/index",
|
||||
"meta":{
|
||||
"loginAuth": false
|
||||
},
|
||||
"style": {
|
||||
"navigationBarTitleText": "浏览文本"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tabBar": {
|
||||
"color": "#000000",
|
||||
"selectedColor": "#000000",
|
||||
"borderStyle": "white",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [{
|
||||
"pagePath": "pages/index",
|
||||
"iconPath": "static/images/tabbar/home.png",
|
||||
"selectedIconPath": "static/images/tabbar/home_.png",
|
||||
"text": "首页"
|
||||
}, {
|
||||
"pagePath": "pages/work/index",
|
||||
"iconPath": "static/images/tabbar/work.png",
|
||||
"selectedIconPath": "static/images/tabbar/work_.png",
|
||||
"text": "工作台"
|
||||
}, {
|
||||
"pagePath": "pages/mine/index",
|
||||
"iconPath": "static/images/tabbar/mine.png",
|
||||
"selectedIconPath": "static/images/tabbar/mine_.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "Kinit",
|
||||
"navigationBarBackgroundColor": "#F8F8F8",
|
||||
"backgroundColor": "#F8F8F8",
|
||||
"app-plus":{
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
}
|
48
kinit-uni/pages/common/textview/index.vue
Normal file
48
kinit-uni/pages/common/textview/index.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<view>
|
||||
<uni-card class="view-title" :title="title">
|
||||
<text class="uni-body view-content">{{ content }}</text>
|
||||
</uni-card>
|
||||
</view>
|
||||
</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
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
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;
|
||||
}
|
||||
</style>
|
35
kinit-uni/pages/common/webview/index.vue
Normal file
35
kinit-uni/pages/common/webview/index.vue
Normal file
@ -0,0 +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"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
src: {
|
||||
type: [String],
|
||||
default: null
|
||||
}
|
||||
},
|
||||
onLoad(event) {
|
||||
this.params = event
|
||||
if (event.title) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: event.title
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
52
kinit-uni/pages/index.vue
Normal file
52
kinit-uni/pages/index.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<image v-if="logo" class="logo" :src="logoImage"></image>
|
||||
<view class="text-area">
|
||||
<text class="title">Hello {{ name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
168
kinit-uni/pages/login.vue
Normal file
168
kinit-uni/pages/login.vue
Normal file
@ -0,0 +1,168 @@
|
||||
<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>
|
||||
<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" />
|
||||
</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" />
|
||||
</view>
|
||||
<view class="action-btn">
|
||||
<button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</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>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loginForm: {
|
||||
telephone: "15020221010",
|
||||
password: "kinit2022",
|
||||
method: '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
},
|
||||
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('Login', this.loginForm).then(() => {
|
||||
this.$modal.closeLoading()
|
||||
this.loginSuccess()
|
||||
})
|
||||
},
|
||||
// 登录成功后,处理函数
|
||||
loginSuccess(result) {
|
||||
// 设置用户信息
|
||||
this.$store.dispatch('GetInfo').then(res => {
|
||||
this.$tab.reLaunch('/pages/index')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.normal-login-container {
|
||||
width: 100%;
|
||||
|
||||
.logo-content {
|
||||
width: 100%;
|
||||
font-size: 21px;
|
||||
text-align: center;
|
||||
padding-top: 15%;
|
||||
|
||||
image {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form-content {
|
||||
text-align: center;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
margin-top: 40px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.xieyi {
|
||||
color: #333;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.easyinput {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.login-code-img {
|
||||
height: 45px;
|
||||
}
|
||||
</style>
|
89
kinit-uni/pages/mine/about/index.vue
Normal file
89
kinit-uni/pages/mine/about/index.vue
Normal file
@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<view class="about-container">
|
||||
<view class="header-section text-center">
|
||||
<image style="width: 150rpx;height: 150rpx;" :src="logoImage" mode="widthFix">
|
||||
</image>
|
||||
<uni-title type="h2" :title="title"></uni-title>
|
||||
</view>
|
||||
|
||||
<view class="content-section">
|
||||
<view class="menu-list">
|
||||
<view class="list-cell list-cell-arrow">
|
||||
<view class="menu-item-box">
|
||||
<view>版本信息</view>
|
||||
<view class="text-right">v{{version}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow">
|
||||
<view class="menu-item-box">
|
||||
<view>官方邮箱</view>
|
||||
<view class="text-right">kinit@xx.com</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow">
|
||||
<view class="menu-item-box">
|
||||
<view>服务热线</view>
|
||||
<view class="text-right">400-999-9999</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow">
|
||||
<view class="menu-item-box">
|
||||
<view>公司网站</view>
|
||||
<view class="text-right">
|
||||
<uni-link :href="siteUrl" :text="siteUrl" showUnderLine="false"></uni-link>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="copyright">
|
||||
<view>{{ footerContent }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
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
|
||||
},
|
||||
footerContent() {
|
||||
return this.$store.state.app.footerContent
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
</style>
|
630
kinit-uni/pages/mine/avatar/index.vue
Normal file
630
kinit-uni/pages/mine/avatar/index.vue
Normal file
@ -0,0 +1,630 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="page-body uni-content-info">
|
||||
<view class='cropper-content'>
|
||||
<view v-if="isShowImg" class="uni-corpper" :style="'width:'+cropperInitW+'px;height:'+cropperInitH+'px;background:#000'">
|
||||
<view class="uni-corpper-content" :style="'width:'+cropperW+'px;height:'+cropperH+'px;left:'+cropperL+'px;top:'+cropperT+'px'">
|
||||
<image :src="imageSrc" :style="'width:'+cropperW+'px;height:'+cropperH+'px'"></image>
|
||||
<view class="uni-corpper-crop-box" @touchstart.stop="contentStartMove" @touchmove.stop="contentMoveing" @touchend.stop="contentTouchEnd"
|
||||
:style="'left:'+cutL+'px;top:'+cutT+'px;right:'+cutR+'px;bottom:'+cutB+'px'">
|
||||
<view class="uni-cropper-view-box">
|
||||
<view class="uni-cropper-dashed-h"></view>
|
||||
<view class="uni-cropper-dashed-v"></view>
|
||||
<view class="uni-cropper-line-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-line-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-line-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-line-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-tr" data-drag="topTight"></view>
|
||||
<view class="uni-cropper-point point-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-rb" data-drag="rightBottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove" @touchend.stop="dragEnd"></view>
|
||||
<view class="uni-cropper-point point-bl" data-drag="bottomLeft"></view>
|
||||
<view class="uni-cropper-point point-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-lt" data-drag="leftTop"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class='cropper-config'>
|
||||
<button type="primary reverse" @click="getImage" style='margin-top: 30rpx;'> 选择头像 </button>
|
||||
<button type="warn" @click="getImageInfo" style='margin-top: 30rpx;'> 提交 </button>
|
||||
</view>
|
||||
<canvas canvas-id="myCanvas" :style="'position:absolute;border: 1px solid red; width:'+imageW+'px;height:'+imageH+'px;top:-9999px;left:-9999px;'"></canvas>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from '@/config'
|
||||
import store from "@/store"
|
||||
import { postCurrentUserUploadAvatar } from "@/common/request/api/vadmin/auth/user.js"
|
||||
|
||||
const baseUrl = config.baseUrl
|
||||
let sysInfo = uni.getSystemInfoSync()
|
||||
let SCREEN_WIDTH = sysInfo.screenWidth
|
||||
let PAGE_X, // 手按下的x位置
|
||||
PAGE_Y, // 手按下y的位置
|
||||
PR = sysInfo.pixelRatio, // dpi
|
||||
T_PAGE_X, // 手移动的时候x的位置
|
||||
T_PAGE_Y, // 手移动的时候Y的位置
|
||||
CUT_L, // 初始化拖拽元素的left值
|
||||
CUT_T, // 初始化拖拽元素的top值
|
||||
CUT_R, // 初始化拖拽元素的
|
||||
CUT_B, // 初始化拖拽元素的
|
||||
CUT_W, // 初始化拖拽元素的宽度
|
||||
CUT_H, // 初始化拖拽元素的高度
|
||||
IMG_RATIO, // 图片比例
|
||||
IMG_REAL_W, // 图片实际的宽度
|
||||
IMG_REAL_H, // 图片实际的高度
|
||||
DRAFG_MOVE_RATIO = 1, //移动时候的比例,
|
||||
INIT_DRAG_POSITION = 100, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度
|
||||
DRAW_IMAGE_W = sysInfo.screenWidth // 设置生成的图片宽度
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 页面的初始数据
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
imageSrc: store.getters.avatar,
|
||||
isShowImg: false,
|
||||
// 初始化的宽高
|
||||
cropperInitW: SCREEN_WIDTH,
|
||||
cropperInitH: SCREEN_WIDTH,
|
||||
// 动态的宽高
|
||||
cropperW: SCREEN_WIDTH,
|
||||
cropperH: SCREEN_WIDTH,
|
||||
// 动态的left top值
|
||||
cropperL: 0,
|
||||
cropperT: 0,
|
||||
|
||||
transL: 0,
|
||||
transT: 0,
|
||||
|
||||
// 图片缩放值
|
||||
scaleP: 0,
|
||||
imageW: 0,
|
||||
imageH: 0,
|
||||
|
||||
// 裁剪框 宽高
|
||||
cutL: 0,
|
||||
cutT: 0,
|
||||
cutB: SCREEN_WIDTH,
|
||||
cutR: '100%',
|
||||
qualityWidth: DRAW_IMAGE_W,
|
||||
innerAspectRadio: DRAFG_MOVE_RATIO
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面初次渲染完成
|
||||
*/
|
||||
onReady: function () {
|
||||
this.loadImage()
|
||||
},
|
||||
methods: {
|
||||
setData: function (obj) {
|
||||
let that = this
|
||||
Object.keys(obj).forEach(function (key) {
|
||||
that.$set(that.$data, key, obj[key])
|
||||
})
|
||||
},
|
||||
getImage: function () {
|
||||
var _this = this
|
||||
uni.chooseImage({
|
||||
success: function (res) {
|
||||
_this.setData({
|
||||
imageSrc: res.tempFilePaths[0],
|
||||
})
|
||||
_this.loadImage()
|
||||
},
|
||||
})
|
||||
},
|
||||
loadImage: function () {
|
||||
var _this = this
|
||||
|
||||
uni.getImageInfo({
|
||||
src: _this.imageSrc,
|
||||
success: function success(res) {
|
||||
IMG_RATIO = 1 / 1
|
||||
if (IMG_RATIO >= 1) {
|
||||
IMG_REAL_W = SCREEN_WIDTH
|
||||
IMG_REAL_H = SCREEN_WIDTH / IMG_RATIO
|
||||
} else {
|
||||
IMG_REAL_W = SCREEN_WIDTH * IMG_RATIO
|
||||
IMG_REAL_H = SCREEN_WIDTH
|
||||
}
|
||||
let minRange = IMG_REAL_W > IMG_REAL_H ? IMG_REAL_W : IMG_REAL_H
|
||||
INIT_DRAG_POSITION = minRange > INIT_DRAG_POSITION ? INIT_DRAG_POSITION : minRange
|
||||
// 根据图片的宽高显示不同的效果 保证图片可以正常显示
|
||||
if (IMG_RATIO >= 1) {
|
||||
let cutT = Math.ceil((SCREEN_WIDTH / IMG_RATIO - (SCREEN_WIDTH / IMG_RATIO - INIT_DRAG_POSITION)) / 2)
|
||||
let cutB = cutT
|
||||
let cutL = Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH + INIT_DRAG_POSITION) / 2)
|
||||
let cutR = cutL
|
||||
_this.setData({
|
||||
cropperW: SCREEN_WIDTH,
|
||||
cropperH: SCREEN_WIDTH / IMG_RATIO,
|
||||
// 初始化left right
|
||||
cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
|
||||
cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2),
|
||||
cutL: cutL,
|
||||
cutT: cutT,
|
||||
cutR: cutR,
|
||||
cutB: cutB,
|
||||
// 图片缩放值
|
||||
imageW: IMG_REAL_W,
|
||||
imageH: IMG_REAL_H,
|
||||
scaleP: IMG_REAL_W / SCREEN_WIDTH,
|
||||
qualityWidth: DRAW_IMAGE_W,
|
||||
innerAspectRadio: IMG_RATIO
|
||||
})
|
||||
} else {
|
||||
let cutL = Math.ceil((SCREEN_WIDTH * IMG_RATIO - (SCREEN_WIDTH * IMG_RATIO)) / 2)
|
||||
let cutR = cutL
|
||||
let cutT = Math.ceil((SCREEN_WIDTH - INIT_DRAG_POSITION) / 2)
|
||||
let cutB = cutT
|
||||
_this.setData({
|
||||
cropperW: SCREEN_WIDTH * IMG_RATIO,
|
||||
cropperH: SCREEN_WIDTH,
|
||||
// 初始化left right
|
||||
cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2),
|
||||
cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
|
||||
|
||||
cutL: cutL,
|
||||
cutT: cutT,
|
||||
cutR: cutR,
|
||||
cutB: cutB,
|
||||
// 图片缩放值
|
||||
imageW: IMG_REAL_W,
|
||||
imageH: IMG_REAL_H,
|
||||
scaleP: IMG_REAL_W / SCREEN_WIDTH,
|
||||
qualityWidth: DRAW_IMAGE_W,
|
||||
innerAspectRadio: IMG_RATIO
|
||||
})
|
||||
}
|
||||
_this.setData({
|
||||
isShowImg: true
|
||||
})
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 拖动时候触发的touchStart事件
|
||||
contentStartMove(e) {
|
||||
PAGE_X = e.touches[0].pageX
|
||||
PAGE_Y = e.touches[0].pageY
|
||||
},
|
||||
|
||||
// 拖动时候触发的touchMove事件
|
||||
contentMoveing(e) {
|
||||
var _this = this
|
||||
var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||||
var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||||
// 左移
|
||||
if (dragLengthX > 0) {
|
||||
if (this.cutL - dragLengthX < 0) dragLengthX = this.cutL
|
||||
} else {
|
||||
if (this.cutR + dragLengthX < 0) dragLengthX = -this.cutR
|
||||
}
|
||||
|
||||
if (dragLengthY > 0) {
|
||||
if (this.cutT - dragLengthY < 0) dragLengthY = this.cutT
|
||||
} else {
|
||||
if (this.cutB + dragLengthY < 0) dragLengthY = -this.cutB
|
||||
}
|
||||
this.setData({
|
||||
cutL: this.cutL - dragLengthX,
|
||||
cutT: this.cutT - dragLengthY,
|
||||
cutR: this.cutR + dragLengthX,
|
||||
cutB: this.cutB + dragLengthY
|
||||
})
|
||||
|
||||
PAGE_X = e.touches[0].pageX
|
||||
PAGE_Y = e.touches[0].pageY
|
||||
},
|
||||
|
||||
contentTouchEnd() {
|
||||
|
||||
},
|
||||
|
||||
// 获取图片
|
||||
getImageInfo() {
|
||||
var _this = this
|
||||
uni.showLoading({
|
||||
title: '图片生成中...',
|
||||
})
|
||||
// 将图片写入画布
|
||||
const ctx = uni.createCanvasContext('myCanvas')
|
||||
ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H)
|
||||
ctx.draw(true, () => {
|
||||
// 获取画布要裁剪的位置和宽度 均为百分比 * 画布中图片的宽度 保证了在微信小程序中裁剪的图片模糊 位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio)
|
||||
var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W
|
||||
var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H
|
||||
var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W
|
||||
var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H
|
||||
uni.canvasToTempFilePath({
|
||||
x: canvasL,
|
||||
y: canvasT,
|
||||
width: canvasW,
|
||||
height: canvasH,
|
||||
destWidth: canvasW,
|
||||
destHeight: canvasH,
|
||||
quality: 0.5,
|
||||
canvasId: 'myCanvas',
|
||||
success: function (res) {
|
||||
uni.hideLoading()
|
||||
postCurrentUserUploadAvatar(res.tempFilePath).then(response => {
|
||||
store.commit('SET_AVATAR', response.data)
|
||||
uni.showToast({ title: "修改成功", icon: 'success' })
|
||||
uni.navigateBack()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 设置大小的时候触发的touchStart事件
|
||||
dragStart(e) {
|
||||
T_PAGE_X = e.touches[0].pageX
|
||||
T_PAGE_Y = e.touches[0].pageY
|
||||
CUT_L = this.cutL
|
||||
CUT_R = this.cutR
|
||||
CUT_B = this.cutB
|
||||
CUT_T = this.cutT
|
||||
},
|
||||
|
||||
// 设置大小的时候触发的touchMove事件
|
||||
dragMove(e) {
|
||||
var _this = this
|
||||
var dragType = e.target.dataset.drag
|
||||
switch (dragType) {
|
||||
case 'right':
|
||||
var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||||
if (CUT_R + dragLength < 0) dragLength = -CUT_R
|
||||
this.setData({
|
||||
cutR: CUT_R + dragLength
|
||||
})
|
||||
break
|
||||
case 'left':
|
||||
var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||||
if (CUT_L - dragLength < 0) dragLength = CUT_L
|
||||
if ((CUT_L - dragLength) > (this.cropperW - this.cutR)) dragLength = CUT_L - (this.cropperW - this.cutR)
|
||||
this.setData({
|
||||
cutL: CUT_L - dragLength
|
||||
})
|
||||
break
|
||||
case 'top':
|
||||
var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||||
if (CUT_T - dragLength < 0) dragLength = CUT_T
|
||||
if ((CUT_T - dragLength) > (this.cropperH - this.cutB)) dragLength = CUT_T - (this.cropperH - this.cutB)
|
||||
this.setData({
|
||||
cutT: CUT_T - dragLength
|
||||
})
|
||||
break
|
||||
case 'bottom':
|
||||
var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||||
if (CUT_B + dragLength < 0) dragLength = -CUT_B
|
||||
this.setData({
|
||||
cutB: CUT_B + dragLength
|
||||
})
|
||||
break
|
||||
case 'rightBottom':
|
||||
var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||||
var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||||
|
||||
if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B
|
||||
if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R
|
||||
let cutB = CUT_B + dragLengthY
|
||||
let cutR = CUT_R + dragLengthX
|
||||
|
||||
this.setData({
|
||||
cutB: cutB,
|
||||
cutR: cutR
|
||||
})
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* pages/uni-cropper/index.wxss */
|
||||
|
||||
.uni-content-info {
|
||||
/* position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
align-items: center;
|
||||
flex-direction: column; */
|
||||
}
|
||||
|
||||
.cropper-config {
|
||||
padding: 20rpx 40rpx;
|
||||
}
|
||||
|
||||
.cropper-content {
|
||||
min-height: 750rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.uni-corpper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.uni-corpper-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.uni-corpper-content image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-width: 0 !important;
|
||||
max-width: none !important;
|
||||
height: 100%;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
image-orientation: 0deg !important;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/* 移动图片效果 */
|
||||
|
||||
.uni-cropper-drag-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: move;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
z-index: 1;
|
||||
}
|
||||
/* 内部的信息 */
|
||||
|
||||
.uni-corpper-crop-box {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.uni-corpper-crop-box .uni-cropper-view-box {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
outline: 1rpx solid #69f;
|
||||
outline-color: rgba(102, 153, 255, .75)
|
||||
}
|
||||
/* 横向虚线 */
|
||||
|
||||
.uni-cropper-dashed-h {
|
||||
position: absolute;
|
||||
top: 33.33333333%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 33.33333333%;
|
||||
border-top: 1rpx dashed rgba(255, 255, 255, 0.5);
|
||||
border-bottom: 1rpx dashed rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
/* 纵向虚线 */
|
||||
|
||||
.uni-cropper-dashed-v {
|
||||
position: absolute;
|
||||
left: 33.33333333%;
|
||||
top: 0;
|
||||
width: 33.33333333%;
|
||||
height: 100%;
|
||||
border-left: 1rpx dashed rgba(255, 255, 255, 0.5);
|
||||
border-right: 1rpx dashed rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
/* 四个方向的线 为了之后的拖动事件*/
|
||||
|
||||
.uni-cropper-line-t {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background-color: #69f;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 1rpx;
|
||||
opacity: 0.1;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.uni-cropper-line-t::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0rpx;
|
||||
width: 100%;
|
||||
-webkit-transform: translate3d(0, -50%, 0);
|
||||
transform: translate3d(0, -50%, 0);
|
||||
bottom: 0;
|
||||
height: 41rpx;
|
||||
background: transparent;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.uni-cropper-line-r {
|
||||
position: absolute;
|
||||
display: block;
|
||||
background-color: #69f;
|
||||
top: 0;
|
||||
right: 0rpx;
|
||||
width: 1rpx;
|
||||
opacity: 0.1;
|
||||
height: 100%;
|
||||
cursor: e-resize;
|
||||
}
|
||||
|
||||
.uni-cropper-line-r::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
width: 41rpx;
|
||||
-webkit-transform: translate3d(-50%, 0, 0);
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.uni-cropper-line-b {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background-color: #69f;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 1rpx;
|
||||
opacity: 0.1;
|
||||
cursor: s-resize;
|
||||
}
|
||||
|
||||
.uni-cropper-line-b::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0rpx;
|
||||
width: 100%;
|
||||
-webkit-transform: translate3d(0, -50%, 0);
|
||||
transform: translate3d(0, -50%, 0);
|
||||
bottom: 0;
|
||||
height: 41rpx;
|
||||
background: transparent;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.uni-cropper-line-l {
|
||||
position: absolute;
|
||||
display: block;
|
||||
background-color: #69f;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1rpx;
|
||||
opacity: 0.1;
|
||||
height: 100%;
|
||||
cursor: w-resize;
|
||||
}
|
||||
|
||||
.uni-cropper-line-l::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
width: 41rpx;
|
||||
-webkit-transform: translate3d(-50%, 0, 0);
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.uni-cropper-point {
|
||||
width: 5rpx;
|
||||
height: 5rpx;
|
||||
background-color: #69f;
|
||||
opacity: .75;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.point-t {
|
||||
top: -3rpx;
|
||||
left: 50%;
|
||||
margin-left: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-tr {
|
||||
top: -3rpx;
|
||||
left: 100%;
|
||||
margin-left: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-r {
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-rb {
|
||||
left: 100%;
|
||||
top: 100%;
|
||||
-webkit-transform: translate3d(-50%, -50%, 0);
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
cursor: n-resize;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
background-color: #69f;
|
||||
position: absolute;
|
||||
z-index: 1112;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.point-b {
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-bl {
|
||||
left: 0%;
|
||||
top: 100%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-l {
|
||||
left: 0%;
|
||||
top: 50%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-lt {
|
||||
left: 0%;
|
||||
top: 0%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
/* 裁剪框预览内容 */
|
||||
|
||||
.uni-cropper-viewer {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.uni-cropper-viewer image {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
105
kinit-uni/pages/mine/help/index.vue
Normal file
105
kinit-uni/pages/mine/help/index.vue
Normal file
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<view class="help-container">
|
||||
<view v-for="(item, findex) in list" :key="findex" :title="item.title" class="list-title">
|
||||
<view class="text-title">
|
||||
<view>{{ item.title }}</view>
|
||||
</view>
|
||||
<view class="childList">
|
||||
<view v-for="(child, zindex) in item.childList" :key="zindex" class="question" hover-class="hover"
|
||||
@click="handleText(child)">
|
||||
<view class="text-item">{{ child.title }}</view>
|
||||
<view class="line" v-if="zindex !== item.childList.length - 1"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
list: [{
|
||||
title: 'KINIT 问题',
|
||||
childList: [{
|
||||
title: 'KINIT-UNI 是使用若依-移动端进行的二次开发吗?',
|
||||
content: '是的,是在若依-移动端的基础上进行的二次开发,在此感谢若依团队!二次开发中我们重新将接口请求改为 luch-request 组件,项目结构也有所改动,并且加入了 uView UI 组件,uni-simple-router 路由拦截。'
|
||||
},{
|
||||
title: 'KINIT 开源吗?',
|
||||
content: '开源'
|
||||
}, {
|
||||
title: 'KINIT 可以商用吗?',
|
||||
content: '可以'
|
||||
}, {
|
||||
title: 'KINIT 源码地址多少?',
|
||||
content: 'https://gitee.com/ktianc/kinit'
|
||||
}]
|
||||
},
|
||||
{
|
||||
title: '其他问题',
|
||||
childList: [{
|
||||
title: '如何退出登录?',
|
||||
content: '请点击[我的] - [应用设置] - [退出登录]即可退出登录',
|
||||
}, {
|
||||
title: '如何修改用户头像?',
|
||||
content: '请点击[我的] - [选择头像] - [点击提交]即可更换用户头像',
|
||||
}, {
|
||||
title: '如何修改登录密码?',
|
||||
content: '请点击[我的] - [应用设置] - [修改密码]即可修改登录密码',
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleText(item) {
|
||||
this.$tab.navigateTo(`/pages/common/textview/index?title=${item.title}&content=${item.content}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.help-container {
|
||||
margin-bottom: 100rpx;
|
||||
padding: 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;
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 100%;
|
||||
height: 1rpx;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.text-title {
|
||||
color: #303133;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.text-item {
|
||||
font-size: 28rpx;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.question {
|
||||
color: #606266;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
242
kinit-uni/pages/mine/index.vue
Normal file
242
kinit-uni/pages/mine/index.vue
Normal file
@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<view class="mine-container" :style="{height: `${windowHeight}px`}">
|
||||
<!--顶部个人信息栏-->
|
||||
<view class="header-section">
|
||||
<view class="flex padding justify-between">
|
||||
<view class="flex align-center">
|
||||
<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>
|
||||
<view v-if="!name" @click="handleToLogin" class="login-tip">
|
||||
点击登录
|
||||
</view>
|
||||
<view v-if="name" @click="handleToInfo" class="user-info">
|
||||
<view class="u_title">
|
||||
用户名:{{ name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view @click="handleToInfo" class="flex align-center">
|
||||
<text style="font-size: 30rpx;">个人信息</text>
|
||||
<view class="iconfont icon-right1"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="content-section">
|
||||
<view class="mine-actions grid col-4 text-center">
|
||||
<view class="action-item" @click="handleJiaoLiuQun">
|
||||
<view class="iconfont icon-xitongjiaose text-pink icon"></view>
|
||||
<text class="text">交流群</text>
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-kefu text-blue icon"></view>
|
||||
<text class="text">在线客服</text>
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-yaoqingdaoshi text-mauve icon"></view>
|
||||
<text class="text">反馈社区</text>
|
||||
</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>
|
||||
<text class="text">点赞我们</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-list">
|
||||
<view class="list-cell list-cell-arrow" @click="handleToEditInfo">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-user menu-icon"></view>
|
||||
<view>编辑资料</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleHelp">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-wenti1-copy menu-icon"></view>
|
||||
<view>常见问题</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleAbout">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-aixin menu-icon"></view>
|
||||
<view>关于我们</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleToSetting">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-shezhi2 menu-icon"></view>
|
||||
<view>应用设置</view>
|
||||
</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
|
||||
}
|
||||
},
|
||||
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')
|
||||
},
|
||||
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/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 通过动画实例的export方法导出动画数据传递给组件的animation属性
|
||||
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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
159
kinit-uni/pages/mine/info/edit.vue
Normal file
159
kinit-uni/pages/mine/info/edit.vue
Normal file
@ -0,0 +1,159 @@
|
||||
<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>
|
||||
</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: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请填写姓名',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
telephone: [
|
||||
{
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请填写正确手机号',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// 上面有说,返回true表示校验通过,返回false表示不通过
|
||||
// uni.$u.test.mobile()就是返回true或者false的
|
||||
return uni.$u.test.mobile(value);
|
||||
},
|
||||
message: '手机号码不正确',
|
||||
// 触发器可以同时用blur和change
|
||||
trigger: ['change','blur'],
|
||||
}
|
||||
]
|
||||
},
|
||||
genderOptions: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.$store.dispatch('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() {
|
||||
getInfo().then(res => {
|
||||
this.form = res.data
|
||||
})
|
||||
},
|
||||
submit(ref) {
|
||||
this.$refs.formRef.validate().then(res => {
|
||||
this.btnLoading = true
|
||||
updateCurrentUser(this.form).then(res => {
|
||||
this.$store.dispatch('UpdateInfo', res.data)
|
||||
this.$modal.msgSuccess("更新成功");
|
||||
}).finally(() => {
|
||||
this.btnLoading = false
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
49
kinit-uni/pages/mine/info/index.vue
Normal file
49
kinit-uni/pages/mine/info/index.vue
Normal file
@ -0,0 +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>
|
||||
</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
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
79
kinit-uni/pages/mine/pwd/index.vue
Normal file
79
kinit-uni/pages/mine/pwd/index.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<view class="pwd-retrieve-container">
|
||||
<uni-forms ref="form" :value="form" labelWidth="80px">
|
||||
<uni-forms-item name="newPassword" label="新密码">
|
||||
<uni-easyinput type="password" v-model="form.password" placeholder="请输入新密码" />
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="confirmPassword" label="确认密码">
|
||||
<uni-easyinput type="password" v-model="form.password_two" placeholder="请确认新密码" />
|
||||
</uni-forms-item>
|
||||
<button :loading="btnLoading" type="primary" @click="submit">提交</button>
|
||||
</uni-forms>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { postCurrentUserResetPassword } from '@/common/request/api/vadmin/auth/user.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
btnLoading: false,
|
||||
form: {
|
||||
password: undefined,
|
||||
password_two: undefined
|
||||
},
|
||||
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: '两次输入的密码不一致'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
this.$refs.form.setRules(this.rules)
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.$refs.form.validate().then(res => {
|
||||
this.btnLoading = true
|
||||
postCurrentUserResetPassword(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
}).finally(() => {
|
||||
this.btnLoading = false
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.pwd-retrieve-container {
|
||||
padding-top: 36rpx;
|
||||
padding: 15px;
|
||||
}
|
||||
</style>
|
76
kinit-uni/pages/mine/setting/index.vue
Normal file
76
kinit-uni/pages/mine/setting/index.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<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">
|
||||
<view class="iconfont icon-password menu-icon"></view>
|
||||
<view>修改密码</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleToUpgrade">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-refresh menu-icon"></view>
|
||||
<view>检查更新</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleCleanTmp">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-clean menu-icon"></view>
|
||||
<view>清理缓存</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cu-list menu">
|
||||
<view class="cu-item item-box">
|
||||
<view class="content text-center" @click="handleLogout">
|
||||
<text class="text-black">退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
windowHeight: uni.getSystemInfoSync().windowHeight
|
||||
}
|
||||
},
|
||||
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('LogOut')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.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;
|
||||
}
|
||||
</style>
|
100
kinit-uni/pages/work/index.vue
Normal file
100
kinit-uni/pages/work/index.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<view class="work-container">
|
||||
<!-- 轮播图 -->
|
||||
<u-swiper
|
||||
:list="images"
|
||||
indicator
|
||||
indicatorMode="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>
|
||||
</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('模块建设中~')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
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;
|
||||
}
|
||||
}
|
||||
</style>
|
38
kinit-uni/permission.js
Normal file
38
kinit-uni/permission.js
Normal file
@ -0,0 +1,38 @@
|
||||
import { getToken } from '@/common/utils/auth'
|
||||
import store from '@/store'
|
||||
import {RouterMount, createRouter} from 'uni-simple-router';
|
||||
|
||||
// 登录页面
|
||||
const loginPage = "/pages/login"
|
||||
|
||||
const router = createRouter({
|
||||
platform: process.env.VUE_APP_PLATFORM,
|
||||
routes: [...ROUTES]
|
||||
});
|
||||
|
||||
//全局路由前置守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.meta.loginAuth) {
|
||||
if (getToken()) {
|
||||
if (!store.state.auth.isUser) {
|
||||
store.dispatch('GetInfo')
|
||||
}
|
||||
if (to.path === loginPage) {
|
||||
uni.reLaunch({ url: "/" })
|
||||
}
|
||||
} else {
|
||||
uni.reLaunch({ url: loginPage })
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// 全局路由后置守卫
|
||||
router.afterEach((to, from) => {
|
||||
// console.log('跳转结束')
|
||||
})
|
||||
|
||||
export {
|
||||
router,
|
||||
RouterMount
|
||||
}
|
60
kinit-uni/plugins/auth.js
Normal file
60
kinit-uni/plugins/auth.js
Normal file
@ -0,0 +1,60 @@
|
||||
import store from '@/store'
|
||||
|
||||
function authPermission(permission) {
|
||||
const all_permission = "*:*:*"
|
||||
const permissions = store.getters && store.getters.permissions
|
||||
if (permission && permission.length > 0) {
|
||||
return permissions.some(v => {
|
||||
return all_permission === v || v === permission
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function authRole(role) {
|
||||
const super_admin = "admin"
|
||||
const roles = store.getters && store.getters.roles
|
||||
if (role && role.length > 0) {
|
||||
return roles.some(v => {
|
||||
return super_admin === v || v === role
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
// 验证用户是否具备某权限
|
||||
hasPermi(permission) {
|
||||
return authPermission(permission)
|
||||
},
|
||||
// 验证用户是否含有指定权限,只需包含其中一个
|
||||
hasPermiOr(permissions) {
|
||||
return permissions.some(item => {
|
||||
return authPermission(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否含有指定权限,必须全部拥有
|
||||
hasPermiAnd(permissions) {
|
||||
return permissions.every(item => {
|
||||
return authPermission(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否具备某角色
|
||||
hasRole(role) {
|
||||
return authRole(role)
|
||||
},
|
||||
// 验证用户是否含有指定角色,只需包含其中一个
|
||||
hasRoleOr(roles) {
|
||||
return roles.some(item => {
|
||||
return authRole(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否含有指定角色,必须全部拥有
|
||||
hasRoleAnd(roles) {
|
||||
return roles.every(item => {
|
||||
return authRole(item)
|
||||
})
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user