diff --git a/web/src/main.js b/web/src/main.js
index 1510fd9f..2b8b9919 100644
--- a/web/src/main.js
+++ b/web/src/main.js
@@ -5,17 +5,21 @@
// * @Author yangjian102621@163.com
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-import { createApp } from "vue";
-import ElementPlus from "element-plus";
-import "element-plus/dist/index.css";
-import "@/assets/iconfont/iconfont.css";
-import "vant/lib/index.css";
-import App from "./App.vue";
-import { useThemeStore } from "@/store/theme";
-import { createPinia } from "pinia";
-import "animate.css/animate.min.css";
-import "@/assets/css/tailwind.css";
+import '@/assets/css/tailwind.css'
+import '@/assets/iconfont/iconfont.css'
+import { useThemeStore } from '@/store/theme'
+import 'animate.css/animate.min.css'
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+import { createPinia } from 'pinia'
+import 'vant/lib/index.css'
+import { createApp } from 'vue'
+import App from './App.vue'
+import '@/assets/css/common.scss'
+import '@/assets/css/theme-dark.scss'
+import '@/assets/css/theme-light.scss'
+import { router } from '@/router'
import {
ActionSheet,
Badge,
@@ -48,6 +52,8 @@ import {
Overlay,
Picker,
Popup,
+ Radio,
+ RadioGroup,
Row,
Search,
ShareSheet,
@@ -61,64 +67,64 @@ import {
Tabs,
Tag,
TextEllipsis,
+ Toast,
Uploader,
-} from "vant";
-import { router } from "@/router";
-import "@/assets/css/theme-dark.scss";
-import "@/assets/css/theme-light.scss";
-import "@/assets/css/common.scss";
+} from 'vant'
-const pinia = createPinia();
-const themeStore = useThemeStore(pinia); // 使用 theme store
+const pinia = createPinia()
+const themeStore = useThemeStore(pinia) // 使用 theme store
// 设置初始主题
-document.documentElement.setAttribute("data-theme", themeStore.theme);
+document.documentElement.setAttribute('data-theme', themeStore.theme)
-const app = createApp(App);
-app.use(createPinia());
-app.use(ConfigProvider);
-app.use(Tabbar);
-app.use(TabbarItem);
-app.use(NavBar);
-app.use(Search);
-app.use(Cell);
-app.use(Image);
-app.use(TextEllipsis);
-app.use(Notify);
-app.use(Picker);
-app.use(Popup);
-app.use(List);
-app.use(Form);
-app.use(Field);
-app.use(CellGroup);
-app.use(Button);
-app.use(DropdownMenu);
-app.use(Icon);
-app.use(DropdownItem);
-app.use(Sticky);
-app.use(SwipeCell);
-app.use(Dialog);
-app.use(ShareSheet);
-app.use(Switch);
-app.use(Uploader);
-app.use(Tag);
-app.use(Overlay);
-app.use(Col);
-app.use(Row);
-app.use(Slider);
-app.use(Badge);
-app.use(Collapse);
-app.use(CollapseItem);
-app.use(Grid);
-app.use(GridItem);
-app.use(Empty);
-app.use(Circle);
-app.use(Loading);
-app.use(Lazyload);
-app.use(ImagePreview);
-app.use(Tab);
-app.use(Tabs);
-app.use(Divider);
-app.use(NoticeBar);
-app.use(ActionSheet);
-app.use(router).use(ElementPlus).mount("#app");
+const app = createApp(App)
+app.use(createPinia())
+app.use(ConfigProvider)
+app.use(Tabbar)
+app.use(TabbarItem)
+app.use(NavBar)
+app.use(Search)
+app.use(Cell)
+app.use(Image)
+app.use(TextEllipsis)
+app.use(Notify)
+app.use(Picker)
+app.use(Popup)
+app.use(Radio)
+app.use(RadioGroup)
+app.use(List)
+app.use(Form)
+app.use(Field)
+app.use(CellGroup)
+app.use(Button)
+app.use(DropdownMenu)
+app.use(Icon)
+app.use(DropdownItem)
+app.use(Sticky)
+app.use(SwipeCell)
+app.use(Dialog)
+app.use(ShareSheet)
+app.use(Switch)
+app.use(Uploader)
+app.use(Tag)
+app.use(Overlay)
+app.use(Col)
+app.use(Row)
+app.use(Slider)
+app.use(Badge)
+app.use(Collapse)
+app.use(CollapseItem)
+app.use(Grid)
+app.use(GridItem)
+app.use(Empty)
+app.use(Circle)
+app.use(Loading)
+app.use(Lazyload)
+app.use(ImagePreview)
+app.use(Tab)
+app.use(Tabs)
+app.use(Divider)
+app.use(NoticeBar)
+app.use(ActionSheet)
+app.use(Toast)
+app.use(router).use(ElementPlus).mount('#app')
diff --git a/web/src/router.js b/web/src/router.js
index a4396afd..99a1a3c5 100644
--- a/web/src/router.js
+++ b/web/src/router.js
@@ -309,15 +309,25 @@ const routes = [
component: () => import('@/views/mobile/ChatList.vue'),
},
{
- path: '/mobile/image',
- name: 'mobile-image',
- component: () => import('@/views/mobile/Image.vue'),
+ path: '/mobile/create',
+ name: 'mobile-create',
+ component: () => import('@/views/mobile/Create.vue'),
+ },
+ {
+ path: '/mobile/discover',
+ name: 'mobile-discover',
+ component: () => import('@/views/mobile/Discover.vue'),
},
{
path: '/mobile/profile',
name: 'mobile-profile',
component: () => import('@/views/mobile/Profile.vue'),
},
+ {
+ path: '/mobile/member',
+ name: 'mobile-member',
+ component: () => import('@/views/mobile/Member.vue'),
+ },
{
path: '/mobile/imgWall',
name: 'mobile-img-wall',
@@ -338,6 +348,37 @@ const routes = [
name: 'mobile-apps',
component: () => import('@/views/mobile/Apps.vue'),
},
+ // 新增的功能页面路由
+ {
+ path: '/mobile/power-log',
+ name: 'mobile-power-log',
+ component: () => import('@/views/mobile/PowerLog.vue'),
+ },
+ {
+ path: '/mobile/invite',
+ name: 'mobile-invite',
+ component: () => import('@/views/mobile/Invite.vue'),
+ },
+ {
+ path: '/mobile/tools',
+ name: 'mobile-tools',
+ component: () => import('@/views/mobile/Tools.vue'),
+ },
+ {
+ path: '/mobile/settings',
+ name: 'mobile-settings',
+ component: () => import('@/views/mobile/Settings.vue'),
+ },
+ {
+ path: '/mobile/help',
+ name: 'mobile-help',
+ component: () => import('@/views/mobile/Help.vue'),
+ },
+ {
+ path: '/mobile/feedback',
+ name: 'mobile-feedback',
+ component: () => import('@/views/mobile/Feedback.vue'),
+ },
],
},
diff --git a/web/src/views/mobile/Create.vue b/web/src/views/mobile/Create.vue
new file mode 100644
index 00000000..1fa18c5d
--- /dev/null
+++ b/web/src/views/mobile/Create.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/views/mobile/Discover.vue b/web/src/views/mobile/Discover.vue
new file mode 100644
index 00000000..6ca15b12
--- /dev/null
+++ b/web/src/views/mobile/Discover.vue
@@ -0,0 +1,328 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
我的服务
+
+
+
+
+
+
+
+
+
+
+
+
实用功能
+
+
+
+
+
+
+
+
+
+
+
+
精选推荐
+
+
+
+
+
+
+
+
{{ item.title }}
+
{{ item.desc }}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/views/mobile/Feedback.vue b/web/src/views/mobile/Feedback.vue
new file mode 100644
index 00000000..8df848a7
--- /dev/null
+++ b/web/src/views/mobile/Feedback.vue
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+ 问题反馈
+ 功能建议
+ 其他
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提交反馈
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/mobile/Help.vue b/web/src/views/mobile/Help.vue
new file mode 100644
index 00000000..a151c43d
--- /dev/null
+++ b/web/src/views/mobile/Help.vue
@@ -0,0 +1,846 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
功能指南
+
+
+
+
+
+
+
{{ guide.title }}
+
{{ guide.desc }}
+
+
+
+
+
+
+
+
问题分类
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
联系我们
+
+
+
+ 在线
+
+
+
+
+
+ 123456789
+
+
+
+
+
+
+
+
+
使用提示
+
+
+
+
+
+
+
{{ tip.title }}
+
{{ tip.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 有用
+
+
+ 分享
+
+
+
+
+
+
+
+
+
+

+
+
扫描二维码添加官方微信
+
+
+
+
+
+
+
+
+
+
+
{{ message.content }}
+
{{ formatTime(message.time) }}
+
+
+
+
+
+
+
+ 发送
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/views/mobile/Home.vue b/web/src/views/mobile/Home.vue
index a9dd1390..50203f21 100644
--- a/web/src/views/mobile/Home.vue
+++ b/web/src/views/mobile/Home.vue
@@ -3,11 +3,37 @@
-
- 首页
- 对话
- 绘图
- 我的
+
+
+ 首页
+
+
+
+
+
+ 对话
+
+
+
+
+
+ 创作
+
+
+
+
+
+ 发现
+
+
+
+
+
+ 我的
+
+
+
+
@@ -38,9 +64,32 @@ watch(
position: fixed;
width: 100%;
}
-
padding: 0 10px;
}
+
+ .van-tabbar {
+ box-shadow: 0 -2px 20px rgba(0, 0, 0, 0.1);
+
+ .van-tabbar-item {
+ .active-icon {
+ color: var(--van-primary-color) !important;
+ transform: scale(1.1);
+ transition: all 0.3s ease;
+ }
+
+ &--active {
+ .van-tabbar-item__text {
+ color: var(--van-primary-color);
+ font-weight: 600;
+ }
+ }
+ }
+
+ .iconfont {
+ font-size: 20px;
+ transition: all 0.3s ease;
+ }
+ }
}
// 黑色主题
diff --git a/web/src/views/mobile/Index.vue b/web/src/views/mobile/Index.vue
index 91af1e5b..5747450c 100644
--- a/web/src/views/mobile/Index.vue
+++ b/web/src/views/mobile/Index.vue
@@ -1,86 +1,150 @@
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
- AI 绘画
-
-
-
-
-
-
-
-
-
-
- AI 画廊
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {{ feature.name }}
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
{{ item.name }}
-
{{ item.hello_msg }}
+
+
{{ item.name }}
+
{{ item.intro }}
+
+
+
+ {{ hasRole(item.key) ? '已添加' : '添加' }}
+
-
-
-
- 对话
-
- {{ hasRole(item.key) ? '移除' : '添加' }}
-
-
-
+
+
+
+
+
+
+
+
+
+
{{ userStats.conversations || 0 }}
+
对话次数
+
+
+
+
+
{{ userStats.images || 0 }}
+
生成图片
+
+
+
+
+
{{ userStats.power || 0 }}
+
剩余算力
+
+
+
+
@@ -99,20 +163,53 @@ const isLogin = ref(false)
const apps = ref([])
const loading = ref(false)
const roles = ref([])
-const slogan = ref('你有多大想象力,AI就有多大创造力!')
-
-// 只显示前5个应用
-const displayApps = computed(() => {
- return apps.value.slice(0, 8)
+const userAvatar = ref('/images/avatar/default.jpg')
+const userStats = ref({
+ conversations: 0,
+ images: 0,
+ power: 0
})
+// 功能配置
+const features = ref([
+ { key: 'mj', name: 'MJ绘画', icon: 'icon-mj', color: '#8B5CF6', url: '/mobile/create?tab=mj' },
+ { key: 'sd', name: 'SD绘画', icon: 'icon-sd', color: '#06B6D4', url: '/mobile/create?tab=sd' },
+ { key: 'dalle', name: 'DALL·E', icon: 'icon-dalle', color: '#F59E0B', url: '/mobile/create?tab=dalle' },
+ { key: 'suno', name: '音乐创作', icon: 'icon-music', color: '#EF4444', url: '/mobile/create?tab=suno' },
+ { key: 'video', name: '视频生成', icon: 'icon-video', color: '#10B981', url: '/mobile/create?tab=video' },
+ { key: 'jimeng', name: '即梦AI', icon: 'icon-jimeng', color: '#F97316', url: '/mobile/create?tab=jimeng' },
+ { key: 'xmind', name: '思维导图', icon: 'icon-mind', color: '#3B82F6', url: '/mobile/tools?tab=xmind' },
+ { key: 'imgWall', name: '作品展示', icon: 'icon-gallery', color: '#EC4899', url: '/mobile/imgWall' }
+])
+
+// 应用分组显示(每行2个)
+const appChunks = computed(() => {
+ const chunks = []
+ const displayApps = apps.value.slice(0, 6) // 只显示前6个
+ for (let i = 0; i < displayApps.length; i += 2) {
+ chunks.push(displayApps.slice(i, i + 2))
+ }
+ return chunks
+})
+
+// 获取问候语
+const getGreeting = () => {
+ const hour = new Date().getHours()
+ if (hour < 6) return '夜深了'
+ if (hour < 12) return '早上好'
+ if (hour < 18) return '下午好'
+ return '晚上好'
+}
+
+// 导航到功能页面
+const navigateToFeature = (feature) => {
+ router.push(feature.url)
+}
+
onMounted(() => {
getSystemInfo()
.then((res) => {
title.value = res.data.title
- if (res.data.slogan) {
- slogan.value = res.data.slogan
- }
})
.catch((e) => {
ElMessage.error('获取系统配置失败:' + e.message)
@@ -122,8 +219,12 @@ onMounted(() => {
.then((user) => {
isLogin.value = true
roles.value = user.chat_roles
+ userAvatar.value = user.avatar || '/images/avatar/default.jpg'
+ // 获取用户统计数据
+ fetchUserStats()
})
.catch(() => {})
+
fetchApps()
})
@@ -133,7 +234,7 @@ const fetchApps = () => {
const items = res.data
// 处理 hello message
for (let i = 0; i < items.length; i++) {
- items[i].intro = substr(items[i].hello_msg, 80)
+ items[i].intro = substr(items[i].hello_msg, 30)
}
apps.value = items
})
@@ -142,6 +243,22 @@ const fetchApps = () => {
})
}
+const fetchUserStats = () => {
+ if (!isLogin.value) return
+
+ // 这里可以调用实际的统计接口
+ // httpGet('/api/user/stats').then(res => {
+ // userStats.value = res.data
+ // })
+
+ // 临时使用模拟数据
+ userStats.value = {
+ conversations: Math.floor(Math.random() * 100),
+ images: Math.floor(Math.random() * 50),
+ power: Math.floor(Math.random() * 1000)
+ }
+}
+
const updateRole = (row, opt) => {
if (!isLogin.value) {
return showLoginDialog(router)
@@ -165,10 +282,10 @@ const updateRole = (row, opt) => {
}
httpPost('/api/app/update', { keys: roles.value })
.then(() => {
- ElMessage.success({ message: title.value + '成功!', duration: 1000 })
+ showNotify({ type: 'success', message: title.value + '成功!', duration: 1000 })
})
.catch((e) => {
- ElMessage.error(title.value + '失败:' + e.message)
+ showNotify({ type: 'danger', message: title.value + '失败:' + e.message })
})
}
@@ -187,148 +304,299 @@ const useRole = (roleId) => {
diff --git a/web/src/views/mobile/Invite.vue b/web/src/views/mobile/Invite.vue
new file mode 100644
index 00000000..11e70d21
--- /dev/null
+++ b/web/src/views/mobile/Invite.vue
@@ -0,0 +1,739 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ userStats.inviteCount }}
+
累计邀请
+
+
+
+
+
{{ userStats.rewardTotal }}
+
获得奖励
+
+
+
+
+
{{ userStats.todayInvite }}
+
今日邀请
+
+
+
+
+
+
+
+
奖励规则
+
+
+
+
+
+
+
{{ rule.title }}
+
{{ rule.desc }}
+
+
+ +{{ rule.reward }}
+ 算力
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ inviteCode }}
+
+
+
+
+ 复制链接
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ record.username }}
+
{{ formatTime(record.created_at) }}
+
+
+
+ {{ record.status === 'completed' ? '已获得奖励' : '待获得奖励' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/views/mobile/Member.vue b/web/src/views/mobile/Member.vue
new file mode 100644
index 00000000..670e2773
--- /dev/null
+++ b/web/src/views/mobile/Member.vue
@@ -0,0 +1,608 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ userInfo.nickname || userInfo.username }}
+
+
+ {{ vipInfo.isVip ? 'VIP会员' : '普通用户' }}
+
+
+
+
+
+
+
+
{{ userInfo.power || 0 }}
+
剩余算力
+
+
+
+
{{ vipInfo.daysLeft }}天
+
VIP剩余
+
+
+
+
+
+
+
+
会员特权
+
+
+
+
+
+
+
{{ privilege.title }}
+
{{ privilege.desc }}
+
+
+
+
+
+
+
+
+
+
+
充值套餐
+
+
+
推荐
+
+
+
+
+ {{ pkg.power }}算力值
+
+
+
+ {{ pkg.days }}天有效期
+
+
+
+ 长期有效
+
+
+
+ {{ pkg.features }}
+
+
+
+
+
+
+
+
+
支付方式
+
+
+
+
+
+
+ {{ payment.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 立即支付 ¥{{ selectedPackage.discount || selectedPackage.price }}
+
+
+
+
+
+
+
+
+
什么是算力?
+
算力是使用AI功能时消耗的虚拟货币,不同功能消耗的算力不同。
+
+
+
如何获得算力?
+
通过充值套餐可以获得算力,会员用户还可享受每月赠送的算力。
+
+
+
VIP特权说明
+
VIP会员享有更多功能权限、优先处理、专属客服等特权服务。
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/views/mobile/PowerLog.vue b/web/src/views/mobile/PowerLog.vue
new file mode 100644
index 00000000..6266d89a
--- /dev/null
+++ b/web/src/views/mobile/PowerLog.vue
@@ -0,0 +1,479 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ stats.total }}
+
总消费
+
+
+
+
+
{{ stats.today }}
+
今日消费
+
+
+
+
+
{{ stats.balance }}
+
剩余算力
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dateRange.start && dateRange.end ? `${dateRange.start} 至 ${dateRange.end}` : '选择时间' }}
+
+
+
+
+
+
+ 全部
+ 对话
+ 绘画
+ 音乐
+
+
+
+
+
+ 重置
+ 确定
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/views/mobile/Profile.vue b/web/src/views/mobile/Profile.vue
index 46545766..e777c0ef 100644
--- a/web/src/views/mobile/Profile.vue
+++ b/web/src/views/mobile/Profile.vue
@@ -1,142 +1,314 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ form.power }}
-
-
-
-
-
- {{ dateFormat(form.expired_time) }}
-
-
-
-
-
-
-
-
- 修改密码
-
-
- 退出登录
-
-
-
- 设置
-
-
-
-
-
-
充值套餐
-
-
-
{{ item.name }}
-
-
-
-
- 支付宝
-
-
- 微信支付
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
{{ form.power || 0 }}
+
剩余算力
+
+
+
+
+
+
+
+
{{ vipDays }}
+
VIP天数
+
+
+
+
+
+
+
+
{{ inviteCount }}
+
邀请好友
+
+
+
+
+
+
+
+
快捷操作
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 退出登录
+
+
+
+
+
+
版本 v{{ appVersion }}
+
© 2024 {{ title }}. All rights reserved.
+
+
+
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
- store.setTheme(val ? 'dark' : 'light')"
- />
-
-
-
-
-
- store.setChatStream(val)" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ store.setTheme(val ? 'dark' : 'light')"
+ />
+
+
+
+
+ store.setChatStream(val)"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
{{ title }}
+
+ 专业的AI创作平台,提供对话、绘画、音乐、视频等多种AI服务,让创作更简单、更高效。
+
+
+
版本:v{{ appVersion }}
+
更新时间:2024-01-01
+
+
+
+
+
+
@@ -148,244 +320,632 @@ import { httpGet, httpPost } from '@/utils/http'
import { dateFormat, showLoginDialog } from '@/utils/libs'
import { ElMessage } from 'element-plus'
import { showFailToast, showLoadingToast, showNotify, showSuccessToast } from 'vant'
-import { onMounted, ref } from 'vue'
+import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
const form = ref({
+ id: 0,
username: 'GeekMaster',
nickname: '极客学长@001',
mobile: '1300000000',
avatar: '',
power: 0,
+ expired_time: 0,
})
+
const fileList = ref([
{
- url: '/images/user-info.png',
+ url: '/images/avatar/default.jpg',
message: '上传中...',
},
])
-const products = ref([])
-const vipMonthPower = ref(0)
-const payWays = ref({})
const router = useRouter()
-const userId = ref(0)
const isLogin = ref(false)
const showSettings = ref(false)
+const showPasswordDialog = ref(false)
+const showAvatarOptions = ref(false)
+const showAbout = ref(false)
+const showLogoutConfirm = ref(false)
const store = useSharedStore()
const stream = ref(store.chatStream)
const dark = ref(store.theme === 'dark')
-const menuList = ref({})
+const title = ref(import.meta.env.VITE_TITLE)
+const appVersion = ref('2.1.0')
-onMounted(() => {
- checkSession()
- .then((user) => {
- userId.value = user.id
- isLogin.value = true
- httpGet('/api/user/profile')
- .then((res) => {
- form.value = res.data
- fileList.value[0].url = form.value.avatar
- })
- .catch((e) => {
- console.log(e.message)
- showFailToast('获取用户信息失败')
- })
- })
- .catch(() => {})
+// 新增状态
+const notifications = ref(true)
+const autoSave = ref(true)
+const inviteCount = ref(0)
+const passwordForm = ref()
- // 获取产品列表
- httpGet('/api/product/list')
- .then((res) => {
- products.value = res.data
- })
- .catch((e) => {
- showFailToast('获取产品套餐失败:' + e.message)
- })
-
- getSystemInfo()
- .then((res) => {
- vipMonthPower.value = res.data['vip_month_power']
- })
- .catch((e) => {
- showFailToast('获取系统配置失败:' + e.message)
- })
-
- httpGet('/api/payment/payWays')
- .then((res) => {
- payWays.value = res.data
- })
- .catch((e) => {
- ElMessage.error('获取支付方式失败:' + e.message)
- })
-
- getMenuList()
-})
-
-// 获取菜单列表
-const getMenuList = () => {
- httpGet('/api/menu/list')
- .then((res) => {
- res.data.forEach((item) => {
- menuList.value[item.url] = item
- })
- })
- .catch((e) => {
- showFailToast('获取菜单列表失败:' + e.message)
- })
-}
-
-const showPasswordDialog = ref(false)
+// 密码相关
const pass = ref({
old: '',
new: '',
renew: '',
})
-const beforeClose = (action) => {
- new Promise((resolve) => {
- resolve(action === 'confirm')
- })
+// 密码验证规则
+const passwordRules = [
+ { required: true, message: '请输入新密码' },
+ { min: 8, max: 16, message: '密码长度为8-16个字符' }
+]
+
+// 计算属性
+const isVip = computed(() => {
+ const now = Date.now()
+ const expiredTime = form.value.expired_time ? form.value.expired_time * 1000 : 0
+ return expiredTime > now
+})
+
+const vipDays = computed(() => {
+ if (!isVip.value) return 0
+ const now = Date.now()
+ const expiredTime = form.value.expired_time * 1000
+ return Math.ceil((expiredTime - now) / (24 * 60 * 60 * 1000))
+})
+
+onMounted(() => {
+ getSystemInfo()
+ .then((res) => {
+ title.value = res.data.title
+ })
+ .catch((e) => {
+ console.error('获取系统配置失败:', e.message)
+ })
+
+ checkSession()
+ .then((user) => {
+ isLogin.value = true
+ form.value = { ...form.value, ...user }
+ fileList.value[0].url = user.avatar || '/images/avatar/default.jpg'
+
+ // 获取用户详细信息
+ fetchUserProfile()
+ fetchUserStats()
+ })
+ .catch(() => {
+ isLogin.value = false
+ })
+})
+
+// 获取用户详细信息
+const fetchUserProfile = () => {
+ httpGet('/api/user/profile')
+ .then((res) => {
+ form.value = { ...form.value, ...res.data }
+ fileList.value[0].url = res.data.avatar || '/images/avatar/default.jpg'
+ })
+ .catch((e) => {
+ console.error('获取用户信息失败:', e.message)
+ })
+}
+
+// 获取用户统计信息
+const fetchUserStats = () => {
+ // 模拟数据,实际项目中应调用API
+ inviteCount.value = Math.floor(Math.random() * 20)
+}
+
+// 确认密码验证
+const validateConfirmPassword = (value) => {
+ if (value !== pass.value.new) {
+ return Promise.reject(new Error('两次输入的密码不一致'))
+ }
+ return Promise.resolve()
+}
+
+// 重置密码表单
+const resetPasswordForm = () => {
+ pass.value = {
+ old: '',
+ new: '',
+ renew: '',
+ }
+ if (passwordForm.value) {
+ passwordForm.value.resetValidation()
+ }
}
// 提交修改密码
const updatePass = () => {
- if (pass.value.old === '') {
+ if (!passwordForm.value) {
+ updatePasswordAPI()
+ return
+ }
+
+ passwordForm.value.validate().then(() => {
+ updatePasswordAPI()
+ }).catch((errors) => {
+ console.log('表单验证失败:', errors)
+ })
+}
+
+const updatePasswordAPI = () => {
+ if (!pass.value.old) {
return showNotify({ type: 'danger', message: '请输入旧密码' })
}
if (!pass.value.new || pass.value.new.length < 8) {
- return showNotify({ type: 'danger', message: '密码的长度为8-16个字符' })
+ return showNotify({ type: 'danger', message: '密码长度为8-16个字符' })
}
if (pass.value.renew !== pass.value.new) {
return showNotify({ type: 'danger', message: '两次输入密码不一致' })
}
+
+ showLoadingToast({
+ message: '正在修改密码...',
+ forbidClick: true,
+ })
+
httpPost('/api/user/password', {
old_pass: pass.value.old,
password: pass.value.new,
repass: pass.value.renew,
})
.then(() => {
- showSuccessToast('更新成功!')
+ showSuccessToast('密码修改成功!')
showPasswordDialog.value = false
+ resetPasswordForm()
})
.catch((e) => {
- showFailToast('更新失败,' + e.message)
- showPasswordDialog.value = false
+ showFailToast('密码修改失败:' + e.message)
})
}
-const pay = (product, payWay) => {
- if (!isLogin.value) {
- return showLoginDialog(router)
+// 头像选择
+const selectAvatar = (type) => {
+ showAvatarOptions.value = false
+
+ switch (type) {
+ case 'camera':
+ // 调用相机
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
+ showNotify({ type: 'primary', message: '正在启动相机...' })
+ } else {
+ showNotify({ type: 'warning', message: '您的设备不支持相机功能' })
+ }
+ break
+ case 'album':
+ // 从相册选择
+ const input = document.createElement('input')
+ input.type = 'file'
+ input.accept = 'image/*'
+ input.onchange = (e) => {
+ const file = e.target.files[0]
+ if (file) {
+ // 这里应该上传到服务器
+ const reader = new FileReader()
+ reader.onload = (e) => {
+ fileList.value[0].url = e.target.result
+ showSuccessToast('头像更新成功')
+ }
+ reader.readAsDataURL(file)
+ }
+ }
+ input.click()
+ break
+ case 'default':
+ // 使用默认头像
+ fileList.value[0].url = '/images/avatar/default.jpg'
+ showSuccessToast('已设置为默认头像')
+ break
}
+}
+// 退出登录
+const logout = function () {
showLoadingToast({
- message: '正在创建订单',
+ message: '正在退出...',
forbidClick: true,
})
- let host = process.env.VUE_APP_API_HOST
- if (host === '') {
- host = `${location.protocol}//${location.host}`
- }
- httpPost(`${process.env.VUE_APP_API_HOST}/api/payment/doPay`, {
- product_id: product.id,
- pay_way: payWay.pay_way,
- pay_type: payWay.pay_type,
- user_id: userId.value,
- host: host,
- device: 'wechat',
- })
- .then((res) => {
- location.href = res.data
- })
- .catch((e) => {
- showFailToast('生成支付订单失败:' + e.message)
- })
-}
-const logout = function () {
httpGet('/api/user/logout')
.then(() => {
removeUserToken()
store.setIsLogin(false)
- router.push('/')
+ isLogin.value = false
+ showSuccessToast('退出登录成功')
+ showLogoutConfirm.value = false
+
+ // 清除用户数据
+ form.value = {
+ id: 0,
+ username: '',
+ nickname: '',
+ mobile: '',
+ avatar: '',
+ power: 0,
+ expired_time: 0,
+ }
+ fileList.value[0].url = '/images/avatar/default.jpg'
})
- .catch(() => {
- showFailToast('注销失败!')
+ .catch((e) => {
+ showFailToast('退出登录失败:' + e.message)
})
}
-
diff --git a/web/src/views/mobile/Settings.vue b/web/src/views/mobile/Settings.vue
new file mode 100644
index 00000000..42a040cf
--- /dev/null
+++ b/web/src/views/mobile/Settings.vue
@@ -0,0 +1,631 @@
+
+
+
+
+
+
+
+
个人设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
应用设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ currentLanguage.name }}
+
+
+
+
+
+
+
+
聊天设置
+
+
+
+
+
+
+ {{ currentModel.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ currentSendMode.name }}
+
+
+
+
+
+
+
+
隐私与安全
+
+
+
+
+
+
+ {{ cacheSize }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
其他
+
+
+
+
+
+
+ v{{ appVersion }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
{{ appName }}
+
+ 专业的AI创作平台,提供对话、绘画、音乐、视频等多种AI服务,让创作更简单、更高效。
+
+
+
版本:v{{ appVersion }}
+
更新时间:2024-01-01
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/views/mobile/Tools.vue b/web/src/views/mobile/Tools.vue
new file mode 100644
index 00000000..f01ee6c9
--- /dev/null
+++ b/web/src/views/mobile/Tools.vue
@@ -0,0 +1,743 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
推荐工具
+
+
+
+
+
+
+
+
{{ tool.name }}
+
{{ tool.desc }}
+
+ 立即使用
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 新建
+ 保存
+ 导出
+ 关闭
+
+
+
+
+
+
思维导图工具
+
功能开发中,敬请期待
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file