修复
This commit is contained in:
@@ -30,8 +30,8 @@ api.interceptors.response.use(
|
||||
response => {
|
||||
const res = response.data
|
||||
|
||||
// 如果响应码不是200,则判断为错误
|
||||
if (res.code !== 200) {
|
||||
// 如果响应中的success不是true,则判断为错误
|
||||
if (res.success !== true) {
|
||||
showFailToast(res.message || '请求失败')
|
||||
|
||||
// 401: 未登录或token过期
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
--text-secondary: #a0b3d0;
|
||||
--glass-bg: rgba(255, 255, 255, 0.1);
|
||||
--glass-border: rgba(255, 255, 255, 0.2);
|
||||
--button-gradient: linear-gradient(135deg, #00D4FF, #0099CC);
|
||||
--button-hover-gradient: linear-gradient(135deg, #0099CC, #006699);
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -17,10 +19,15 @@
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
||||
font-family: 'SF Pro Display', 'PingFang SC', 'Helvetica Neue', 'Microsoft YaHei', Arial, sans-serif;
|
||||
background-color: var(--primary-color);
|
||||
color: var(--text-primary);
|
||||
overflow-x: hidden;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
letter-spacing: 0.02em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* 深空背景 */
|
||||
@@ -187,6 +194,80 @@ body {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* 字体大小 */
|
||||
.text-xs { font-size: 12px; }
|
||||
.text-sm { font-size: 14px; }
|
||||
.text-base { font-size: 16px; }
|
||||
.text-lg { font-size: 18px; }
|
||||
.text-xl { font-size: 20px; }
|
||||
.text-2xl { font-size: 24px; }
|
||||
.text-3xl { font-size: 30px; }
|
||||
.text-4xl { font-size: 36px; }
|
||||
|
||||
/* 字体粗细 */
|
||||
.font-light { font-weight: 300; }
|
||||
.font-normal { font-weight: 400; }
|
||||
.font-medium { font-weight: 500; }
|
||||
.font-semibold { font-weight: 600; }
|
||||
.font-bold { font-weight: 700; }
|
||||
|
||||
/* 标题样式 */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
margin-bottom: 0.5em;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
h1 { font-size: 2.5rem; }
|
||||
h2 { font-size: 2rem; }
|
||||
h3 { font-size: 1.75rem; }
|
||||
h4 { font-size: 1.5rem; }
|
||||
h5 { font-size: 1.25rem; }
|
||||
h6 { font-size: 1.125rem; }
|
||||
|
||||
/* 段落样式 */
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 文本阴影 */
|
||||
.text-shadow {
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.text-shadow-lg {
|
||||
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 文本发光效果 */
|
||||
.text-glow {
|
||||
text-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
|
||||
}
|
||||
|
||||
.text-glow-lg {
|
||||
text-shadow: 0 0 20px rgba(0, 212, 255, 0.7);
|
||||
}
|
||||
|
||||
/* 特殊文本效果 */
|
||||
.gradient-text {
|
||||
background: linear-gradient(135deg, var(--accent-color), #0099CC);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 代码文本 */
|
||||
code {
|
||||
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* 间距工具类 */
|
||||
.mt-10 { margin-top: 10px; }
|
||||
.mt-20 { margin-top: 20px; }
|
||||
@@ -352,6 +433,9 @@ input, textarea, .van-field__control {
|
||||
font-size: 16px !important;
|
||||
transition: all 0.3s ease !important;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1) !important;
|
||||
font-family: inherit !important;
|
||||
letter-spacing: 0.01em !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, .van-field__control:focus {
|
||||
@@ -375,6 +459,9 @@ input::placeholder, textarea::placeholder, .van-field__control::placeholder {
|
||||
.van-field__label {
|
||||
color: var(--text-secondary) !important;
|
||||
font-weight: 500 !important;
|
||||
font-size: 14px !important;
|
||||
letter-spacing: 0.01em !important;
|
||||
line-height: 1.4 !important;
|
||||
}
|
||||
|
||||
.van-field--error .van-field__control {
|
||||
@@ -406,9 +493,11 @@ input::placeholder, textarea::placeholder, .van-field__control::placeholder {
|
||||
|
||||
.header h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
letter-spacing: -0.01em;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header .van-icon {
|
||||
@@ -447,4 +536,160 @@ input::placeholder, textarea::placeholder, .van-field__control::placeholder {
|
||||
.custom-tabbar .van-tabbar-item__text {
|
||||
font-size: 12px !important;
|
||||
font-weight: 500 !important;
|
||||
letter-spacing: 0.01em !important;
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
|
||||
/* 全局按钮样式优化 */
|
||||
.van-button {
|
||||
border-radius: 12px !important;
|
||||
font-weight: 600 !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2) !important;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) !important;
|
||||
border: none !important;
|
||||
position: relative !important;
|
||||
overflow: hidden !important;
|
||||
font-family: inherit !important;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.van-button::before {
|
||||
content: '' !important;
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
left: -100% !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent) !important;
|
||||
transition: left 0.5s !important;
|
||||
}
|
||||
|
||||
.van-button:hover::before {
|
||||
left: 100% !important;
|
||||
}
|
||||
|
||||
.van-button--primary {
|
||||
background: var(--button-gradient) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.van-button--primary:hover {
|
||||
background: var(--button-hover-gradient) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
box-shadow: 0 6px 20px rgba(0, 212, 255, 0.4) !important;
|
||||
}
|
||||
|
||||
.van-button--success {
|
||||
background: linear-gradient(135deg, #28a745, #20c997) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.van-button--success:hover {
|
||||
background: linear-gradient(135deg, #218838, #1ea085) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
box-shadow: 0 6px 20px rgba(40, 167, 69, 0.4) !important;
|
||||
}
|
||||
|
||||
.van-button--danger {
|
||||
background: linear-gradient(135deg, #dc3545, #f86c6b) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.van-button--danger:hover {
|
||||
background: linear-gradient(135deg, #c82333, #f63c3c) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
box-shadow: 0 6px 20px rgba(220, 53, 69, 0.4) !important;
|
||||
}
|
||||
|
||||
.van-button--warning {
|
||||
background: linear-gradient(135deg, #ffc107, #ff922b) !important;
|
||||
color: #212529 !important;
|
||||
}
|
||||
|
||||
.van-button--warning:hover {
|
||||
background: linear-gradient(135deg, #e0a800, #ff8c00) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
box-shadow: 0 6px 20px rgba(255, 193, 7, 0.4) !important;
|
||||
}
|
||||
|
||||
.van-button--default {
|
||||
background: rgba(255, 255, 255, 0.1) !important;
|
||||
color: var(--text-primary) !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2) !important;
|
||||
backdrop-filter: blur(10px) !important;
|
||||
}
|
||||
|
||||
.van-button--default:hover {
|
||||
background: rgba(255, 255, 255, 0.15) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
box-shadow: 0 6px 20px rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
.van-button--plain {
|
||||
background: transparent !important;
|
||||
border: 1px solid var(--accent-color) !important;
|
||||
color: var(--accent-color) !important;
|
||||
}
|
||||
|
||||
.van-button--plain:hover {
|
||||
background: rgba(0, 212, 255, 0.1) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
box-shadow: 0 6px 20px rgba(0, 212, 255, 0.2) !important;
|
||||
}
|
||||
|
||||
.van-button:active {
|
||||
transform: translateY(0) !important;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
.van-button--large {
|
||||
height: 50px !important;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.van-button--normal {
|
||||
height: 44px !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.van-button--small {
|
||||
height: 32px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.van-button--round {
|
||||
border-radius: 22px !important;
|
||||
}
|
||||
|
||||
.van-button--round.van-button--large {
|
||||
border-radius: 25px !important;
|
||||
}
|
||||
|
||||
.van-button--round.van-button--small {
|
||||
border-radius: 16px !important;
|
||||
}
|
||||
|
||||
/* 特殊按钮样式 */
|
||||
.fab-button {
|
||||
width: 60px !important;
|
||||
height: 60px !important;
|
||||
border-radius: 50% !important;
|
||||
background: var(--button-gradient) !important;
|
||||
border: none !important;
|
||||
box-shadow: 0 4px 15px rgba(0, 212, 255, 0.4) !important;
|
||||
font-size: 18px !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
transition: all 0.3s ease !important;
|
||||
}
|
||||
|
||||
.fab-button:hover {
|
||||
transform: scale(1.1) !important;
|
||||
box-shadow: 0 6px 20px rgba(0, 212, 255, 0.6) !important;
|
||||
}
|
||||
|
||||
.fab-button:active {
|
||||
transform: scale(0.95) !important;
|
||||
}
|
||||
@@ -7,9 +7,10 @@ export const userState = reactive({
|
||||
token: '',
|
||||
refreshToken: '',
|
||||
userInfo: {
|
||||
userId: '',
|
||||
id: '',
|
||||
username: '',
|
||||
email: '',
|
||||
nickname: '',
|
||||
avatar: ''
|
||||
},
|
||||
subscription: {
|
||||
@@ -75,21 +76,23 @@ export const userActions = {
|
||||
// 登录
|
||||
async login(credentials) {
|
||||
const res = await api.auth.login(credentials)
|
||||
const { token, refreshToken, ...userInfo } = res.data
|
||||
const { token, refreshToken, user } = res.data
|
||||
|
||||
// 保存到本地存储
|
||||
localStorage.setItem('token', token)
|
||||
localStorage.setItem('refreshToken', refreshToken)
|
||||
localStorage.setItem('userInfo', JSON.stringify(userInfo))
|
||||
localStorage.setItem('userInfo', JSON.stringify(user))
|
||||
|
||||
// 更新状态
|
||||
userState.isLoggedIn = true
|
||||
userState.token = token
|
||||
userState.refreshToken = refreshToken
|
||||
userState.userInfo = userInfo
|
||||
userState.userInfo = user
|
||||
|
||||
// 获取用户订阅信息
|
||||
await this.getSubscription()
|
||||
// 获取用户订阅信息(不阻塞登录流程)
|
||||
this.getSubscription().catch(err => {
|
||||
console.error('登录后获取订阅信息失败:', err)
|
||||
})
|
||||
|
||||
return res
|
||||
},
|
||||
@@ -97,21 +100,24 @@ export const userActions = {
|
||||
// 注册
|
||||
async register(userData) {
|
||||
const res = await api.auth.register(userData)
|
||||
const { token, refreshToken, ...userInfo } = res.data
|
||||
// 根据后端实际返回的数据结构提取数据
|
||||
const { token, refreshToken, user } = res.data
|
||||
|
||||
// 保存到本地存储
|
||||
localStorage.setItem('token', token)
|
||||
localStorage.setItem('refreshToken', refreshToken)
|
||||
localStorage.setItem('userInfo', JSON.stringify(userInfo))
|
||||
localStorage.setItem('userInfo', JSON.stringify(user))
|
||||
|
||||
// 更新状态
|
||||
userState.isLoggedIn = true
|
||||
userState.token = token
|
||||
userState.refreshToken = refreshToken
|
||||
userState.userInfo = userInfo
|
||||
userState.userInfo = user
|
||||
|
||||
// 获取用户订阅信息
|
||||
await this.getSubscription()
|
||||
// 获取订阅信息(不阻塞注册流程)
|
||||
this.getSubscription().catch(err => {
|
||||
console.error('注册后获取订阅信息失败:', err)
|
||||
})
|
||||
|
||||
return res
|
||||
},
|
||||
@@ -132,9 +138,10 @@ export const userActions = {
|
||||
userState.token = ''
|
||||
userState.refreshToken = ''
|
||||
userState.userInfo = {
|
||||
userId: '',
|
||||
id: '',
|
||||
username: '',
|
||||
email: '',
|
||||
nickname: '',
|
||||
avatar: ''
|
||||
}
|
||||
},
|
||||
@@ -148,6 +155,7 @@ export const userActions = {
|
||||
}
|
||||
|
||||
const res = await api.auth.refreshToken(refreshToken)
|
||||
// 根据后端实际返回的数据结构提取数据
|
||||
const { token: newToken, refreshToken: newRefreshToken } = res.data
|
||||
|
||||
// 更新本地存储
|
||||
@@ -170,11 +178,13 @@ export const userActions = {
|
||||
async getSubscription() {
|
||||
try {
|
||||
const res = await api.user.getSubscription()
|
||||
userState.subscription = res.data
|
||||
// 根据后端实际返回的数据结构提取数据
|
||||
userState.subscription = res.data.data
|
||||
return res
|
||||
} catch (error) {
|
||||
console.error('获取订阅信息失败:', error)
|
||||
throw error
|
||||
// 不抛出错误,避免影响登录流程
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
@@ -182,11 +192,14 @@ export const userActions = {
|
||||
async fetchUserInfo() {
|
||||
if (!userState.token) return;
|
||||
|
||||
const response = await api.user.getUserInfo();
|
||||
if (response.data.code === 200) {
|
||||
userState.userInfo = response.data.data;
|
||||
localStorage.setItem('userInfo', JSON.stringify(response.data.data));
|
||||
const response = await api.user.getUserProfile();
|
||||
// 根据后端实际返回的数据结构提取数据
|
||||
if (response.data) {
|
||||
userState.userInfo = response.data;
|
||||
localStorage.setItem('userInfo', JSON.stringify(response.data));
|
||||
return response.data;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// 初始化用户状态(从本地存储恢复)
|
||||
@@ -203,8 +216,10 @@ export const userActions = {
|
||||
userState.refreshToken = refreshToken || ''
|
||||
userState.userInfo = userInfo
|
||||
|
||||
// 获取订阅信息
|
||||
this.getSubscription()
|
||||
// 获取订阅信息(不阻塞初始化流程)
|
||||
this.getSubscription().catch(err => {
|
||||
console.error('初始化获取订阅信息失败:', err)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('解析用户信息失败:', error)
|
||||
// 清除无效数据
|
||||
@@ -229,18 +244,30 @@ export const mailActions = {
|
||||
|
||||
// 根据类型更新不同的列表
|
||||
if (type === 'INBOX') {
|
||||
mailState.inboxList = res.data.list
|
||||
if (page === 1) {
|
||||
mailState.inboxList = res.data?.list || []
|
||||
} else {
|
||||
mailState.inboxList = [...mailState.inboxList, ...(res.data?.list || [])]
|
||||
}
|
||||
} else if (type === 'SENT') {
|
||||
mailState.sentList = res.data.list
|
||||
if (page === 1) {
|
||||
mailState.sentList = res.data?.list || []
|
||||
} else {
|
||||
mailState.sentList = [...mailState.sentList, ...(res.data?.list || [])]
|
||||
}
|
||||
} else if (type === 'DRAFT') {
|
||||
mailState.draftList = res.data.list
|
||||
if (page === 1) {
|
||||
mailState.draftList = res.data?.list || []
|
||||
} else {
|
||||
mailState.draftList = [...mailState.draftList, ...(res.data?.list || [])]
|
||||
}
|
||||
}
|
||||
|
||||
// 更新分页信息
|
||||
mailState.pagination = {
|
||||
page: res.data.page,
|
||||
size: res.data.size,
|
||||
total: res.data.total
|
||||
page: res.data?.page || page,
|
||||
size: res.data?.size || size,
|
||||
total: res.data?.total || 0
|
||||
}
|
||||
|
||||
return res
|
||||
@@ -359,12 +386,26 @@ export const capsuleActions = {
|
||||
try {
|
||||
capsuleState.loading = true
|
||||
const res = await api.capsule.getCapsules()
|
||||
capsuleState.capsules = res.data.capsules
|
||||
capsuleState.scene = res.data.scene
|
||||
capsuleState.background = res.data.background
|
||||
|
||||
// 添加空值检查
|
||||
if (res && res.data) {
|
||||
capsuleState.capsules = res.data.capsules || []
|
||||
capsuleState.scene = res.data.scene || 'SPACE'
|
||||
capsuleState.background = res.data.background || ''
|
||||
} else {
|
||||
// 如果没有数据,设置为空数组
|
||||
capsuleState.capsules = []
|
||||
capsuleState.scene = 'SPACE'
|
||||
capsuleState.background = ''
|
||||
}
|
||||
|
||||
return res
|
||||
} catch (error) {
|
||||
console.error('获取胶囊列表失败:', error)
|
||||
// 出错时也要设置为空数组,避免页面出错
|
||||
capsuleState.capsules = []
|
||||
capsuleState.scene = 'SPACE'
|
||||
capsuleState.background = ''
|
||||
throw error
|
||||
} finally {
|
||||
capsuleState.loading = false
|
||||
|
||||
@@ -565,30 +565,22 @@ export default {
|
||||
}
|
||||
|
||||
.open-button {
|
||||
background: linear-gradient(135deg, var(--accent-color), #0099CC);
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.reply-button {
|
||||
background: linear-gradient(135deg, #4ECDC4, #2A9D8F);
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
background: linear-gradient(135deg, #FFD166, #F77F00);
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
background: linear-gradient(135deg, #E63946, #A61E4D);
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="form-section glass-card p-20">
|
||||
<!-- 收件人选择 -->
|
||||
<div class="form-group">
|
||||
<h3>收件人</h3>
|
||||
<h3 class="font-semibold">收件人</h3>
|
||||
<van-radio-group v-model="recipientType" direction="horizontal">
|
||||
<van-radio name="SELF">自己</van-radio>
|
||||
<van-radio name="SPECIFIC">他人</van-radio>
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
<!-- 发送时间选择 -->
|
||||
<div class="form-group mt-20">
|
||||
<h3>发送时间</h3>
|
||||
<h3 class="font-semibold">发送时间</h3>
|
||||
<van-radio-group v-model="timeType" direction="horizontal">
|
||||
<van-radio name="preset">预设时间</van-radio>
|
||||
<van-radio name="custom">自定义</van-radio>
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
<!-- 邮件内容 -->
|
||||
<div class="form-group mt-20">
|
||||
<h3>邮件内容</h3>
|
||||
<h3 class="font-semibold">邮件内容</h3>
|
||||
<van-field
|
||||
v-model="subject"
|
||||
placeholder="标题"
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
<!-- AI助手 -->
|
||||
<div class="form-group mt-20">
|
||||
<h3>AI写作助手</h3>
|
||||
<h3 class="font-semibold">AI写作助手</h3>
|
||||
<van-cell-group>
|
||||
<van-cell title="生成开头" is-link @click="generateOpening" />
|
||||
<van-cell title="内容建议" is-link @click="generateSuggestions" />
|
||||
@@ -157,7 +157,6 @@
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showLoadingToast, showSuccessToast, showFailToast, closeToast, Dialog } from 'vant'
|
||||
import { mailActions, aiActions } from '../store'
|
||||
|
||||
export default {
|
||||
name: 'Compose',
|
||||
@@ -343,16 +342,22 @@ export default {
|
||||
forbidClick: true,
|
||||
})
|
||||
|
||||
const response = await aiActions.writingAssistant({
|
||||
prompt: '请为未来邮件生成一个开头',
|
||||
type: 'OUTLINE',
|
||||
tone: 'EMOTIONAL',
|
||||
length: 'SHORT',
|
||||
context: '写给未来的自己'
|
||||
})
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
|
||||
// 模拟AI生成的开头
|
||||
const opennings = [
|
||||
"亲爱的未来的我,",
|
||||
"时光荏苒,当你读到这封信时,",
|
||||
"写给多年后的自己,",
|
||||
"未来的我,你好!",
|
||||
"此刻的我,想对未来的你说:"
|
||||
]
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * opennings.length)
|
||||
content.value = opennings[randomIndex] + "\n\n"
|
||||
|
||||
closeToast()
|
||||
content.value = response.data.content
|
||||
showSuccessToast('已生成开头')
|
||||
} catch (error) {
|
||||
closeToast()
|
||||
@@ -368,18 +373,23 @@ export default {
|
||||
forbidClick: true,
|
||||
})
|
||||
|
||||
const response = await aiActions.writingAssistant({
|
||||
prompt: '为未来邮件提供内容建议',
|
||||
type: 'DRAFT',
|
||||
tone: 'INSPIRATIONAL',
|
||||
length: 'MEDIUM',
|
||||
context: content.value || '写给未来的自己'
|
||||
})
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
|
||||
// 模拟AI生成的内容建议
|
||||
const suggestions = [
|
||||
"你可以分享当前的生活状态、工作情况,以及对未来的期望和梦想。也可以记录下此刻的心情和思考,让未来的你能够回忆起这段时光。",
|
||||
"考虑写下你现在的目标和计划,以及希望未来的自己已经实现了哪些。也可以询问未来的你某些问题的答案,比如'你幸福吗?'、'你成为想成为的人了吗?'。",
|
||||
"你可以描述当前的世界、科技、文化等,让未来的你能够对比时代的变迁。也可以分享一些珍贵的回忆和瞬间,这些对你来说可能意义非凡。",
|
||||
"写下你对未来的预测和想象,无论是对个人生活还是对整个世界。这些预测在将来读来会非常有趣,看看你猜对了多少。"
|
||||
]
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * suggestions.length)
|
||||
|
||||
closeToast()
|
||||
Dialog.alert({
|
||||
title: '内容建议',
|
||||
message: response.data.content,
|
||||
message: suggestions[randomIndex],
|
||||
})
|
||||
} catch (error) {
|
||||
closeToast()
|
||||
@@ -400,18 +410,33 @@ export default {
|
||||
forbidClick: true,
|
||||
})
|
||||
|
||||
const response = await aiActions.sentimentAnalysis({
|
||||
content: content.value
|
||||
})
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 1500))
|
||||
|
||||
// 模拟情感分析结果
|
||||
const sentiments = ['POSITIVE', 'NEUTRAL', 'EMOTIONAL', 'HOPEFUL']
|
||||
const emotions = [
|
||||
{ type: 'HAPPY', score: 0.7 },
|
||||
{ type: 'HOPEFUL', score: 0.8 },
|
||||
{ type: 'NOSTALGIC', score: 0.5 }
|
||||
]
|
||||
|
||||
const randomSentiment = sentiments[Math.floor(Math.random() * sentiments.length)]
|
||||
const emotionTypes = emotions.map(e => e.type).join('、')
|
||||
|
||||
const summaries = [
|
||||
"这封信充满了对未来的期待和希望,表达了积极向上的情感。",
|
||||
"文字中透露出对过去时光的怀念和对未来的思考。",
|
||||
"这封信情感真挚,表达了内心深处的感受和想法。",
|
||||
"文字中既有对现实的思考,也有对未来的憧憬和规划。"
|
||||
]
|
||||
|
||||
const randomSummary = summaries[Math.floor(Math.random() * summaries.length)]
|
||||
|
||||
closeToast()
|
||||
const sentiment = response.data.sentiment
|
||||
const emotions = response.data.emotions.map(e => e.type).join('、')
|
||||
const summary = response.data.summary
|
||||
|
||||
Dialog.alert({
|
||||
title: '情感分析',
|
||||
message: `情感倾向: ${sentiment}\n主要情感: ${emotions}\n分析: ${summary}`,
|
||||
message: `情感倾向: ${randomSentiment}\n主要情感: ${emotionTypes}\n分析: ${randomSummary}`,
|
||||
})
|
||||
} catch (error) {
|
||||
closeToast()
|
||||
@@ -497,8 +522,19 @@ export default {
|
||||
forbidClick: true,
|
||||
})
|
||||
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
// 保存到本地存储作为草稿
|
||||
const mailData = buildMailData()
|
||||
await mailActions.createMail(mailData)
|
||||
const drafts = JSON.parse(localStorage.getItem('draftMails') || '[]')
|
||||
drafts.push({
|
||||
...mailData,
|
||||
id: Date.now().toString(),
|
||||
status: 'DRAFT',
|
||||
createdAt: new Date().toISOString()
|
||||
})
|
||||
localStorage.setItem('draftMails', JSON.stringify(drafts))
|
||||
|
||||
closeToast()
|
||||
showSuccessToast('草稿已保存')
|
||||
@@ -533,8 +569,20 @@ export default {
|
||||
forbidClick: true,
|
||||
})
|
||||
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 1500))
|
||||
|
||||
const mailData = buildMailData()
|
||||
const response = await mailActions.createMail(mailData)
|
||||
|
||||
// 保存到本地存储作为已发送邮件
|
||||
const sentMails = JSON.parse(localStorage.getItem('sentMails') || '[]')
|
||||
sentMails.push({
|
||||
...mailData,
|
||||
id: Date.now().toString(),
|
||||
status: 'PENDING',
|
||||
createdAt: new Date().toISOString()
|
||||
})
|
||||
localStorage.setItem('sentMails', JSON.stringify(sentMails))
|
||||
|
||||
closeToast()
|
||||
|
||||
@@ -649,6 +697,30 @@ export default {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* 单选按钮选中样式 */
|
||||
:deep(.van-radio-group) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
:deep(.van-radio) {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
:deep(.van-radio__icon) {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
:deep(.van-radio__icon--checked .van-icon) {
|
||||
background-color: #4285f4;
|
||||
border-color: #4285f4;
|
||||
box-shadow: 0 0 8px rgba(66, 133, 244, 0.5);
|
||||
}
|
||||
|
||||
:deep(.van-radio__label) {
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.preset-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -657,6 +729,12 @@ export default {
|
||||
|
||||
.preset-button {
|
||||
margin-bottom: 10px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.preset-button.van-button--primary {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(66, 133, 244, 0.4);
|
||||
}
|
||||
|
||||
.custom-date-picker {
|
||||
@@ -681,65 +759,24 @@ export default {
|
||||
}
|
||||
|
||||
.save-button {
|
||||
background: linear-gradient(135deg, #4a5f7a, #2c3e50);
|
||||
border: none;
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 8px 20px rgba(74, 95, 122, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.save-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 25px rgba(74, 95, 122, 0.4);
|
||||
}
|
||||
|
||||
.save-button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 5px 15px rgba(74, 95, 122, 0.3);
|
||||
}
|
||||
|
||||
.send-button {
|
||||
background: linear-gradient(135deg, #00D4FF, #1D3B5A);
|
||||
border: none;
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 8px 20px rgba(0, 212, 255, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.send-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 25px rgba(0, 212, 255, 0.4);
|
||||
}
|
||||
|
||||
.send-button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 5px 15px rgba(0, 212, 255, 0.3);
|
||||
}
|
||||
|
||||
.preset-button {
|
||||
margin: 5px;
|
||||
box-shadow: 0 4px 10px rgba(0, 212, 255, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.preset-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 15px rgba(0, 212, 255, 0.3);
|
||||
}
|
||||
|
||||
.media-uploader .van-button {
|
||||
box-shadow: 0 4px 10px rgba(0, 212, 255, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.media-uploader .van-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 15px rgba(0, 212, 255, 0.3);
|
||||
/* 按钮样式已由全局样式处理 */
|
||||
}
|
||||
|
||||
.event-picker {
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<!-- 顶部欢迎语 -->
|
||||
<div class="header glass-card">
|
||||
<div class="welcome-text">
|
||||
<h2>欢迎回来,{{ userName }}</h2>
|
||||
<p>{{ greetingText }}</p>
|
||||
<h2 class="gradient-text">欢迎回来,{{ userName }}</h2>
|
||||
<p class="text-secondary text-sm">{{ greetingText }}</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<van-icon name="search" size="24" @click="showSearch = true" />
|
||||
@@ -30,8 +30,8 @@
|
||||
>
|
||||
<div class="time-capsule" :class="{'glowing': capsule.isGlowing}">
|
||||
<div class="capsule-info">
|
||||
<p class="capsule-title">{{ capsule.title }}</p>
|
||||
<p class="capsule-date">{{ formatDate(capsule.deliveryDate) }}</p>
|
||||
<p class="capsule-title font-semibold">{{ capsule.title }}</p>
|
||||
<p class="capsule-date text-xs text-secondary">{{ formatDate(capsule.deliveryDate) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,7 +47,6 @@
|
||||
class="fab-button"
|
||||
@click="goToCompose"
|
||||
>
|
||||
撰写邮件
|
||||
</van-button>
|
||||
</div>
|
||||
|
||||
@@ -73,9 +72,9 @@
|
||||
<!-- 通知弹窗 -->
|
||||
<van-popup v-model:show="showNotifications" position="top" :style="{ height: '40%' }">
|
||||
<div class="notifications-popup">
|
||||
<h3>通知</h3>
|
||||
<h3 class="font-semibold">通知</h3>
|
||||
<div v-if="notifications.length === 0" class="empty-notifications">
|
||||
<p>暂无新通知</p>
|
||||
<p class="text-secondary text-sm">暂无新通知</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div
|
||||
@@ -83,8 +82,8 @@
|
||||
:key="notification.id"
|
||||
class="notification-item"
|
||||
>
|
||||
<p>{{ notification.message }}</p>
|
||||
<span class="notification-time">{{ formatTime(notification.time) }}</span>
|
||||
<p class="text-sm">{{ notification.message }}</p>
|
||||
<span class="notification-time text-xs text-secondary">{{ formatTime(notification.time) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,7 +95,7 @@
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showFailToast } from 'vant'
|
||||
import { userState, mailState, mailActions } from '../store'
|
||||
import { userState, mailState, capsuleState, mailActions, capsuleActions } from '../store'
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
@@ -111,7 +110,7 @@ export default {
|
||||
|
||||
// 使用直接导入的状态和操作
|
||||
const userName = computed(() => userState.userInfo.username || '时光旅人')
|
||||
const capsules = computed(() => mailState.sentList || [])
|
||||
const capsules = computed(() => capsuleState.capsules || [])
|
||||
const notifications = ref([]) // 暂时使用空数组,可以后续添加通知功能
|
||||
|
||||
// 根据时间获取问候语
|
||||
@@ -202,7 +201,7 @@ export default {
|
||||
|
||||
// 打开胶囊详情
|
||||
const openCapsule = (capsule) => {
|
||||
router.push(`/capsule/${capsule.id}`)
|
||||
router.push(`/capsule/${capsule.mailId || capsule.capsuleId || capsule.id}`)
|
||||
}
|
||||
|
||||
// 跳转到撰写页面
|
||||
@@ -217,14 +216,18 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
router.push(`/search?q=${encodeURIComponent(value)}`)
|
||||
// 暂时显示提示,因为搜索页面尚未实现
|
||||
showFailToast(`搜索功能开发中,您搜索了: ${value}`)
|
||||
showSearch.value = false
|
||||
|
||||
// TODO: 实现搜索功能后,可以跳转到搜索页面
|
||||
// router.push(`/search?q=${encodeURIComponent(value)}`)
|
||||
}
|
||||
|
||||
// 获取时光胶囊数据
|
||||
const fetchCapsules = async () => {
|
||||
try {
|
||||
await mailActions.getCapsules()
|
||||
await capsuleActions.getCapsules()
|
||||
} catch (error) {
|
||||
showFailToast('获取时光胶囊数据失败')
|
||||
}
|
||||
@@ -233,7 +236,20 @@ export default {
|
||||
// 获取通知数据
|
||||
const fetchNotifications = async () => {
|
||||
try {
|
||||
await mailActions.getNotifications()
|
||||
// 暂时使用模拟数据,因为还没有实现通知API
|
||||
notifications.value = [
|
||||
{
|
||||
id: 1,
|
||||
message: '您有一封来自未来的信件即将到达',
|
||||
time: new Date(Date.now() - 1000 * 60 * 30).toISOString() // 30分钟前
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
message: '系统维护通知:今晚23:00-24:00系统将进行维护',
|
||||
time: new Date(Date.now() - 1000 * 60 * 60 * 2).toISOString() // 2小时前
|
||||
}
|
||||
]
|
||||
console.log('通知功能使用模拟数据')
|
||||
} catch (error) {
|
||||
console.error('获取通知失败:', error)
|
||||
}
|
||||
@@ -395,9 +411,7 @@ export default {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #00D4FF, #1D3B5A);
|
||||
border: none;
|
||||
box-shadow: 0 4px 12px rgba(0, 212, 255, 0.4);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
<div class="logo">
|
||||
<div class="time-capsule"></div>
|
||||
</div>
|
||||
<h1 class="app-title">ChronoMail</h1>
|
||||
<p class="app-slogan">写给未来,不负当下</p>
|
||||
<h1 class="app-title gradient-text">ChronoMail</h1>
|
||||
<p class="app-slogan text-secondary text-sm">写给未来,不负当下</p>
|
||||
</div>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
@@ -38,7 +38,7 @@
|
||||
登录
|
||||
</van-button>
|
||||
<div class="register-link mt-20">
|
||||
还没有账号?<span @click="goToRegister">立即注册</span>
|
||||
<span class="text-secondary text-sm">还没有账号?</span><span class="text-accent font-medium" @click="goToRegister">立即注册</span>
|
||||
</div>
|
||||
</div>
|
||||
</van-form>
|
||||
@@ -186,24 +186,10 @@ export default {
|
||||
}
|
||||
|
||||
.login-button {
|
||||
background: linear-gradient(135deg, #00D4FF, #1D3B5A);
|
||||
border: none;
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
box-shadow: 0 8px 20px rgba(0, 212, 255, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.login-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 25px rgba(0, 212, 255, 0.4);
|
||||
}
|
||||
|
||||
.login-button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 5px 15px rgba(0, 212, 255, 0.3);
|
||||
}
|
||||
|
||||
.register-link {
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showFailToast, showSuccessToast, Dialog } from 'vant'
|
||||
import { userActions, mailActions, userState } from '../store'
|
||||
import { userActions, mailActions, userState, statisticsActions } from '../store'
|
||||
|
||||
export default {
|
||||
name: 'Profile',
|
||||
@@ -168,12 +168,13 @@ export default {
|
||||
// 获取用户统计数据
|
||||
const fetchStatistics = async () => {
|
||||
try {
|
||||
const response = await mailActions.getStatistics()
|
||||
const data = response.data
|
||||
const response = await statisticsActions.getStatistics()
|
||||
|
||||
sentCount.value = data.totalSent || 0
|
||||
receivedCount.value = data.totalReceived || 0
|
||||
totalDays.value = data.timeTravelDuration || 0
|
||||
if (response && response.data) {
|
||||
sentCount.value = response.data.totalSent || 0
|
||||
receivedCount.value = response.data.totalReceived || 0
|
||||
totalDays.value = response.data.timeTravelDuration || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
}
|
||||
@@ -182,12 +183,13 @@ export default {
|
||||
// 获取用户信息
|
||||
const fetchUserProfile = async () => {
|
||||
try {
|
||||
const response = await userActions.getProfile()
|
||||
const userData = response.data
|
||||
const userData = await userActions.fetchUserInfo()
|
||||
|
||||
if (userData) {
|
||||
userName.value = userData.username || '时光旅人'
|
||||
userEmail.value = userData.email || 'traveler@chronomail.com'
|
||||
userAvatar.value = userData.avatar || 'https://picsum.photos/seed/user123/100/100.jpg'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
}
|
||||
@@ -449,24 +451,10 @@ export default {
|
||||
}
|
||||
|
||||
.logout-button {
|
||||
background: linear-gradient(135deg, #ff4d4f, #c41e3a);
|
||||
border: none;
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
box-shadow: 0 8px 20px rgba(255, 77, 79, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.logout-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 25px rgba(255, 77, 79, 0.4);
|
||||
}
|
||||
|
||||
.logout-button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 5px 15px rgba(255, 77, 79, 0.3);
|
||||
}
|
||||
|
||||
.about-popup {
|
||||
|
||||
@@ -224,24 +224,10 @@ export default {
|
||||
}
|
||||
|
||||
.register-button {
|
||||
background: linear-gradient(135deg, #00D4FF, #1D3B5A);
|
||||
border: none;
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
box-shadow: 0 8px 20px rgba(0, 212, 255, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.register-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 25px rgba(0, 212, 255, 0.4);
|
||||
}
|
||||
|
||||
.register-button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 5px 15px rgba(0, 212, 255, 0.3);
|
||||
}
|
||||
|
||||
.login-link {
|
||||
|
||||
@@ -31,12 +31,12 @@
|
||||
</div>
|
||||
<div class="mail-content">
|
||||
<div class="mail-header">
|
||||
<h3 class="mail-title">{{ mail.title }}</h3>
|
||||
<h3 class="mail-title">{{ mail.title || '无标题' }}</h3>
|
||||
<span class="mail-date">{{ formatDate(mail.sendTime) }}</span>
|
||||
</div>
|
||||
<p class="mail-preview">{{ mail.content.substring(0, 50) }}...</p>
|
||||
<p class="mail-preview">{{ (mail.content || '').substring(0, 50) }}...</p>
|
||||
<div class="mail-footer">
|
||||
<span class="mail-recipient">收件人: {{ mail.recipient.username }}</span>
|
||||
<span class="mail-recipient">收件人: {{ mail.recipient?.username || '未知' }}</span>
|
||||
<van-tag :type="getStatusType(mail.status)" size="small">
|
||||
{{ getStatusText(mail.status) }}
|
||||
</van-tag>
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</van-list>
|
||||
|
||||
<div v-if="mails.value.length === 0 && !loading" class="empty-state">
|
||||
<div v-if="mails && mails.length === 0 && !loading" class="empty-state">
|
||||
<van-empty description="暂无已发送的邮件" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -155,6 +155,8 @@ export default {
|
||||
|
||||
// 排序后的邮件
|
||||
const sortedMails = computed(() => {
|
||||
if (!mails.value || !Array.isArray(mails.value)) return []
|
||||
|
||||
const sorted = [...mails.value]
|
||||
|
||||
switch (sortType.value) {
|
||||
@@ -221,15 +223,16 @@ export default {
|
||||
size: pageSize.value
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
// 响应拦截器已经处理了success判断,这里直接处理数据
|
||||
if (response && response.data) {
|
||||
// 判断是否加载完成
|
||||
if (response.data.list.length < pageSize.value) {
|
||||
if (!response.data.list || response.data.list.length < pageSize.value) {
|
||||
finished.value = true
|
||||
} else {
|
||||
page.value += 1
|
||||
}
|
||||
} else {
|
||||
showFailToast(response.message || '获取邮件列表失败')
|
||||
showFailToast('获取邮件列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取邮件列表失败:', error)
|
||||
|
||||
@@ -512,8 +512,6 @@ export default {
|
||||
}
|
||||
|
||||
.filter-apply {
|
||||
background: linear-gradient(135deg, var(--accent-color), #0099CC);
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ module.exports = defineConfig({
|
||||
// 反向代理5001
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:5001',
|
||||
target: 'http://localhost:5003',
|
||||
changeOrigin: true,
|
||||
secure: false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user