diff --git a/env/.env b/env/.env
index 03c7ecf799633712edb043f11adb9293f63c36c9..1cfc50527d3b9dc57717347f35ca36d98e794e69 100644
--- a/env/.env
+++ b/env/.env
@@ -1,15 +1,25 @@
-VITE_APP_TITLE = 'unibest'
+VITE_APP_TITLE = '芋道管理系统'
VITE_APP_PORT = 9000
VITE_UNI_APPID = 'H57F2ACE4'
-VITE_WX_APPID = 'wxa2abb91f64032a2b'
+VITE_WX_APPID = 'wx90bcb2127c1d8720'
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
VITE_APP_PUBLIC_BASE=/unibest/
-VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
-VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
+VITE_SERVER_BASEURL = 'http://localhost:48080/admin-api'
+VITE_UPLOAD_BASEURL = 'http://localhost:48080/upload'
# h5是否需要配置代理
VITE_APP_PROXY=false
-VITE_APP_PROXY_PREFIX = '/api'
+VITE_APP_PROXY_PREFIX = '/admin-api'
+
+
+# 租户开关
+VITE_APP_TENANT_ENABLE=true
+# 验证码的开关
+VITE_APP_CAPTCHA_ENABLE=true
+# 默认账户密码
+VITE_APP_DEFAULT_LOGIN_TENANT = 芋道源码
+VITE_APP_DEFAULT_LOGIN_USERNAME = admin
+VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123
\ No newline at end of file
diff --git a/package.json b/package.json
index 7d8b8dad87c65b83a7d45cbd5124b6354d380b8d..04106a1168837061dd18393e2a9b95cde06ee4ca 100644
--- a/package.json
+++ b/package.json
@@ -107,6 +107,7 @@
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@iconify-json/carbon": "^1.1.35",
+ "@iconify-json/ic": "^1.2.1",
"@rollup/rollup-darwin-x64": "^4.18.0",
"@types/node": "^20.14.2",
"@types/wechat-miniprogram": "^3.4.7",
diff --git a/pages.config.ts b/pages.config.ts
index c84a2719f69e9bdb19bcadca745496eb73b84385..bd75c40b1b21e06e96c62304dd8f6869608a1815 100644
--- a/pages.config.ts
+++ b/pages.config.ts
@@ -30,28 +30,34 @@ export default defineUniPages({
list: [
// 注意tabbar路由需要使用 layout:tabbar 布局
{
- pagePath: 'pages/index/index',
- text: '首页',
- icon: 'home',
- iconType: 'wot',
+ pagePath: 'pages/message/index',
+ text: '消息',
+ icon: 'i-ic-outline-message',
+ iconType: 'unocss',
+ },
+ {
+ pagePath: 'pages/colab/index',
+ text: '协作',
+ icon: 'i-ic-outline-handshake',
+ iconType: 'unocss',
},
{
- pagePath: 'pages/about/about',
- text: '关于',
- icon: 'i-carbon-code',
+ pagePath: 'pages/work/index',
+ text: '工作台',
+ icon: 'i-ic-baseline-apps',
+ iconType: 'unocss',
+ },
+ {
+ pagePath: 'pages/contacts/index',
+ text: '通讯录',
+ icon: 'i-ic-baseline-contact-page',
iconType: 'unocss',
},
- // {
- // pagePath: 'pages/my/index',
- // text: '我的',
- // icon: '/static/logo.svg',
- // iconType: 'local',
- // },
{
pagePath: 'pages/my/index',
text: '我的',
- icon: 'iconfont icon-my',
- iconType: 'iconfont',
+ icon: 'i-ic-baseline-person',
+ iconType: 'unocss',
},
],
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8b40ecdd22440de498b3365dd5022efb2355a273..576f4d6cbf6fd0eb2315e6884c37e0e7bdd1d569 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -78,6 +78,9 @@ importers:
'@iconify-json/carbon':
specifier: ^1.1.35
version: 1.1.35
+ '@iconify-json/ic':
+ specifier: ^1.2.1
+ version: 1.2.1
'@rollup/rollup-darwin-x64':
specifier: ^4.18.0
version: 4.18.0
@@ -1185,6 +1188,9 @@ packages:
'@iconify-json/carbon@1.1.35':
resolution: {integrity: sha512-zKqioWceqFRiLJvxpjcCpVP3j2YcokYshlbwSAHBhOih5XNUymUS3hm1kpV4KljMI1xWH96UcozHaaf6x4YzdA==}
+ '@iconify-json/ic@1.2.1':
+ resolution: {integrity: sha512-UjL/bjJP/T5EV881+hTzcfTKVo0KEUjhnMiJcLtPzNgPtU2KZZmRx8BSKKR61H4CN/5FTEbyawGyG0aEt3SwGQ==}
+
'@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
@@ -6887,6 +6893,10 @@ snapshots:
dependencies:
'@iconify/types': 2.0.0
+ '@iconify-json/ic@1.2.1':
+ dependencies:
+ '@iconify/types': 2.0.0
+
'@iconify/types@2.0.0': {}
'@iconify/utils@2.1.24':
diff --git a/src/App.vue b/src/App.vue
index d0dface7f82e8106b83a8516ad2307f527dc3899..21c41bbdf60122843e6e2446914adfbe535db153 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -13,6 +13,10 @@ onHide(() => {
diff --git a/src/pages/about/components/request.vue b/src/pages/about/components/request.vue
deleted file mode 100644
index 89a656d4c9d0d44e1b3596f0d3112c5a8b2c5280..0000000000000000000000000000000000000000
--- a/src/pages/about/components/request.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-
-{
- layout: 'demo',
- style: {
- navigationBarTitleText: '请求',
- },
-}
-
-
-
-
- 使用的是 laf 云后台
- 我的推荐码,可以获得佣金
-
-
-
- {{ recommendUrl }}
-
-
-
-
- {{ recommendUrl }}
-
-
-
- 发送请求
-
- loading...
-
- 请求数据如下
- {{ JSON.stringify(data) }}
-
-
- 重置数据
-
-
-
-
diff --git a/src/pages/about/components/upload.vue b/src/pages/about/components/upload.vue
deleted file mode 100644
index 07f81f5f54fc103df261c364d42e99f2954610c1..0000000000000000000000000000000000000000
--- a/src/pages/about/components/upload.vue
+++ /dev/null
@@ -1,30 +0,0 @@
-
-{
- layout: 'default',
- style: {
- navigationBarTitleText: '上传-状态一体化',
- },
-}
-
-
-
-
- 选择图片并上传
- 上传...
-
- 上传后返回的接口数据:
- {{ data }}
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/colab/index.vue b/src/pages/colab/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..5645a484f5417364a3776c6d57b798a28a542072
--- /dev/null
+++ b/src/pages/colab/index.vue
@@ -0,0 +1,23 @@
+
+
+{
+ layout: 'tabbar',
+ style: {
+ navigationBarTitleText: '协作',
+ },
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/contacts/components/ContactsItem.vue b/src/pages/contacts/components/ContactsItem.vue
new file mode 100644
index 0000000000000000000000000000000000000000..d5d539a766c41f255830c4b14fdf121098a575b9
--- /dev/null
+++ b/src/pages/contacts/components/ContactsItem.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ props.item.name }}
+
+ {{ props.item.post }}
+
+
+
+
+ {{ props.item.name }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/contacts/index.vue b/src/pages/contacts/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1cc263c8f72a1e652581c4b689fb16420a78d52f
--- /dev/null
+++ b/src/pages/contacts/index.vue
@@ -0,0 +1,114 @@
+
+
+{
+ layout: 'tabbar',
+ style: {
+ navigationStyle: 'custom',
+ navigationBarTitleText: '通讯录',
+ },
+}
+
+
+
+
+
+
+
+
+
+
+
+
+ 通讯录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/contacts/mock.js b/src/pages/contacts/mock.js
new file mode 100644
index 0000000000000000000000000000000000000000..1896f174e9b332e34ad9f70e3c7bde5adae5399f
--- /dev/null
+++ b/src/pages/contacts/mock.js
@@ -0,0 +1,70 @@
+export default [
+ {
+ id: 1,
+ name: '芋道集团',
+ isLeaf: false,
+ children: [
+ {
+ id: 11,
+ parentId: 1,
+ name: 'IT中心',
+ isLeaf: false,
+ children: [
+ {
+ id: 111,
+ parentId: 11,
+ name: '马青楷',
+ post: 'CTO',
+ avatar: '/static/images/avatar3.jpg',
+ isLeaf: true,
+ },
+ {
+ id: 112,
+ parentId: 11,
+ name: '王伟',
+ post: '前端开发',
+ avatar: '/static/images/avatar2.jpg',
+ isLeaf: true,
+ },
+ {
+ id: 113,
+ parentId: 11,
+ name: '王博',
+ post: '技术主管',
+ avatar: '/static/images/avatar1.jpg',
+ isLeaf: true,
+ },
+ ],
+ },
+ {
+ id: 12,
+ parentId: 1,
+ name: '行政部门',
+ isLeaf: true,
+ },
+ ],
+ },
+ {
+ id: 2,
+ name: '中软集团',
+ isLeaf: false,
+ children: [
+ {
+ id: 21,
+ parentId: 2,
+ name: '中软IT',
+ isLeaf: false,
+ children: [
+ {
+ id: 211,
+ parentId: 21,
+ name: '马青楷',
+ post: 'CTO',
+ isLeaf: true,
+ avatar: '/static/images/avatar3.jpg',
+ },
+ ],
+ },
+ ],
+ },
+]
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
deleted file mode 100644
index 893ed8fa7dc8d603b72f0ac79a8b15f9c6a346d0..0000000000000000000000000000000000000000
--- a/src/pages/index/index.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-{
- layout: 'tabbar',
- style: {
- navigationStyle: 'custom',
- navigationBarTitleText: '首页',
- },
-}
-
-
-
-
-
-
- unibest
- 最好用的 uniapp 开发模板
-
- {{ description }}
-
- 当前平台是:
- {{ PLATFORM.platform }}
-
-
- 模板分支是:
- tabbar
-
-
-
-
-
-
-
diff --git a/src/pages/login/components/LoginForm.vue b/src/pages/login/components/LoginForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b449cbb129f3a2575bb08ab9b24c9e7124ac14a9
--- /dev/null
+++ b/src/pages/login/components/LoginForm.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+ handleInputFocus('tenantName')"
+ />
+
+
+
+ handleInputFocus('username')"
+ />
+
+
+
+ handleInputFocus('password')"
+ />
+
+
+
+
+
+
+ 登录
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..9479b3b234f9272b67a23b3a3be2210266d57ace
--- /dev/null
+++ b/src/pages/login/index.vue
@@ -0,0 +1,74 @@
+
+{
+ style: {
+ navigationBarTitleText: '登录页面',
+ },
+}
+
+
+
+
+
+
+
+ 你好
+ 欢迎登录芋道快速开发平台
+
+
+
+
+
+
+
+
+
+
+ 手机号登录
+
+
+
+
+
+
+
+
+
+
+
+ 已阅读并同意
+ 《用户协议》
+ 和
+ 《隐私政策》
+
+
+
+
+
+
+
+
diff --git a/src/pages/message/components/MessageItem.vue b/src/pages/message/components/MessageItem.vue
new file mode 100644
index 0000000000000000000000000000000000000000..d3222f7051b6203bcb09322ad5c4ef0ce9c64913
--- /dev/null
+++ b/src/pages/message/components/MessageItem.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+ {{ props.item.title }}
+
+
+ {{ props.item.content }}
+
+
+
+
+
+ {{ props.item.time }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/message/index.vue b/src/pages/message/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..c784323b91de8b87863793ae22bd425445c2d2a1
--- /dev/null
+++ b/src/pages/message/index.vue
@@ -0,0 +1,41 @@
+
+
+{
+ layout: 'tabbar',
+ style: {
+ navigationBarTitleText: '消息',
+ },
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/message/mock.js b/src/pages/message/mock.js
new file mode 100644
index 0000000000000000000000000000000000000000..6589646a7d6ff1c1c67cb79c00075ac2db8a8ed7
--- /dev/null
+++ b/src/pages/message/mock.js
@@ -0,0 +1,92 @@
+export default [
+ {
+ title: 'IT中心工作群',
+ content: '今天下午2点到5点,开部门会议,请各位提前准备好汇报材料。',
+ time: '昨天 15:30',
+ avatar: '/static/images/avatar1.jpg',
+ },
+ {
+ title: '产品设计讨论组',
+ content: '关于新功能的设计稿,大家有什么建议可以在群里提出来。',
+ time: '昨天 18:45',
+ avatar: '/static/images/avatar2.jpg',
+ },
+ {
+ title: '前端开发小组',
+ content: '明天开始进行项目代码review,请各位准备好自己的代码。',
+ time: '今天 09:00',
+ avatar: '/static/images/avatar3.jpg',
+ },
+ {
+ title: '公司年会筹备组',
+ content: '年会节目征集开始了,欢迎大家积极报名参加!',
+ time: '今天 10:20',
+ avatar: '/static/images/avatar4.jpg',
+ },
+ {
+ title: '技术分享会',
+ content: '本周五下午的技术分享会主题是“Vue 3的新特性”,不要错过哦。',
+ time: '今天 14:30',
+ avatar: '/static/images/avatar5.jpg',
+ },
+ {
+ title: 'IT中心工作群',
+ content: '今天下午2点到5点,开部门会议,请各位提前准备好汇报材料。',
+ time: '昨天 15:30',
+ avatar: '/static/images/avatar1.jpg',
+ },
+ {
+ title: '产品设计讨论组',
+ content: '关于新功能的设计稿,大家有什么建议可以在群里提出来。',
+ time: '昨天 18:45',
+ avatar: '/static/images/avatar2.jpg',
+ },
+ {
+ title: '前端开发小组',
+ content: '明天开始进行项目代码review,请各位准备好自己的代码。',
+ time: '今天 09:00',
+ avatar: '/static/images/avatar3.jpg',
+ },
+ {
+ title: '公司年会筹备组',
+ content: '年会节目征集开始了,欢迎大家积极报名参加!',
+ time: '今天 10:20',
+ avatar: '/static/images/avatar4.jpg',
+ },
+ {
+ title: '技术分享会',
+ content: '本周五下午的技术分享会主题是“Vue 3的新特性”,不要错过哦。',
+ time: '今天 14:30',
+ avatar: '/static/images/avatar5.jpg',
+ },
+ {
+ title: 'IT中心工作群',
+ content: '今天下午2点到5点,开部门会议,请各位提前准备好汇报材料。',
+ time: '昨天 15:30',
+ avatar: '/static/images/avatar2.jpg',
+ },
+ {
+ title: '产品设计讨论组',
+ content: '关于新功能的设计稿,大家有什么建议可以在群里提出来。',
+ time: '昨天 18:45',
+ avatar: '/static/images/avatar1.jpg',
+ },
+ {
+ title: '前端开发小组',
+ content: '明天开始进行项目代码review,请各位准备好自己的代码。',
+ time: '今天 09:00',
+ avatar: '/static/images/avatar3.jpg',
+ },
+ {
+ title: '公司年会筹备组',
+ content: '年会节目征集开始了,欢迎大家积极报名参加!',
+ time: '今天 10:20',
+ avatar: '/static/images/avatar2.jpg',
+ },
+ {
+ title: '技术分享会',
+ content: '本周五下午的技术分享会主题是“Vue 3的新特性”,不要错过哦。',
+ time: '今天 14:30',
+ avatar: '/static/images/avatar4.jpg',
+ },
+]
diff --git a/src/pages/my/index.vue b/src/pages/my/index.vue
index 169627bc9e4e9a93cec0911c8210cb57289ee3dd..19cb4d121568ac5c5dc291f309c1121caef28f34 100644
--- a/src/pages/my/index.vue
+++ b/src/pages/my/index.vue
@@ -8,11 +8,121 @@
- 我的页面
+
+
+
+
+
+
+
+
+ {{ user?.nickname }}
+
+
+ 芋道集团
+
+
+
+
+
+
+
+
+
+
+ 点击登录
+
+
+
+
+
+
+ 应用设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 退出登录
+
+
+
+
+
+
diff --git a/src/pages/work/index.vue b/src/pages/work/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..9ec4f989261913e9910ea9fc2b4dbd7a0cdf0f9f
--- /dev/null
+++ b/src/pages/work/index.vue
@@ -0,0 +1,49 @@
+
+
+{
+ layout: 'tabbar',
+ style: {
+ navigationBarTitleText: '工作台',
+ },
+}
+
+
+
+
+
+
+
+
+ 系统管理
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service/login/LoginAPI.ts b/src/service/login/LoginAPI.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b844b64048d5243a5e821c39d988b92cba07ca5
--- /dev/null
+++ b/src/service/login/LoginAPI.ts
@@ -0,0 +1,21 @@
+import { http, httpGet, httpPost } from '@/utils/http'
+
+export const login = (data) => {
+ return http({
+ url: '/system/auth/login',
+ method: 'POST',
+ data,
+ })
+}
+
+export const loginOut = () => {
+ return httpPost('/system/auth/logout')
+}
+
+export const getInfo = (): Promise => {
+ return httpGet('/system/auth/get-permission-info')
+}
+
+export const getTenantIdByName = (name: string) => {
+ return httpGet('/system/tenant/get-id-by-name', { name })
+}
diff --git a/src/static/images/avatar1.jpg b/src/static/images/avatar1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9d0c8ad3e1b782f85a8d73076e7597c1caa2909b
Binary files /dev/null and b/src/static/images/avatar1.jpg differ
diff --git a/src/static/images/avatar2.jpg b/src/static/images/avatar2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..62ccbbfff52630c04264de973b8abecfc7494d64
Binary files /dev/null and b/src/static/images/avatar2.jpg differ
diff --git a/src/static/images/avatar3.jpg b/src/static/images/avatar3.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..898248de808b18bdd0b2b24038e85ae7aaf2e92c
Binary files /dev/null and b/src/static/images/avatar3.jpg differ
diff --git a/src/static/images/avatar4.jpg b/src/static/images/avatar4.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..23d5047917adfa107ca98b55bb4b50321a81e776
Binary files /dev/null and b/src/static/images/avatar4.jpg differ
diff --git a/src/static/images/avatar5.jpg b/src/static/images/avatar5.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..673ffda08c8978e79ece187771def76bbb61575d
Binary files /dev/null and b/src/static/images/avatar5.jpg differ
diff --git a/src/static/images/contacts.png b/src/static/images/contacts.png
new file mode 100644
index 0000000000000000000000000000000000000000..312a5a1fa4727154072abc2f8b0012e44083ee74
Binary files /dev/null and b/src/static/images/contacts.png differ
diff --git a/src/static/images/empty.png b/src/static/images/empty.png
new file mode 100644
index 0000000000000000000000000000000000000000..9e167021a291fe7e3fb9039d95a236e54a78b655
Binary files /dev/null and b/src/static/images/empty.png differ
diff --git a/src/static/images/login-bg.png b/src/static/images/login-bg.png
new file mode 100644
index 0000000000000000000000000000000000000000..c749afde6642b94f47e6760d83a0112f4044fdef
Binary files /dev/null and b/src/static/images/login-bg.png differ
diff --git a/src/store/user.ts b/src/store/user.ts
index 82bd873f152edc3fc384631f561253e942cf75d6..0c6e902accc490184e999dd1ff0a158c6de08d58 100644
--- a/src/store/user.ts
+++ b/src/store/user.ts
@@ -1,35 +1,74 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
+import { getAccessToken, removeToken } from '@/utils/auth'
+import { getInfo, loginOut } from '@/service/login/LoginAPI'
-const initState = { nickname: '', avatar: '' }
+const initState = {
+ permissions: [],
+ roles: [],
+ isSetUser: false,
+ user: {
+ id: 0,
+ avatar: '',
+ nickname: '',
+ deptId: 0,
+ },
+}
export const useUserStore = defineStore(
'user',
() => {
- const userInfo = ref({ ...initState })
+ // state
+
+ const userInfo = ref({ ...initState })
+
+ // actions methods
+ const setUserInfoAction = async () => {
+ if (!getAccessToken()) {
+ // 获取不到accessToken,直接返回
+ resetState()
+ return
+ }
+
+ const data = await getInfo()
+ userInfo.value = {
+ ...data,
+ isSetUser: true,
+ }
+ }
+
+ const setUserAvatarAction = async (avatar: string) => {
+ userInfo.value.user.avatar = avatar
+ }
- const setUserInfo = (val: IUserInfo) => {
- userInfo.value = val
+ const setUserNicknameAction = async (nickname: string) => {
+ userInfo.value.user.nickname = nickname
}
- const clearUserInfo = () => {
- userInfo.value = { ...initState }
+ const LogOut = async () => {
+ await loginOut()
+ removeToken()
+ resetState()
}
- // 一般没有reset需求,不需要的可以删除
- const reset = () => {
- userInfo.value = { ...initState }
+
+ const resetState = () => {
+ console.log('initState', initState)
+ userInfo.value = initState
+ console.log('重置userInfo', userInfo.value)
}
- const isLogined = computed(() => !!userInfo.value.token)
+ // 暴露到外面的方法
return {
userInfo,
- setUserInfo,
- clearUserInfo,
- isLogined,
- reset,
+ setUserInfoAction,
+ setUserAvatarAction,
+ setUserNicknameAction,
+ LogOut,
+ resetState,
}
},
{
+ // 持久化
persist: true,
},
)
diff --git a/src/style/index.scss b/src/style/index.scss
index 86184d9722516629ad4e6f595252beba829e36f4..4a9f82c86784b66614dc325661b07c36a609b2a6 100644
--- a/src/style/index.scss
+++ b/src/style/index.scss
@@ -15,4 +15,8 @@ page {
// 修改按钮背景色
// --wot-button-primary-bg-color: green;
+
+ //
+ --wot-search-light-bg: #f2f3f7;
+ --wot-search-input-radius: 5px;
}
diff --git a/src/types/system.d.ts b/src/types/system.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a18f0f4198edc93bbaff5c4d8c91569ee1104fc
--- /dev/null
+++ b/src/types/system.d.ts
@@ -0,0 +1,20 @@
+type UserVO = {
+ id: number
+ avatar: string
+ nickname: string
+ deptId: number
+}
+
+// USER 缓存
+type UserInfoVO = {
+ permissions: string[]
+ roles: string[]
+ isSetUser: boolean
+ user: UserVO
+}
+
+declare namespace JSX {
+ interface IntrinsicElements {
+ block: any // 或者更具体的类型定义
+ }
+}
diff --git a/src/types/uni-pages.d.ts b/src/types/uni-pages.d.ts
index f73e4a9a304a2bce94a1935895e3e9774921f1e3..b2589ec0f85507fa8e3f888d7f2e6e18a4da3f10 100644
--- a/src/types/uni-pages.d.ts
+++ b/src/types/uni-pages.d.ts
@@ -4,14 +4,17 @@
// Generated by vite-plugin-uni-pages
interface NavigateToOptions {
- url: "/pages/index/index" |
- "/pages/about/about" |
+ url: "/pages/work/index" |
+ "/pages/colab/index" |
+ "/pages/contacts/index" |
+ "/pages/login/index" |
+ "/pages/message/index" |
"/pages/my/index";
}
interface RedirectToOptions extends NavigateToOptions {}
interface SwitchTabOptions {
- url: "/pages/index/index" | "/pages/about/about" | "/pages/my/index"
+ url: "/pages/message/index" | "/pages/colab/index" | "/pages/work/index" | "/pages/contacts/index" | "/pages/my/index"
}
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
diff --git a/src/utils/auth.ts b/src/utils/auth.ts
new file mode 100644
index 0000000000000000000000000000000000000000..97996146d6b7184ed5f4f8f7af132a4602f297b4
--- /dev/null
+++ b/src/utils/auth.ts
@@ -0,0 +1,37 @@
+const AccessTokenKey = 'ACCESS_TOKEN'
+const RefreshTokenKey = 'REFRESH_TOKEN'
+const TenantIdKey = 'tenantId'
+
+// ========== Token 相关 ==========
+
+// 获取 Token
+export function getAccessToken() {
+ return uni.getStorageSync(AccessTokenKey)
+}
+
+// 获取 RefreshToken
+export function getRefreshToken() {
+ return uni.getStorageSync(RefreshTokenKey)
+}
+
+// 设置 Token
+export function setToken(token) {
+ uni.setStorageSync(AccessTokenKey, token.accessToken)
+ uni.setStorageSync(RefreshTokenKey, token.refreshToken)
+}
+
+// 移除 Token
+export function removeToken() {
+ uni.removeStorageSync(AccessTokenKey)
+ uni.removeStorageSync(RefreshTokenKey)
+}
+
+// ========== 租户相关 ==========
+
+export const getTenantId = () => {
+ return uni.getStorageSync(TenantIdKey)
+}
+
+export const setTenantId = (username: string) => {
+ uni.setStorageSync(TenantIdKey, username)
+}
diff --git a/src/utils/common.ts b/src/utils/common.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a122276d595975fe624508e8b155f8b1826ede0d
--- /dev/null
+++ b/src/utils/common.ts
@@ -0,0 +1,30 @@
+/**
+ * 显示模态弹窗
+ *
+ * @param content 提示的标题
+ */
+export function showConfirm(content: string) {
+ return new Promise((resolve, reject) => {
+ uni.showModal({
+ title: '提示',
+ content,
+ cancelText: '取消',
+ confirmText: '确定',
+ success: function (res) {
+ resolve(res)
+ },
+ })
+ })
+}
+
+/**
+ * 显示消息提示框
+ *
+ * @param content 提示的标题
+ */
+export function toast(content: string) {
+ uni.showToast({
+ title: content,
+ icon: 'none',
+ })
+}
diff --git a/src/utils/errorCode.ts b/src/utils/errorCode.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abb34090a07f2d26ef9b152f1ff979096018eeb9
--- /dev/null
+++ b/src/utils/errorCode.ts
@@ -0,0 +1,7 @@
+export default {
+ '401': '认证失败,无法访问系统资源',
+ '403': '当前操作没有权限',
+ '404': '访问资源不存在',
+ '500': '服务器错误',
+ default: '系统未知错误,请反馈给管理员',
+}
diff --git a/src/utils/http.ts b/src/utils/http.ts
index 4e3f38c4a6900cc0891e1f56ecc866b2d2fdf954..e2e6fe376ea16654a5432acca3c84c168035a4b9 100644
--- a/src/utils/http.ts
+++ b/src/utils/http.ts
@@ -1,8 +1,27 @@
import { CustomRequestOptions } from '@/interceptors/request'
+import { useUserStore } from '@/store'
+import { getAccessToken, getRefreshToken, setToken } from '@/utils/auth'
+import { showConfirm, toast } from '@/utils/common'
+import { getEvnBaseUrl } from '@/utils'
+import errorCode from './errorCode'
+
+/*
+ 双 token 刷新说明:
+ 1. token 和 tenantId 都是在拦截器中赋值
+ 2. 后端并不直接更改 http statusCode,而是返回一个重新包装后的对象,所以不能使用失败回调函数就行判断token是否过期需要重新获取
+ 3. 应该在 success 中,解析 res,如果 code 是 401,则说明 access token 过期,需要重新获取
+*/
+
+// 是否正在刷新中
+let isRefreshToken = false
+// 请求的方法栈
+let requestList = []
+// 请求基准地址
+const baseUrl = getEvnBaseUrl()
export const http = (options: CustomRequestOptions) => {
// 1. 返回 Promise 对象
- return new Promise>((resolve, reject) => {
+ return new Promise((resolve, reject) => {
uni.request({
...options,
dataType: 'json',
@@ -10,25 +29,97 @@ export const http = (options: CustomRequestOptions) => {
responseType: 'json',
// #endif
// 响应成功
- success(res) {
- // 状态码 2xx,参考 axios 的设计
- if (res.statusCode >= 200 && res.statusCode < 300) {
- // 2.1 提取核心数据 res.data
- resolve(res.data as IResData)
- } else if (res.statusCode === 401) {
- // 401错误 -> 清理用户信息,跳转到登录页
- // userStore.clearUserInfo()
- // uni.navigateTo({ url: '/pages/login/login' })
- reject(res)
- } else {
- // 其他错误 -> 根据后端错误信息轻提示
- !options.hideErrorToast &&
- uni.showToast({
- icon: 'none',
- title: (res.data as IResData).msg || '请求错误',
+ async success(res) {
+ const resp = res.data as IResData
+ const { data, code, msg } = resp
+
+ if (code === 401) {
+ // 未授权
+ const userStore = useUserStore()
+
+ if (!isRefreshToken) {
+ // 设置为正在刷新
+ isRefreshToken = true
+
+ // 1. 如果获取不到刷新令牌,则只能执行登出操作
+ if (!getRefreshToken()) {
+ showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(
+ (res: any) => {
+ if (res.confirm) {
+ // 清除缓存起来的用户信息
+ userStore.LogOut().then((res) => {
+ uni.reLaunch({ url: '/pages/login/index' })
+ })
+ }
+ },
+ )
+ }
+
+ // 2. 刷新accesstoken
+ try {
+ const refreshTokenRes: any = await refreshToken()
+ if (refreshTokenRes.data.code !== 0) {
+ // 如果获取不到refresh token,就直接跳转到登录页面
+ showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(
+ (res: any) => {
+ if (res.confirm) {
+ // 清除缓存起来的用户信息
+ userStore.LogOut().then((res) => {
+ uni.reLaunch({ url: '/pages/login/index' })
+ })
+ }
+ },
+ )
+ const rejMsg = '无效的会话,或者会话已过期,请重新登录。'
+ reject(rejMsg)
+ }
+
+ // 2.1 刷新成功,则回放队列的请求 + 当前请求
+ setToken(refreshTokenRes.data.data)
+ options.header.Authorization = 'Bearer ' + getAccessToken()
+ // 将所有的请求方法都进行调用
+ requestList.forEach((cb) => {
+ cb()
+ })
+ // 调用之后就清空
+ requestList = []
+
+ // 到达这里,属于是请求已经失败了,需要重新请求的
+ // 所以这里是一个递归调用
+ resolve(http(options))
+ } catch (e) {
+ // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
+ // 2.2 刷新失败,只回放队列的请求
+ requestList.forEach((cb) => {
+ cb()
+ })
+ const rejMsg = '无效的会话,或者会话已过期,请重新登录。'
+ reject(rejMsg)
+ } finally {
+ requestList = []
+ isRefreshToken = false
+ }
+ } else {
+ // 添加到队列,等待刷新获取到新的令牌
+ return new Promise((resolve) => {
+ requestList.push(() => {
+ options.header.Authorization = 'Bearer ' + getAccessToken()
+ resolve(http(options))
+ })
})
- reject(res)
+ }
+ } else if (code === 500) {
+ // 服务器错误
+ toast(msg)
+ reject(errorCode['500'])
+ } else if (code !== 0) {
+ // 其他的错误
+ // ps:yudao 的 success 的 code,默认是 0 而不是 200
+ toast(msg)
+ reject(code)
}
+
+ resolve(data)
},
// 响应失败
fail(err) {
@@ -78,3 +169,13 @@ export const httpPost = (
http.get = httpGet
http.post = httpPost
+
+const refreshToken = async () => {
+ return await uni.request({
+ method: 'POST',
+ url: baseUrl + '/system/auth/refresh-token?refreshToken=' + getRefreshToken(),
+ header: {
+ 'tenant-id': 1,
+ },
+ })
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 70131bf37a5fec4dae507f0fb0a11a08c9363025..8283d7df23995c33ea1e6b837f14312404376bec 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -131,16 +131,16 @@ export const getEvnBaseUrl = () => {
const {
miniProgram: { envVersion },
} = uni.getAccountInfoSync()
-
+ // 开发、体验、正式版 三种不同的小程序都可以配置不同的后端 url
switch (envVersion) {
case 'develop':
- baseUrl = 'https://ukw0y1.laf.run'
+ baseUrl = 'http://localhost:48080/admin-api'
break
case 'trial':
- baseUrl = 'https://ukw0y1.laf.run'
+ baseUrl = 'http://localhost:48080/admin-api'
break
case 'release':
- baseUrl = 'https://ukw0y1.laf.run'
+ baseUrl = 'http://localhost:48080/admin-api'
break
}
}