前端服务共享

This commit is contained in:
2025-12-11 14:21:36 +08:00
parent fa3dbe0496
commit 5ee9770747
46 changed files with 3732 additions and 1782 deletions

View File

@@ -1,156 +0,0 @@
<script setup lang="ts">
/**
* Import Maps 使用示例
*
* 通过 HTML 中的 <script type="importmap"> 配置,
* 可以直接从 HTTP URL 导入共享组件
*/
// ✅ 直接导入!浏览器会自动从 http://localhost/shared/components.js 加载
import { UlTable } from '@shared/components'
import { http } from '@shared/utils'
import { authApi } from '@shared/api'
import { useTable } from '@shared/composables'
// 类型定义
interface User {
id: string
username: string
email: string
createTime: string
}
// 使用共享的 useTable 组合式函数
const {
loading,
tableData,
pagination,
handlePageChange,
handleSizeChange,
refresh
} = useTable<User>({
fetchData: async (params) => {
// 使用共享的 API 函数
return await authApi.getUserList(params)
}
})
// 表格列配置
const columns = [
{ prop: 'username', label: '用户名', minWidth: 150 },
{ prop: 'email', label: '邮箱', minWidth: 200 },
{ prop: 'createTime', label: '创建时间', width: 180 }
]
// 测试 HTTP 请求
const testRequest = async () => {
try {
// 使用共享的 http 工具
const result = await http.get('/api/test')
console.log('请求结果:', result)
} catch (error) {
console.error('请求失败:', error)
}
}
</script>
<template>
<div class="import-maps-example">
<div class="header">
<h1>Import Maps 示例</h1>
<p class="description">
共享组件从 <code>http://localhost/shared/</code> 动态加载<br>
无需打包浏览器原生 ES Module 支持<br>
真正的运行时共享所有应用使用同一份代码
</p>
<el-button type="primary" @click="testRequest">
测试 HTTP 请求
</el-button>
</div>
<!-- 使用从 HTTP 加载的共享组件 -->
<UlTable
:data="tableData"
:columns="columns"
:loading="loading"
:pagination="pagination"
@page-change="handlePageChange"
@size-change="handleSizeChange"
/>
<div class="info">
<h3>📦 当前加载的模块</h3>
<ul>
<li><code>@shared/components</code> http://localhost/shared/components.js</li>
<li><code>@shared/utils</code> http://localhost/shared/utils.js</li>
<li><code>@shared/api</code> http://localhost/shared/api.js</li>
<li><code>@shared/composables</code> http://localhost/shared/composables.js</li>
</ul>
<h3>🔍 如何查看</h3>
<ol>
<li>打开浏览器开发者工具 (F12)</li>
<li>切换到 Network 标签页</li>
<li>筛选 JS 类型</li>
<li>刷新页面可以看到从 /shared/ 加载的模块</li>
</ol>
<h3> 优势</h3>
<ul>
<li> 真正的代码共享所有应用共用一份</li>
<li> 支持热更新修改共享组件所有应用自动更新</li>
<li> 减小构建体积共享代码不打包到业务应用</li>
<li> 浏览器缓存共享模块只下载一次</li>
</ul>
</div>
</div>
</template>
<style scoped>
.import-maps-example {
padding: 24px;
}
.header {
margin-bottom: 24px;
}
.description {
color: #666;
line-height: 1.8;
margin: 16px 0;
}
.description code {
background: #f0f0f0;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
}
.info {
margin-top: 32px;
padding: 20px;
background: #f5f7fa;
border-radius: 8px;
}
.info h3 {
margin-top: 20px;
margin-bottom: 12px;
color: #409eff;
}
.info ul, .info ol {
line-height: 2;
color: #606266;
}
.info code {
background: #fff;
padding: 2px 8px;
border-radius: 3px;
font-family: 'Courier New', monospace;
color: #e6a23c;
}
</style>

View File

