企迹(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,124 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const auth = useAuthStore()
|
||||
const loading = ref(false)
|
||||
|
||||
// Check for Casdoor callback
|
||||
onMounted(async () => {
|
||||
const code = route.query.code as string
|
||||
const state = route.query.state as string
|
||||
|
||||
if (code) {
|
||||
loading.value = true
|
||||
try {
|
||||
await auth.casdoorLogin(code, state)
|
||||
ElMessage.success('登录成功')
|
||||
router.push(auth.isManager ? '/m' : '/')
|
||||
} catch (e: any) {
|
||||
ElMessage.error('登录失败: ' + (e.response?.data?.detail || e.message))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check for wecom code
|
||||
const wecomCode = route.query.wecom_code as string
|
||||
if (wecomCode) {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await auth.wecomLogin(wecomCode)
|
||||
if (res.data.need_bind) {
|
||||
// Redirect to Casdoor for binding
|
||||
window.location.href = res.data.casdoor_url
|
||||
return
|
||||
}
|
||||
auth.saveLogin({
|
||||
access_token: res.data.access_token,
|
||||
user_id: res.data.user_id,
|
||||
name: res.data.name,
|
||||
role: res.data.role,
|
||||
})
|
||||
ElMessage.success('登录成功')
|
||||
router.push(auth.isManager ? '/m' : '/')
|
||||
} catch (e: any) {
|
||||
ElMessage.error('企微登录失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function goCasdoorLogin() {
|
||||
// Redirect to Casdoor authorization page
|
||||
const endpoint = import.meta.env.VITE_CASDOOR_ENDPOINT
|
||||
const clientId = import.meta.env.VITE_CASDOOR_CLIENT_ID
|
||||
const redirectUri = encodeURIComponent(window.location.origin + '/login')
|
||||
const casdoorUrl = `${endpoint}/login/oauth/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=openid+profile&state=login`
|
||||
window.location.href = casdoorUrl
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<div class="login-card">
|
||||
<div class="brand">
|
||||
<h1>企迹</h1>
|
||||
<p>政企周报管理系统</p>
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="loading"
|
||||
@click="goCasdoorLogin"
|
||||
style="width: 100%"
|
||||
>
|
||||
登录 / 注册
|
||||
</el-button>
|
||||
<p class="hint">使用 Casdoor 账号登录</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.login-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background: linear-gradient(135deg, #1a73e8 0%, #4080ff 50%, #69a0ff 100%);
|
||||
}
|
||||
.login-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 48px 40px;
|
||||
width: 360px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.2);
|
||||
}
|
||||
.brand {
|
||||
text-align: center;
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
.brand h1 {
|
||||
margin: 0;
|
||||
font-size: 32px;
|
||||
color: #1a73e8;
|
||||
}
|
||||
.brand p {
|
||||
margin: 8px 0 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
.hint {
|
||||
text-align: center;
|
||||
color: #c0c4cc;
|
||||
font-size: 12px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user