Add support for collapse navigator and global config (#33)

This commit is contained in:
AceDataCloud
2024-01-07 19:05:16 +08:00
parent 9a18b417f6
commit 439be1797d
20 changed files with 305 additions and 50 deletions
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "add config support global",
"packageName": "@zhishuyun/hub",
"email": "cqc@cuiqingcai.com",
"dependentChangeType": "patch"
}
+20
View File
@@ -0,0 +1,20 @@
module.exports = {
site: {
logo: {
url: 'https://hub.zhishuyun.com/assets/logo.da8de841.svg',
width: 'auto',
height: 'auto'
}
},
apps: {
chat: {
enabled: true
},
midjourney: {
enabled: true
}
},
distribution: {
inviterId: '123'
}
};
+9 -1
View File
@@ -27,7 +27,15 @@ export default defineComponent({
return `${getBaseUrlAuth()}/auth/login?inviter_id=${this.inviterId}`;
},
inviterId() {
const result = this.$route.query.inviter_id?.toString() || getCookie('INVITER_ID');
// if forceInviterId is set, then use forceInviterId
if (this.$config?.distribution?.forceInviterId) {
return this.$config?.distribution?.defaultInviterId;
}
// Otherwise, use the inviter_id in the url, then use the inviter_id in the cookie, and finally use the default inviter_id
const result =
this.$route.query.inviter_id?.toString() ||
getCookie('INVITER_ID') ||
this.$config?.distribution?.defaultInviterId;
return result;
},
authenticated() {
+38
View File
@@ -0,0 +1,38 @@
<template>
<font-awesome-icon :icon="icon" class="icon" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
export default defineComponent({
name: 'Chevron',
components: {
FontAwesomeIcon
},
props: {
direction: {
type: String,
default: 'right'
}
},
computed: {
icon(): string {
return `fa-solid fa-chevron-${this.direction}`;
}
}
});
</script>
<style lang="scss" scoped>
.icon {
font-size: 12px;
border: 1px solid var(--el-border-color);
border-radius: 50%;
padding: 5px 6px;
cursor: pointer;
color: var(--el-color-black);
background: var(--el-color-white);
}
</style>
+114 -32
View File
@@ -1,13 +1,26 @@
<template>
<div class="navigator">
<chevron v-if="collapsed" class="chevron" direction="right" @click="onOpenMenu"></chevron>
<chevron v-else class="chevron" direction="left" @click="onCollapseMenu"></chevron>
<div class="top">
<div v-for="(link, linkIndex) in links" :key="linkIndex" class="link">
<el-tooltip effect="dark" :content="link.displayName" placement="right">
<div v-if="!collapsed">
<img src="@/assets/images/logo.svg" class="logo" @click="onHome" />
</div>
<el-menu v-if="!collapsed" :default-active="activeIndex">
<el-menu-item
v-for="(link, linkIndex) in links"
:key="linkIndex"
:index="link.route.name"
@click="$router.push(link.route)"
>
<font-awesome-icon :icon="link.icon" class="mr-2" />
<template #title>{{ link.displayName }}</template>
</el-menu-item>
</el-menu>
<div v-for="(link, linkIndex) in links" v-else :key="linkIndex" class="link">
<el-tooltip v-if="collapsed" effect="dark" :content="link.displayName" placement="right">
<el-button
:class="{
button: true,
active: link.routes.includes($route.name as string)
}"
:class="{button: true, active: link.routes.includes($route.name as string)}"
@click="$router.push(link.route)"
>
<font-awesome-icon :icon="link.icon" />
@@ -16,15 +29,27 @@
</div>
</div>
<div class="middle" />
<div class="bottom">
<div class="link">
<div v-if="!collapsed" class="bottom">
<el-menu :default-active="activeIndex">
<el-menu-item @click="onConsole">
<font-awesome-icon icon="fa-solid fa-compass" class="mr-2" />
<template #title>{{ $t('common.nav.console') }}</template>
</el-menu-item>
<el-menu-item @click="onLogout">
<font-awesome-icon icon="fa-solid fa-arrow-right-from-bracket" class="mr-2" />
<template #title>{{ $t('common.nav.logOut') }}</template>
</el-menu-item>
</el-menu>
</div>
<div v-else class="bottom">
<div v-if="$config.navigation?.console" class="link">
<el-tooltip effect="dark" :content="$t('common.nav.console')" placement="right">
<el-button class="button" @click="onConsole">
<font-awesome-icon icon="fa-solid fa-compass" />
</el-button>
</el-tooltip>
</div>
<div class="link">
<div v-if="$config.navigation?.help" class="link">
<help-entry />
</div>
<div v-if="authenticated" class="link">
@@ -40,16 +65,18 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { ElButton, ElTooltip } from 'element-plus';
import { ElButton, ElTooltip, ElMenu, ElMenuItem } from 'element-plus';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import {
ROUTE_CHAT_CONVERSATION,
ROUTE_CHAT_CONVERSATION_NEW,
ROUTE_CONSOLE_ROOT,
ROUTE_INDEX,
ROUTE_MIDJOURNEY_HISTORY,
ROUTE_MIDJOURNEY_INDEX
} from '@/router/constants';
import HelpEntry from './HelpEntry2.vue';
import Chevron from './Chevron.vue';
export default defineComponent({
name: 'Navigator',
@@ -57,38 +84,68 @@ export default defineComponent({
ElButton,
HelpEntry,
ElTooltip,
FontAwesomeIcon
FontAwesomeIcon,
ElMenu,
ElMenuItem,
Chevron
},
data() {
return {
links: [
{
route: {
name: ROUTE_CHAT_CONVERSATION_NEW
},
displayName: this.$t('common.nav.chat'),
icon: 'fa-regular fa-comment',
image: 'https://cdn.zhishuyun.com/9ad12c99b2.png/thumb_100x100',
routes: [ROUTE_CHAT_CONVERSATION, ROUTE_CHAT_CONVERSATION_NEW]
const links = [];
if (this.$config?.navigation?.chat) {
links.push({
route: {
name: ROUTE_CHAT_CONVERSATION_NEW
},
{
route: {
name: ROUTE_MIDJOURNEY_INDEX
},
displayName: this.$t('common.nav.midjourney'),
icon: 'fa-solid fa-palette',
image: 'https://cdn.zhishuyun.com/83ee211091.png/thumb_100x100',
routes: [ROUTE_MIDJOURNEY_INDEX, ROUTE_MIDJOURNEY_HISTORY]
}
]
displayName: this.$t('common.nav.chat'),
icon: 'fa-regular fa-comment',
image: 'https://cdn.zhishuyun.com/9ad12c99b2.png/thumb_100x100',
routes: [ROUTE_CHAT_CONVERSATION, ROUTE_CHAT_CONVERSATION_NEW]
});
}
if (this.$config.navigation?.midjourney) {
links.push({
route: {
name: ROUTE_MIDJOURNEY_INDEX
},
displayName: this.$t('common.nav.midjourney'),
icon: 'fa-solid fa-palette',
image: 'https://cdn.zhishuyun.com/83ee211091.png/thumb_100x100',
routes: [ROUTE_MIDJOURNEY_INDEX, ROUTE_MIDJOURNEY_HISTORY]
});
}
return {
links,
activeIndex: this.$route.name as string
};
},
computed: {
authenticated() {
return !!this.$store.state.token.access;
},
collapsed: {
get() {
return this.$store.state.setting?.navigationCollapsed;
},
set(val: boolean) {
this.$store.commit('setSetting', {
navigationCollapsed: val
});
}
}
},
methods: {
onHome() {
this.$router.push({
name: ROUTE_INDEX
});
},
async onOpenMenu() {
this.collapsed = false;
},
async onCollapseMenu() {
this.collapsed = true;
},
async onLogout() {
await this.$store.dispatch('resetAll');
await this.$store.dispatch('chat/resetAll');
@@ -106,6 +163,31 @@ export default defineComponent({
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.el-menu {
width: 150px;
border-right: none;
.el-menu-item {
height: 50px;
}
}
.chevron {
position: absolute;
right: -12px;
top: 50%;
transform: translateY(-50%) scale(0.8);
z-index: 10;
}
.logo {
width: 80%;
max-height: 40px;
cursor: pointer;
margin: 10px auto 20px auto;
display: block;
}
.top,
.bottom {
@@ -115,7 +197,7 @@ export default defineComponent({
.link {
width: 40px;
height: 40px;
margin-bottom: 10px;
margin: 0 10px 10px 10px;
.button {
width: 40px;
height: 40px;
+11 -5
View File
@@ -1,7 +1,7 @@
<template>
<div class="side-panel">
<div>
<img v-if="isOfficial" src="@/assets/images/logo.svg" class="logo" @click="onHome" />
<img src="@/assets/images/logo.svg" class="logo" @click="onHome" />
</div>
<div class="links">
<a
@@ -75,14 +75,20 @@ export default defineComponent({
text: this.$t('console.menu.orderList'),
name: ROUTE_CONSOLE_ORDER_LIST,
icon: 'fa-solid fa-store'
},
{
}
];
// if forcedInviterId is set, only the forced inviter can see the distribution menu
// if forcedInviterId is not set, everyone can see the distribution menu
if (!this.$config.distribution?.forceInviterId || this.user?.id === this.$config.distribution?.forceInviterId) {
links.push({
key: 'distribution-index',
text: this.$t('console.menu.distributionIndex'),
name: ROUTE_CONSOLE_DISTRIBUTION_INDEX,
icon: 'fa-solid fa-coins'
}
];
});
}
return links;
}
},
+62
View File
@@ -0,0 +1,62 @@
export default {
/**
* The global configuration.
*/
global: {},
/**
* The left navigation configuration.
*/
navigation: {
/**
* Show chat entry in left navigation.
*/
chat: true,
/**
* Show midjourney entry in left navigation.
*/
midjourney: true,
/**
* Show console entry in left navigation.
*/
console: true,
/**
* Show help entry in left navigation.
*/
help: true
},
/**
* The features configuration.
*/
features: {
/**
* The chat feature config.
*/
chat: {},
/**
* The midjourney feature config.
*/
midjourney: {}
},
/**
* The distribution configuration.
*/
distribution: {
/**
* The default inviter id to use when no inviter id (in url) is provided,
*/
defaultInviterId: undefined,
/**
* Force the inviter id to be used, even if an inviter id is provided.
* Note: if the forceInviterId is set, there will be no distribution page in console except for the forced inviter.
*/
forceInviterId: undefined
}
};
+3
View File
@@ -13,3 +13,6 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, {}>;
export default component;
}
// declare config object
declare const config: any;
+1 -1
View File
@@ -1,6 +1,6 @@
export default {
chat: 'AI 问答',
midjourney: 'AI 绘画 - Midjourney',
midjourney: 'AI 绘画',
editor: '编辑器',
setting: '设置',
home: '首页',
+1 -1
View File
@@ -53,7 +53,7 @@ export default defineComponent({
<style lang="scss" scoped>
.main {
width: calc(100% - 60px);
flex: 1;
display: flex;
flex-direction: row;
.side {
+4 -5
View File
@@ -1,8 +1,6 @@
<template>
<div class="wrapper">
<div class="left">
<navigator />
</div>
<navigator class="navigator" />
<div class="main">
<side-panel class="side" />
<router-view class="operation" />
@@ -46,11 +44,12 @@ export default defineComponent({
height: 100%;
display: flex;
flex-direction: row;
.left {
width: 60px;
.navigator {
height: 100%;
border-right: 1px solid var(--el-border-color);
}
.main {
height: 100%;
width: calc(100% - 60px);
-1
View File
@@ -25,7 +25,6 @@ export default defineComponent({
flex-direction: row;
overflow: hidden;
.navigator {
width: 60px;
height: 100%;
border-right: 1px solid var(--el-border-color);
}
+1 -1
View File
@@ -60,8 +60,8 @@ export default defineComponent({
<style lang="scss" scoped>
.main {
flex: 1;
height: 100%;
width: calc(100% - 60px);
display: flex;
flex-direction: row;
.menu {
+6
View File
@@ -13,6 +13,7 @@ import hl from 'highlight.js';
import 'highlight.js/styles/night-owl.css';
import copyToClipboard from 'copy-to-clipboard';
import { initializeCookies } from './utils/initializer';
import config from './plugins/config';
initializeCookies();
@@ -24,6 +25,7 @@ app.use(i18n);
app.use(dayjs, {
formatString: 'YYYY-MM-DD HH:mm:ss'
});
app.use(config);
app.directive('loading', vLoading);
app.mount('#app');
console.debug('app mounted');
@@ -43,3 +45,7 @@ app.directive('highlight', async (el) => {
hl.highlightBlock(block);
});
});
// make app available globally
// @ts-ignore
window.app = app;
+3
View File
@@ -260,6 +260,9 @@ export default defineComponent({
},
distributionLink() {
const origin = window.location.origin;
if (!this.$store.getters.user?.id) {
return origin;
}
return `${origin}?inviter_id=${this.$store.getters.user.id}`;
}
},
+14
View File
@@ -0,0 +1,14 @@
import config from '@/config';
import { App } from 'vue';
export default {
install: (app: App) => {
app.config.globalProperties.$config = config;
}
};
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$config: typeof config;
}
}
+3 -1
View File
@@ -8,6 +8,8 @@ import {
faClock as faRegularClock
} from '@fortawesome/free-regular-svg-icons';
import {
faChevronRight as faSolidChevronRight,
faChevronLeft as faSolidChevronLeft,
faFire as faSolidFire,
faBook as faSolidBook,
faCompass as faSolidCompass,
@@ -15,7 +17,6 @@ import {
faPaperclip as faSolidPaperclip,
faHashtag as faSolidHashtag,
faCircleInfo as faSolidCircleInfo,
faChevronLeft as faSolidChevronLeft,
faMicrophone as faSolidMicrophone,
faArrowLeft as faSolidArrowLeft,
faUpload as faSolidUpload,
@@ -55,6 +56,7 @@ import {
} from '@fortawesome/free-solid-svg-icons';
library.add(faRegularCopy);
library.add(faSolidStore);
library.add(faSolidChevronRight);
library.add(faSolidCube);
library.add(faSolidCompass);
library.add(faSolidPaperclip);
+3 -1
View File
@@ -15,7 +15,9 @@ export interface IToken {
expiration?: number;
}
export interface ISetting {}
export interface ISetting {
navigationCollapsed?: boolean;
}
export interface ICommonState {
token: IToken;
+4
View File
@@ -10,6 +10,10 @@ export default (): IRootState => {
refresh: undefined,
expiration: undefined
},
setting: {
// if PC, set default to true, else false
navigationCollapsed: window.innerWidth < 768
},
chat: chatState(),
midjourney: midjourneyState()
};
+1 -1
View File
@@ -16,7 +16,7 @@
"paths": {
"@/*": ["./src/*"]
},
"types": ["vite/client"]
"types": ["vite/client", "node"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"]
}