@@ -1,24 +1,256 @@
<template>
<div>
<div class="login-container">
<div class="login-card">
<div class="login-header">
<h1 class="login-title">泰豪电源 AI 数智化平台</h1>
<p class="login-subtitle">Urban Lifeline Platform</p>
</div>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
class="login-form"
@submit.prevent="handleLogin"
>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
placeholder="请输入用户名"
size="large"
prefix-icon="User"
clearable
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
size="large"
prefix-icon="Lock"
show-password
@keyup.enter="handleLogin"
/>
</el-form-item>
<el-form-item>
<el-checkbox v-model="loginForm.rememberMe">
记住我
</el-checkbox>
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="large"
:loading="loading"
class="login-button"
@click="handleLogin"
>
{{ loading ? '登录中...' : '登录' }}
</el-button>
</el-form-item>
</el-form>
<div class="login-footer">
<span class="footer-link">忘记密码</span>
<span class="footer-divider">|</span>
<span class="footer-link">注册账号</span>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from "vue"
import type { LoginParam} from "@shared/types/auth"
import { authAPI } from "@shared/api/auth/auth"
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import type { LoginParam } from 'shared/types'
import { authAPI } from 'shared/authAPI'
import { getAesInstance } from 'shared/utils'
import { TokenManager } from 'shared/api'
// 路由
const router = useRouter()
const loginParam = reactive<LoginParam>({
loginType: "password"
// 表单引用
const loginFormRef = ref<FormInstance>()
// 加载状态
const loading = ref(false)
// 登录表单
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
async function login(){
// const result = await authAPI.login(loginParam)
// 表单验证规则
const loginRules: FormRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度为3-20个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码至少6个字符', trigger: 'blur' }
]
}
/**
* 处理登录
*/
async function handleLogin() {
if (!loginFormRef.value) return
try {
// 1. 表单验证
await loginFormRef.value.validate()
// 2. 显示加载状态
loading.value = true
// 3. 获取 AES 加密实例
const aes = getAesInstance()
// 4. 加密密码
const encryptedPassword = await aes.encryptPassword(loginForm.password)
// 5. 构建登录参数
const loginParam: LoginParam = {
username: loginForm.username,
password: encryptedPassword, // 使用加密后的密码
loginType: 'password'
}
// 6. 调用登录接口
const response = await authAPI.login(loginParam)
// 7. 检查登录结果
if (response.data.success && response.data.data) {
const loginData = response.data.data
// 8. 保存 Token
if (loginData.token) {
TokenManager.setToken(loginData.token, loginForm.rememberMe)
}
// 9. 显示成功消息
ElMessage.success('登录成功!')
// 10. 跳转到首页
router.push('/home')
} else {
// 登录失败
ElMessage.error(response.data.message || '登录失败,请检查用户名和密码')
}
} catch (error: any) {
console.error('登录失败:', error)
// 显示错误消息
if (error.response) {
// HTTP 错误
ElMessage.error(error.response.data?.message || '登录失败,请稍后重试')
} else if (error.message) {
// 其他错误
ElMessage.error(error.message)
} else {
ElMessage.error('登录失败,请检查网络连接')
}
} finally {
// 隐藏加载状态
loading.value = false
}
}
</script>
<style lang="scss" scoped>
.login-container {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
}
.login-card {
width: 100%;
max-width: 420px;
padding: 40px;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
.login-header {
text-align: center;
margin-bottom: 40px;
}
.login-title {
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.login-subtitle {
font-size: 14px;
color: #999;
}
.login-form {
:deep(.el-form-item) {
margin-bottom: 24px;
}
:deep(.el-input__wrapper) {
padding: 12px 16px;
}
}
.login-button {
width: 100%;
height: 44px;
font-size: 16px;
font-weight: 500;
border-radius: 8px;
}
.login-footer {
display: flex;
align-items: center;
justify-content: center;
margin-top: 24px;
font-size: 14px;
color: #999;
}
.footer-link {
cursor: pointer;
transition: color 0.3s;
&:hover {
color: var(--el-color-primary);
}
}
.footer-divider {
margin: 0 12px;
}
// 响应式设计
@media (max-width: 480px) {
.login-card {
padding: 30px 20px;
}
.login-title {
font-size: 20px;
}
}
</style>