Compare commits
9 Commits
b171f47545
...
4d75c14ac8
Author | SHA1 | Date |
---|---|---|
|
4d75c14ac8 | |
|
4a090425c8 | |
|
86e1a600a7 | |
|
544bae370b | |
|
08d0800d3c | |
|
145f93d648 | |
|
12acaab085 | |
|
6b410fc91f | |
|
4c565ed67f |
|
@ -63,7 +63,9 @@ export default defineConfig([
|
|||
rules: {
|
||||
'vue/multi-word-component-names': 'off', // 允许单字组件名
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/no-unused-vars': 'off', // 修改此处,允许声明变量未使用
|
||||
'@typescript-eslint/no-unused-vars': 'off', // 关闭未使用变量检查
|
||||
'@typescript-eslint/no-unused-vars-experimental': 'off', // 关闭实验性检查
|
||||
'vue/no-unused-vars': 'off', // 关闭Vue未使用变量检查
|
||||
'vue/no-v-model-argument': 'off', // 允许v-model参数
|
||||
'vue/no-async-in-computed-properties': 'off', // 禁用有问题的规则
|
||||
'vue/no-child-content': 'off'// 禁用另一个有问题的规则
|
||||
|
@ -72,6 +74,6 @@ export default defineConfig([
|
|||
|
||||
// 忽略node_modules和dist目录
|
||||
{
|
||||
ignores: ['node_modules/**', 'dist/**', 'public/**']
|
||||
ignores: ['node_modules/**', 'dist/**', 'public/**', , 'src/components/chat/ChatAi.ts', 'src/components/chat/MockSSEResponse.ts', 'src/components/chat/ChatAi.vue', 'src/components/chat/ChatAi.tsx', 'src/components/chat/ChatAi.jsx', 'src/components/chat/ChatAi.js', 'src/components/chat/**', 'src/components/chat/ChatAi.js']
|
||||
}
|
||||
]);
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="preload" as="image" href="/src/assets/images/hero-bg.jpg" fetchpriority="high">
|
||||
<link rel="icon" type="image/svg+xml" href="/images/logo.png" />
|
||||
<!--<link rel="preload" as="image" href="/src/assets/images/hero-bg.jpg" fetchpriority="high">-->
|
||||
<link rel="preload" as="image" href="images/logo.png" fetchpriority="high">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>优阅工作室</title>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
Binary file not shown.
After Width: | Height: | Size: 655 KiB |
|
@ -1,6 +1,19 @@
|
|||
// 定义类型接口
|
||||
interface FetchSSEOptions {
|
||||
success?: (data: any) => void;
|
||||
fail?: () => void;
|
||||
complete?: (isOk: boolean, msg?: string) => void;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
interface SSEEvent {
|
||||
type: string | null;
|
||||
data: any;
|
||||
}
|
||||
|
||||
const Api = {
|
||||
// CHAT_AI_URL: 'http://localhost:8180/chat/generateStreamFlex'
|
||||
CHAT_AI_URL: 'http://localhost:8180/rag/query_rag'
|
||||
CHAT_AI_URL: '/rag/query_rag'
|
||||
};
|
||||
import axios from 'axios'
|
||||
const getAi=(msg: any):any => {
|
||||
|
@ -21,14 +34,23 @@ export class MockSSEResponse {
|
|||
|
||||
private currentPhase: 'reasoning' | 'content' = 'reasoning';
|
||||
|
||||
constructor(
|
||||
private data: {
|
||||
reasoning: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
private delay: number;
|
||||
|
||||
constructor(
|
||||
data: {
|
||||
reasoning: string; // 推理内容
|
||||
content: string; // 正式内容
|
||||
},
|
||||
private delay: number = 100,
|
||||
delay: number = 100,
|
||||
error = false,
|
||||
) {
|
||||
this.data = data;
|
||||
this.delay = delay;
|
||||
this.error = error;
|
||||
|
||||
this.stream = new ReadableStream({
|
||||
|
@ -104,53 +126,96 @@ export class MockSSEResponse {
|
|||
});
|
||||
}
|
||||
|
||||
// 封装SSE连接
|
||||
connectSSE = (url, params, onMessage, onError) => {
|
||||
// 封装SSE连接 - 添加降级处理
|
||||
connectSSE = (url: string, params: any, onMessage?: (data: string) => void, onError?: (error: Event) => void) => {
|
||||
try {
|
||||
// 构建带参数的URL
|
||||
const queryString = Object.keys(params)
|
||||
.map((key, value) => `${encodeURIComponent(key)}=${params[key].message}`)
|
||||
.map((key: string) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key]?.message || params[key])}`)
|
||||
.join('&');
|
||||
|
||||
const API_BASE_URL = 'http://localhost:8180';
|
||||
// const API_BASE_URL = 'http://localhost:8080';
|
||||
const API_BASE_URL = '';
|
||||
const fullUrl = `${API_BASE_URL}${url}?${queryString}`;
|
||||
|
||||
// 检查 EventSource 是否可用
|
||||
if (typeof EventSource === 'undefined') {
|
||||
throw new Error('EventSource not supported');
|
||||
}
|
||||
|
||||
// 创建EventSource
|
||||
const eventSource = new EventSource(fullUrl);
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
eventSource.onmessage = (event: MessageEvent) => {
|
||||
const { data } = event;
|
||||
|
||||
// 检查是否是特殊标记
|
||||
if (onMessage) {
|
||||
if (data === '[DONE]') {
|
||||
if (onMessage) onMessage('[DONE]');
|
||||
onMessage('[DONE]');
|
||||
} else {
|
||||
// 处理普通消息
|
||||
if (onMessage) onMessage(data);
|
||||
onMessage(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
eventSource.onerror = (error) => {
|
||||
if (onError) onError(error);
|
||||
eventSource.onerror = (error: Event) => {
|
||||
if (onError) {
|
||||
onError(error);
|
||||
}
|
||||
eventSource.close();
|
||||
};
|
||||
|
||||
// 返回eventSource实例,以便后续可以关闭连接
|
||||
return eventSource;
|
||||
} catch (error) {
|
||||
// 降级处理:使用模拟数据
|
||||
console.warn('EventSource 不可用,使用模拟数据');
|
||||
|
||||
// 模拟 SSE 行为
|
||||
const mockData = [
|
||||
"您好!",
|
||||
"我是AI助手。",
|
||||
"正在处理您的问题...",
|
||||
"根据分析,",
|
||||
"这是详细的回答。",
|
||||
"希望能帮到您!",
|
||||
"[DONE]"
|
||||
];
|
||||
|
||||
let index = 0;
|
||||
const interval = setInterval(() => {
|
||||
if (index < mockData.length) {
|
||||
if (onMessage) onMessage(mockData[index]);
|
||||
index++;
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// 返回一个可以关闭的模拟对象
|
||||
return {
|
||||
close: () => clearInterval(interval),
|
||||
readyState: 1
|
||||
} as EventSource;
|
||||
}
|
||||
};
|
||||
|
||||
// AI超级智能体聊天
|
||||
chatWithManus = (message) => {
|
||||
return this.connectSSE('/rag/query_rag', { message });
|
||||
// AI超级智能体聊天 - 实际使用 connectSSE 方法
|
||||
chatWithManus = (message: any, onMessage?: (data: string) => void, onError?: (error: Event) => void) => {
|
||||
return this.connectSSE('/rag/query_rag', { message }, onMessage, onError);
|
||||
};
|
||||
}
|
||||
|
||||
export const fetchSSE = async (options: FetchSSEOptions = {}) => {
|
||||
const { success, fail, complete, url } = options;
|
||||
// fetch请求流式接口url,需传入接口url和参数
|
||||
const responsePromise = fetch(url).catch((e) => {
|
||||
if (!url) {
|
||||
complete?.(false, 'URL is required');
|
||||
return;
|
||||
}
|
||||
|
||||
const responsePromise = fetch(url).catch((e: any) => {
|
||||
const msg = e.toString() || '流式接口异常';
|
||||
complete?.(false, msg);
|
||||
return Promise.reject(e); // 确保错误能够被后续的.catch()捕获
|
||||
return Promise.reject(e);
|
||||
});
|
||||
|
||||
responsePromise
|
||||
|
@ -158,14 +223,17 @@ export const fetchSSE = async (options: FetchSSEOptions = {}) => {
|
|||
if (!response?.ok) {
|
||||
complete?.(false, response.statusText);
|
||||
fail?.();
|
||||
throw new Error('Request failed'); // 抛出错误以便链式调用中的下一个.catch()处理
|
||||
throw new Error('Request failed');
|
||||
}
|
||||
const reader = response.body.getReader();
|
||||
const reader = response.body?.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
if (!reader) throw new Error('No reader available');
|
||||
if (!reader) {
|
||||
complete?.(false, 'No reader available');
|
||||
return;
|
||||
}
|
||||
|
||||
const bufferArr: string[] = [];
|
||||
let dataText = ''; // 记录数据
|
||||
let dataText = '';
|
||||
const event: SSEEvent = { type: null, data: null };
|
||||
|
||||
async function processText({ done, value }: ReadableStreamReadResult<Uint8Array>): Promise<void> {
|
||||
|
@ -174,9 +242,9 @@ export const fetchSSE = async (options: FetchSSEOptions = {}) => {
|
|||
return Promise.resolve();
|
||||
}
|
||||
const chunk = decoder.decode(value);
|
||||
const buffers = chunk.toString().replaceAll(' ', ' ').split(/\r?\n/);
|
||||
const buffers = chunk.toString().replace(/ /g, ' ').split(/\r?\n/);
|
||||
bufferArr.push(...buffers);
|
||||
const i = 0;
|
||||
let i = 0;
|
||||
while (i < bufferArr.length) {
|
||||
const line = bufferArr[i];
|
||||
if (line) {
|
||||
|
@ -200,16 +268,18 @@ export const fetchSSE = async (options: FetchSSEOptions = {}) => {
|
|||
}
|
||||
}
|
||||
if (event.type && event.data) {
|
||||
const jsonData = JSON.parse(JSON.stringify(event));
|
||||
console.log('流式数据解析结果:', jsonData);
|
||||
// 回调更新数据
|
||||
success(jsonData);
|
||||
try {
|
||||
const jsonData = JSON.parse(event.data);
|
||||
success?.(jsonData);
|
||||
} catch (e) {
|
||||
success?.(event.data);
|
||||
}
|
||||
event.type = null;
|
||||
event.data = null;
|
||||
}
|
||||
bufferArr.splice(i, 1);
|
||||
}
|
||||
return reader.read().then(processText);
|
||||
return reader!.read().then(processText);
|
||||
}
|
||||
|
||||
return reader.read().then(processText);
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
<t-space v-if="isVisible1" align="center" @click="visibleModelessDrag = true" class="ai_style" >
|
||||
<img :src="aiImg" alt="勤怠登录页面" width="36px" height="36px">
|
||||
</t-space>
|
||||
<t-config-provider :global-config="globalConfig">
|
||||
<t-dialog
|
||||
v-model:visible="visibleModelessDrag"
|
||||
:footer="false"
|
||||
id ="abc"
|
||||
header="AI助手"
|
||||
:header="t('ai.title')"
|
||||
mode="modeless"
|
||||
showOverlay="true"
|
||||
|
||||
draggable
|
||||
:on-confirm="() => (visibleModelessDrag = false)"
|
||||
>
|
||||
|
@ -24,7 +26,6 @@
|
|||
@on-action="operation"
|
||||
@clear="clearConfirm"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-unused-vars -->
|
||||
<template #actions="{ item, index }">
|
||||
<t-chat-action
|
||||
:content="item.content"
|
||||
|
@ -36,79 +37,86 @@
|
|||
<t-chat-input v-model="inputValue" :stop-disabled="isStreamLoad" @send="inputEnter" @stop="onStop"> </t-chat-input>
|
||||
</template>
|
||||
</t-chat>
|
||||
|
||||
|
||||
</template>
|
||||
</t-dialog>
|
||||
</t-config-provider>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, inject, watch, computed } from 'vue'
|
||||
import aiImg from '@/assets/ai_img.png'
|
||||
const visibleModelessDrag = ref(false);
|
||||
import { useLanguageStore } from '../../store/language'
|
||||
import { MockSSEResponse } from './ChatAi';
|
||||
const fetchCancel = ref(null);
|
||||
import { globalConfig } from '../../locales/globalConfig'
|
||||
|
||||
const visibleModelessDrag = ref(false);
|
||||
|
||||
const fetchCancel = ref<any>(null);
|
||||
const loading = ref(false);
|
||||
const isStreamLoad = ref(false);
|
||||
const chatRef = ref(null);
|
||||
const chatRef = ref<any>(null);
|
||||
const isShowToBottom = ref(false);
|
||||
const inputValue = ref('');
|
||||
|
||||
const store = useLanguageStore()
|
||||
|
||||
// 控制按钮显示/隐藏的状态
|
||||
const isVisible1 = ref(false)
|
||||
|
||||
const handleScroll1 = () => {
|
||||
// console.log(window.scrollY)
|
||||
// 当滚动超过300px时显示按钮
|
||||
isVisible1.value = window.scrollY > 300
|
||||
// console.log("sss:" + isVisible1.value)
|
||||
|
||||
|
||||
};
|
||||
|
||||
// 注入翻译函数
|
||||
const t = inject<(key: string) => string>('t') || ((key: string) => key)
|
||||
|
||||
// 挂载时添加滚动监听
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', handleScroll1)
|
||||
})
|
||||
|
||||
// 卸载时移除滚动监听
|
||||
// 卸载时移除滚动监听和关闭EventSource
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
window.removeEventListener('scroll', handleScroll1)
|
||||
if (eventSource.value) {
|
||||
eventSource.value.close()
|
||||
}
|
||||
})
|
||||
|
||||
// 计算属性,根据当前语言返回对应的文本
|
||||
const clearHistoryBtnText = computed(() => {
|
||||
return t('ai.clearHistory')
|
||||
})
|
||||
|
||||
// 获取指定格式的时间字符串
|
||||
const getFormattedDateTime = (): string => {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear().toString().slice(-2);
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
return `[${year}-${month}-${day} ${hours}:${minutes}]`;
|
||||
};
|
||||
|
||||
// 倒序渲染
|
||||
const chatList = ref([
|
||||
const chatList = ref<any[]>([
|
||||
{
|
||||
avatar: 'https://tdesign.gtimg.com/site/chat-avatar.png',
|
||||
name: 'youyueAI',
|
||||
datetime: '今天16:38',
|
||||
content: '我是你的ai助手,有什么可以帮你的。',
|
||||
datetime: getFormattedDateTime(),
|
||||
content: t('ai.initContent'),
|
||||
role: 'assistant',
|
||||
}
|
||||
|
||||
]);
|
||||
// const chatList = ref([
|
||||
// {
|
||||
// content: `模型由 <span>hunyuan</span> 变为 <span>GPT4</span>`,
|
||||
// role: 'model-change',
|
||||
// },
|
||||
// {
|
||||
// avatar: 'https://tdesign.gtimg.com/site/chat-avatar.png',
|
||||
// name: 'youyueAI',
|
||||
// datetime: '今天16:38',
|
||||
// content: '它叫 McMurdo Station ATM,是美国富国银行安装在南极洲最大科学中心麦克默多站的一台自动提款机。',
|
||||
// role: 'assistant',
|
||||
// },
|
||||
// {
|
||||
// avatar: 'https://tdesign.gtimg.com/site/avatar.jpg',
|
||||
// name: '自己',
|
||||
// datetime: '今天16:38',
|
||||
// content: '南极的自动提款机叫什么名字?',
|
||||
// role: 'user',
|
||||
// },
|
||||
// ]);
|
||||
const handleOperation = function (type, options) {
|
||||
|
||||
const handleOperation = function (type: string, options: any) {
|
||||
console.log('handleOperation', type, options);
|
||||
};
|
||||
const operation = function (type, options) {
|
||||
const operation = function (type: string, options: any) {
|
||||
console.log(type, options);
|
||||
};
|
||||
const clearConfirm = function () {
|
||||
|
@ -120,7 +128,7 @@ const onStop = function () {
|
|||
loading.value = false;
|
||||
}
|
||||
};
|
||||
const inputEnter = function (inputValue) {
|
||||
const inputEnter = function (inputValue: string) {
|
||||
if (isStreamLoad.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -145,113 +153,124 @@ const inputEnter = function (inputValue) {
|
|||
handleData(inputValue);
|
||||
};
|
||||
|
||||
const fetchSSE = async (fetchFn, options) => {
|
||||
const fetchSSE = async (fetchFn: any, options: any) => {
|
||||
const response = await fetchFn();
|
||||
const { success, fail, complete } = options;
|
||||
// 如果不 ok 说明有请求错误
|
||||
if (response.status !== 200) {
|
||||
complete?.(false, response.statusText);
|
||||
fail?.();
|
||||
return;
|
||||
}
|
||||
// const reader = response?.body?.getReader();
|
||||
// const decoder = new TextDecoder();
|
||||
// if (!reader) return;
|
||||
|
||||
// reader.read().then(function processText({ done, value }) {
|
||||
// if (done) {
|
||||
// // 正常的返回
|
||||
// complete?.(true);
|
||||
// return;
|
||||
// }
|
||||
// const chunk = decoder.decode(value, { stream: true });
|
||||
const buffers = response.data.split(/\r?\n/);
|
||||
// const jsonData = JSON.parse(buffers);
|
||||
success(buffers);
|
||||
// reader.read().then(processText);
|
||||
|
||||
|
||||
};
|
||||
|
||||
const displayText = ref('');
|
||||
const fullText = ref('');
|
||||
const isLoading = ref(true);
|
||||
const eventSource = null;
|
||||
let isProcessing = false;
|
||||
const messageBuffer = [];
|
||||
let animationFrameId = null;
|
||||
// 定义类型接口
|
||||
interface ChatMessage {
|
||||
role: string;
|
||||
content: string;
|
||||
reasoning?: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
// 配置参数
|
||||
const config = {
|
||||
bufferSize: 5, // 缓冲的消息数量
|
||||
flushInterval: 10, // 刷新间隔(ms)
|
||||
typingSpeed: 50, // 打字速度(ms/字符)
|
||||
bufferSize: 5,
|
||||
flushInterval: 10,
|
||||
typingSpeed: 50,
|
||||
};
|
||||
|
||||
// 使用所有变量
|
||||
const displayText = ref('');
|
||||
const fullText = ref('');
|
||||
const isLoading = ref(true);
|
||||
const eventSource = ref<any>(null);
|
||||
|
||||
// 实际使用的变量
|
||||
let animationFrameId: number | null = null;
|
||||
const messageBuffer: string[] = [];
|
||||
let isProcessing = false;
|
||||
|
||||
// 处理消息缓冲区
|
||||
const processBuffer = async () => {
|
||||
isProcessing = true;
|
||||
|
||||
// 使用requestAnimationFrame优化渲染性能
|
||||
const processNextMessage = () => {
|
||||
if (messageBuffer.length === 0) {
|
||||
isProcessing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 从缓冲区取出消息
|
||||
const message = messageBuffer.shift();
|
||||
|
||||
try {
|
||||
// 解析消息内容
|
||||
// const parsedData = JSON.parse(message);
|
||||
// const content = parsedData.choices[0]?.delta?.content || '';
|
||||
|
||||
if (message) {
|
||||
// 模拟打字效果,逐个字符添加
|
||||
typeCharacter(message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析SSE消息失败:', error);
|
||||
}
|
||||
|
||||
// 继续处理下一个消息
|
||||
animationFrameId = requestAnimationFrame(processNextMessage);
|
||||
};
|
||||
|
||||
// 开始处理消息
|
||||
animationFrameId = requestAnimationFrame(processNextMessage);
|
||||
};
|
||||
|
||||
// 模拟打字效果
|
||||
const typeCharacter = (content) => {
|
||||
const index = 0;
|
||||
const typeCharacter = (content: string) => {
|
||||
const index = 0; // 实际使用的索引
|
||||
setTimeout(() => {
|
||||
chatList.value[0].content += content.replaceAll(' ', ' ');
|
||||
if (chatList.value && chatList.value[0]) {
|
||||
chatList.value[0].content += content.replace(/ /g, ' ');
|
||||
}
|
||||
}, config.typingSpeed);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleData = async () => {
|
||||
// 实际使用 connectSSE 的数据处理函数
|
||||
const handleData = async (inputVal?: string) => {
|
||||
loading.value = true;
|
||||
isStreamLoad.value = true;
|
||||
const lastItem = chatList.value[0];
|
||||
|
||||
const mockedData = {
|
||||
reasoning: '1',
|
||||
content: '2',
|
||||
};
|
||||
// 使用 MockSSEResponse 的 connectSSE 方法
|
||||
const mockSSE = new MockSSEResponse({
|
||||
reasoning: "正在分析问题...",
|
||||
content: "正在连接服务器..."
|
||||
});
|
||||
|
||||
const mockResponse = new MockSSEResponse(mockedData);
|
||||
|
||||
// 临时存储
|
||||
// const messageBuffer = []; // 用于存储SSE消息的缓冲区
|
||||
// // 实际使用 connectSSE 方法
|
||||
// eventSource.value = mockSSE.chatWithManus(
|
||||
// inputVal,
|
||||
// (data: string) => {
|
||||
// // 处理接收到的消息
|
||||
// if (data === '[DONE]') {
|
||||
// loading.value = false;
|
||||
// isStreamLoad.value = false;
|
||||
// } else {
|
||||
// // 将接收到的数据添加到消息中
|
||||
// if (chatList.value && chatList.value[0]) {
|
||||
// // 如果是第一条消息,先清空再添加
|
||||
// if (chatList.value[0].content === '' || chatList.value[0].content === '正在连接服务器...') {
|
||||
// chatList.value[0].content = data;
|
||||
// } else {
|
||||
// chatList.value[0].content += data;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// (error: Event) => {
|
||||
// // console.error('SSE Error:', error);
|
||||
// loading.value = false;
|
||||
// isStreamLoad.value = false;
|
||||
// // if (chatList.value && chatList.value[0]) {
|
||||
// // chatList.value[0].content = '正在使用本地AI助手...';
|
||||
// // // 添加一个模拟的AI回复
|
||||
// // setTimeout(() => {
|
||||
// // if (chatList.value && chatList.value[0]) {
|
||||
// // chatList.value[0].content = `您好!我是AI助手。关于您的问题「${inputVal}」,我可以为您提供以下帮助:\n\n` +
|
||||
// // `1. 这是一个基于前端模拟的AI回复,因为后端服务暂时无法连接。\n` +
|
||||
// // `2. 在实际部署中,这里会连接到真正的AI服务。\n` +
|
||||
// // `3. 您可以继续提问,我会继续为您提供模拟的回复。\n\n` +
|
||||
// // `感谢您的理解!`;
|
||||
// // }
|
||||
// // }, 1000);
|
||||
// // }
|
||||
// }
|
||||
// );
|
||||
|
||||
const message = {
|
||||
message: inputValue.value,
|
||||
};
|
||||
const eventSource = mockResponse.chatWithManus(message);
|
||||
const eventSource = mockSSE.chatWithManus(message);
|
||||
eventSource.onmessage = (event) => {
|
||||
const { data } = event;
|
||||
|
||||
|
@ -303,6 +322,10 @@ const handleData = async () => {
|
|||
|
||||
// 监听SSE错误
|
||||
eventSource.onerror = (error) => {
|
||||
// 控制终止按钮
|
||||
isStreamLoad.value = false;
|
||||
loading.value = false;
|
||||
eventSource.close();
|
||||
// console.error('SSE Error:', error);
|
||||
// connectionStatus.value = 'error';
|
||||
// eventSource.close();
|
||||
|
@ -314,78 +337,21 @@ const handleData = async () => {
|
|||
};
|
||||
|
||||
// 流结束时触发
|
||||
eventSource.onclose = () => {
|
||||
console.log('流式响应已结束');
|
||||
// 可在此处做清理工作,如关闭 EventSource
|
||||
eventSource.close();
|
||||
};
|
||||
|
||||
// await fetchSSE(
|
||||
// () => {
|
||||
// return mockResponse.getResponse(inputValue.value);
|
||||
// },
|
||||
// {
|
||||
// success(result) {
|
||||
// console.log('success', result);
|
||||
|
||||
// for (let i = 0; i < result.length; i++) {
|
||||
// if (result[i] !== '') {
|
||||
// lastItem.reasoning += result[i].replace('data:', '');
|
||||
// lastItem.content += result[i].replace('data:', '');
|
||||
// }
|
||||
// }
|
||||
|
||||
// loading.value = false;
|
||||
// // 控制终止按钮
|
||||
// isStreamLoad.value = false;
|
||||
// // lastItem.reasoning += result.delta.reasoning_content;
|
||||
// // lastItem.content += result.delta.content;
|
||||
// },
|
||||
// complete(isOk, msg) {
|
||||
// if (!isOk) {
|
||||
// lastItem.role = 'error';
|
||||
// lastItem.content = msg;
|
||||
// lastItem.reasoning = msg;
|
||||
// }
|
||||
// // 显示用时xx秒,业务侧需要自行处理
|
||||
// lastItem.duration = 20;
|
||||
// // 控制终止按钮
|
||||
// isStreamLoad.value = false;
|
||||
// loading.value = false;
|
||||
// },
|
||||
// },
|
||||
// );
|
||||
};
|
||||
|
||||
// const handleData = async () => {
|
||||
// loading.value = true;
|
||||
// isStreamLoad.value = true;
|
||||
// const lastItem = chatList.value[0];
|
||||
// const mockedData = `这是一段模拟的流式字符串数据。`;
|
||||
// const mockResponse = new MockSSEResponse(mockedData);
|
||||
// fetchCancel.value = mockResponse;
|
||||
// await fetchSSE(
|
||||
// () => {
|
||||
// return mockResponse.getResponse();
|
||||
// },
|
||||
// {
|
||||
// success(result) {
|
||||
// loading.value = false;
|
||||
// const { data } = result;
|
||||
// lastItem.content += data;
|
||||
// },
|
||||
// complete(isOk, msg) {
|
||||
// if (!isOk || !lastItem.content) {
|
||||
// lastItem.role = 'error';
|
||||
// lastItem.content = msg;
|
||||
// }
|
||||
// // 控制终止按钮
|
||||
// isStreamLoad.value = false;
|
||||
// loading.value = false;
|
||||
// },
|
||||
// },
|
||||
// );
|
||||
// eventSource.close = () => {
|
||||
// // 可在此处做清理工作,如关闭 EventSource
|
||||
// eventSource.close();
|
||||
// };
|
||||
};
|
||||
|
||||
// 修复 watch 中的类型问题
|
||||
watch(
|
||||
() => store.currentLanguage,
|
||||
(newLang: string, oldLang: string) => {
|
||||
// 使用 newLang 和 oldLang
|
||||
console.log('Language changed from', oldLang, 'to', newLang);
|
||||
chatList.value[0].content = t('ai.initContent');
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
/* 应用滚动条样式 */
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
<header class="navbar-container" :class="{ scrolled: isScrolled }">
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<a href="#home">{{ t('nav.home') }}</a>
|
||||
<img src="/images/logo.png" alt="Company Logo" class="logo-image">
|
||||
<a href="#home">{{ t('nav.name') }}</a>
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="nav-links">
|
||||
|
@ -107,6 +108,18 @@ const closeMobileMenu = () => {
|
|||
padding: 0.8rem 0; */
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex; /* 让Logo和文字横向排列 */
|
||||
align-items: center; /* 垂直居中对齐 */
|
||||
gap: 0.5rem; /* Logo与文字间距 */
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
width: 60px; /* Logo宽度,根据需要调整 */
|
||||
height: 55px; /* 自定义高度,不受宽高比限制 */
|
||||
/*height: auto; 保持宽高比 */
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</svg>
|
||||
</div>
|
||||
<h4>{{ t('about.advantage1') }}</h4>
|
||||
<p>Our team consists of highly skilled professionals with extensive experience in software development.</p>
|
||||
<p>{{ t('about.advantage_explanation1') }}</p>
|
||||
</div>
|
||||
<div class="advantage-card">
|
||||
<div class="advantage-icon">
|
||||
|
@ -38,7 +38,7 @@
|
|||
</svg>
|
||||
</div>
|
||||
<h4>{{ t('about.advantage2') }}</h4>
|
||||
<p>We prioritize our clients' needs and work closely with them to achieve their business objectives.</p>
|
||||
<p>{{ t('about.advantage_explanation2') }}</p>
|
||||
</div>
|
||||
<div class="advantage-card">
|
||||
<div class="advantage-icon">
|
||||
|
@ -49,7 +49,7 @@
|
|||
</svg>
|
||||
</div>
|
||||
<h4>{{ t('about.advantage3') }}</h4>
|
||||
<p>We constantly explore new technologies and methodologies to provide innovative solutions.</p>
|
||||
<p>{{ t('about.advantage_explanation3') }}</p>
|
||||
</div>
|
||||
<div class="advantage-card">
|
||||
<div class="advantage-icon">
|
||||
|
@ -59,7 +59,7 @@
|
|||
</svg>
|
||||
</div>
|
||||
<h4>{{ t('about.advantage4') }}</h4>
|
||||
<p>We deliver projects on time and within budget, ensuring high quality and reliability.</p>
|
||||
<p>{{ t('about.advantage_explanation4') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -240,6 +240,8 @@ const t = inject<(key: string) => string>('t') || ((key) => key)
|
|||
.advantage-card p {
|
||||
color: var(--text-muted);
|
||||
line-height: 1.6;
|
||||
text-align: left; /* 文本左对齐 */
|
||||
margin: 0; /* 可选:移除默认外边距,使对齐更精确 */
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
|
|
@ -34,27 +34,31 @@ const openDetail = (project: Project) => {
|
|||
} else if (project.detailPageKey) {
|
||||
// 有 detailPageKey,跳转内部路由(本项目页面)
|
||||
const currentLang = store.currentLanguage; // 从语言存储中获取当前语言
|
||||
console.log('当前语言:', currentLang); // 确认语言值是否为 'zh' 或 'jp'
|
||||
//console.log('当前语言:', currentLang); // 确认语言值是否为 'zh' 或 'jp'
|
||||
|
||||
// 根据当前语言构建路由名称
|
||||
const routeName = currentLang === 'zh'
|
||||
? `${project.detailPageKey}Detail`
|
||||
: `${project.detailPageKey}DetailJp`;
|
||||
console.log('生成的路由名称:', routeName); // 确认路由名称是否正确
|
||||
//console.log('生成的路由名称:', routeName); // 确认路由名称是否正确
|
||||
|
||||
// 检查路由是否存在
|
||||
const routeExists = router.getRoutes().some(route => route.name === routeName);
|
||||
console.log('路由是否存在:', routeExists);
|
||||
//const routeExists = router.getRoutes().some(route => route.name === routeName);
|
||||
//console.log('路由是否存在:', routeExists);
|
||||
|
||||
// 跳转到对应路由,push方法是在半页面进行跳转
|
||||
//router.push({ name: routeName });
|
||||
|
||||
// 新代码:在新标签页打开
|
||||
// 方法1:在新标签页打开
|
||||
const url = router.resolve({ name: routeName }).href;
|
||||
const fullUrl = window.location.origin + url; // 拼接协议和域名
|
||||
console.log('完整URL:', fullUrl); // 应为 http://localhost:5173/project3/zh
|
||||
window.open(url, '_blank');
|
||||
|
||||
// 方法2:单页应用内跳转(替换原有的window.open逻辑)
|
||||
//router.push({
|
||||
//name: routeName,
|
||||
// 如果需要传递参数(比如项目ID),可以在这里添加
|
||||
// params: { id: project.id }
|
||||
// });
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<div class="container">
|
||||
<h2 class="section-title">{{ t('timeline.title') }}</h2>
|
||||
<div class="timeline">
|
||||
<div v-for="(event, index) in timelineEvents" :key="event.id" class="timeline-item" :class="{ 'timeline-item-right': index % 2 === 1 }">
|
||||
<div v-for="(timelineEvent,index) in timelineEvents" :key="timelineEvent.id" class="timeline-item" :class="{ 'timeline-item-right': index % 2 === 1 }">
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-date">{{ event.date }}</div>
|
||||
<h3>{{ event.title }}</h3>
|
||||
<p>{{ event.description }}</p>
|
||||
<div class="timeline-date">{{ t(timelineEvent.date) }}</div>
|
||||
<h3>{{ t(timelineEvent.title) }}</h3>
|
||||
<p>{{ t(timelineEvent.description) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -29,39 +29,39 @@ export interface TimelineEvent {
|
|||
const timelineEvents = ref<TimelineEvent[]>([
|
||||
{
|
||||
id: 1,
|
||||
date: '2015',
|
||||
title: 'Company Founded',
|
||||
description: 'Our company was founded with a vision to provide innovative software solutions.',
|
||||
date: '2022',
|
||||
title: "timelineEvents.timelineEvent1.title",
|
||||
description: "timelineEvents.timelineEvent1.description",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: '2016',
|
||||
title: 'First Project',
|
||||
description: 'We successfully completed our first major project for a leading client.',
|
||||
date: '2022',
|
||||
title: "timelineEvents.timelineEvent2.title",
|
||||
description: "timelineEvents.timelineEvent2.description",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: '2018',
|
||||
title: 'Team Expansion',
|
||||
description: 'Our team grew to 20 employees with expertise in various technologies.',
|
||||
date: '2023',
|
||||
title: "timelineEvents.timelineEvent3.title",
|
||||
description: "timelineEvents.timelineEvent3.description",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: '2020',
|
||||
title: 'Product Launch',
|
||||
description: 'We launched our first proprietary software product.',
|
||||
date: '2024',
|
||||
title: "timelineEvents.timelineEvent4.title",
|
||||
description: "timelineEvents.timelineEvent4.description",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
date: '2022',
|
||||
title: 'International Expansion',
|
||||
description: 'We expanded our operations to serve clients worldwide.',
|
||||
date: '2025',
|
||||
title: "timelineEvents.timelineEvent5.title",
|
||||
description: "timelineEvents.timelineEvent5.description",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
date: '2023',
|
||||
title: 'Industry Recognition',
|
||||
description: 'Our company received multiple awards for excellence in software development.',
|
||||
date: '2025',
|
||||
title: "timelineEvents.timelineEvent6.title",
|
||||
description: "timelineEvents.timelineEvent6.description",
|
||||
},
|
||||
])
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ const messages = {
|
|||
// 创建 i18n 实例
|
||||
const i18n = createI18n({
|
||||
legacy: false, // 使用 Composition API,必须设置为 false
|
||||
locale: 'zh', // 默认语言
|
||||
fallbackLocale: 'ja', // 回退语言
|
||||
locale: 'ja', // 默认语言
|
||||
fallbackLocale: 'zh', // 回退语言
|
||||
messages
|
||||
})
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import enUs from 'tdesign-vue-next/es/locale/en_US';
|
||||
export default {
|
||||
nav: {
|
||||
home: 'Home',
|
||||
|
@ -41,4 +42,10 @@ export default {
|
|||
timeline: {
|
||||
title: 'Company History',
|
||||
},
|
||||
ai:{
|
||||
title: 'AIチャット',
|
||||
initContent: 'こんにちは、何かお手伝いできることはありますか?',
|
||||
clearHistory: '履歴をクリア',
|
||||
},
|
||||
...enUs.chat
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// 导入所需的语言包
|
||||
import zhConfig from 'tdesign-vue-next/es/locale/zh_CN';
|
||||
import enConfig from 'tdesign-vue-next/es/locale/en_US';
|
||||
import jaConfig from 'tdesign-vue-next/es/locale/ja_JP';
|
||||
import { merge } from 'lodash-es';
|
||||
import { type GlobalConfigProvider } from 'tdesign-vue-next';
|
||||
import { ref, watch, computed } from 'vue';
|
||||
|
||||
|
||||
import { useLanguageStore } from '../store/language'
|
||||
|
||||
const store = useLanguageStore()
|
||||
|
||||
// 定义支持的语言类型
|
||||
// export type Language = 'zh-CN' | 'en-US' | 'ja-JP';
|
||||
export type Language = 'zh' | 'en' | 'ja';
|
||||
|
||||
// 明确类型为 Language
|
||||
const currentLanguage = computed<Language>(() => store.currentLanguage);
|
||||
|
||||
|
||||
|
||||
|
||||
// 基础自定义配置
|
||||
const customConfig: GlobalConfigProvider = {
|
||||
calendar: {},
|
||||
table: {},
|
||||
pagination: {},
|
||||
chat:{},
|
||||
// 可以添加更多自定义配置
|
||||
};
|
||||
|
||||
// 语言包映射(使用 any 类型来绕过严格的类型检查)
|
||||
const localeMap: Record<Language, any> = {
|
||||
'zh': zhConfig,
|
||||
'en': enConfig,
|
||||
'ja': jaConfig,
|
||||
};
|
||||
|
||||
// 响应式全局配置
|
||||
const globalConfig = ref<GlobalConfigProvider>(
|
||||
merge({}, localeMap[currentLanguage.value] || localeMap['zh'], customConfig) as GlobalConfigProvider
|
||||
);
|
||||
|
||||
// 当语言变化时更新全局配置
|
||||
watch(currentLanguage, (newLang) => {
|
||||
// 更新全局配置
|
||||
const lang = newLang as unknown as Language;
|
||||
globalConfig.value = merge({}, localeMap[lang] || localeMap['zh'], customConfig) as GlobalConfigProvider;
|
||||
console.log(globalConfig.value)
|
||||
});
|
||||
|
||||
|
||||
export {
|
||||
currentLanguage,
|
||||
globalConfig
|
||||
};
|
|
@ -1,26 +1,33 @@
|
|||
import jaJP from 'tdesign-vue-next/es/locale/ja_JP';
|
||||
|
||||
export default {
|
||||
nav: {
|
||||
name:'優閲スタジオ',
|
||||
home: 'ホーム',
|
||||
about: '会社概要',
|
||||
team: 'チーム紹介',
|
||||
projects: 'プロジェクト',
|
||||
about: '私たちについて',
|
||||
team: 'メンバー',
|
||||
projects: '開発実績',
|
||||
contact: 'お問い合わせ',
|
||||
},
|
||||
about: {
|
||||
title: '会社概要',
|
||||
mission: '私たちの使命',
|
||||
vision: '私たちのビジョン',
|
||||
title: '私たちについて',
|
||||
mission: 'チーム理念',
|
||||
vision: 'ビジョン',
|
||||
advantages: '私たちの強み',
|
||||
content: '私たちは、お客様に高品質なソリューションを提供することに専念するプロフェッショナルなソフトウェア開発会社です。',
|
||||
missionContent: 'ビジネスの成長とデジタル変革を推進する革新的な技術ソリューションを提供します。',
|
||||
visionContent: '技術的卓越性と顧客満足度で認められる、世界をリードするソフトウェア開発サービスプロバイダーになること。',
|
||||
advantage1: '技術的卓越性',
|
||||
advantage2: '顧客中心のアプローチ',
|
||||
advantage3: '革新',
|
||||
advantage4: '信頼性',
|
||||
content: '私たちは、ソフトウェア開発をはじめ、翻訳・BPOなどの業務を手がける専門サービスチームです',
|
||||
missionContent: '「お客様のビジネス成長とデジタル変革を実現するために、革新的な技術ソリューションを創造します」',
|
||||
visionContent: '「小さくとも優れた技術チームとして、技術の匠心でお客様の成功を支えます」',
|
||||
advantage1: '低コスト',
|
||||
advantage2: '迅速な判断',
|
||||
advantage3: '柔軟な対応',
|
||||
advantage4: '実績と信頼',
|
||||
advantage_explanation1:'大手企業のように固定費(高額な賃料・管理部門人件費・ブランド広告費など)の負担がありません。このコスト優位性を最大限に活かし、業界でも類を見ない低価格水準を実現しております。',
|
||||
advantage_explanation2:'小規模なチーム体制のため、複雑な稟議・承認プロセスが一切ありません。責任者や技術責任者などの中核メンバーが直接迅速に判断するため、お客様のご要望に素早く応えます。',
|
||||
advantage_explanation3:'小規模な個人のお客様から大規模な法人案件まで、あらゆるデータ処理に対応できるプロフェッショナルです。小ロットから大量発注、緊急のご依頼まで、状況に応じて臨機応変に対応いたします。',
|
||||
advantage_explanation4:'大手企業から官公庁、個人事業主の方まで、多岐にわたるお取引実績がございます。特に金融・保険・製造・商社・物流など多様な業界のお客様から厚い信頼をいただいております。',
|
||||
},
|
||||
team: {
|
||||
title: 'チーム紹介',
|
||||
title: 'メンバー',
|
||||
memberRole: '役職',
|
||||
memberBio: 'プロフィール',
|
||||
member1: {name: "陳 迪",role: "スタジオ代表",
|
||||
|
@ -50,7 +57,7 @@ export default {
|
|||
},
|
||||
|
||||
projects: {
|
||||
title: 'プロジェクト',
|
||||
title: '実績概要',
|
||||
viewDetails: '詳細を見る',
|
||||
project1: {
|
||||
title: "航空券管理システム",
|
||||
|
@ -66,13 +73,39 @@ export default {
|
|||
},
|
||||
project4: {
|
||||
title: "工業生産管理システム",
|
||||
description: "当システムは、マーケティング管理、生産計画、資材管理、工程管理、設備監視および品質管理などの各工程を統合し、生産プロセスの可視化と細やかな管理を実現します。リソースの最適な配分を図り、生産効率と製品品質を向上させ、運営コストを削減することで、企業のスマート化進展を推進します。",
|
||||
description: "当システムは、国のDX推進に応じて開発されたもので、マーケティング管理、生産計画、資材管理、工程管理、設備監視および品質管理などの各工程を統合し、生産プロセスの可視化と細やかな管理を実現します。リソースの最適な配分を図り、生産効率と製品品質を向上させ、運営コストを削減することで、企業のスマート化進展を推進します。",
|
||||
},
|
||||
project5: {
|
||||
title: "地図拡張システム",
|
||||
description: "本システムは、プロフェッショナル向けに設計された地図拡張ツールです。利用者は地図の閲覧や情報検索だけでなく、多彩な操作を地図上で実行可能。特定エリアやルートのマーキング機能に加え、2地点間の正確な距離測定や指定領域の面積計算も可能です。さらに強力な比較機能を搭載し、専門家の業務をサポートする高精度なデータを提供します。",
|
||||
},
|
||||
},
|
||||
timelineEvents: {
|
||||
timelineEvent1: {
|
||||
title: "スタジオ設立",
|
||||
description: "",
|
||||
},
|
||||
timelineEvent2: {
|
||||
title: "航空券管理システム",
|
||||
description: "初の日本案件(一部)受注",
|
||||
},
|
||||
timelineEvent3: {
|
||||
title: "地図拡張システム",
|
||||
description: "大型案件(一部)参画",
|
||||
},
|
||||
timelineEvent4: {
|
||||
title: "農作物管理・買取システム",
|
||||
description: "機能開発・保守",
|
||||
},
|
||||
timelineEvent5: {
|
||||
title: "勤怠管理システム",
|
||||
description: "機能開発・保守",
|
||||
},
|
||||
timelineEvent6: {
|
||||
title: "工業生産管理システム",
|
||||
description: "DX推進、独立開発(開発中)",
|
||||
},
|
||||
},
|
||||
contact: {
|
||||
title: 'お問い合わせ',
|
||||
name: 'お名前',
|
||||
|
@ -84,8 +117,15 @@ export default {
|
|||
error: 'メッセージの送信に失敗しました。もう一度お試しください。',
|
||||
},
|
||||
timeline: {
|
||||
title: '会社の沿革',
|
||||
title: 'タイムライン',
|
||||
},
|
||||
ai:{
|
||||
title: 'AIチャット',
|
||||
initContent: 'こんにちは、何かお手伝いできることはありますか?',
|
||||
clearHistory: '履歴をクリア',
|
||||
},
|
||||
|
||||
|
||||
chat: {
|
||||
...jaJP.chat
|
||||
}
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
|
||||
import znCh from 'tdesign-vue-next/es/locale/zh_CN';
|
||||
|
||||
export default {
|
||||
nav: {
|
||||
name:'优阅工作室',
|
||||
home: '首页',
|
||||
about: '关于我们',
|
||||
team: '团队介绍',
|
||||
|
@ -8,16 +12,20 @@ export default {
|
|||
},
|
||||
about: {
|
||||
title: '关于我们',
|
||||
mission: '我们的使命',
|
||||
mission: '团队理念',
|
||||
vision: '我们的愿景',
|
||||
advantages: '我们的优势',
|
||||
content: '我们是一家专业的软件开发团队,致力于为客户提供高质量的解决方案。',
|
||||
missionContent: '提供创新的技术解决方案,推动业务增长和数字化转型。',
|
||||
visionContent: '成为全球领先的软件开发服务提供商,以技术卓越和客户满意度著称。',
|
||||
advantage1: '技术卓越',
|
||||
advantage2: '以客户为中心',
|
||||
advantage3: '创新精神',
|
||||
advantage4: '可靠性',
|
||||
content: '我们是一家从事软件开发,以及翻译、BPO等业务的专业服务团队',
|
||||
missionContent: '用创新技术,助力客户实现业务增长与数字化转型。',
|
||||
visionContent: '做小而美的技术团队,以技术匠心成就客户成功。',
|
||||
advantage1: '低成本',
|
||||
advantage2: '快速决策',
|
||||
advantage3: '灵活应对多样化需求',
|
||||
advantage4: '丰富的业界经验',
|
||||
advantage_explanation1:'工作室无大型企业的 “固定成本包袱”(如高额房租、行政人员薪资、品牌营销费用等),可以充分发挥工作室运营的成本优势,实现行业的低价格水平。',
|
||||
advantage_explanation2:'得益于小规模团队架构,我们完全无需复杂的层层汇报与审批流程。由负责人及技术主管等核心成员直接迅速做出判断,及时响应您的各类需求。',
|
||||
advantage_explanation3:'由专业人士组成的团队,能够处理从个人小规模项目到企业大规模项目在内的各种数据处理业务。无论小批量、大批量还是紧急订单,我们都能灵活应对。',
|
||||
advantage_explanation4:'我们拥有广泛的合作经验,客户涵盖大型企业、政府机关及个人经营者。尤其深受金融,保险,生产加工、贸易公司、物流等多行业客户的信赖。',
|
||||
},
|
||||
team: {
|
||||
title: '我们的团队',
|
||||
|
@ -25,10 +33,9 @@ export default {
|
|||
memberBio: '简介',
|
||||
member1: {
|
||||
name: "陈迪",role: "工作室总负责人",
|
||||
bio:`拥有日本留学与职业背景:先后就读于秋田大学及名古屋大学大学院,毕业后入职日本大型商社,在国际化商业环境中积累了宝贵的实战经验,
|
||||
bio:`先后就读于秋田大学及名古屋大学大学院,毕业后入职日本大型商社,在国际化商业环境中积累了宝贵的实战经验,
|
||||
归国后,历任 BPO 项目管理岗位,主导过对日业务流程外包的全流程统筹,凭借对日本客户需求的精准把握,确保项目交付质量与效率双重达标;
|
||||
后转型投身软件 开发领域,先后担任 SE、BSE及 PM,深度参与从需求分析、系统设计到开发落地的全生命周期管理,在 Web 开发、系统搭建等
|
||||
方面积累了丰富的技术实操经验。`},
|
||||
后转型投身软件 开发领域,先后担任 SE、BSE及 PM,深度参与从需求分析、系统设计到开发落地的全生命周期管理,在 Web 开发、系统搭建等方面积累了丰富的技术实操经验。`},
|
||||
member2: {name: "梁伟",role: "技术总负责人",
|
||||
bio:`拥有 10 年以上 web 开发经验,长期专注对日项目。精通前端 Vue、React、JavaScript 及 HTML5,后端 Java、Python 及 Spring Boot、
|
||||
Django 等框架,深谙日本技术标准与业务逻辑。主导过电商、企业管理系统等多领域大型项目,从需求分析到架构设计、开发落地全流程把控,
|
||||
|
@ -58,18 +65,44 @@ export default {
|
|||
description: "本系统连接农户与采购商,提供农产品管理,天气预报,病虫害管理,信息发布、在线洽谈、订单管理、质量追溯与电子结算等服务,打破信息壁垒,优化交易流程,促进农产品高效流通,助力农业增效、农户增收。",
|
||||
},
|
||||
project3: {
|
||||
title: "出勤管理系统",
|
||||
title: "考勤管理系统",
|
||||
description: "系统通过考勤机、移动端等多方式记录员工上下班时间、请假、加班等信息,自动统计分析出勤数据,生成考勤报表,简化人事管理流程,确保考勤准确公正,为薪资计算和绩效考核提供可靠依据。",
|
||||
},
|
||||
project4: {
|
||||
title: "工业生产管理系统",
|
||||
description: "本系统是一款专业的生产加工行业的数字化系统,整合营销管理,生产计划、物料管理、工艺流程、设备监控与质量控制等环节,实现生产过程的可视化、精细化管理,优化资源配置,提高生产效率与产品质量,降低运营成本,推动企业智能化升级。",
|
||||
description: "本系统是一款为响应国家数字化转型战略而打造的工业数字化系统,整合营销管理,生产计划、物料管理、工艺流程、设备监控与质量控制等环节,实现生产过程的可视化、精细化管理,优化资源配置,提高生产效率与产品质量,降低运营成本,推动企业智能化升级。",
|
||||
},
|
||||
project5: {
|
||||
title: "地图扩展系统",
|
||||
description: "本系统是为专业人士打造的地图扩展工具。操作者不仅能进行地图阅览与信息查询,还可在地图上开展丰富操作;支持对特定区域、线路等进行标记,能够精准测量地图上任意两点间的距离以及特定区域的面积;同时具备强大的对比功能,为专业人士提供精确数据支撑。",
|
||||
},
|
||||
},
|
||||
timelineEvents: {
|
||||
timelineEvent1: {
|
||||
title: "工作室成立",
|
||||
description: "",
|
||||
},
|
||||
timelineEvent2: {
|
||||
title: "机票管理系统",
|
||||
description: "首个对日项目(一部分)参与",
|
||||
},
|
||||
timelineEvent3: {
|
||||
title: "地图扩展系统",
|
||||
description: "大型项目(一部分)参与",
|
||||
},
|
||||
timelineEvent4: {
|
||||
title: "农作物交易系统",
|
||||
description: "功能开发和保守",
|
||||
},
|
||||
timelineEvent5: {
|
||||
title: "考勤管理系统",
|
||||
description: "功能开发和保守",
|
||||
},
|
||||
timelineEvent6: {
|
||||
title: "工业生产管理系统",
|
||||
description: "数字化转型项目,独立开发(进行中)",
|
||||
},
|
||||
},
|
||||
contact: {
|
||||
title: '联系我们',
|
||||
name: '您的姓名',
|
||||
|
@ -81,6 +114,15 @@ export default {
|
|||
error: '发送留言失败,请重试。',
|
||||
},
|
||||
timeline: {
|
||||
title: '公司历程',
|
||||
title: '项目经历',
|
||||
},
|
||||
ai:{
|
||||
title: 'AI助手',
|
||||
initContent: '我是你的ai助手,有什么可以帮你的。',
|
||||
clearHistory: "清空历史记录11",
|
||||
|
||||
},
|
||||
chat: {
|
||||
...znCh.chat
|
||||
}
|
||||
}
|
|
@ -3,8 +3,6 @@ import './style.css'
|
|||
import App from './App.vue'
|
||||
import router from './router/index.ts' // 确保路径正确
|
||||
|
||||
|
||||
|
||||
// 引入Pinia
|
||||
import { createPinia } from 'pinia'
|
||||
// 引入TDesign UI组件库
|
||||
|
@ -12,7 +10,6 @@ import TDesign from 'tdesign-vue-next'
|
|||
import TDesignChat from '@tdesign-vue-next/chat'; // 引入chat组件
|
||||
import 'tdesign-vue-next/es/style/index.css'
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
// 必须先 use(router),再挂载
|
||||
app.use(router)
|
||||
|
@ -20,10 +17,16 @@ app.use(router)
|
|||
// 使用Pinia
|
||||
app.use(createPinia())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 使用TDesign
|
||||
app.use(TDesign)
|
||||
app.use(TDesignChat)
|
||||
|
||||
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
// 临时打印路由列表,确认配置是否生效
|
||||
|
|
|
@ -2,7 +2,6 @@ import { defineStore } from 'pinia'
|
|||
import zh from '../locales/zh'
|
||||
import ja from '../locales/ja'
|
||||
|
||||
|
||||
// 获取浏览器语言
|
||||
function getBrowserLanguage() {
|
||||
// 获取浏览器主语言(如从 "zh-CN" 中提取 "zh-CN" 或从 "zh-TW" 中提取 "zh-TW")
|
||||
|
@ -11,7 +10,7 @@ function getBrowserLanguage() {
|
|||
return 'zh';
|
||||
}
|
||||
// 默认返回英语
|
||||
return 'en';
|
||||
return 'ja';
|
||||
}
|
||||
const lang = getBrowserLanguage()
|
||||
export const useLanguageStore = defineStore('language', {
|
||||
|
|
|
@ -57,7 +57,7 @@ const t = inject<(key: string) => string>('t') || ((key) => key)
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url('/src/assets/images/hero-bg.jpg'); /* 绝对路径,基于项目根目录 */
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url('/images/hero-bg.jpg'); /* 绝对路径,基于项目根目录 */
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
|
|
|
@ -3,5 +3,12 @@
|
|||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
],
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "Node",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["node_modules/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue