panhui před 2 roky
rodič
revize
a67849b209

+ 34 - 1
src/api/index.ts

@@ -53,6 +53,33 @@ export function fetchChatAPIProcess<T = any>(params: {
     })
 }
 
+export function fetchChatPDF<T = any>(params: {
+    prompt: string
+    options?: { conversationId?: string; parentMessageId?: string }
+    signal?: GenericAbortSignal
+    onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void
+    name?: string
+}) {
+    const settingStore = useSettingStore()
+    const authStore = useAuthStore()
+
+    let data: Record<string, any> = {
+        prompt: params.prompt,
+        options: params.options
+    }
+    data = {
+        q: params.prompt,
+        name: params.name
+    }
+
+    return post<T>({
+        url: '/chat-pdf/ask',
+        data,
+        signal: params.signal,
+        onDownloadProgress: params.onDownloadProgress
+    })
+}
+
 export function fetchSession<T>() {
     return post<T>({
         url: '/chat/session'
@@ -203,4 +230,10 @@ export function fetchGetChatRole<T>(id: any) {
     return get<T>({
         url: '/chatRole/get/' + id
     })
-}
+}
+
+export function fetchGetCompany<T>(id: any) {
+    return get<T>({
+        url: '/apiUser/get/' + id
+    })
+}

binární
src/assets/bg_desktop2.jpg


binární
src/assets/bg_mobile2.jpg


binární
src/assets/brand2.png


binární
src/assets/logo-text2.png


binární
src/assets/share2.png


+ 2 - 0
src/components/common/LoginForm.vue

@@ -106,6 +106,8 @@ import userImg from '@/assets/login_icon_shoujihao.png'
 import codeDarkImg from '@/assets/login_icon_yanzhengma_dark.png'
 import codeImg from '@/assets/login_icon_yanzhengma.png'
 
+const loginType = 'code'
+
 const props = defineProps({
     theme: {
         type: String,

+ 13 - 5
src/components/common/ShareModal.vue

@@ -2,7 +2,7 @@
     <n-modal :show="show" @update:show="newValue => updateShow(newValue)" transform-origin="center">
         <div class="share-box">
             <img :src="imgUrl" v-if="imgUrl" alt="" class="block share-canvas w-5/6 max-w-md" />
-            <div class="share-img w-5/6 relative max-w-md" :class="['share-img-' + shareType]" v-else ref="postRef">
+            <div class="share-img w-5/6 relative max-w-md" v-else ref="postRef">
                 <img :src="shareImgs[shareType]" class="block share-bg" alt="" />
                 <!-- <div class="absolute inset-x-0 top-0">
                     <user-avatar avatarType="small" onlyAvatar />
@@ -12,7 +12,7 @@
                     <!-- <img :src="shareLogo" alt="" class="share-logo" /> -->
                 </div>
 
-                <div class="user flex items-center" v-if="shareType === 1">
+                <!-- <div class="user flex items-center" v-if="shareType === 1">
                     <template v-if="isString(userInfo.avatar) && userInfo.avatar.length > 0">
                         <NAvatar :size="31" round :src="userInfo.avatar" :fallback-src="defaultAvatar" />
                     </template>
@@ -20,7 +20,7 @@
                         <NAvatar :size="31" round :src="defaultAvatar" />
                     </template>
                     <span class="text-xs ml-[7px]">{{ userInfo.name }}</span>
-                </div>
+                </div> -->
             </div>
             <!-- <div class="share-btns w-5/6 max-w-md flex">
                 <n-button text v-if="isMobile">长按保存图片</n-button>
@@ -60,6 +60,7 @@ import shareImg6 from '@/assets/share6.png'
 import shareImg7 from '@/assets/share7.png'
 import shareImg8 from '@/assets/share8.png'
 import shareLogo from '@/assets/share-logo.png'
+import { useCompanyStore } from '@/store'
 
 const shareImgs = [shareImg1, shareImg2, shareImg3, shareImg4, shareImg5, shareImg6, shareImg7, shareImg8]
 const shareType = ref(0)
@@ -81,9 +82,17 @@ const imgUrl = ref('')
 
 const userStore = useUserStore()
 const shareUrl = ref('')
-fetchRedirectUrl(location.origin + '/ui/home?invitor=' + userStore.userInfo.id).then((res: any) => {
+const company = useCompanyStore().company
+fetchRedirectUrl(
+    location.origin +
+        (company.id === 0 ? '/ui/home?invitor=' : `/ui/${company.id}/home?invitor=`) +
+        userStore.userInfo.id
+).then((res: any) => {
     shareUrl.value = res.url
 })
+if (company.id !== 0) {
+    shareType.value = 1
+}
 const userInfo = computed(() => userStore.userInfo)
 
 const postRef: Ref<HTMLElement | null> = ref(null)
@@ -102,7 +111,6 @@ watch(
 function changeType() {
     loading.value = true
     imgUrl.value = ''
-    shareType.value = 0
     setTimeout(() => {
         html2canvas(postRef.value!, {
             useCORS: true,

+ 9 - 5
src/components/common/minePannel.vue

@@ -15,7 +15,7 @@
                 </div> -->
             </div>
         </n-card>
-        <n-card :bordered="false">
+        <n-card :bordered="false" v-if="company.id === 0">
             <vip-card @goVip="goVip"></vip-card>
         </n-card>
         <n-card :bordered="false">
@@ -24,7 +24,7 @@
                     <div class="share-btn cursor-pointer" @click="shareEvent">
                         <div class="share-text">
                             <div class="text1">邀请好友</div>
-                            <div class="text2">一起赚钱</div>
+                            <div class="text2">一起加入</div>
                         </div>
                         <img src="@/assets/png--anniu.png" alt="" />
                     </div>
@@ -57,7 +57,7 @@
                 class="!rounded-xl balance"
                 content-style="padding:0;overflow:auto"
             >
-            <commission-pannel></commission-pannel>
+                <commission-pannel></commission-pannel>
             </n-card>
         </n-modal>
 
@@ -69,8 +69,8 @@
 import { UserAvatar, VipCard, Share, CommissionPannel, OfficalAccount } from '@/components/common'
 import { NCard, NRow, NCol, useMessage, NButton, useDialog, NModal } from 'naive-ui'
 import { useRouter } from 'vue-router'
-import { ref } from 'vue'
-import { useUserStore, useUserMemberStore } from '@/store'
+import { ref, computed } from 'vue'
+import { useUserStore, useUserMemberStore, useCompanyStore } from '@/store'
 import { emitter } from '@/plugins'
 import { fetchUserBalance } from '@/api'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
@@ -140,6 +140,10 @@ function shareEvent() {
 }
 
 const showOffical = ref(false)
+
+const company = computed(() => {
+    return useCompanyStore().company
+})
 </script>
 
 <style lang="less" scoped>

+ 80 - 64
src/router/index.ts

@@ -40,78 +40,93 @@ function jsapiSign() {
 const routes: RouteRecordRaw[] = [
     {
         path: '/',
-        redirect: '/home'
-    },
-    {
-        path: '/home',
-        name: 'home',
-        component: HomeView,
-        meta: {
-            public: true
+        redirect: {
+            name: 'home'
         }
     },
     {
-        path: '/',
-        name: 'Root',
-        component: ChatLayout,
+        path: '/:companyId?',
+        name: 'companyIndex',
+        component: () => import('../views/page/CompanyIndex.vue'),
         children: [
             {
-                path: '/chat/:uuid?',
-                name: 'Chat',
-                component: Chat
+                path: '',
+                redirect: {
+                    name: 'home'
+                }
+            },
+            {
+                path: 'home',
+                name: 'home',
+                component: HomeView,
+                meta: {
+                    public: true
+                }
+            },
+            {
+                path: '',
+                name: 'Root',
+                component: ChatLayout,
+                children: [
+                    {
+                        path: 'chat/:uuid?',
+                        name: 'Chat',
+                        component: Chat
+                    }
+                ]
+            },
+            {
+                path: 'login',
+                name: 'login',
+                component: () => import('@/views/page/LoginView.vue')
+            },
+            {
+                path: '/vip',
+                name: 'vip',
+                component: () => import('@/views/page/VipView.vue')
+            },
+            {
+                path: 'mine',
+                name: 'mine',
+                component: () => import('@/views/page/MineView.vue')
+            },
+            {
+                path: 'mask',
+                name: 'mask',
+                component: () => import('@/views/page/MaskView.vue')
+            },
+            {
+                path: 'rule',
+                name: 'rule',
+                component: () => import('@/views/page/RuleView.vue')
+            },
+            {
+                path: 'balance',
+                name: 'balance',
+                component: () => import('@/views/page/BalanceView.vue')
+            },
+            {
+                path: 'commission',
+                name: 'commission',
+                component: () => import('@/views/page/CommissionView.vue')
+            },
+            {
+                path: 'moments',
+                name: 'moments',
+                component: () => import('@/views/page/MomentsView.vue')
+            },
+            {
+                path: 'momentsDetail',
+                name: 'momentsDetail',
+                component: () => import('@/views/page/MomentsDetailView.vue')
+            },
+            {
+                path: 'agent',
+                name: 'agent',
+                component: () => import('@/views/page/AgentView.vue')
             }
         ]
     },
-    {
-        path: '/login',
-        name: 'login',
-        component: () => import('@/views/page/LoginView.vue')
-    },
-    {
-        path: '/vip',
-        name: 'vip',
-        component: () => import('@/views/page/VipView.vue')
-    },
-    {
-        path: '/mine',
-        name: 'mine',
-        component: () => import('@/views/page/MineView.vue')
-    },
-    {
-        path: '/mask',
-        name: 'mask',
-        component: () => import('@/views/page/MaskView.vue')
-    },
-    {
-        path: '/rule',
-        name: 'rule',
-        component: () => import('@/views/page/RuleView.vue')
-    },
-    {
-        path: '/balance',
-        name: 'balance',
-        component: () => import('@/views/page/BalanceView.vue')
-    },
-    {
-        path: '/commission',
-        name: 'commission',
-        component: () => import('@/views/page/CommissionView.vue')
-    },
-    {
-        path: '/moments',
-        name: 'moments',
-        component: () => import('@/views/page/MomentsView.vue')
-    },
-    {
-        path: '/momentsDetail',
-        name: 'momentsDetail',
-        component: () => import('@/views/page/MomentsDetailView.vue')
-    },
-    {
-        path: '/agent',
-        name: 'agent',
-        component: () => import('@/views/page/AgentView.vue')
-    },
     {
         path: '/404',
         name: '404',
@@ -132,6 +147,7 @@ const routes: RouteRecordRaw[] = [
 export const router = createRouter({
     history: createWebHistory(import.meta.env.VITE_GLOB_APP_PUBLIC_PATH),
     routes,
+    sensitive: true,
     scrollBehavior: () => ({ left: 0, top: 0 })
 })
 

+ 6 - 2
src/router/permission.ts

@@ -1,10 +1,14 @@
 import { fetchMy } from '@/api'
 import type { Router } from 'vue-router'
-import { useUserStore, useUserMemberStore } from '@/store'
+import { useUserStore, useUserMemberStore, useCompanyStore } from '@/store'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
 
 export function setupPageGuard(router: Router) {
     router.beforeEach(async (to, from, next) => {
+        if (to.params.companyId) {
+            const companyStore = useCompanyStore()
+            companyStore.getCompanyInfo(Number(to.params.companyId))
+        }
         const { isMobile } = useBasicLayout()
         const userStore = useUserStore()
         const userMemberStore = useUserMemberStore()
@@ -31,4 +35,4 @@ export function setupPageGuard(router: Router) {
             next()
         }
     })
-}
+}

+ 6 - 2
src/store/modules/auth/helper.ts

@@ -1,15 +1,19 @@
 import { ss } from '@/utils/storage'
 
-const LOCAL_NAME = 'SECRET_TOKEN'
+let LOCAL_NAME = 'SECRET_TOKEN'
 
 export function getToken() {
     return ss.get(LOCAL_NAME)
 }
 
-export function setToken(token: string) {
+export function setToken(token: string | undefined) {
     return ss.set(LOCAL_NAME, token)
 }
 
 export function removeToken() {
     return ss.remove(LOCAL_NAME)
 }
+
+export function changeLoalName(name: string) {
+    LOCAL_NAME = name
+}

+ 20 - 2
src/store/modules/auth/index.ts

@@ -1,8 +1,9 @@
 import { defineStore } from 'pinia'
-import { getToken, removeToken, setToken } from './helper'
-import { store } from '@/store'
+import { getToken, removeToken, setToken, changeLoalName } from './helper'
 import { fetchSession, fetchPhoneLogin } from '@/api'
 import { Response } from '@/utils/request'
+import { useUserStore } from '../user'
+import { useCompanyStore } from '../company'
 
 interface SessionResponse {
     auth: boolean
@@ -52,6 +53,23 @@ export const useAuthStore = defineStore('auth-store', {
         async phoneLogin(phone: string | number, code: string | number, invitor?: string | number) {
             const data = await fetchPhoneLogin<any>(phone, code, invitor)
             this.setToken(data.access_token)
+            const userStore = useUserStore()
+            await userStore.fetch()
+            const companyStore = useCompanyStore()
+            if (userStore.userInfo.roles?.includes('api')) {
+                this.removeToken()
+                companyStore.getCompanyInfo(userStore.userInfo.apiUserId)
+                this.setToken(data.access_token)
+            } else {
+                this.removeToken()
+                companyStore.getCompanyInfo(0)
+                this.setToken(data.access_token)
+            }
+        },
+
+        changeToken(name: string) {
+            changeLoalName(name)
+            this.token = getToken()
         }
     }
 })

+ 31 - 0
src/store/modules/company/helper.ts

@@ -0,0 +1,31 @@
+import { ss } from '@/utils/storage/session'
+
+const LOCAL_NAME = 'companyStorage'
+
+export interface CompanyInfo {
+    id?: string | number | undefined
+    name?: string
+    code?: string
+}
+
+export interface CompanyState {
+    company: CompanyInfo
+}
+
+export function defaultSetting(): CompanyState {
+    return {
+        company: {
+            id: 0,
+            name: '走马AI'
+        }
+    }
+}
+
+export function getLocalState(): CompanyState {
+    const localSetting: CompanyInfo | undefined = ss.get(LOCAL_NAME)
+    return { ...defaultSetting(), ...localSetting }
+}
+
+export function setLocalState(setting: CompanyState): void {
+    ss.set(LOCAL_NAME, setting)
+}

+ 37 - 0
src/store/modules/company/index.ts

@@ -0,0 +1,37 @@
+import { defineStore } from 'pinia'
+import { store } from '@/store'
+import type { CompanyInfo, CompanyState } from './helper'
+import { defaultSetting, getLocalState, setLocalState } from './helper'
+import { fetchGetCompany } from '@/api'
+import { Response } from '@/utils/request'
+import { useAuthStore } from '../auth'
+
+export const useCompanyStore = defineStore('company-store', {
+    state: (): CompanyState => getLocalState(),
+
+    getters: {},
+
+    actions: {
+        setCompanyInfo(company: Partial<CompanyInfo>) {
+            this.company = { ...this.company, ...company }
+            this.recordState()
+        },
+        recordState() {
+            setLocalState(this.$state)
+        },
+        async getCompanyInfo(companyId: number | undefined) {
+            if (companyId === 0) {
+                this.setCompanyInfo({
+                    id: 0,
+                    name: '走马AI'
+                })
+                useAuthStore().changeToken(`SECRET_TOKEN`)
+            } else {
+                await fetchGetCompany(companyId).then((res: any) => {
+                    this.setCompanyInfo(res)
+                    useAuthStore().changeToken(`SECRET_TOKEN${res.id === 0 ? '' : res.id}`)
+                })
+            }
+        }
+    }
+})

+ 1 - 0
src/store/modules/index.ts

@@ -5,3 +5,4 @@ export * from './prompt'
 export * from './settings'
 export * from './auth'
 export * from './memberShip'
+export * from './company'

+ 2 - 0
src/store/modules/user/helper.ts

@@ -7,6 +7,8 @@ export interface UserInfo {
     avatar: string
     name: string
     description: string
+    roles?: Array<string>
+    apiUserId?: number
 }
 
 export interface UserState {

+ 5 - 0
src/store/modules/user/index.ts

@@ -4,6 +4,7 @@ import { defaultSetting, getLocalState, setLocalState } from './helper'
 import { fetchMy } from '@/api'
 import { useAuthStore } from '../auth'
 import { useUserMemberStore } from '../memberShip'
+import { useCompanyStore } from '../company'
 
 export const useUserStore = defineStore('user-store', {
     state: (): UserState => defaultSetting(),
@@ -23,6 +24,10 @@ export const useUserStore = defineStore('user-store', {
         },
 
         async fetch() {
+            const companyStore = useCompanyStore()
+            if (companyStore.company.id !== 0) {
+                useAuthStore().changeToken(`SECRET_TOKEN${companyStore.company.id}`)
+            }
             const data = await fetchMy<UserInfo>()
             this.setUserInfo(data)
             await useUserMemberStore().fetchMember()

+ 68 - 0
src/utils/storage/session.ts

@@ -0,0 +1,68 @@
+import { deCrypto, enCrypto } from '../crypto'
+
+interface StorageData<T = any> {
+    data: T
+    expire: number | null
+}
+
+export function createsessionStorage(options?: { expire?: number | null; crypto?: boolean }) {
+    const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
+
+    const { expire, crypto } = Object.assign(
+        {
+            expire: DEFAULT_CACHE_TIME,
+            crypto: true
+        },
+        options
+    )
+
+    function set<T = any>(key: string, data: T) {
+        const storageData: StorageData<T> = {
+            data,
+            expire: expire !== null ? new Date().getTime() + expire * 1000 : null
+        }
+
+        const json = crypto ? enCrypto(storageData) : JSON.stringify(storageData)
+        window.sessionStorage.setItem(key, json)
+    }
+
+    function get(key: string) {
+        const json = window.sessionStorage.getItem(key)
+        if (json) {
+            let storageData: StorageData | null = null
+
+            try {
+                storageData = crypto ? deCrypto(json) : JSON.parse(json)
+            } catch {
+                // Prevent failure
+            }
+
+            if (storageData) {
+                const { data, expire } = storageData
+                if (expire === null || expire >= Date.now()) return data
+            }
+
+            remove(key)
+            return null
+        }
+    }
+
+    function remove(key: string) {
+        window.sessionStorage.removeItem(key)
+    }
+
+    function clear() {
+        window.sessionStorage.clear()
+    }
+
+    return {
+        set,
+        get,
+        remove,
+        clear
+    }
+}
+
+export const ls = createsessionStorage()
+
+export const ss = createsessionStorage({ expire: null, crypto: false })

+ 167 - 78
src/views/chat/Chat.vue

@@ -13,8 +13,8 @@ import { useUsingContext } from './hooks/useUsingContext'
 import HeaderComponent from './components/Header/index.vue'
 import { HoverButton, SvgIcon, MinePannel, VipPannel, MaskPannel, Share } from '@/components/common'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
-import { useChatStore, useAppStore, usePromptStore, useAuthStore, useUserMemberStore } from '@/store'
-import { fetchChatAPIProcess } from '@/api'
+import { useChatStore, useAppStore, usePromptStore, useAuthStore, useUserMemberStore, useCompanyStore } from '@/store'
+import { fetchChatAPIProcess, fetchChatPDF } from '@/api'
 import { t } from '@/locales'
 import { useTheme } from '@/hooks/useTheme'
 import { emitter } from '@/plugins'
@@ -75,12 +75,14 @@ if (histroyData.value?.maskId) {
 
 const userMemberStore = useUserMemberStore()
 function handleSubmit() {
-    if (!userMemberStore.isVip()) {
-        const inversions = dataSources.value.filter(item => {
-            return item.inversion
-        })
-        if (inversions.length > 0 && inversions.length % 3 === 0) {
-            showVipTips.value = true
+    if (company.value.id === 1) {
+        if (!userMemberStore.isVip()) {
+            const inversions = dataSources.value.filter(item => {
+                return item.inversion
+            })
+            if (inversions.length > 0 && inversions.length % 3 === 0) {
+                showVipTips.value = true
+            }
         }
     }
 
@@ -128,42 +130,82 @@ async function onConversation() {
     try {
         let lastText = ''
         const fetchChatAPIOnce = async () => {
-            await fetchChatAPIProcess<Chat.ConversationResponse>({
-                prompt: message,
-                options,
-                signal: controller.signal,
-                onDownloadProgress: ({ event }) => {
-                    const xhr = event.target
-                    const { responseText } = xhr
-                    // Always process the final line
-                    const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
-                    let chunk = responseText
-                    if (lastIndex !== -1) chunk = responseText.substring(lastIndex)
-                    try {
-                        const data = JSON.parse(chunk)
-                        updateChat(+uuid, dataSources.value.length - 1, {
-                            dateTime: new Date().toLocaleString(),
-                            text: lastText + (data.text ?? ''),
-                            inversion: false,
-                            error: false,
-                            loading: true,
-                            conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
-                            requestOptions: { prompt: message, options: { ...options } }
-                        })
-
-                        if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
-                            options.parentMessageId = data.id
-                            lastText = data.text
-                            message = ''
-                            return fetchChatAPIOnce()
+            if (company.value.id === 0) {
+                await fetchChatAPIProcess<Chat.ConversationResponse>({
+                    prompt: message,
+                    options,
+                    signal: controller.signal,
+                    onDownloadProgress: ({ event }) => {
+                        const xhr = event.target
+                        const { responseText } = xhr
+                        // Always process the final line
+                        const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
+                        let chunk = responseText
+                        if (lastIndex !== -1) chunk = responseText.substring(lastIndex)
+                        try {
+                            const data = JSON.parse(chunk)
+                            updateChat(+uuid, dataSources.value.length - 1, {
+                                dateTime: new Date().toLocaleString(),
+                                text: lastText + (data.text ?? ''),
+                                inversion: false,
+                                error: false,
+                                loading: true,
+                                conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
+                                requestOptions: { prompt: message, options: { ...options } }
+                            })
+
+                            if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
+                                options.parentMessageId = data.id
+                                lastText = data.text
+                                message = ''
+                                return fetchChatAPIOnce()
+                            }
+
+                            scrollToBottomIfAtBottom()
+                        } catch (error) {
+                            //
                         }
-
-                        scrollToBottomIfAtBottom()
-                    } catch (error) {
-                        //
                     }
-                }
-            })
+                })
+            } else {
+                await fetchChatPDF<Chat.ConversationResponse>({
+                    prompt: message,
+                    options,
+                    signal: controller.signal,
+                    name: company.value.code,
+                    onDownloadProgress: ({ event }) => {
+                        const xhr = event.target
+                        const { responseText } = xhr
+                        // Always process the final line
+                        const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
+                        let chunk = responseText
+                        if (lastIndex !== -1) chunk = responseText.substring(lastIndex)
+                        try {
+                            const data = JSON.parse(chunk)
+                            updateChat(+uuid, dataSources.value.length - 1, {
+                                dateTime: new Date().toLocaleString(),
+                                text: lastText + (data.answer ?? ''),
+                                inversion: false,
+                                error: false,
+                                loading: true,
+                                conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
+                                requestOptions: { prompt: message, options: { ...options } }
+                            })
+
+                            if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
+                                options.parentMessageId = data.id
+                                lastText = data.answer
+                                message = ''
+                                return fetchChatAPIOnce()
+                            }
+
+                            scrollToBottomIfAtBottom()
+                        } catch (error) {
+                            //
+                        }
+                    }
+                })
+            }
             updateChatSome(+uuid, dataSources.value.length - 1, { loading: false })
         }
 
@@ -233,40 +275,78 @@ async function onRegenerate(index: number) {
     try {
         let lastText = ''
         const fetchChatAPIOnce = async () => {
-            await fetchChatAPIProcess<Chat.ConversationResponse>({
-                prompt: message,
-                options,
-                signal: controller.signal,
-                onDownloadProgress: ({ event }) => {
-                    const xhr = event.target
-                    const { responseText } = xhr
-                    // Always process the final line
-                    const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
-                    let chunk = responseText
-                    if (lastIndex !== -1) chunk = responseText.substring(lastIndex)
-                    try {
-                        const data = JSON.parse(chunk)
-                        updateChat(+uuid, index, {
-                            dateTime: new Date().toLocaleString(),
-                            text: lastText + (data.text ?? ''),
-                            inversion: false,
-                            error: false,
-                            loading: true,
-                            conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
-                            requestOptions: { prompt: message, options: { ...options } }
-                        })
-
-                        if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
-                            options.parentMessageId = data.id
-                            lastText = data.text
-                            message = ''
-                            return fetchChatAPIOnce()
+            if (company.value.id === 0) {
+                await fetchChatAPIProcess<Chat.ConversationResponse>({
+                    prompt: message,
+                    options,
+                    signal: controller.signal,
+                    onDownloadProgress: ({ event }) => {
+                        const xhr = event.target
+                        const { responseText } = xhr
+                        // Always process the final line
+                        const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
+                        let chunk = responseText
+                        if (lastIndex !== -1) chunk = responseText.substring(lastIndex)
+                        try {
+                            const data = JSON.parse(chunk)
+                            updateChat(+uuid, index, {
+                                dateTime: new Date().toLocaleString(),
+                                text: lastText + (data.text ?? ''),
+                                inversion: false,
+                                error: false,
+                                loading: true,
+                                conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
+                                requestOptions: { prompt: message, options: { ...options } }
+                            })
+
+                            if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
+                                options.parentMessageId = data.id
+                                lastText = data.text
+                                message = ''
+                                return fetchChatAPIOnce()
+                            }
+                        } catch (error) {
+                            //
                         }
-                    } catch (error) {
-                        //
                     }
-                }
-            })
+                })
+            } else {
+                await fetchChatPDF<Chat.ConversationResponse>({
+                    prompt: message,
+                    options,
+                    signal: controller.signal,
+                    name: company.value.code,
+                    onDownloadProgress: ({ event }) => {
+                        const xhr = event.target
+                        const { responseText } = xhr
+                        // Always process the final line
+                        const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
+                        let chunk = responseText
+                        if (lastIndex !== -1) chunk = responseText.substring(lastIndex)
+                        try {
+                            const data = JSON.parse(chunk)
+                            updateChat(+uuid, index, {
+                                dateTime: new Date().toLocaleString(),
+                                text: lastText + (data.answer ?? ''),
+                                inversion: false,
+                                error: false,
+                                loading: true,
+                                conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
+                                requestOptions: { prompt: message, options: { ...options } }
+                            })
+
+                            if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
+                                options.parentMessageId = data.id
+                                lastText = data.answer
+                                message = ''
+                                return fetchChatAPIOnce()
+                            }
+                        } catch (error) {
+                            //
+                        }
+                    }
+                })
+            }
             updateChatSome(+uuid, index, { loading: false })
         }
         await fetchChatAPIOnce()
@@ -384,7 +464,7 @@ function handleFocus() {
             positiveText: t('common.yes'),
             negativeText: t('common.no'),
             onPositiveClick: () => {
-                router.push('/login')
+                router.push({ name: 'login' })
             }
         })
     }
@@ -479,7 +559,9 @@ onMounted(() => {
     })
     emitter.on('changeMask', res => {
         if (isMobile.value) {
-            router.push('/mask')
+            router.push({
+                name: 'mask'
+            })
         } else {
             showMask.value = true
         }
@@ -498,13 +580,20 @@ function goVip() {
 
 function handleAdd() {
     if (isMobile.value) {
-        router.push('/mask')
+        router.push({
+            name: 'mask'
+        })
     } else {
         showMask.value = true
     }
 }
 
 const showShareModal = ref(false)
+
+const companyStore = useCompanyStore()
+const company = computed(() => {
+    return companyStore.company
+})
 </script>
 
 <template>
@@ -560,7 +649,7 @@ const showShareModal = ref(false)
         <footer :class="footerClass">
             <div class="w-full max-w-screen-xl m-auto">
                 <div class="flex items-center justify-between space-x-2">
-                    <HoverButton v-if="!isMobile" @click="handleAdd">
+                    <HoverButton v-if="!isMobile && company.id === 0" @click="handleAdd">
                         <img class="w-[24px] h-[24px]" :src="isDark ? maskDarkIcon : maskIcon" alt="" />
                     </HoverButton>
                     <HoverButton @click="handleClear">

+ 11 - 3
src/views/chat/components/Header/index.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 import { computed, nextTick } from 'vue'
 import { HoverButton, SvgIcon, UserAvatar } from '@/components/common'
-import { useAppStore, useChatStore } from '@/store'
+import { useAppStore, useChatStore, useCompanyStore } from '@/store'
 import maskIcon from '@/assets/png-mianju.png'
 import maskDarkIcon from '@/assets/png-mianju-dark.png'
 import { useTheme } from '@/hooks/useTheme'
@@ -53,6 +53,11 @@ function goMine() {
 function handleAdd() {
     emitter.emit('changeMask', true)
 }
+
+const companyStore = useCompanyStore()
+const company = computed(() => {
+    return companyStore.company
+})
 </script>
 
 <template>
@@ -73,11 +78,14 @@ function handleAdd() {
                 {{ currentChatHistory?.title ?? '' }}
             </h1>
             <div class="flex items-center space-x-2">
-                <HoverButton @click="handleAdd">
+                <HoverButton @click="handleAdd" v-if="company.id === 0">
                     <img class="w-[24px] h-[24px]" :src="isDark ? maskDarkIcon : maskIcon" alt="" />
                 </HoverButton>
                 <HoverButton @click="toggleUsingContext">
-                    <span class="text-[24px]" :class="{ 'text-[#4b9e5f]': usingContext, 'text-[#a8071a]': !usingContext }">
+                    <span
+                        class="text-[24px]"
+                        :class="{ 'text-[#4b9e5f]': usingContext, 'text-[#a8071a]': !usingContext }"
+                    >
                         <SvgIcon icon="ri:chat-history-line" />
                     </span>
                 </HoverButton>

+ 14 - 3
src/views/chat/layout/sider/Footer.vue

@@ -4,7 +4,7 @@ import { HoverButton, SvgIcon, UserAvatar } from '@/components/common'
 import { NButton, NIcon } from 'naive-ui'
 import { useRouter } from 'vue-router'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
-import { useAppStore, useUserStore, useUserMemberStore } from '@/store'
+import { useAppStore, useUserStore, useUserMemberStore, useCompanyStore } from '@/store'
 import { emitter } from '@/plugins'
 import { storeToRefs } from 'pinia'
 import Setting from '@/components/common/Setting/SettingIndex.vue'
@@ -50,6 +50,10 @@ function goVip() {
     emitter.emit('changeVipShow', true)
 }
 
+const company = computed(() => {
+    return useCompanyStore().company
+})
+
 const { isDark } = useTheme()
 </script>
 
@@ -80,7 +84,11 @@ const { isDark } = useTheme()
             <Setting v-model:visible="show" />
         </div>
 
-        <div @click="goVip" class="bg-[#F5F7FA] dark:bg-[#18191E] px-[10px] mt-3 h-[36px] flex cursor-pointer items-center rounded-lg">
+        <div
+            v-if="company.id == 0"
+            @click="goVip"
+            class="bg-[#F5F7FA] dark:bg-[#18191E] px-[10px] mt-3 h-[36px] flex cursor-pointer items-center rounded-lg"
+        >
             <img :src="isDark ? vipDarkImg : vipImg" class="w-[18px] h-[18px] block" alt="" />
             <span class="text-xs ml-[8px] dark:#fff">{{ isVip ? '会员续费' : '开通会员' }}</span>
             <div class="flex-1"></div>
@@ -88,7 +96,10 @@ const { isDark } = useTheme()
                 <ChevronRight />
             </n-icon>
         </div>
-        <div @click="share" class="bg-[#F5F7FA] dark:bg-[#18191E] px-[10px] mt-3 h-[36px] cursor-pointer flex items-center rounded-lg">
+        <div
+            @click="share"
+            class="bg-[#F5F7FA] dark:bg-[#18191E] px-[10px] mt-3 h-[36px] cursor-pointer flex items-center rounded-lg"
+        >
             <img :src="isDark ? shareDarkImg : shareImg" class="w-[18px] h-[18px] block" alt="" />
             <span class="text-xs ml-[8px] dark:#fff">分享好友</span>
             <div class="flex-1"></div>

+ 5 - 0
src/views/page/CompanyIndex.vue

@@ -0,0 +1,5 @@
+<template>
+    <router-view v-slot="{ Component }">
+        <component :is="Component" />
+    </router-view>
+</template>

+ 67 - 17
src/views/page/HomeView.vue

@@ -18,7 +18,7 @@
                     >
                         <!-- <n-avatar :size="isMobile ? 'small' : 'medium'" :src="logo"></n-avatar> -->
                         <!-- <span class="ml-[6px] lg:ml-3 text-white text-base lg:text-lg alimamaShuHeiTi">走马AI助手 </span> -->
-                        <img src="@/assets/logo-text.png" class="h-[28px] md:h-[40px]" alt="" />
+                        <img :src="logo" class="h-[28px] md:h-[40px]" alt="" />
                     </div>
                 </template>
                 <template #extra>
@@ -47,7 +47,7 @@
 
                         <template v-else>
                             <n-button v-if="!isMobile" type="primary" round @click="showLogin = true"> 登录 </n-button>
-                            <n-button v-else round @click="$router.push('/login')" type="primary" size="small"
+                            <n-button v-else round @click="$router.push({ name: 'login' })" type="primary" size="small"
                                 >登录</n-button
                             >
                         </template>
@@ -57,12 +57,16 @@
         </NConfigProvider>
 
         <div
-            class="flex flex-col items-center content-center pl-4 md:pl-10 lg:pl-15 2xl:pl-52 md:items-start flex-1 pt-4 pb-24 page-content md:pb-4 md:justify-center"
+            class="flex flex-col items-center content-center px-4 md:pl-10 lg:pl-15 2xl:pl-52 md:items-start flex-1 pt-4 pb-24 page-content md:pb-4 md:justify-center"
         >
-            <img src="@/assets/brand.png" class="w-3/4 max-w-[510px] md:translate-x-[-10px]" alt="" />
+            <img :src="brand" class="w-3/4 max-w-[510px] md:translate-x-[-10px]" alt="" />
 
-            <div class="text-xs leading-6 md:text-base text-white opacity-80 text-center md:text-left mt-4 w-[90%] max-w-[705px]">
-                走马AI助手是人工智能技术驱动的自然语言处理工具,它能够通过理解和学习人类的语言来进行对话,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流,并能完成撰写邮件、视频脚本、文案、翻译、代码,写论文等任务。后续更新:向量数据库应用,AI助手角色赋予,AI助手私有化,FileChat等。
+            <div
+                class="text-xs leading-6 md:text-base text-white opacity-80 text-center md:text-left mt-4 w-[90%] max-w-[705px]"
+            >
+                {{
+                    company.name
+                }}助手是人工智能技术驱动的自然语言处理工具,它能够通过理解和学习人类的语言来进行对话,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流,并能完成撰写邮件、视频脚本、文案、翻译、代码,写论文等任务。后续更新:向量数据库应用,AI助手角色赋予,AI助手私有化,FileChat等。
             </div>
 
             <NConfigProvider
@@ -85,15 +89,20 @@
                 >
                     <n-grid cols="4" responsive="screen">
                         <n-grid-item v-for="(item, index) in items" :key="item.name">
-                            <div class="flex flex-col items-center h-[120px] md:h-[108px] justify-center md:items-start">
+                            <div
+                                class="flex flex-col items-center h-[120px] md:h-[108px] justify-center md:items-start"
+                            >
                                 <div class="flex flex-col items-center relative" :class="{ 'pb-[44px]': index === 1 }">
-                                    <img class="w-[60px] h-[60px]" :src="item.img" alt="" />
+                                    <img class="w-[40px] h-[40px] md:w-[60px] md:h-[60px]" :src="item.img" alt="" />
                                     <div
                                         class="text text-xs text-gray-50 mt-2 text-center"
                                         :class="{ 'absolute bottom-0': index === 1 }"
                                     >
                                         <div class="whitespace-nowrap">{{ item.name }}</div>
-                                        <div class="mt-1 text-[10px] text-neutral-400 whitespace-nowrap">
+                                        <div
+                                            class="mt-1 text-[10px] text-neutral-400 whitespace-nowrap"
+                                            :class="{ 'text-[#FCF7FE]': pageType === 'Legal' }"
+                                        >
                                             {{ item.desc }}
                                         </div>
                                     </div>
@@ -203,10 +212,15 @@ import {
     useMessage,
     useDialog
 } from 'naive-ui'
-import h5Bg from '@/assets/bg_mobile.jpg'
-import pcBg from '@/assets/bg_desktop1.jpg'
-import logo from '@/assets/logo.png'
-import { useUserStore } from '@/store'
+import h5Img from '@/assets/bg_mobile.jpg'
+import h5Img2 from '@/assets/bg_mobile2.jpg'
+import pcImg1 from '@/assets/bg_desktop1.jpg'
+import pcImg2 from '@/assets/bg_desktop2.jpg'
+import logo1 from '@/assets/logo-text.png'
+import brand1 from '@/assets/brand.png'
+import logo2 from '@/assets/logo-text2.png'
+import brand2 from '@/assets/brand2.png'
+import { useUserStore, useCompanyStore } from '@/store'
 import { useRouter, useRoute } from 'vue-router'
 import { ref, computed } from 'vue'
 import { UserAvatar, LoginForm } from '@/components/common'
@@ -214,11 +228,7 @@ import { useBasicLayout } from '@/hooks/useBasicLayout'
 import imgItem01 from '@/assets/png-01.png'
 import imgQiye from '@/assets/qiye.png'
 import imgHuihua from '@/assets/huihua.png'
-import imgItem02 from '@/assets/png-02.png'
-import imgItem03 from '@/assets/png-03.png'
-import imgItem05 from '@/assets/png-05.png'
 import imgItem06 from '@/assets/png-06.png'
-import imgItem07 from '@/assets/png-07.png'
 import { init as initEruda } from '@/utils/console'
 import { useStorage } from '@vueuse/core'
 import borderImg from '@/assets/png-biankuang.png'
@@ -325,6 +335,46 @@ setTimeout(() => {
 
 //介绍
 const showDesc = ref(false)
+
+const companyStore = useCompanyStore()
+const company = computed(() => {
+    return companyStore.company
+})
+
+const h5Bg = computed(() => {
+    if (pageType.value === 'Legal') {
+        return h5Img2
+    }
+    return h5Img
+})
+
+const pcBg = computed(() => {
+    if (pageType.value === 'Legal') {
+        return pcImg2
+    }
+    return pcImg1
+})
+
+const logo = computed(() => {
+    if (pageType.value === 'Legal') {
+        return logo2
+    }
+    return logo1
+})
+
+const brand = computed(() => {
+    if (pageType.value === 'Legal') {
+        return brand2
+    }
+    return brand1
+})
+
+const pageType = computed(() => {
+    if (Number(company.value.id) === 2) {
+        return 'Legal'
+    }
+    return 'default'
+})
 </script>
 
 <style lang="less" scoped>

+ 7 - 1
src/views/page/LoginView.vue

@@ -5,7 +5,7 @@
                 <ChevronLeft />
             </n-icon>
         </n-el>
-        <n-el class="text-2xl font-bold text-gray-900 dark:text-white">登陆走马AI助手</n-el>
+        <n-el class="text-2xl font-bold text-gray-900 dark:text-white">登录{{company.name}}助手</n-el>
         <n-el class="text-xs mt-2 text-gray-400 dark:text-gray-500">未注册手机验证后自动注册登录</n-el>
 
         <div class="py-9">
@@ -18,5 +18,11 @@
 import { NIcon, NEl } from 'naive-ui'
 import { ChevronLeft } from '@vicons/tabler'
 import { LoginForm } from '@/components/common'
+import { useCompanyStore } from '@/store'
+import { computed } from 'vue'
 
+const companyStore = useCompanyStore()
+const company = computed(() => {
+    return companyStore.company
+})
 </script>