-
-
- Copyright © 2018 - {{ new Date().getFullYear() }}
- leafage.top
- All rights reserved.
+
+ © {{ new Date().getFullYear() }}
+ All Rights Reserved.
diff --git a/src/pages/system/group/IndexPage.vue b/src/pages/system/groups/IndexPage.vue
similarity index 75%
rename from src/pages/system/group/IndexPage.vue
rename to src/pages/system/groups/IndexPage.vue
index a1b73ed..7c5246d 100644
--- a/src/pages/system/group/IndexPage.vue
+++ b/src/pages/system/groups/IndexPage.vue
@@ -1,7 +1,7 @@
-
+
@@ -13,29 +13,39 @@
:rules="[val => val && val.length > 0 || 'Please type something']" />
-
-
-
+
+
+
-
-
+
-
-
+
+
+
+
+
+ {{ $t(col.label) }}
+
+
+
+
@@ -45,14 +55,14 @@
-
+
-
-
+
@@ -71,7 +81,7 @@ import type { Group } from 'src/models'
const $q = useQuasar()
-const visiable = ref(false)
+const visible = ref(false)
const tableRef = ref()
const rows = ref([])
@@ -93,10 +103,10 @@ const pagination = ref({
const selected = ref([])
const columns: QTableProps['columns'] = [
- { name: 'name', label: 'Name', align: 'left', field: 'groupName', sortable: true },
- { name: 'members', label: 'Members', align: 'center', field: 'members' },
- { name: 'enabled', label: 'Enabled', align: 'center', field: 'enabled' },
- { name: 'id', label: 'Actions', field: 'id' }
+ { name: 'name', label: 'name', align: 'left', field: 'groupName', sortable: true },
+ { name: 'members', label: 'members', align: 'center', field: 'members' },
+ { name: 'enabled', label: 'enabled', align: 'center', field: 'enabled' },
+ { name: 'id', label: 'actions', field: 'id' }
]
onMounted(() => {
@@ -133,12 +143,18 @@ async function onRequest(props: Parameters
}
function addRow() {
- visiable.value = true
+ visible.value = true
}
function editRow(id: number) {
- visiable.value = true
- console.log('id: ', id)
+ visible.value = true
+ // You can populate the form with existing user data based on the id
+ if (rows.value) {
+ const row = rows.value.find(u => u.id === id)
+ if (row) {
+ form.value = { ...row }
+ }
+ }
}
function removeRow(id: number) {
@@ -149,7 +165,10 @@ function removeRow(id: number) {
}, 500)
}
-function onSubmit() { }
+function onSubmit() {
+ // Close the dialog after submitting
+ visible.value = false
+}
function onReset() { }
diff --git a/src/pages/system/privilege/IndexPage.vue b/src/pages/system/privileges/IndexPage.vue
similarity index 58%
rename from src/pages/system/privilege/IndexPage.vue
rename to src/pages/system/privileges/IndexPage.vue
index 27d5a4b..546c04e 100644
--- a/src/pages/system/privilege/IndexPage.vue
+++ b/src/pages/system/privileges/IndexPage.vue
@@ -1,7 +1,7 @@
-
+
@@ -15,41 +15,64 @@
-
-
-
+
+
+
-
-
+
-
-
+
-
-
-
-
+
+
+
+
+
+ {{ $t(col.label) }}
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t(col.value) }}
+
+ {{ col.value }}
+
+
+
+
+
+
+
@@ -60,13 +83,14 @@ import { ref, onMounted } from 'vue'
import type { QTableProps } from 'quasar'
import { exportFile, useQuasar } from 'quasar'
import { api } from 'boot/axios'
+import SubPage from './SubPage.vue'
import { SERVER_URL } from 'src/api/paths'
import type { Privilege } from 'src/models'
const $q = useQuasar()
-const visiable = ref(false)
+const visible = ref(false)
const tableRef = ref()
const rows = ref([])
@@ -75,9 +99,9 @@ const loading = ref(false)
const form = ref({
name: '',
- meta: {
- icon: ''
- },
+ path: '',
+ icon: '',
+ order: 1,
description: ''
})
@@ -92,12 +116,11 @@ const pagination = ref({
const selected = ref([])
const columns: QTableProps['columns'] = [
- { name: 'name', label: 'Name', align: 'left', field: 'name', sortable: true },
- { name: 'postalCode', label: 'Postal Code', align: 'left', field: 'postalCode', sortable: true },
- { name: 'areaCode', label: 'Area Code', align: 'left', field: 'areaCode', sortable: true },
- { name: 'enabled', label: 'Enabled', align: 'center', field: 'enabled' },
- { name: 'description', label: 'Description', align: 'left', field: 'description' },
- { name: 'id', label: 'Actions', field: 'id' }
+ { name: 'name', label: 'name', align: 'left', field: 'name', sortable: true },
+ { name: 'path', label: 'path', align: 'left', field: 'path', sortable: true },
+ { name: 'enabled', label: 'enabled', align: 'center', field: 'enabled' },
+ { name: 'description', label: 'description', align: 'left', field: 'description' },
+ { name: 'id', label: 'actions', field: 'id' }
]
onMounted(() => {
@@ -133,25 +156,26 @@ async function onRequest(props: Parameters
})
}
-function addRow() {
- visiable.value = true
+function refresh() {
+ tableRef.value.requestServerInteraction()
}
function editRow(id: number) {
- visiable.value = true
- console.log('id: ', id)
+ visible.value = true
+ // You can populate the form with existing user data based on the id
+ if (rows.value) {
+ const row = rows.value.find(u => u.id === id)
+ if (row) {
+ form.value = { ...row }
+ }
+ }
}
-function removeRow(id: number) {
- console.log('id: ', id)
- loading.value = true
- setTimeout(() => {
- loading.value = false
- }, 500)
+function onSubmit() {
+ // Close the dialog after submitting
+ visible.value = false
}
-function onSubmit() { }
-
function onReset() { }
function wrapCsvValue(val: string, formatFn?: (val: string, row?: string) => string, row?: string) {
diff --git a/src/pages/system/privileges/SubPage.vue b/src/pages/system/privileges/SubPage.vue
new file mode 100644
index 0000000..5a63393
--- /dev/null
+++ b/src/pages/system/privileges/SubPage.vue
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+ Group
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t(col.label) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t(col.value) }}
+
+ {{ col.value }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/system/region/IndexPage.vue b/src/pages/system/regions/IndexPage.vue
similarity index 60%
rename from src/pages/system/region/IndexPage.vue
rename to src/pages/system/regions/IndexPage.vue
index d4ec371..439c801 100644
--- a/src/pages/system/region/IndexPage.vue
+++ b/src/pages/system/regions/IndexPage.vue
@@ -1,7 +1,7 @@
-
+
@@ -15,41 +15,63 @@
-
-
-
+
+
+
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+ {{ $t(col.label) }}
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ col.value }}
+
+
+
+
+
+
+
@@ -60,13 +82,14 @@ import { ref, onMounted } from 'vue'
import type { QTableProps } from 'quasar'
import { exportFile, useQuasar } from 'quasar'
import { api } from 'boot/axios'
+import SubPage from './SubPage.vue'
import { SERVER_URL } from 'src/api/paths'
import type { Region } from 'src/models'
const $q = useQuasar()
-const visiable = ref(false)
+const visible = ref(false)
const tableRef = ref()
const rows = ref([])
@@ -91,12 +114,12 @@ const pagination = ref({
const selected = ref([])
const columns: QTableProps['columns'] = [
- { name: 'name', label: 'Name', align: 'left', field: 'name', sortable: true },
- { name: 'postalCode', label: 'Postal Code', align: 'left', field: 'postalCode', sortable: true },
- { name: 'areaCode', label: 'Area Code', align: 'left', field: 'areaCode', sortable: true },
- { name: 'enabled', label: 'Enabled', align: 'center', field: 'enabled' },
- { name: 'description', label: 'Description', align: 'left', field: 'description' },
- { name: 'id', label: 'Actions', field: 'id' }
+ { name: 'name', label: 'name', align: 'left', field: 'name', sortable: true },
+ { name: 'postalCode', label: 'postalCode', align: 'left', field: 'postalCode', sortable: true },
+ { name: 'areaCode', label: 'areaCode', align: 'left', field: 'areaCode', sortable: true },
+ { name: 'enabled', label: 'enabled', align: 'center', field: 'enabled' },
+ { name: 'description', label: 'description', align: 'left', field: 'description' },
+ { name: 'id', label: 'actions', field: 'id' }
]
onMounted(() => {
@@ -133,12 +156,18 @@ async function onRequest(props: Parameters
}
function addRow() {
- visiable.value = true
+ visible.value = true
}
function editRow(id: number) {
- visiable.value = true
- console.log('id: ', id)
+ visible.value = true
+ // You can populate the form with existing user data based on the id
+ if (rows.value) {
+ const row = rows.value.find(u => u.id === id)
+ if (row) {
+ form.value = { ...row }
+ }
+ }
}
function removeRow(id: number) {
@@ -149,7 +178,10 @@ function removeRow(id: number) {
}, 500)
}
-function onSubmit() { }
+function onSubmit() {
+ // Close the dialog after submitting
+ visible.value = false
+}
function onReset() { }
diff --git a/src/pages/system/regions/SubPage.vue b/src/pages/system/regions/SubPage.vue
new file mode 100644
index 0000000..55ff61b
--- /dev/null
+++ b/src/pages/system/regions/SubPage.vue
@@ -0,0 +1,231 @@
+
+
+
+
+
+ Region
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t(col.label) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ col.value }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/system/role/IndexPage.vue b/src/pages/system/roles/IndexPage.vue
similarity index 75%
rename from src/pages/system/role/IndexPage.vue
rename to src/pages/system/roles/IndexPage.vue
index 23386cb..5cedda5 100644
--- a/src/pages/system/role/IndexPage.vue
+++ b/src/pages/system/roles/IndexPage.vue
@@ -1,7 +1,7 @@
-
+
@@ -15,29 +15,39 @@
-
-
-
+
+
+
-
-
+
-
-
+
+
+
+
+
+ {{ $t(col.label) }}
+
+
+
+
@@ -47,14 +57,14 @@
-
+
-
-
+
@@ -73,7 +83,7 @@ import type { Role } from 'src/models'
const $q = useQuasar()
-const visiable = ref(false)
+const visible = ref(false)
const tableRef = ref()
const rows = ref([])
@@ -96,11 +106,11 @@ const pagination = ref({
const selected = ref([])
const columns: QTableProps['columns'] = [
- { name: 'name', label: 'Name', align: 'left', field: 'name', sortable: true },
- { name: 'members', label: 'Members', align: 'center', field: 'members' },
- { name: 'enabled', label: 'Enabled', align: 'center', field: 'enabled' },
- { name: 'description', label: 'Description', align: 'left', field: 'description' },
- { name: 'id', label: 'Actions', field: 'id' }
+ { name: 'name', label: 'name', align: 'left', field: 'name', sortable: true },
+ { name: 'members', label: 'members', align: 'center', field: 'members' },
+ { name: 'enabled', label: 'enabled', align: 'center', field: 'enabled' },
+ { name: 'description', label: 'description', align: 'left', field: 'description' },
+ { name: 'id', label: 'actions', field: 'id' }
]
onMounted(() => {
@@ -137,12 +147,18 @@ async function onRequest(props: Parameters
}
function addRow() {
- visiable.value = true
+ visible.value = true
}
function editRow(id: number) {
- visiable.value = true
- console.log('id: ', id)
+ visible.value = true
+ // You can populate the form with existing user data based on the id
+ if (rows.value) {
+ const row = rows.value.find(u => u.id === id)
+ if (row) {
+ form.value = { ...row }
+ }
+ }
}
function removeRow(id: number) {
@@ -153,7 +169,10 @@ function removeRow(id: number) {
}, 500)
}
-function onSubmit() { }
+function onSubmit() {
+ // Close the dialog after submitting
+ visible.value = false
+}
function onReset() { }
diff --git a/src/pages/system/user/IndexPage.vue b/src/pages/system/users/IndexPage.vue
similarity index 67%
rename from src/pages/system/user/IndexPage.vue
rename to src/pages/system/users/IndexPage.vue
index 84ac409..3be78bd 100644
--- a/src/pages/system/user/IndexPage.vue
+++ b/src/pages/system/users/IndexPage.vue
@@ -4,7 +4,7 @@
- {{ editingUser ? 'Edit User' : 'Add User' }}
+ User
@@ -16,32 +16,40 @@
-
-
-
-
+
+
-
+ binary-state-sort @request="onRequest" class="full-width q-pl-md">
-
+
-
-
+
+
+
+
+
+ {{ $t(col.label) }}
+
+
+
+
@@ -50,36 +58,47 @@
{{ props.row.username }}
+
+
+ {{ props.row.firstname }}
+ {{ props.row.lastname }}
+
+
-
+
+
{{ date.formatDate(props.row.accountExpiresAt, 'YYYY/MM/DD HH:mm:ss') }}
- {{ date.formatDate(props.row.credentialsExpiresAt, 'YYYY/MM/DD HH:mm:ss') }}
+
+ {{ date.formatDate(props.row.credentialsExpiresAt, 'YYYY/MM/DD HH: mm:ss') }}
-
+
-
-
+
+
@@ -95,7 +114,6 @@ import type { User } from 'src/models'
const $q = useQuasar()
const visible = ref(false)
-const editingUser = ref(false)
const tableRef = ref()
const rows = ref([])
@@ -120,14 +138,13 @@ const pagination = ref({
const selected = ref([])
const columns: QTableProps['columns'] = [
- { name: 'username', label: 'Username', align: 'left', field: 'username', sortable: true },
- { name: 'firstname', label: 'Firstname', align: 'left', field: 'firstname', sortable: true },
- { name: 'lastname', label: 'Lastname', align: 'left', field: 'lastname', sortable: true },
- { name: 'enabled', label: 'Enabled', align: 'center', field: 'enabled' },
- { name: 'accountNonLocked', label: 'Is Locked', align: 'center', field: 'accountNonLocked' },
- { name: 'accountExpiresAt', label: 'Expires At', align: 'center', field: 'accountExpiresAt', sortable: true },
- { name: 'credentialsExpiresAt', label: 'Credentials Expires At', align: 'center', field: 'credentialsExpiresAt', sortable: true },
- { name: 'id', label: 'Actions', field: 'id' }
+ { name: 'username', label: 'username', align: 'left', field: 'username', sortable: true },
+ { name: 'fullname', label: 'fullname', align: 'center', field: 'fullname', sortable: true },
+ { name: 'enabled', label: 'enabled', align: 'center', field: 'enabled' },
+ { name: 'accountNonLocked', label: 'accountLocked', align: 'center', field: 'accountNonLocked' },
+ { name: 'accountExpiresAt', label: 'accountExpiresAt', align: 'center', field: 'accountExpiresAt', sortable: true },
+ { name: 'credentialsExpiresAt', label: 'credentialsExpiresAt', align: 'center', field: 'credentialsExpiresAt', sortable: true },
+ { name: 'id', label: 'actions', field: 'id' }
]
onMounted(() => {
@@ -164,17 +181,15 @@ async function onRequest(props: Parameters
function addRow() {
visible.value = true
- editingUser.value = false
}
function editRow(id: number) {
visible.value = true
- editingUser.value = true
// You can populate the form with existing user data based on the id
if (rows.value) {
- const user = rows.value.find(u => u.id === id)
- if (user) {
- form.value = { ...user }
+ const row = rows.value.find(u => u.id === id)
+ if (row) {
+ form.value = { ...row }
}
}
}
@@ -190,15 +205,14 @@ function removeRow(id: number) {
}, 500)
}
-async function onSubmit() {
- if (editingUser.value) {
- // Logic for editing an existing user
- // You may need to send a request to update the user with the form data
- } else {
- // Logic for adding a new user
- // You may need to send a request to add a new user with the form data
+function lockRow(row: User) {
+ // You can populate the form with existing user data based on the id
+ if (row) {
+ row.accountNonLocked = !row.accountNonLocked
}
+}
+async function onSubmit() {
// Close the dialog after submitting
visible.value = false
}
@@ -249,4 +263,22 @@ function exportTable() {
})
}
}
+
+function calculate(target: string) {
+ const now = new Date()
+ const targetDate = new Date(target)
+ // 失效时间是否小于7天
+ const diff = date.getDateDiff(targetDate, now, 'days')
+ if (diff > 7) {
+ return 'positive'
+ } else {
+ // 是否失效
+ const diffSec = date.getDateDiff(targetDate, now, 'seconds')
+ if (diffSec > 0) {
+ return 'warning'
+ } else {
+ return 'negative'
+ }
+ }
+}
diff --git a/src/router/routes.ts b/src/router/routes.ts
index 26a1bef..36e8a1f 100644
--- a/src/router/routes.ts
+++ b/src/router/routes.ts
@@ -5,71 +5,73 @@ const routes: RouteRecordRaw[] = [
{
path: '/',
component: MainLayout,
- meta: { icon: 'sym_r_grid_view' },
+ meta: { icon: 'mdi-grid_view' },
children: [
{
path: '',
name: 'home',
component: () => import('pages/IndexPage.vue'),
- meta: { icon: 'sym_r_home' }
- }
- ]
- },
- {
- path: '/system',
- name: 'system',
- component: MainLayout,
- meta: { icon: 'sym_r_settings' },
- redirect: { path: '/system/users' }, // redirect must be absolute path
- children: [
- {
- path: 'users',
- name: 'users',
- component: () => import('pages/system/user/IndexPage.vue'),
- meta: { icon: 'sym_r_manage_accounts' }
- },
- {
- path: 'groups',
- name: 'groups',
- component: () => import('pages/system/group/IndexPage.vue'),
- meta: { icon: 'sym_r_group' }
- },
- {
- path: 'roles',
- name: 'roles',
- component: () => import('pages/system/role/IndexPage.vue'),
- meta: { icon: 'sym_r_admin_panel_settings' }
- },
- {
- path: 'privileges',
- name: 'privileges',
- component: () => import('pages/system/privilege/IndexPage.vue'),
- meta: { icon: 'sym_r_admin_panel_settings' }
- },
- {
- path: 'dictionaries',
- name: 'dictionaries',
- component: () => import('pages/system/dictionary/IndexPage.vue'),
- meta: { icon: 'sym_r_menu_book' }
- },
- {
- path: 'regions',
- name: 'regions',
- component: () => import('pages/system/region/IndexPage.vue'),
- meta: { icon: 'sym_r_public' }
+ meta: { icon: 'mdi-home' }
}
]
},
+ // {
+ // path: '/system',
+ // name: 'system',
+ // component: MainLayout,
+ // meta: { icon: 'mdi-settings' },
+ // redirect: { path: '/system/users' }, // redirect must be absolute path
+ // children: [
+ // {
+ // path: 'users',
+ // name: 'users',
+ // component: () => import('pages/system/user/IndexPage.vue'),
+ // meta: { icon: 'mdi-manage_accounts' }
+ // },
+ // {
+ // path: 'groups',
+ // name: 'groups',
+ // component: () => import('pages/system/group/IndexPage.vue'),
+ // meta: { icon: 'mdi-group' }
+ // },
+ // {
+ // path: 'roles',
+ // name: 'roles',
+ // component: () => import('pages/system/role/IndexPage.vue'),
+ // meta: { icon: 'mdi-admin_panel_settings' }
+ // },
+ // {
+ // path: 'privileges',
+ // name: 'privileges',
+ // component: () => import('pages/system/privilege/IndexPage.vue'),
+ // meta: { icon: 'mdi-admin_panel_settings' }
+ // },
+ // {
+ // path: 'dictionaries',
+ // name: 'dictionaries',
+ // component: () => import('pages/system/dictionary/IndexPage.vue'),
+ // meta: { icon: 'mdi-menu_book' }
+ // },
+ // {
+ // path: 'regions',
+ // name: 'regions',
+ // component: () => import('pages/system/region/IndexPage.vue'),
+ // meta: { icon: 'mdi-public' }
+ // }
+ // ]
+ // },
{
path: '/login',
+ name: 'login',
component: () => import('pages/LoginPage.vue')
- },
+ }
// Always leave this as last one,
// but you can also remove it
- {
- path: '/:catchAll(.*)*',
- component: () => import('pages/ErrorNotFound.vue')
- }
+ // {
+ // path: '/:catchAll(.*)*',
+ // name: 'error',
+ // component: () => import('pages/ErrorNotFound.vue')
+ // }
]
export default routes
diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts
index 4e6894b..4d263aa 100644
--- a/src/shims-vue.d.ts
+++ b/src/shims-vue.d.ts
@@ -7,4 +7,4 @@ declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
-}
+}
\ No newline at end of file
diff --git a/src/stores/index.ts b/src/stores/index.ts
index ce16f63..5a85030 100644
--- a/src/stores/index.ts
+++ b/src/stores/index.ts
@@ -1,11 +1,13 @@
import { store } from 'quasar/wrappers'
import { createPinia } from 'pinia'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export default store((/* { ssrContext } */) => {
const pinia = createPinia()
// You can add Pinia plugins here
// pinia.use(SomePiniaPlugin)
+ pinia.use(piniaPluginPersistedstate)
return pinia
})
diff --git a/src/stores/locale-store.ts b/src/stores/locale-store.ts
new file mode 100644
index 0000000..f88bec0
--- /dev/null
+++ b/src/stores/locale-store.ts
@@ -0,0 +1,14 @@
+import { defineStore } from 'pinia'
+
+export const useLocaleStore = defineStore('locale', {
+ state: () => ({
+ lang: 'en-US'
+ }),
+ actions: {
+ changeLang(lang: string) {
+ // 更新用户状态
+ this.lang = lang
+ }
+ },
+ persist: true
+})
diff --git a/src/stores/user-store.ts b/src/stores/user-store.ts
index 2a67b9c..d96d927 100644
--- a/src/stores/user-store.ts
+++ b/src/stores/user-store.ts
@@ -1,31 +1,48 @@
import { defineStore } from 'pinia'
-import { SessionStorage } from 'quasar'
+import { Cookies } from 'quasar'
+import { api } from 'boot/axios'
+import { retrievePrivilegeTree } from 'src/api/privileges'
+import type { Privilege } from 'src/models'
+import type { RouteRecordRaw } from 'vue-router'
interface User {
- username: string;
+ username: string
+ avatar: string
}
export const useUserStore = defineStore('user', {
state: () => ({
- user: null as User | null
+ user: null as User | null,
+ access_token: null as string | null,
+ privileges: [] as Privilege[],
+ routes: [] as RouteRecordRaw[]
}),
- getters: {
- getUsername(): string | null {
- const user = JSON.parse(SessionStorage.getItem('user') || '{}') as User | null
- return this.user ? this.user.username : (user ? user.username : null)
- }
- },
actions: {
- updateUser(username: string) {
- // 更新用户状态
- this.user = { username }
- SessionStorage.set('user', JSON.stringify(this.user))
+ async logout() {
+ await api.post('/logout').then(() => {
+ Cookies.remove('logged_in')
+ this.$reset()
+ })
},
-
- clearUser() {
- // 清除用户状态
- this.user = null
- SessionStorage.remove('user')
+ async login(username: string, password: string) {
+ await api.post('/login', new URLSearchParams({ username, password })).then(res => {
+ this.$patch({
+ user: res.data.user,
+ access_token: res.data.access_token
+ })
+ // privileges
+ retrievePrivilegeTree(username).then(response => {
+ this.$patch({
+ privileges: response.data
+ })
+ })
+ })
+ },
+ updateRoutes(routes: RouteRecordRaw[]) {
+ this.routes = routes
}
+ },
+ persist: {
+ paths: ['user', 'access_token', 'privileges']
}
})
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 0000000..e43d6d5
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1,7 @@
+export function pathResolve(parentPath: string, path: string | undefined): string {
+ if (!path) {
+ return ''
+ }
+ const childPath = path.startsWith('/') ? path : `/${path}`
+ return `${parentPath}${childPath}`.replace(/\/\//g, '/').trim()
+}
diff --git a/yarn.lock b/yarn.lock
index 381d034..a854af6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -64,6 +64,16 @@ __metadata:
languageName: node
linkType: hard
+"@bundled-es-modules/tough-cookie@npm:^0.1.6":
+ version: 0.1.6
+ resolution: "@bundled-es-modules/tough-cookie@npm:0.1.6"
+ dependencies:
+ "@types/tough-cookie": "npm:^4.0.5"
+ tough-cookie: "npm:^4.1.4"
+ checksum: 10c0/28bcac878bff6b34719ba3aa8341e9924772ee55de5487680ebe784981ec9fccb70ed5d46f563e2404855a04de606f9e56aa4202842d4f5835bc04a4fe820571
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-loong64@npm:0.14.54":
version: 0.14.54
resolution: "@esbuild/linux-loong64@npm:0.14.54"
@@ -269,13 +279,6 @@ __metadata:
languageName: node
linkType: hard
-"@mswjs/cookies@npm:^1.1.0":
- version: 1.1.0
- resolution: "@mswjs/cookies@npm:1.1.0"
- checksum: 10c0/c8442b77f4d4f72c63a29049bbd33e7f9d85517471c09e1a1a71f424e5261feee5311b096d42d4447a51f199017b2227feb2b5dd77da83b733917560ace58940
- languageName: node
- linkType: hard
-
"@mswjs/interceptors@npm:^0.29.0":
version: 0.29.1
resolution: "@mswjs/interceptors@npm:0.29.1"
@@ -672,6 +675,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/tough-cookie@npm:^4.0.5":
+ version: 4.0.5
+ resolution: "@types/tough-cookie@npm:4.0.5"
+ checksum: 10c0/68c6921721a3dcb40451543db2174a145ef915bc8bcbe7ad4e59194a0238e776e782b896c7a59f4b93ac6acefca9161fccb31d1ce3b3445cb6faa467297fb473
+ languageName: node
+ linkType: hard
+
"@types/wrap-ansi@npm:^3.0.0":
version: 3.0.0
resolution: "@types/wrap-ansi@npm:3.0.0"
@@ -4019,8 +4029,9 @@ __metadata:
eslint-plugin-promise: "npm:^6.1.1"
eslint-plugin-vue: "npm:^9.24.0"
lottie-web: "npm:^5.12.2"
- msw: "npm:^2.3.0"
+ msw: "npm:^2.3.5"
pinia: "npm:^2.1.7"
+ pinia-plugin-persistedstate: "npm:^3.2.1"
postcss: "npm:^8.4.38"
quasar: "npm:^2.16.4"
typescript: "npm:^5.4.4"
@@ -4402,14 +4413,14 @@ __metadata:
languageName: node
linkType: hard
-"msw@npm:^2.3.0":
- version: 2.3.0
- resolution: "msw@npm:2.3.0"
+"msw@npm:^2.3.5":
+ version: 2.3.5
+ resolution: "msw@npm:2.3.5"
dependencies:
"@bundled-es-modules/cookie": "npm:^2.0.0"
"@bundled-es-modules/statuses": "npm:^1.0.1"
+ "@bundled-es-modules/tough-cookie": "npm:^0.1.6"
"@inquirer/confirm": "npm:^3.0.0"
- "@mswjs/cookies": "npm:^1.1.0"
"@mswjs/interceptors": "npm:^0.29.0"
"@open-draft/until": "npm:^2.1.0"
"@types/cookie": "npm:^0.6.0"
@@ -4430,7 +4441,7 @@ __metadata:
optional: true
bin:
msw: cli/index.js
- checksum: 10c0/704d808741c7a7abc8757406816fd8fffa5450c1cdf8669355e7d01748c372818c61b4bf6fab3ffce5c3ad32e25302737da664e079973a18becb10396989f933
+ checksum: 10c0/f944d8eb67ccdcf74aba0ce433395a9616420b8b36d43f033635817ef7535553c7bf6854392a95b093546e3481ca99e9017525b6c0bd02f51528b669e672214d
languageName: node
linkType: hard
@@ -4828,6 +4839,15 @@ __metadata:
languageName: node
linkType: hard
+"pinia-plugin-persistedstate@npm:^3.2.1":
+ version: 3.2.1
+ resolution: "pinia-plugin-persistedstate@npm:3.2.1"
+ peerDependencies:
+ pinia: ^2.0.0
+ checksum: 10c0/c01ff96176f71298a4ae42f7814f48c7b4860fe1cf90888e2500b856077e6b2f9272ecde1979324b4489ad5e4e80175d9e125425fa21439ae66871ab1837a5dd
+ languageName: node
+ linkType: hard
+
"pinia@npm:^2.1.7":
version: 2.1.7
resolution: "pinia@npm:2.1.7"
@@ -4930,7 +4950,14 @@ __metadata:
languageName: node
linkType: hard
-"punycode@npm:^2.1.0":
+"psl@npm:^1.1.33":
+ version: 1.9.0
+ resolution: "psl@npm:1.9.0"
+ checksum: 10c0/6a3f805fdab9442f44de4ba23880c4eba26b20c8e8e0830eff1cb31007f6825dace61d17203c58bfe36946842140c97a1ba7f67bc63ca2d88a7ee052b65d97ab
+ languageName: node
+ linkType: hard
+
+"punycode@npm:^2.1.0, punycode@npm:^2.1.1":
version: 2.3.1
resolution: "punycode@npm:2.3.1"
checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
@@ -4953,6 +4980,13 @@ __metadata:
languageName: node
linkType: hard
+"querystringify@npm:^2.1.1":
+ version: 2.2.0
+ resolution: "querystringify@npm:2.2.0"
+ checksum: 10c0/3258bc3dbdf322ff2663619afe5947c7926a6ef5fb78ad7d384602974c467fadfc8272af44f5eb8cddd0d011aae8fabf3a929a8eee4b86edcc0a21e6bd10f9aa
+ languageName: node
+ linkType: hard
+
"queue-microtask@npm:^1.2.2":
version: 1.2.3
resolution: "queue-microtask@npm:1.2.3"
@@ -5072,6 +5106,13 @@ __metadata:
languageName: node
linkType: hard
+"requires-port@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "requires-port@npm:1.0.0"
+ checksum: 10c0/b2bfdd09db16c082c4326e573a82c0771daaf7b53b9ce8ad60ea46aa6e30aaf475fe9b164800b89f93b748d2c234d8abff945d2551ba47bf5698e04cd7713267
+ languageName: node
+ linkType: hard
+
"resolve-from@npm:^4.0.0":
version: 4.0.0
resolution: "resolve-from@npm:4.0.0"
@@ -5751,6 +5792,18 @@ __metadata:
languageName: node
linkType: hard
+"tough-cookie@npm:^4.1.4":
+ version: 4.1.4
+ resolution: "tough-cookie@npm:4.1.4"
+ dependencies:
+ psl: "npm:^1.1.33"
+ punycode: "npm:^2.1.1"
+ universalify: "npm:^0.2.0"
+ url-parse: "npm:^1.5.3"
+ checksum: 10c0/aca7ff96054f367d53d1e813e62ceb7dd2eda25d7752058a74d64b7266fd07be75908f3753a32ccf866a2f997604b414cfb1916d6e7f69bc64d9d9939b0d6c45
+ languageName: node
+ linkType: hard
+
"ts-api-utils@npm:^1.0.1":
version: 1.3.0
resolution: "ts-api-utils@npm:1.3.0"
@@ -5928,6 +5981,13 @@ __metadata:
languageName: node
linkType: hard
+"universalify@npm:^0.2.0":
+ version: 0.2.0
+ resolution: "universalify@npm:0.2.0"
+ checksum: 10c0/cedbe4d4ca3967edf24c0800cfc161c5a15e240dac28e3ce575c689abc11f2c81ccc6532c8752af3b40f9120fb5e454abecd359e164f4f6aa44c29cd37e194fe
+ languageName: node
+ linkType: hard
+
"universalify@npm:^2.0.0":
version: 2.0.1
resolution: "universalify@npm:2.0.1"
@@ -5965,6 +6025,16 @@ __metadata:
languageName: node
linkType: hard
+"url-parse@npm:^1.5.3":
+ version: 1.5.10
+ resolution: "url-parse@npm:1.5.10"
+ dependencies:
+ querystringify: "npm:^2.1.1"
+ requires-port: "npm:^1.0.0"
+ checksum: 10c0/bd5aa9389f896974beb851c112f63b466505a04b4807cea2e5a3b7092f6fbb75316f0491ea84e44f66fed55f1b440df5195d7e3a8203f64fcefa19d182f5be87
+ languageName: node
+ linkType: hard
+
"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"