企迹(qiji) 政企周报管理系统 — v0.1
后端: FastAPI + SQLAlchemy 2.0 (async) + Alembic + MinIO + Casdoor + 企微 前端: Vue 3 + Vite + TypeScript + Element Plus + Pinia 功能清单: - 8 张数据表自动建表 / Casdoor OIDC 登录 / 企微静默登录 - 双布局: 移动端(填报) + PC端(汇总管理) - 拜访记录 CRUD + MinIO 照片直传 + 缩略图预览 + 同访人草稿 - 今日纪要 (6 分类) / 工作计划 / 小微商机 / 要客拜访 CRUD - 客户档案: 备注/收支费用/联系人/归属分配/批量转移 - 客户导入导出 + 模板下载 + 搜索/分页/筛选 - 仪表盘: 四卡统计 + 填报进度 (拜访+纪要双维度) - 周报详情: 五 Tab + 按人/客户筛选 + 时间轴 - 用户管理 / 客户经理 PC 端工作台 - 企微: 催办/公告/定时提醒 / 时区修正 - Docker 部署配置 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import api from './index'
|
||||
|
||||
export const authApi = {
|
||||
casdoorLogin(code: string, state: string) {
|
||||
return api.post('/auth/casdoor-login', { code, state })
|
||||
},
|
||||
wecomLogin(code: string) {
|
||||
return api.post('/auth/wecom-login', { code })
|
||||
},
|
||||
bindWecom(casdoorCode: string, wecomUserid: string) {
|
||||
return api.post('/auth/bind-wecom', { casdoor_code: casdoorCode, wecom_userid: wecomUserid })
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import api from './index'
|
||||
|
||||
export const customersApi = {
|
||||
list(params?: any) {
|
||||
return api.get('/customers/', { params })
|
||||
},
|
||||
get(id: string) {
|
||||
return api.get(`/customers/${id}`)
|
||||
},
|
||||
create(data: any) {
|
||||
return api.post('/customers/', data)
|
||||
},
|
||||
update(id: string, data: any) {
|
||||
return api.put(`/customers/${id}`, data)
|
||||
},
|
||||
delete(id: string) {
|
||||
return api.delete(`/customers/${id}`)
|
||||
},
|
||||
checkDuplicate(name: string) {
|
||||
return api.get(`/customers/check-duplicate/${encodeURIComponent(name)}`)
|
||||
},
|
||||
// Assignments
|
||||
listAssignments(customerId: string) {
|
||||
return api.get(`/customers/${customerId}/assignments`)
|
||||
},
|
||||
assignManager(customerId: string, data: any) {
|
||||
return api.post(`/customers/${customerId}/assignments`, data)
|
||||
},
|
||||
batchAssign(data: any) {
|
||||
return api.post('/customers/batch-assign', data)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import api from './index'
|
||||
|
||||
export const dashboardApi = {
|
||||
getStats() {
|
||||
return api.get('/dashboard/stats')
|
||||
},
|
||||
getProgress() {
|
||||
return api.get('/dashboard/progress')
|
||||
},
|
||||
getWeeklyReport(params?: any) {
|
||||
return api.get('/dashboard/weekly-report', { params })
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import axios from 'axios'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import router from '@/router'
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: '/api',
|
||||
timeout: 30000,
|
||||
})
|
||||
|
||||
api.interceptors.request.use((config) => {
|
||||
const auth = useAuthStore()
|
||||
if (auth.token) {
|
||||
config.headers.Authorization = `Bearer ${auth.token}`
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
const auth = useAuthStore()
|
||||
auth.logout()
|
||||
router.push('/login')
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default api
|
||||
@@ -0,0 +1,16 @@
|
||||
import api from './index'
|
||||
|
||||
export const keyVisitsApi = {
|
||||
list(params?: any) {
|
||||
return api.get('/key-visits/', { params })
|
||||
},
|
||||
create(data: any) {
|
||||
return api.post('/key-visits/', data)
|
||||
},
|
||||
update(id: string, data: any) {
|
||||
return api.put(`/key-visits/${id}`, data)
|
||||
},
|
||||
delete(id: string) {
|
||||
return api.delete(`/key-visits/${id}`)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import api from './index'
|
||||
|
||||
export const miniBusinessApi = {
|
||||
list(params?: any) {
|
||||
return api.get('/mini-business/', { params })
|
||||
},
|
||||
create(data: any) {
|
||||
return api.post('/mini-business/', data)
|
||||
},
|
||||
update(id: string, data: any) {
|
||||
return api.put(`/mini-business/${id}`, data)
|
||||
},
|
||||
delete(id: string) {
|
||||
return api.delete(`/mini-business/${id}`)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import api from './index'
|
||||
import axios from 'axios'
|
||||
|
||||
export const uploadApi = {
|
||||
async getPresignedUrl(filename: string, contentType: string = 'image/jpeg') {
|
||||
return api.post('/upload/presigned-url', null, {
|
||||
params: { filename, content_type: contentType },
|
||||
})
|
||||
},
|
||||
getDownloadUrl(objectKey: string) {
|
||||
return api.get('/upload/download-url', { params: { object_key: objectKey } })
|
||||
},
|
||||
// Direct upload to MinIO
|
||||
async uploadFile(uploadUrl: string, file: File) {
|
||||
return axios.put(uploadUrl, file, {
|
||||
headers: { 'Content-Type': file.type },
|
||||
timeout: 60000,
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import api from './index'
|
||||
|
||||
export const visitsApi = {
|
||||
list(params?: any) {
|
||||
return api.get('/visits/', { params })
|
||||
},
|
||||
getToday() {
|
||||
return api.get('/visits/today')
|
||||
},
|
||||
get(id: string) {
|
||||
return api.get(`/visits/${id}`)
|
||||
},
|
||||
create(data: any) {
|
||||
return api.post('/visits/', data)
|
||||
},
|
||||
update(id: string, data: any) {
|
||||
return api.put(`/visits/${id}`, data)
|
||||
},
|
||||
delete(id: string) {
|
||||
return api.delete(`/visits/${id}`)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import api from './index'
|
||||
|
||||
export const workPlansApi = {
|
||||
list(params?: any) {
|
||||
return api.get('/work-plans/', { params })
|
||||
},
|
||||
create(data: any) {
|
||||
return api.post('/work-plans/', data)
|
||||
},
|
||||
update(id: string, data: any) {
|
||||
return api.put(`/work-plans/${id}`, data)
|
||||
},
|
||||
delete(id: string) {
|
||||
return api.delete(`/work-plans/${id}`)
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user