Merge branch 'feature/dsw' of https://git.nianxx.cn/duanshuwen/zn-ai into feature/zoujing

This commit is contained in:
2025-12-03 18:19:58 +08:00
25 changed files with 298 additions and 19 deletions

17
.editorconfig Normal file
View File

@@ -0,0 +1,17 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
tab_width = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
insert_final_newline = false

1
.npmrc
View File

@@ -1,2 +1,3 @@
# registry=https://registry.npmmirror.com
electron_mirror=https://npmmirror.com/mirrors/electron/ electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,30 @@
<template>
<div class="w-[80px] h-full box-border pt-[12px] pb-[12px] flex flex-col items-center justify-center">
<div :class="['flex flex-col gap-[16px]', {'mt-auto mb-[8px] shrink-1': item.id === 7}]" v-for="(item) in menus" :key="item.id">
<div :class="['cursor-pointer flex flex-col items-center justify-center']" @click="handleClick(item)">
<div :class="['box-border rounded-[16px] p-[8px]', {'bg-white': item.id === currentId}]">
<component :is="item.icon" :color="item.id === currentId ? item.activeColor : item.color" :class="['w-[32px] h-[32px]']" />
</div>
<div :class="['text-[14px] mt-[4px] mb-[8px]', item.id === currentId ? `text-[${item.activeColor}]` : item.color]">
{{ item.name }}
</div>
</div>
</div>
<div class="w-[48px] h-[48px] rounded-full overflow-hidden">
<img class="w-full h-full object-cover" src="@assets/images/login/black_logo.png" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { menus } from '@constant/menus'
const currentId = ref(null)
const handleClick = (item: any) => {
currentId.value = item.id
}
</script>
<style></style>

View File

@@ -0,0 +1,9 @@
<template>
<div class="task-list w-[392px] h-full rounded-[16px] bg-white">
</div>
</template>
<script setup lang="ts"></script>
<style></style>

View File

@@ -0,0 +1,62 @@
import { RiHomeLine, RiFileListLine, RiHotelLine, RiChatQuoteLine, RiBarChartBoxAiLine, RiMoreLine, RiSettingsLine } from '@remixicon/vue'
// 菜单列表申明
export interface MenuItem {
id: number
name: string
icon: any
color: string
activeColor: string
}
export const menus: MenuItem[] = [
{
id: 1,
name: '首页',
icon: RiHomeLine,
color: '#525866',
activeColor: '#2B7FFF',
},
{
id: 2,
name: '订单',
icon: RiFileListLine,
color: '#525866',
activeColor: '#2B7FFF',
},
{
id: 3,
name: '库存',
icon: RiHotelLine,
color: '#525866',
activeColor: '#2B7FFF',
},
{
id: 4,
name: '评价',
icon: RiChatQuoteLine,
color: '#525866',
activeColor: '#2B7FFF',
},
{
id: 5,
name: '数据看板',
icon: RiBarChartBoxAiLine,
color: '#525866',
activeColor: '#2B7FFF',
},
{
id: 6,
name: '更多',
icon: RiMoreLine,
color: '#525866',
activeColor: '#2B7FFF',
},
{
id: 7,
name: '设置',
icon: RiSettingsLine,
color: '#525866',
activeColor: '#2B7FFF',
},
]

View File

@@ -0,0 +1,52 @@
import xc from '@assets/images/rate/xc.png'
import qne from '@assets/images/rate/qne.png'
import fz from '@assets/images/rate/fz.png'
import mt from '@assets/images/rate/mt.png'
import dy from '@assets/images/rate/dy.png'
// 菜单列表申明
interface Item {
id: number
name: string
icon: any
total: number,
score: number
}
export const channels: Item[] = [
{
id: 1,
name: '携程',
icon: xc,
total: 110,
score: 4.5
},
{
id: 2,
name: '去哪儿',
icon: qne,
total: 100,
score: 4.5
},
{
id: 3,
name: '飞猪',
icon: fz,
total: 90,
score: 4.5
},
{
id: 4,
name: '美团',
icon: mt,
total: 80,
score: 4.5
},
{
id: 5,
name: '抖音',
icon: dy,
total: 70,
score: 4.5
}
]

View File

@@ -1,2 +1,7 @@
declare module "@store/counter"; declare module "@store/counter";
declare module "@utils/request"; declare module "@utils/request";
declare module "@assets/images/*";
declare module "@constant/rate";
declare module "@constant/menus";
declare module "@remixicon/vue";
declare module "vue-router";

View File

