refactor: split project into web and server directories

- Move frontend to web/ directory
- Add server/ directory for backend
- Restructure project for前后端分离架构

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 11:00:38 +08:00
parent 9a7b0794a1
commit 9785fb527b
85 changed files with 10474 additions and 10047 deletions

209
web/src/scripts/App.js Normal file
View File

@@ -0,0 +1,209 @@
import '../assets/styles/global.css'
import SidebarRail from '../components/layout/SidebarRail.vue'
import TopBar from '../components/layout/TopBar.vue'
import FilterBar from '../components/layout/FilterBar.vue'
import ToastNotification from '../components/shared/ToastNotification.vue'
import LoginView from '../views/LoginView.vue'
import OverviewView from '../views/OverviewView.vue'
import PersonalWorkbenchView from '../views/PersonalWorkbenchView.vue'
import ChatView from '../views/ChatView.vue'
import TravelReimbursementCreateView from '../views/TravelReimbursementCreateView.vue'
import TravelRequestDetailView from '../views/TravelRequestDetailView.vue'
import RequestsView from '../views/RequestsView.vue'
import ApprovalCenterView from '../views/ApprovalCenterView.vue'
import PoliciesView from '../views/PoliciesView.vue'
import AuditView from '../views/AuditView.vue'
import EmployeeManagementView from '../views/EmployeeManagementView.vue'
import { ref, computed } from 'vue'
import { useNavigation, navItems } from '../composables/useNavigation.js'
import { useRequests } from '../composables/useRequests.js'
import { useChat } from '../composables/useChat.js'
import { useToast } from '../composables/useToast.js'
import { documents } from '../data/requests.js'
export default {
name: 'App',
components: {
SidebarRail,
TopBar,
FilterBar,
ToastNotification,
LoginView,
OverviewView,
PersonalWorkbenchView,
ChatView,
TravelReimbursementCreateView,
TravelRequestDetailView,
RequestsView,
ApprovalCenterView,
PoliciesView,
AuditView,
EmployeeManagementView
},
setup() {
const loggedIn = ref(false)
const travelCreateMode = ref(false)
const detailMode = ref(false)
const selectedTravelRequest = ref(null)
const smartEntryOpen = ref(false)
const smartEntryContext = ref({ prompt: '', source: 'requests', request: null })
const smartEntrySessionId = ref(0)
function handleLogin(credentials) {
if (credentials.username && credentials.password) {
loggedIn.value = true
}
}
function handleRecoverPassword() {
toast('请联系系统管理员重置密码。')
}
function handleSsoLogin() {
toast('SSO 登录通道建设中。')
}
const { activeView, currentView, setView } = useNavigation()
const { requests, search, filters, ranges, activeRange, filteredRequests, approveRequest, rejectRequest } = useRequests()
const { messages, draft, uploadedFiles, messageList, activeCase, prompts, sendMessage, handleUpload, openChat, openNewChat } = useChat(activeView)
const { toastText, toast } = useToast()
const docSearch = ref('')
const customRange = ref({ start: '2024-07-06', end: '2024-07-12' })
const travelPrompts = ['帮我提交出差申请', '预订机票', '预订酒店', '预订火车票', '查询差旅政策']
const topBarView = computed(() => {
if (detailMode.value) {
return {
title: '差旅报销详情',
desc: '查看报销单据详情、票据识别与审批进度'
}
}
return currentView.value
})
const filteredDocuments = computed(() => {
const key = docSearch.value.trim().toLowerCase()
return documents.filter((doc) => {
const matchesSearch = !key || `${doc.id}${doc.applicant}${doc.destination}${doc.type}`.toLowerCase().includes(key)
return matchesSearch
})
})
function handleApprove(request) {
const msg = approveRequest(request)
toast(msg)
}
function handleReject(request) {
const msg = rejectRequest(request)
toast(msg)
}
function handleNavigate(view) {
travelCreateMode.value = false
detailMode.value = false
selectedTravelRequest.value = null
smartEntryOpen.value = false
setView(view)
}
function handleOpenChat(request) {
travelCreateMode.value = false
openChat(request)
}
function openTravelCreate() {
smartEntryOpen.value = true
travelCreateMode.value = false
detailMode.value = false
selectedTravelRequest.value = null
smartEntryContext.value = { prompt: '', source: 'topbar', request: null }
smartEntrySessionId.value += 1
}
function openSmartEntry(payload = {}) {
smartEntryOpen.value = true
travelCreateMode.value = false
if (payload.source !== 'detail') {
detailMode.value = false
selectedTravelRequest.value = null
}
smartEntryContext.value = {
prompt: payload.prompt ?? '',
source: payload.source ?? 'workbench',
request: payload.request ?? selectedTravelRequest.value
}
smartEntrySessionId.value += 1
}
function closeSmartEntry() {
smartEntryOpen.value = false
}
function openRequestDetail(request) {
selectedTravelRequest.value = request
detailMode.value = true
activeView.value = 'requests'
}
function closeRequestDetail() {
detailMode.value = false
selectedTravelRequest.value = null
}
return {
loggedIn,
travelCreateMode,
detailMode,
selectedTravelRequest,
smartEntryOpen,
smartEntryContext,
smartEntrySessionId,
handleLogin,
handleRecoverPassword,
handleSsoLogin,
activeView,
currentView,
setView,
requests,
search,
filters,
ranges,
activeRange,
filteredRequests,
approveRequest,
rejectRequest,
messages,
draft,
uploadedFiles,
messageList,
activeCase,
prompts,
sendMessage,
handleUpload,
openChat,
openNewChat,
toastText,
toast,
docSearch,
customRange,
travelPrompts,
topBarView,
filteredDocuments,
handleApprove,
handleReject,
handleNavigate,
handleOpenChat,
openTravelCreate,
openSmartEntry,
closeSmartEntry,
openRequestDetail,
closeRequestDetail,
navItems
}
}
}