ソースを参照

build: 新框架配置

王晓东 1 ヶ月 前
コミット
938070b7bd

+ 8 - 5
.env.development

@@ -1,15 +1,18 @@
 # 应用配置面板
-VITE_APP_SETTING = true
+VITE_APP_SETTING = false
 # 页面标题
-VITE_APP_TITLE = Fantastic-admin 基础版
+VITE_APP_TITLE = Anycall|AI HUB
 # 接口请求地址,会设置到 axios 的 baseURL 参数上
-VITE_APP_API_BASEURL = /
+VITE_APP_API_BASEURL = https://dev.nexthuman.cn/fara/api
+# 上传文件接口地址cdn
+VITE_APP_UPLOAD_BASEURL = https://dev.nexthuman.cn/zfilm/cdn
+#VITE_PUBLIC_PATH = /admin/
 # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
-VITE_APP_DEBUG_TOOL =
+VITE_APP_DEBUG_TOOL = false
 # 是否禁用开发者工具,可防止被调试
 VITE_APP_DISABLE_DEVTOOL = false
 
 # 是否开启代理
 VITE_OPEN_PROXY = false
 # 是否开启开发者工具
-VITE_OPEN_DEVTOOLS = true
+VITE_OPEN_DEVTOOLS = false

+ 20 - 0
.env.prod

@@ -0,0 +1,20 @@
+# 应用配置面板
+VITE_APP_SETTING = true
+# 页面标题
+VITE_APP_TITLE = Anycall|AI HUB
+VITE_BASE_URL = https://dev.nexthuman.cn
+# 接口请求地址,会设置到 axios 的 baseURL 参数上
+VITE_APP_API_BASEURL = 	https://dev.nexthuman.cn/fara/api
+# 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
+VITE_APP_DEBUG_TOOL =
+# 是否禁用开发者工具,可防止被调试
+VITE_APP_DISABLE_DEVTOOL = true
+
+# 是否在打包时启用 Mock
+VITE_BUILD_MOCK = false
+# 是否在打包时生成 sourcemap
+VITE_BUILD_SOURCEMAP = false
+# 是否在打包时开启压缩,支持 gzip 和 brotli
+VITE_BUILD_COMPRESS = gzip,brotli
+# 是否在打包后生成存档,支持 zip 和 tar
+VITE_BUILD_ARCHIVE =

+ 1 - 1
package.json