@@ -6,3 +6,7 @@ body {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.bg {
background: linear-gradient( 180deg, #EFF6FF 0%, #F5F7FA 40%);
}

View File

@@ -0,0 +1,16 @@
<template>
<div class="bg box-border w-full h-screen flex pt-[8px] pb-[8px] pl-[8px]">
<div class="flex-1 h-full">
<slot></slot>
</div>
<TaskList />
<Menus />
</div>
</template>
<script setup lang="ts" name="Layout">
import TaskList from '@/components/TaskList/index.vue'
import Menus from '@/components/Menus/index.vue'
</script>

View File

@@ -3,6 +3,7 @@ import { createApp } from "vue";
import { createPinia } from "pinia"; import { createPinia } from "pinia";
import router from "./router"; import router from "./router";
import App from "./App.vue"; import App from "./App.vue";
import Layout from "./layout/index.vue";
// 创建 Vue 应用实例 // 创建 Vue 应用实例
const app = createApp(App); const app = createApp(App);
@@ -10,6 +11,9 @@ const app = createApp(App);
// 使用 Pinia 状态管理 // 使用 Pinia 状态管理
app.use(createPinia()); app.use(createPinia());
// 注册 Layout 组件
app.component('Layout', Layout);
// 使用 Vue Router // 使用 Vue Router
app.use(router); app.use(router);

View File

@@ -12,6 +12,18 @@ const routes = [
component: () => import("@/views/browser/BrowserLayout.vue"), component: () => import("@/views/browser/BrowserLayout.vue"),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{
path: "/home",
name: "Home",
component: () => import("@/views/home/index.vue"),
meta: { requiresAuth: true },
},
{
path: "/rate",
name: "Rate",
component: () => import("@/views/rate/index.vue"),
meta: { requiresAuth: true },
},
{ {
path: "/about", path: "/about",
name: "About", name: "About",
@@ -24,16 +36,18 @@ const router = createRouter({
routes, routes,
}); });
router.beforeEach((to, _from, next) => { router.beforeEach((to: any, from: any, next: any) => {
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
if (to.meta && (to.meta as any).requiresAuth && !token) { if (to.meta && (to.meta as any).requiresAuth && !token) {
next({ path: "/login" }); next({ path: "/login" });
return; return;
} }
if (token && to.path !== "/browser") {
next({ path: "/browser" }); if (token && to.path !== "/rate") {
next({ path: "/rate" });
return; return;
} }
next(); next();
}); });

View File

View File

@@ -1,18 +1,13 @@
<template> <template>
<div class="home"> <Layout>
<h2 class="text-2xl font-bold mb-4">欢迎使用智念科技 AI</h2> <div class="bg-white w-[952px] h-full rounded-[16px]">
<div class="bg-white p-6"> 454646
<button
class="bg-blue-300 hover:bg-blue-500 text-white px-4 py-2 rounded mr-2"
@click="openBaidu"
>
打开百度
</button>
</div>
</div> </div>
</Layout>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const openBaidu = () => { const openBaidu = () => {
(window as any).ipcAPI?.openBaidu() (window as any).ipcAPI?.openBaidu()

View File

@@ -41,6 +41,19 @@
/> />
</div> </div>
<p v-if="errors.password" class="mt-1 text-xs text-red-500">{{ errors.password }}</p> <p v-if="errors.password" class="mt-1 text-xs text-red-500">{{ errors.password }}</p>
<!-- 验证码 -->
<div class="font-[14px] text-gray-700 mb-[8px] mt-[12px]">验证码</div>
<div class="flex items-center gap-2 border rounded-[10px] box-border px-[12px] py-[10px]">
<input
class="flex-1 focus-visible:outline-none"
type="text"
v-model.trim="form.code"
placeholder="请输入验证码"
@keyup.enter="onSubmit"
/>
<img class="w-[80px] h-[40px]" src="" />
</div>
<p v-if="errors.code" class="mt-1 text-xs text-red-500">{{ errors.code }}</p>
<!-- 记住密码|忘记密码 --> <!-- 记住密码|忘记密码 -->
<div class="flex items-center justify-between mb-[24px] mt-[24px]"> <div class="flex items-center justify-between mb-[24px] mt-[24px]">
@@ -79,7 +92,7 @@
<!-- Copy Right --> <!-- Copy Right -->
<div class="text-[14px] text-gray-500 text-center mt-[24px] mt-auto"> <div class="text-[14px] text-gray-500 text-center mt-[24px] mt-auto">
© 2025 智能体智能科技有限公司 版权所有 © 2025 贵州智念科技服务有限公司 版权所有
</div> </div>
</div> </div>
@@ -94,8 +107,8 @@ import { login as apiLogin } from "@/renderer/api/login";
import { RiUser3Fill , RiKey2Fill} from '@remixicon/vue' import { RiUser3Fill , RiKey2Fill} from '@remixicon/vue'
const router = useRouter(); const router = useRouter();
const form = reactive({ account: "", password: "", agreement: "" }); const form = reactive({ account: "", password: "", agreement: "", code: "" });
const errors = reactive<{ account?: string; password?: string }>({}); const errors = reactive<{ account?: string; password?: string; code?: string }>({});
const loading = ref(false); const loading = ref(false);
const showPwd = ref(false); const showPwd = ref(false);
@@ -118,7 +131,7 @@ const onSubmit = async () => {
// const token = res && (res.token || res.data?.token || res.access_token); // const token = res && (res.token || res.data?.token || res.access_token);
// if (!token) throw new Error("登录失败"); // if (!token) throw new Error("登录失败");
// localStorage.setItem("token", token); // localStorage.setItem("token", token);
await (window as any).ipcAPI.app.setFrameless('/browser') await (window as any).ipcAPI.app.setFrameless('/home')
} finally { } finally {
// loading.value = false; // loading.value = false;
} }

View File

@@ -0,0 +1,27 @@
<template>
<div class="grid grid-cols-5 gap-[15px]">
<div class="bg-[#F5F7FA] box-border flex flex-col p-[16px] rounded-[12px]" v-for="item in channels" :key="item.id">
<div class="flex items-center mb-[8px]">
<img :src="item.icon" class="w-[24px] h-[24px] mr-[8px]">
<span class="text-[16px] font-400 text-[#171717] leading-[24px]">
{{ item.name }}
</span>
</div>
<div class="text-[14px] font-400 text-[#171717] leading-[20px] mb-[4px]">
{{ item.total }}
</div>
<div class="flex items-center">
<RiStarFill :color="i <= item.score ? '#FA7319' : '#CACFD8'" class="w-[20px] h-[20px] mr-[2px]" v-for="i in 5" :key="i" />
<span class="text-[14px] text-[#525866] leading-[20px] pl-[2px]">
{{ item.score }}
</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { channels } from '@constant/rate'
import { RiStarFill } from '@remixicon/vue'
</script>

View File

@@ -0,0 +1,14 @@
<template>
<div class="box-border border-b-[1px] border-b-[#E5E8EE] mb-[20px] pb-[20px]">
<span class="text-[24px] font-500 text-[#171717] leading-[32px] mr-[8px]">
评价
</span>
<span class="text-[12px] font-400 text-[#99A0AE] leading-[16px]">
评价数据智能整理精准优化服务
</span>
</div>
</template>
<script></script>
<style></style>

View File

@@ -0,0 +1,14 @@
<template>
<Layout>
<div class="bg-white box-border w-[936px] h-full rounded-[16px] p-[20px]">
<RateTitleSection />
<RatePanelSection />
</div>
</Layout>
</template>
<script setup lang="ts" name="Rate">
import RateTitleSection from './components/RateTitleSection/index.vue'
import RatePanelSection from './components/RatePanelSection/index.vue'
</script>

View File

@@ -17,6 +17,7 @@
"paths": { "paths": {
"@/*": ["./src/*"], "@/*": ["./src/*"],
"@store/*": ["src/renderer/store/*"], "@store/*": ["src/renderer/store/*"],
"@constant/*": ["src/renderer/constant/*"],
"@utils/*": ["src/renderer/utils/*"], "@utils/*": ["src/renderer/utils/*"],
"@api/*": ["src/renderer/api/*"], "@api/*": ["src/renderer/api/*"],
"@modules/*": ["src/electron/main/modules/*"], "@modules/*": ["src/electron/main/modules/*"],

View File

@@ -16,6 +16,7 @@ export default defineConfig({
"@": resolve(__dirname, "./src/renderer"), "@": resolve(__dirname, "./src/renderer"),
"@assets": resolve(__dirname, "./src/assets"), "@assets": resolve(__dirname, "./src/assets"),
"@store": resolve(__dirname, "./src/renderer/store"), "@store": resolve(__dirname, "./src/renderer/store"),
"@constant": resolve(__dirname, "./src/renderer/constant"),
"@utils": resolve(__dirname, "./src/renderer/utils"), "@utils": resolve(__dirname, "./src/renderer/utils"),
"@api": resolve(__dirname, "./src/renderer/api"), "@api": resolve(__dirname, "./src/renderer/api"),
}, },