@@ -6,6 +6,7 @@
   },
   "scripts": {
     "dev": "vite",
+    "build:prod": "vite build --mode prod",
     "build:example": "vue-tsc -b && vite build --mode example",
     "serve:example": "http-server ./dist-example -o",
     "svgo": "svgo -f src/assets/icons",
@@ -16,7 +17,6 @@
     "lint:eslint": "eslint . --cache --fix",
     "lint:stylelint": "stylelint \"src/**/*.{css,scss,vue}\" --cache --fix",
     "preinstall": "npx only-allow pnpm",
-    "postinstall": "simple-git-hooks",
     "taze": "taze minor -wIr"
   },
   "dependencies": {

+ 73 - 19
src/api/index.ts

@@ -1,6 +1,9 @@
 import axios from 'axios'
-// import qs from 'qs'
 import { toast } from 'vue-sonner'
+import type { AxiosRequestConfig } from 'axios'
+import type {
+  BaseResponse,
+} from '@/types'
 
 // 请求重试配置
 const MAX_RETRY_COUNT = 3 // 最大重试次数
@@ -18,6 +21,11 @@ const api = axios.create({
   baseURL: (import.meta.env.DEV && import.meta.env.VITE_OPEN_PROXY) ? '/proxy/' : import.meta.env.VITE_APP_API_BASEURL,
   timeout: 1000 * 60,
   responseType: 'json',
+  headers: {
+      Accept: 'application/json',
+      'Content-Type': 'application/json',
+      'accessToken': 'rcOBHJ0Hb8h5xgM/CWtNd8RBhA6WS4OPyJcxrxk4xPZtzeh5PtRXVDA7Um0NZA6NQmnbnZgWB0nNPb8iCrneQj4badFveWLrFq4LrySto3pIo/Zg1dJubbwmu3Vr1LCbSYyVIFrrgt9PXiA85kb9g38FSG3KTSi3AEY/UgjLNLBtH2+91YXKEy2KRZV3v75f',
+  },
 })
 
 api.interceptors.request.use(
@@ -28,6 +36,7 @@ api.interceptors.request.use(
     if (request.headers) {
       if (userStore.isLogin) {
         request.headers.Token = userStore.token
+        // request.headers.adminToken = userStore.token
       }
     }
     // 是否将 POST 请求参数进行字符串化处理
@@ -44,22 +53,21 @@ api.interceptors.request.use(
 function handleError(error: any) {
   if (error.status === 401) {
     useUserStore().requestLogout()
+    throw error
   }
-  else {
-    let message = error.message
-    if (message === 'Network Error') {
-      message = '后端网络故障'
-    }
-    else if (message.includes('timeout')) {
-      message = '接口请求超时'
-    }
-    else if (message.includes('Request failed with status code')) {
-      message = `接口${message.substr(message.length - 3)}异常`
-    }
-    toast.error('Error', {
-      description: message,
-    })
+  let message = error.message
+  if (message === 'Network Error') {
+    message = '后端网络故障'
+  }
+  else if (message.includes('timeout')) {
+    message = '接口请求超时'
   }
+  else if (message.includes('Request failed with status code')) {
+    message = `接口${message.substr(message.length - 3)}异常`
+  }
+  toast.error('Error', {
+    description: message,
+  })
   return Promise.reject(error)
 }
 
@@ -67,10 +75,12 @@ api.interceptors.response.use(
   (response) => {
     /**
      * 全局拦截请求发送后返回的数据,如果数据有报错则在这做全局的错误提示
-     * 假设返回数据格式为:{ status: 1, error: '', data: {} }
-     * 规则是当 status 为 1 时表示请求成功,为 0 时表示接口需要登录或者登录状态失效,需要重新登录
+     * 假设返回数据格式为:{code: 0, data: 'DiGJfn3jDc6EQ1KoH/ckj8ZN'}
+     * 规则是当 status 为 0 时表示请求成功,为 -40 时表示接口需要登录或者登录状态失效,需要重新登录
      * 请求出错时 error 会返回错误信息
      */
+    const { code, msg } = response.data
+    console.log(code, msg)
     if (typeof response.data === 'object') {
       if (response.data.status === 1) {
         if (response.data.error !== '') {
@@ -85,30 +95,74 @@ api.interceptors.response.use(
       }
       return Promise.resolve(response.data)
     }
-    else {
-      return Promise.reject(response.data)
+
+    // 成功状态
+    if (code === 0) {
+      return Promise.resolve(response.data)
     }
+
+    handleErrorCode(code, msg)
+
+
+    return Promise.reject(response.data)
   },
   async (error) => {
     // 获取请求配置
     const config = error.config
+
     // 如果配置不存在或未启用重试,则直接处理错误
     if (!config || !config.retry) {
       return handleError(error)
     }
+
     // 设置重试次数
     config.retryCount = config.retryCount || 0
+
     // 判断是否超过重试次数
     if (config.retryCount >= MAX_RETRY_COUNT) {
       return handleError(error)
     }
+
     // 重试次数自增
     config.retryCount += 1
+
     // 延迟重试
     await new Promise(resolve => setTimeout(resolve, RETRY_DELAY))
+
     // 重新发起请求
     return api(config)
   },
 )
 
+// 处理错误 code
+async function handleErrorCode(code: number, msg: string) {
+  console.log('EE', code, msg)
+
+  if (code === -94) {
+    // 登录失效,退出登录
+    useUserStore().requestLogout()
+    toast.error('Error', {
+      description: msg,
+    })
+  }
+  else if (code === -40) {
+    // 特殊错误,只显示提示
+    toast.error('Error', {
+      description: msg,
+    })
+  }
+  else {
+    // 所有其他业务错误(如 code: -90),只显示错误提示,不退出登录
+    toast.error('Error', {
+      description: msg || '请求失败',
+    })
+  }
+}
+
 export default api
+
+
+// 封装一个 request 请求,直接返回 BaseResponse
+export const request = async <T>(url: string, data?:Record<string,any>, config?:  AxiosRequestConfig<any> | undefined)=> {
+  return await api.post(url, data, config) as BaseResponse<T>
+}

+ 28 - 0
src/api/modules/anycallService.ts

@@ -0,0 +1,28 @@
+// anycall
+import { request } from '@/api'
+
+export function anycallPage(params: any){
+  return request(`/anycall/selectLib`, params)
+}
+export function updateCallings(params: any){
+  return request(`/anycall/updateCallings`, params)
+}
+export function updateTopFlag(params: any){
+  return request(`/anycall/updateTopFlag`, params)
+}
+export function voiceList(params: any){
+  return request(`/anycall/selectVoiceList`, params)
+}
+export function addRole(params: any){
+  return request(`/anycall/create`, params)
+}
+export function importZip(params: any){
+  const config = {
+            headers: {
+                Accept: 'application/json',
+                'Content-Type': 'multipart/form-data;',
+                'accessToken': 'rcOBHJ0Hb8h5xgM/CWtNd8RBhA6WS4OPyJcxrxk4xPZtzeh5PtRXVDA7Um0NZA6NQmnbnZgWB0nNPb8iCrneQj4badFveWLrFq4LrySto3pIo/Zg1dJubbwmu3Vr1LCbSYyVIFrrgt9PXiA85kb9g38FSG3KTSi3AEY/UgjLNLBtH2+91YXKEy2KRZV3v75f',
+            }
+        };
+  return request(`/anycall/import`, params, config)
+}

+ 16 - 0
src/service/api.js

@@ -0,0 +1,16 @@
+import axios from 'axios'
+
+// 创建axios实例
+const service = axios.create({
+    baseURL: 'https://dev.nexthuman.cn/fara/api',
+    withCredentials: false, // 跨域请求时是否需要使用凭证
+    headers: {
+        Accept: 'application/json',
+        'Content-Type': 'application/json',
+        'accessToken': 'rcOBHJ0Hb8h5xgM/CWtNd8RBhA6WS4OPyJcxrxk4xPZtzeh5PtRXVDA7Um0NZA6NQmnbnZgWB0nNPb8iCrneQj4badFveWLrFq4LrySto3pIo/Zg1dJubbwmu3Vr1LCbSYyVIFrrgt9PXiA85kb9g38FSG3KTSi3AEY/UgjLNLBtH2+91YXKEy2KRZV3v75f',
+    },
+    // 超时
+    timeout: 10000
+})
+
+export default service

+ 13 - 0
src/service/cdnService.js

@@ -0,0 +1,13 @@
+import cdnService from "./cdnapi";
+import service from "./api"; // 导入配置好的 axios 实例
+
+// 定义所有用户相关的 API 请求函数
+export default {
+    upload(formData) {
+        return cdnService.post(`/cmn/upload`, formData);
+    },
+    uploadVoice(file) {
+        return cdnService.post(`/audio/upload`, file);
+    },
+};
+

+ 16 - 0
src/service/cdnapi.js

@@ -0,0 +1,16 @@
+import axios from 'axios'
+
+// 创建axios实例
+const cdnService = axios.create({
+    baseURL: 'https://dev.nexthuman.cn/fara/cdn',
+    withCredentials: false, // 跨域请求时是否需要使用凭证
+    headers: {
+        Accept: 'application/json',
+        'Content-Type': 'multipart/form-data;',
+        'accessToken': 'rcOBHJ0Hb8h5xgM/CWtNd8RBhA6WS4OPyJcxrxk4xPZtzeh5PtRXVDA7Um0NZA6NQmnbnZgWB0nNPb8iCrneQj4badFveWLrFq4LrySto3pIo/Zg1dJubbwmu3Vr1LCbSYyVIFrrgt9PXiA85kb9g38FSG3KTSi3AEY/UgjLNLBtH2+91YXKEy2KRZV3v75f',
+    },
+    // 超时
+    timeout: 10000
+})
+
+export default cdnService

+ 2 - 2
src/settings.ts

@@ -34,8 +34,8 @@ const globalSettings: Settings.all = {
   copyright: {
     enable: true,
     dates: '2020-present',
-    company: 'Fantastic-admin',
-    website: 'https://fantastic-admin.hurui.me',
+    company: 'Anycall',
+    website: 'https://nexthuman.cn',
   },
 }
 

+ 124 - 0
src/store/modules/anycall.js

@@ -0,0 +1,124 @@
+// 模拟数据存储
+import anycallService from "../service/anycallService";
+
+const store = {
+  // 用户信息
+  user: null,
+  
+  // 角色数据
+  roles: [
+  ],
+  
+  // 声音数据
+  voices: [
+
+  ],
+  
+  // 推荐数据
+  recommendations: [
+  ],
+
+
+  // 登录
+  login(username, password) {
+    // 模拟登录验证
+    if (username && password) {
+      this.user = { id: 1, username, role: "admin" };
+      return true;
+    }
+    return false;
+  },
+  
+  // 登出
+  logout() {
+    this.user = null;
+  },
+};
+
+export function useStore() {
+  let roleData = {
+    page: 1,
+    size: 10,
+    name: ""
+  };
+  let topRoleData = {
+    page: 1,
+    size: 999,
+    name: "",
+    topFlag: true
+  };
+  let data = {
+    page: 1,
+    size: 10,
+    system: true,
+    gender: 1
+  };
+
+  async function fetchRoles() {
+
+    try {
+      const response = await anycallService.anycallPage(roleData);
+
+      store.roles = response.data.data.content.map(item => ({
+        id: item.id || '',
+        name: item.name || '',
+        prompt: item.prompt || '',
+        callings: item.callings || '',
+        language: item.language || '',
+        photo: item.photo || '',
+        voice: item.voice || '',
+        voiceName: item.voiceName || '',
+        topFlag: item.topFlag || '',
+      }));
+      store.rolesCount = response.data.data.total;
+
+    } catch (err) {
+      // error.value = err.message || '获取数据失败';
+    }
+  }
+
+  async function fetchVoices() {
+
+    try {
+      const response = await anycallService.voiceList(data);
+      store.voices = response.data.data.content.map(item => ({
+        id: item.id || '',
+        name: item.name || '',
+        gender: item.gender || '',
+        ctime: new Date(item.ctime).getFullYear()+'-'+new Date(item.ctime).getMonth()+'-'+new Date(item.ctime).getDay() || '',
+      }));
+      store.voicesCount = response.data.data.total;
+
+      console.log(response.data.data.total);
+    } catch (err) {
+      // error.value = err.message || '获取数据失败';
+    }
+  }
+
+  async function fetchRecommendations() {
+
+    try {
+      const response = await anycallService.anycallPage(topRoleData);
+
+      store.recommendations = response.data.data.content.map(item => ({
+        id: item.id || '',
+        name: item.name || '',
+        prompt: item.prompt || '',
+        callings: item.callings || '',
+        language: item.language || '',
+        photo: item.photo || '',
+        voice: item.voice || '',
+        voiceName: item.voiceName || '',
+      }));
+      store.recommendationsCount = response.data.data.total;
+
+    } catch (err) {
+    }
+  }
+
+  fetchRecommendations();
+  fetchRoles();
+  fetchVoices();
+
+  return store;
+}

+ 1 - 0
src/types/auto-imports.d.ts

@@ -84,6 +84,7 @@ declare global {
   const useRouter: typeof import('vue-router')['useRouter']
   const useSettingsStore: typeof import('../store/modules/settings')['useSettingsStore']
   const useSlots: typeof import('vue')['useSlots']
+  const useStore: typeof import('../store/modules/anycall.js')['useStore']
   const useTabbar: typeof import('../utils/composables/useTabbar')['default']
   const useTabbarStore: typeof import('../store/modules/tabbar')['useTabbarStore']
   const useTemplateRef: typeof import('vue')['useTemplateRef']

+ 6 - 1
src/types/env.d.ts

@@ -13,9 +13,14 @@ interface ImportMetaEnv {
    */
   readonly VITE_APP_API_BASEURL: string
   /**
+   * 上传文件接口地址cdn
+   */
+  readonly VITE_APP_UPLOAD_BASEURL: string
+  /**
+   * VITE_PUBLIC_PATH = /admin/
    * 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
    */
-  readonly VITE_APP_DEBUG_TOOL: string
+  readonly VITE_APP_DEBUG_TOOL: boolean
   /**
    * 是否禁用开发者工具,可防止被调试
    */

+ 13 - 0
src/types/index.ts

@@ -0,0 +1,13 @@
+export interface BaseResponse<T = any> {
+  code: number
+  data: T
+  msg: string
+  success?: boolean
+}
+
+/** 分页响应类型 */
+export interface PageResponse<T = any> {
+  total: number
+  content: T[]
+  result: T[]
+}