Merge branch 'main' of https://git.nianxx.cn/duanshuwen/zn-ai into feature/lishaohua
# Conflicts: # src/renderer/components/TaskList/index.vue # src/renderer/views/home/index.vue
This commit is contained in:
@@ -1,18 +1,5 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useCounterStore } from "@store/counter";
|
||||
|
||||
// 使用 Pinia store
|
||||
const counterStore = useCounterStore();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#app {
|
||||
@apply min-h-screen bg-gray-100;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
@@ -2,21 +2,39 @@
|
||||
<div class="h-screen w-screen bg-gray-100">
|
||||
<div class="flex items-center gap-1 px-3 h-10 border-b bg-white" style="-webkit-app-region: drag">
|
||||
<div class="flex items-center gap-2 mr-2">
|
||||
<button class="w-3 h-3 rounded-full bg-red-500" style="-webkit-app-region: no-drag" @click="onCloseWindow"></button>
|
||||
<button class="w-3 h-3 rounded-full bg-yellow-400" style="-webkit-app-region: no-drag" @click="onMinimizeWindow"></button>
|
||||
<button class="w-3 h-3 rounded-full bg-green-500" style="-webkit-app-region: no-drag" @click="onMaximizeWindow"></button>
|
||||
<button class="w-3 h-3 rounded-full bg-red-500" style="-webkit-app-region: no-drag" @click="onCloseWindow">
|
||||
<RiCloseLine />
|
||||
</button>
|
||||
<button class="w-3 h-3 rounded-full bg-yellow-400" style="-webkit-app-region: no-drag"
|
||||
@click="onMinimizeWindow">
|
||||
<RiSubtractLine />
|
||||
</button>
|
||||
<button class="w-3 h-3 rounded-full bg-green-500" style="-webkit-app-region: no-drag" @click="onMaximizeWindow">
|
||||
<RiSquareLine />
|
||||
</button>
|
||||
</div>
|
||||
<div v-for="t in tabs" :key="t.id" class="flex items-center px-2 py-1 rounded cursor-pointer" :class="t.id===activeId?'bg-blue-100':'hover:bg-gray-200'" @click="onSwitch(t.id)" style="-webkit-app-region: no-drag">
|
||||
<div v-for="t in tabs" :key="t.id" class="flex items-center px-2 py-1 rounded cursor-pointer"
|
||||
:class="t.id === activeId ? 'bg-blue-100' : 'hover:bg-gray-200'" @click="onSwitch(t.id)"
|
||||
style="-webkit-app-region: no-drag">
|
||||
<span class="text-sm mr-2 truncate max-w-[160px]">{{ t.title || t.url || '新标签页' }}</span>
|
||||
<button class="text-gray-600 hover:text-black" style="-webkit-app-region: no-drag" @click.stop="onCloseTabId(t.id)">✕</button>
|
||||
<button class="text-gray-600 hover:text-black" style="-webkit-app-region: no-drag"
|
||||
@click.stop="onCloseTabId(t.id)">✕</button>
|
||||
</div>
|
||||
<button class="ml-2 px-2 py-1 rounded bg-gray-200 hover:bg-gray-300" style="-webkit-app-region: no-drag" @click="onNewTab">+</button>
|
||||
<button class="ml-2 px-2 py-1 rounded bg-gray-200 hover:bg-gray-300" style="-webkit-app-region: no-drag"
|
||||
@click="onNewTab">+</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 px-3 h-12 border-b bg-gray-50">
|
||||
<button class="px-2 py-1 bg-gray-200 rounded" @click="onBack" :disabled="!active?.canGoBack">←</button>
|
||||
<button class="px-2 py-1 bg-gray-200 rounded" @click="onForward" :disabled="!active?.canGoForward">→</button>
|
||||
<button class="px-2 py-1 bg-gray-200 rounded" @click="onReload">⟳</button>
|
||||
<input class="flex-1 px-3 py-1 border rounded-full" v-model="address" @keyup.enter="onNavigate" placeholder="输入地址后回车" />
|
||||
<button class="px-2 py-1 bg-gray-200 rounded" @click="onBack" :disabled="!active?.canGoBack">
|
||||
<RiArrowLeftSLine />
|
||||
</button>
|
||||
<button class="px-2 py-1 bg-gray-200 rounded" @click="onForward" :disabled="!active?.canGoForward">
|
||||
<RiArrowRightSLine />
|
||||
</button>
|
||||
<button class="px-2 py-1 bg-gray-200 rounded" @click="onReload">
|
||||
<RiResetLeftLine />
|
||||
</button>
|
||||
<input class="flex-1 px-3 py-1 border rounded-full" v-model="address" @keyup.enter="onNavigate"
|
||||
placeholder="输入地址后回车" />
|
||||
</div>
|
||||
<div class="h-[calc(100vh-5.5rem)]"></div>
|
||||
</div>
|
||||
@@ -24,6 +42,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, computed } from 'vue'
|
||||
import { RiArrowRightSLine, RiArrowLeftSLine, RiResetLeftLine, RiCloseLine, RiSubtractLine, RiSquareLine } from '@remixicon/vue'
|
||||
|
||||
type TabInfo = { id: string; url: string; title: string; isLoading: boolean; canGoBack: boolean; canGoForward: boolean }
|
||||
|
||||
@@ -99,32 +118,31 @@ const onMaximizeWindow = () => (window as any).ipcAPI.window.maximize()
|
||||
|
||||
onMounted(async () => {
|
||||
await syncList()
|
||||
;(window as any).ipcAPI.tabs.on('tab-created', (info: TabInfo) => {
|
||||
const i = tabs.findIndex(t => t.id === info.id)
|
||||
if (i === -1) tabs.push(info)
|
||||
activeId.value = info.id
|
||||
refreshActiveAddress()
|
||||
})
|
||||
;(window as any).ipcAPI.tabs.on('tab-updated', (info: TabInfo) => {
|
||||
const i = tabs.findIndex(t => t.id === info.id)
|
||||
if (i >= 0) tabs[i] = info
|
||||
if (activeId.value === info.id) refreshActiveAddress()
|
||||
})
|
||||
;(window as any).ipcAPI.tabs.on('tab-closed', ({ tabId }: { tabId: string }) => {
|
||||
const i = tabs.findIndex(t => t.id === tabId)
|
||||
if (i >= 0) tabs.splice(i, 1)
|
||||
if (activeId.value === tabId) {
|
||||
const next = tabs[0]
|
||||
activeId.value = next?.id || ''
|
||||
; (window as any).ipcAPI.tabs.on('tab-created', (info: TabInfo) => {
|
||||
const i = tabs.findIndex(t => t.id === info.id)
|
||||
if (i === -1) tabs.push(info)
|
||||
activeId.value = info.id
|
||||
refreshActiveAddress()
|
||||
}
|
||||
})
|
||||
;(window as any).ipcAPI.tabs.on('tab-switched', ({ tabId }: { tabId: string }) => {
|
||||
activeId.value = tabId
|
||||
refreshActiveAddress()
|
||||
})
|
||||
})
|
||||
; (window as any).ipcAPI.tabs.on('tab-updated', (info: TabInfo) => {
|
||||
const i = tabs.findIndex(t => t.id === info.id)
|
||||
if (i >= 0) tabs[i] = info
|
||||
if (activeId.value === info.id) refreshActiveAddress()
|
||||
})
|
||||
; (window as any).ipcAPI.tabs.on('tab-closed', ({ tabId }: { tabId: string }) => {
|
||||
const i = tabs.findIndex(t => t.id === tabId)
|
||||
if (i >= 0) tabs.splice(i, 1)
|
||||
if (activeId.value === tabId) {
|
||||
const next = tabs[0]
|
||||
activeId.value = next?.id || ''
|
||||
refreshActiveAddress()
|
||||
}
|
||||
})
|
||||
; (window as any).ipcAPI.tabs.on('tab-switched', ({ tabId }: { tabId: string }) => {
|
||||
activeId.value = tabId
|
||||
refreshActiveAddress()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
@@ -1,12 +1,15 @@
|
||||
<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="w-[80px] h-full box-border 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 :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]">
|
||||
|
||||
<div
|
||||
:class="['text-[14px] mt-[4px] mb-[8px]', item.id === currentId ? `text-[${item.activeColor}]` : item.color]">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -20,11 +23,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { menus } from '@constant/menus'
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const currentId = ref(null)
|
||||
const handleClick = (item: any) => {
|
||||
const router = useRouter();
|
||||
const currentId = ref(1)
|
||||
|
||||
const handleClick = async (item: any) => {
|
||||
console.log("🚀 ~ handleClick ~ item:", item)
|
||||
currentId.value = item.id
|
||||
router.push(item.url);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style></style>
|
||||
|
||||
49
src/renderer/components/Pagination/index.vue
Normal file
49
src/renderer/components/Pagination/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" class="mt-[15px]"
|
||||
:pager-count="5" :page-sizes="pageSizes" :current-page="current" background :page-size="size" :layout="layout"
|
||||
:total="total">
|
||||
</el-pagination>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="pagination">
|
||||
const emit = defineEmits(['sizeChange', 'currentChange']);
|
||||
|
||||
const props = defineProps({
|
||||
current: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
pageSizes: {
|
||||
type: Array as () => number[],
|
||||
default: () => {
|
||||
return [10, 20, 50, 100, 200];
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper',
|
||||
},
|
||||
});
|
||||
// 分页改变
|
||||
const sizeChangeHandle = (val: number) => {
|
||||
emit('sizeChange', val);
|
||||
};
|
||||
// 分页改变
|
||||
const currentChangeHandle = (val: number) => {
|
||||
emit('currentChange', val);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-pagination__sizes) {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -7,7 +7,45 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TaskList from './List.vue'
|
||||
import TaskList from './List.vue';
|
||||
import { ref, reactive } from "vue";
|
||||
const tabs = reactive([{
|
||||
name: '待处理',
|
||||
value: 1,
|
||||
total: 10,
|
||||
}, {
|
||||
name: '已处理',
|
||||
value: 2,
|
||||
total: 99,
|
||||
}])
|
||||
const active = ref(1);
|
||||
const changeTab = (val: number) => {
|
||||
active.value = val;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style scoped>
|
||||
.task-tab .text {
|
||||
color: #525866;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.task-tab .active {
|
||||
position: relative;
|
||||
color: #2B7FFF;
|
||||
background: #FFFFFF;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.task-tab .active::after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: '';
|
||||
border-radius: 8px;
|
||||
border: 1px solid #2B7FFF;
|
||||
}
|
||||
</style>
|
||||
25
src/renderer/components/TitleSection/index.vue
Normal file
25
src/renderer/components/TitleSection/index.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<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]">
|
||||
{{ title }}
|
||||
</span>
|
||||
<span class="text-[12px] font-400 text-[#99A0AE] leading-[16px]">
|
||||
{{ desc }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
desc: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
46
src/renderer/constant/channel.ts
Normal file
46
src/renderer/constant/channel.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import pms from '@assets/images/channel/pms.png'
|
||||
import xc from '@assets/images/channel/xc.png'
|
||||
import qne from '@assets/images/channel/qne.png'
|
||||
import fz from '@assets/images/channel/fz.png'
|
||||
import mt from '@assets/images/channel/mt.png'
|
||||
import dy from '@assets/images/channel/dy.png'
|
||||
|
||||
// 菜单列表申明
|
||||
interface Item {
|
||||
id: number
|
||||
name: string
|
||||
icon: any
|
||||
}
|
||||
|
||||
export const channel: Item[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'PMS',
|
||||
icon: pms,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '携程',
|
||||
icon: xc,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '去哪儿',
|
||||
icon: qne,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '飞猪',
|
||||
icon: fz,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '美团',
|
||||
icon: mt,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '抖音',
|
||||
icon: dy,
|
||||
}
|
||||
]
|
||||
@@ -7,6 +7,7 @@ export interface MenuItem {
|
||||
icon: any
|
||||
color: string
|
||||
activeColor: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export const menus: MenuItem[] = [
|
||||
@@ -16,6 +17,7 @@ export const menus: MenuItem[] = [
|
||||
icon: RiHomeLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
url: '/home',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@@ -23,6 +25,7 @@ export const menus: MenuItem[] = [
|
||||
icon: RiFileListLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
url: '/order',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@@ -30,6 +33,7 @@ export const menus: MenuItem[] = [
|
||||
icon: RiHotelLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
url: '/stock',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
@@ -37,6 +41,7 @@ export const menus: MenuItem[] = [
|
||||
icon: RiChatQuoteLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
url: '/rate',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
@@ -44,6 +49,7 @@ export const menus: MenuItem[] = [
|
||||
icon: RiBarChartBoxAiLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
url: '/dashboard',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
@@ -51,6 +57,7 @@ export const menus: MenuItem[] = [
|
||||
icon: RiMoreLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
url: '/more',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
@@ -58,5 +65,6 @@ export const menus: MenuItem[] = [
|
||||
icon: RiSettingsLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
url: '/setting',
|
||||
},
|
||||
]
|
||||
100
src/renderer/constant/rate.ts
Normal file
100
src/renderer/constant/rate.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
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
|
||||
}
|
||||
]
|
||||
|
||||
interface ListItem {
|
||||
id: number
|
||||
userName: string
|
||||
content: string
|
||||
createTime: string
|
||||
channelName: string
|
||||
channelIcon: any
|
||||
imageList: any[]
|
||||
score: number
|
||||
}
|
||||
|
||||
export const commentList: ListItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
userName: '用户98882',
|
||||
content: '环境不错,温泉也很好',
|
||||
createTime: '2022-11-09 16:24:30',
|
||||
channelName: '携程',
|
||||
channelIcon: xc,
|
||||
imageList: [
|
||||
xc,
|
||||
xc,
|
||||
xc,
|
||||
xc,
|
||||
xc,
|
||||
xc,
|
||||
],
|
||||
score: 5,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
userName: '用户98882',
|
||||
content: '环境不错,温泉也很好',
|
||||
createTime: '2022-11-09 16:24:30',
|
||||
channelName: '抖音',
|
||||
channelIcon: dy,
|
||||
imageList: [
|
||||
dy,
|
||||
dy,
|
||||
dy,
|
||||
dy,
|
||||
dy,
|
||||
dy,
|
||||
],
|
||||
score: 5,
|
||||
},
|
||||
]
|
||||
46
src/renderer/constant/system-config.ts
Normal file
46
src/renderer/constant/system-config.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { RiUserLine, RiHotelLine, RiHotelBedLine, RiSettingsLine } from '@remixicon/vue'
|
||||
|
||||
// 菜单列表申明
|
||||
export interface MenuItem {
|
||||
id: number
|
||||
name: string
|
||||
icon: any
|
||||
color: string
|
||||
activeColor: string
|
||||
componentName: string
|
||||
}
|
||||
|
||||
export const systemMenus: MenuItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: '账号',
|
||||
icon: RiUserLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
componentName: 'AccountSetting',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '渠道管理',
|
||||
icon: RiHotelLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
componentName: 'ChannelSetting',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '房型管理',
|
||||
icon: RiHotelBedLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
componentName: 'RoomTypeSetting',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '通用设置',
|
||||
icon: RiSettingsLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
componentName: 'Version',
|
||||
},
|
||||
]
|
||||
6
src/renderer/env.d.ts
vendored
6
src/renderer/env.d.ts
vendored
@@ -1,3 +1,7 @@
|
||||
declare module "@store/counter";
|
||||
declare module "@utils/request";
|
||||
declare module "@constant/menus";
|
||||
declare module "@assets/images/*";
|
||||
declare module "@constant/rate";
|
||||
declare module "@constant/menus";
|
||||
declare module "@remixicon/vue";
|
||||
declare module "vue-router";
|
||||
@@ -1,19 +1,24 @@
|
||||
<template>
|
||||
<div class="bg box-border w-full h-screen flex pt-[8px] pb-[8px]">
|
||||
<div class="w-[952px] h-full">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="bg box-border w-full h-screen flex pt-[8px] pb-[8px] pl-[8px] ">
|
||||
<div class="flex-1 flex gap-[8px]">
|
||||
<div class="flex-1 h-full">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="fade-transform" mode="out-in">
|
||||
<keep-alive>
|
||||
<component :is="Component" :key="route.path" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
|
||||
<TaskList>
|
||||
<slot name="task"></slot>
|
||||
</TaskList>
|
||||
<TaskList />
|
||||
</div>
|
||||
|
||||
<Menus />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup lang="ts" name="Layout">
|
||||
import TaskList from '@/components/TaskList/index.vue'
|
||||
import Menus from '@/components/Menus/index.vue'
|
||||
</script>
|
||||
|
||||
</script>
|
||||
@@ -3,7 +3,9 @@ import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import router from "./router";
|
||||
import App from "./App.vue";
|
||||
import Layout from "./layout/index.vue";
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import locale from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
// 创建 Vue 应用实例
|
||||
const app = createApp(App);
|
||||
@@ -11,11 +13,12 @@ const app = createApp(App);
|
||||
// 使用 Pinia 状态管理
|
||||
app.use(createPinia());
|
||||
|
||||
// 注册 Layout 组件
|
||||
app.component('Layout', Layout);
|
||||
|
||||
// 使用 Vue Router
|
||||
app.use(router);
|
||||
app.use(ElementPlus, {
|
||||
locale,
|
||||
})
|
||||
|
||||
// 挂载应用到 DOM
|
||||
app.mount("#app");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import Layout from '@/layout/index.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -9,20 +10,56 @@ const routes = [
|
||||
{
|
||||
path: "/browser",
|
||||
name: "Browser",
|
||||
component: () => import("@/views/browser/BrowserLayout.vue"),
|
||||
component: () => import("@/browser/BrowserLayout.vue"),
|
||||
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: "/",
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: "home",
|
||||
component: () => import("@/views/home/index.vue"),
|
||||
name: "Home",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "stock",
|
||||
name: "Stock",
|
||||
component: () => import("@/views/stock/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "rate",
|
||||
name: "Rate",
|
||||
component: () => import("@/views/rate/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "order",
|
||||
name: "Order",
|
||||
component: () => import("@/views/order/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "more",
|
||||
name: "More",
|
||||
component: () => import("@/views/more/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "setting",
|
||||
name: "Setting",
|
||||
component: () => import("@/views/setting/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/dashboard",
|
||||
name: "Dashboard",
|
||||
component: () => import("@/views/dashboard/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/about",
|
||||
@@ -34,16 +71,28 @@ const routes = [
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
scrollBehavior(to: any, from: any, savedPosition: any) {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
}
|
||||
|
||||
return { top: 0 }
|
||||
},
|
||||
});
|
||||
|
||||
router.beforeEach((to, _from, next) => {
|
||||
router.beforeEach((to: any, from: any, next: any) => {
|
||||
const token = localStorage.getItem("token");
|
||||
if (to.meta && (to.meta as any).requiresAuth && !token) {
|
||||
next({ path: "/login" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (token && to.path !== "/home") {
|
||||
if (token && to.path === "/login") {
|
||||
next({ path: "/home" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (token && to.path === "/") {
|
||||
next({ path: "/home" });
|
||||
return;
|
||||
}
|
||||
|
||||
12
src/renderer/types/PaginationProps.ts
Normal file
12
src/renderer/types/PaginationProps.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export interface PaginationProps {
|
||||
currentPage?: number
|
||||
total: number
|
||||
pageSize: number
|
||||
pageSizes?: number[]
|
||||
}
|
||||
|
||||
export interface EmitsProps {
|
||||
(e: 'update:currentPage', value: number): void
|
||||
(e: 'update:pageSize', value: string): void
|
||||
(e: 'currentChange', value: number): void
|
||||
}
|
||||
3
src/renderer/types/index.ts
Normal file
3
src/renderer/types/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { PaginationProps, EmitsProps } from './PaginationProps'
|
||||
|
||||
export { PaginationProps, EmitsProps }
|
||||
7
src/renderer/views/dashboard/index.vue
Normal file
7
src/renderer/views/dashboard/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div>看板</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
@@ -1,15 +1,9 @@
|
||||
<template>
|
||||
<Layout>
|
||||
</Layout>
|
||||
<div>
|
||||
首页
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
const openBaidu = () => {
|
||||
(window as any).ipcAPI?.openBaidu()
|
||||
|
||||
// 发送日志
|
||||
(window as any).ipcAPI?.logToMain('info', '打开百度')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<template>
|
||||
<div
|
||||
class="h-screen box-border p-[8px] login-bg flex items-center justify-center"
|
||||
>
|
||||
<div class="h-screen box-border p-[8px] login-bg flex items-center justify-center">
|
||||
<div class="w-[836px] h-full bg-white rounded-2xl p-[32px] flex flex-col">
|
||||
<div class="flex items-center">
|
||||
<img class="w-[48px] h-[48px]" src="@assets/images/login/blue_logo.png" />
|
||||
|
||||
|
||||
<span class="ml-auto text-[14px] text-gray-600">没有账号?</span>
|
||||
<button class="bg-sky-50 rounded-[8px] text-[14px] text-sky-600 px-[12px] py-[6px] focus-visible:outline-none">注册</button>
|
||||
<button
|
||||
class="bg-sky-50 rounded-[8px] text-[14px] text-sky-600 px-[12px] py-[6px] focus-visible:outline-none">注册</button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center justify-center mb-[24px] box-border pt-[108px]">
|
||||
@@ -18,39 +17,24 @@
|
||||
|
||||
<div class="w-[392px] ml-auto mr-auto">
|
||||
<div class="font-[14px] text-gray-700 mb-2">账号</div>
|
||||
<div class="border rounded-[10px] flex items-center gap-2 box-border px-[12px] py-[10px]">
|
||||
<RiUser3Fill size="20px" color="#99A0AE" />
|
||||
<input
|
||||
class="flex-1 focus-visible:outline-none"
|
||||
type="text"
|
||||
v-model.trim="form.account"
|
||||
placeholder="请输入账号"
|
||||
@keyup.enter="onSubmit"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="errors.account" class="mt-1 text-xs text-red-500">{{ errors.account }}</p>
|
||||
<div class="border rounded-[10px] flex items-center gap-2 box-border px-[12px] py-[10px]">
|
||||
<RiUser3Fill size="20px" color="#99A0AE" />
|
||||
<input class="flex-1 focus-visible:outline-none" type="text" v-model.trim="form.account" placeholder="请输入账号"
|
||||
@keyup.enter="onSubmit" />
|
||||
</div>
|
||||
<p v-if="errors.account" class="mt-1 text-xs text-red-500">{{ errors.account }}</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]">
|
||||
<RiKey2Fill size="20px" color="#99A0AE" />
|
||||
<input
|
||||
class="flex-1 focus-visible:outline-none"
|
||||
:type="showPwd ? 'text' : 'password'"
|
||||
v-model.trim="form.password"
|
||||
placeholder="请输入密码"
|
||||
@keyup.enter="onSubmit"
|
||||
/>
|
||||
<input class="flex-1 focus-visible:outline-none" :type="showPwd ? 'text' : 'password'"
|
||||
v-model.trim="form.password" placeholder="请输入密码" @keyup.enter="onSubmit" />
|
||||
</div>
|
||||
<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"
|
||||
/>
|
||||
<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>
|
||||
@@ -58,31 +42,21 @@
|
||||
<!-- 记住密码|忘记密码 -->
|
||||
<div class="flex items-center justify-between mb-[24px] mt-[24px]">
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="showPwd"
|
||||
class="w-[14px] h-[14px] rounded-[4px]"
|
||||
/>
|
||||
<input type="checkbox" v-model="showPwd" class="w-[14px] h-[14px] rounded-[4px]" />
|
||||
<span class="text-[14px] text-gray-600">记住密码</span>
|
||||
</div>
|
||||
<span class="text-[14px] text-sky-600">忘记密码?</span>
|
||||
</div>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<button
|
||||
class="w-full py-2 bg-blue-600 text-white rounded-[8px] hover:bg-blue-700 disabled:bg-blue-300"
|
||||
@click="onSubmit"
|
||||
>
|
||||
<button class="w-full py-2 bg-blue-600 text-white rounded-[8px] hover:bg-blue-700 disabled:bg-blue-300"
|
||||
@click="onSubmit">
|
||||
{{ loading ? '登录中…' : '登录' }}
|
||||
</button>
|
||||
|
||||
<!-- 同意协议 -->
|
||||
<div class="flex items-center justify-center gap-2 mt-[24px]">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="form.agreement"
|
||||
class="w-[14px] h-[14px] rounded-[4px]"
|
||||
/>
|
||||
<input type="checkbox" v-model="form.agreement" class="w-[14px] h-[14px] rounded-[4px]" />
|
||||
<span class="text-[14px] text-gray-600">我已同意</span>
|
||||
<span class="text-[14px] text-sky-600">《使用协议》</span>
|
||||
<span class="text-[14px] text-gray-600">和</span>
|
||||
@@ -103,8 +77,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { login as apiLogin } from "@/renderer/api/login";
|
||||
import { RiUser3Fill , RiKey2Fill} from '@remixicon/vue'
|
||||
// import { login as apiLogin } from "@/renderer/api/login";
|
||||
import { RiUser3Fill, RiKey2Fill } from '@remixicon/vue'
|
||||
|
||||
const router = useRouter();
|
||||
const form = reactive({ account: "", password: "", agreement: "", code: "" });
|
||||
@@ -131,7 +105,8 @@ const onSubmit = async () => {
|
||||
// const token = res && (res.token || res.data?.token || res.access_token);
|
||||
// if (!token) throw new Error("登录失败");
|
||||
// localStorage.setItem("token", token);
|
||||
await (window as any).ipcAPI.app.setFrameless('/home')
|
||||
// await (window as any).ipcAPI.app.setFrameless('/home')
|
||||
router.push('/home');
|
||||
} finally {
|
||||
// loading.value = false;
|
||||
}
|
||||
|
||||
7
src/renderer/views/more/index.vue
Normal file
7
src/renderer/views/more/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div>更多</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
7
src/renderer/views/order/index.vue
Normal file
7
src/renderer/views/order/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div>订单</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="flex flex-col box-border border-[1px] border-[#E5E8EE] rounded-[16px] p-[16px]">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="`${item.label}(${item.total})`" :name="item.name" v-for="item in tabs"
|
||||
:key="item.label" />
|
||||
</el-tabs>
|
||||
|
||||
<RateFilterSection />
|
||||
|
||||
<RateListSection :list="commentList" />
|
||||
|
||||
<!-- 分页 -->
|
||||
<Pagination v-model:current-page="queryParams.pageIndex" :page-size="queryParams.pageSize" :total="total" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="RateContentSection">
|
||||
import { ref } from 'vue'
|
||||
import { commentList } from '@/constant/rate'
|
||||
import Pagination from '@/components/Pagination/index.vue'
|
||||
import RateFilterSection from '../RateFilterSection/index.vue'
|
||||
import RateListSection from '../RateListSection/index.vue'
|
||||
|
||||
const activeName = ref('all')
|
||||
const tabs = ref([
|
||||
{
|
||||
label: '全部评价',
|
||||
name: 'all',
|
||||
total: 200,
|
||||
},
|
||||
{
|
||||
label: '待回复',
|
||||
name: 'second',
|
||||
total: 100,
|
||||
},
|
||||
{
|
||||
label: '差评',
|
||||
name: 'third',
|
||||
total: 50,
|
||||
}
|
||||
])
|
||||
const list = ref([])
|
||||
const total = ref(200)
|
||||
const queryParams = ref({
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-[12px]">
|
||||
<el-form ref="queryRef" :inline="true" :model="queryParams" @keyup.enter="handleQuery">
|
||||
<el-form-item prop="dateRange">
|
||||
<el-date-picker v-model="dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期" style="width: 240px;" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="channel">
|
||||
<el-select v-model="queryParams.channel" placeholder="请选择渠道" style="width: 240px;">
|
||||
<el-option v-for="item in channels" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery">搜索</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="RateFilterSection">
|
||||
import { ref, getCurrentInstance } from 'vue'
|
||||
|
||||
const { proxy }: any = getCurrentInstance()
|
||||
const queryParams = ref({
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
channel: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
})
|
||||
const dateRange = ref([])
|
||||
const channels = [
|
||||
{
|
||||
label: '全部',
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
label: '携程',
|
||||
value: 'ctrip',
|
||||
},
|
||||
{
|
||||
label: '飞猪',
|
||||
value: 'flyingpig',
|
||||
},
|
||||
{
|
||||
label: '抖音',
|
||||
value: 'douyin',
|
||||
},
|
||||
]
|
||||
|
||||
const loading = ref(false)
|
||||
const total = ref(0)
|
||||
const queryRef = ref()
|
||||
|
||||
/** 查询评论列表 */
|
||||
function getList() {
|
||||
loading.value = true
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageIndex = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
dateRange.value = []
|
||||
queryRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
</script>
|
||||
37
src/renderer/views/rate/components/RateListSection/index.vue
Normal file
37
src/renderer/views/rate/components/RateListSection/index.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="list h-[420px] px-[16px] overflow-y-auto">
|
||||
<div class="mb-[16px]" v-for="item in list" :key="item.id">
|
||||
<div class="flex items-center text-[14px] select-none">
|
||||
<el-image :src="item.channelIcon" />
|
||||
<span class="text-[#1B74F5] ml-[12px]">{{ item.channelName }}</span>
|
||||
<span class="text-[#1B74F5] mx-[3px]">-</span>
|
||||
<span class="text-[#171717]">{{ item.userName }}</span>
|
||||
<span class="text-[#99A0AE] mx-[6px]">·</span>
|
||||
<span class="text-[#2B7FFF]">评分{{ item.score }}</span>
|
||||
<span class="text-[#99A0AE] ml-auto">{{ item.createTime }}</span>
|
||||
</div>
|
||||
<div class="content pl-[60px]">
|
||||
<p class="text-[14px] text-[#525866] leading-[20px] mb-[8px]">{{ item.content }}</p>
|
||||
|
||||
<div class="grid grid-cols-3 gap-[8px] w-[316px]">
|
||||
<el-image :src="img" class="w-[100px] h-[100px] rounded-[8px]"
|
||||
v-for="(img, index) in item.imageList" :key="index" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="reply pl-[60px]">
|
||||
<span class="text-[14px] text-[#2B7FFF] cursor-pointer">回复</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="RateContentSection">
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="grid grid-cols-5 gap-[15px] mb-[20px] select-none">
|
||||
<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" name="RatePanelSection">
|
||||
import { channels } from '@constant/rate'
|
||||
import { RiStarFill } from '@remixicon/vue'
|
||||
</script>
|
||||
BIN
src/renderer/views/rate/images/2.jpg
Normal file
BIN
src/renderer/views/rate/images/2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -1,16 +1,13 @@
|
||||
<template>
|
||||
<Layout>
|
||||
|
||||
</Layout>
|
||||
<div class="bg-white box-border w-full h-full rounded-[16px] p-[20px]">
|
||||
<TitleSection title="评价" desc="评价数据智能整理,精准优化服务" />
|
||||
<RatePanelSection />
|
||||
<RateContentSection />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
const openBaidu = () => {
|
||||
(window as any).ipcAPI?.openBaidu()
|
||||
|
||||
// 发送日志
|
||||
(window as any).ipcAPI?.logToMain('info', '打开百度')
|
||||
}
|
||||
<script setup lang="ts" name="Rate">
|
||||
import TitleSection from '@/components/TitleSection/index.vue'
|
||||
import RatePanelSection from './components/RatePanelSection/index.vue'
|
||||
import RateContentSection from './components/RateContentSection/index.vue'
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="flex-1 h-full p-[20px] select-none">
|
||||
<TitleSection title="账号设置" desc="请关联PMS和渠道房型名称,可使用智能对标" />
|
||||
|
||||
<div
|
||||
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE]">
|
||||
<div class="label w-[64px] text-[16px] font-medium text-[#171717] mr-[24px]">账号</div>
|
||||
<div class="value text-[14px] font-medium text-[#171717]">1234567890</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex items-center py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE]">
|
||||
<div class="label w-[64px] text-[16px] font-medium text-[#171717] mr-[24px]">登录密码</div>
|
||||
<div class="value text-[14px] text-[#99A0AE]">保障投资者登录操作时使用,上次登录时间:2022-11-09 16:24:30</div>
|
||||
|
||||
<div class="border-[1px] border-[#E5E8EE] rounded-[6px] px-[6px] py-[4px] flex items-center ml-[24px]">
|
||||
<RiCheckboxCircleFill class="w-[16px] h-[16px]" color="#1FC16B" />
|
||||
<span class="text-[12px] text-[#525866] ml-[2px]">已设置</span>
|
||||
</div>
|
||||
|
||||
<el-button type="text" class="ml-auto">修改密码</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RiCheckboxCircleFill } from '@remixicon/vue'
|
||||
import TitleSection from '@/components/TitleSection/index.vue'
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="flex-1 h-full p-[20px] select-none">
|
||||
<TitleSection title="渠道管理" desc="绑定酒店使用的相关渠道的账户和密码用于智能操作" />
|
||||
|
||||
<div class="grid grid-cols-3 gap-[12px] mb-[12px] select-none">
|
||||
<div class="border-[1px] border-[#E5E8ED] box-border flex flex-col rounded-[12px] overflow-hidden"
|
||||
v-for="item in channel" :key="item.id">
|
||||
<div class="bg-[#E0E0E0] h-[120px]"></div>
|
||||
<div class="flex items-center relative mt-[-20px] pl-[12px]">
|
||||
<img :src="item.icon" class="w-[40px] h-[40px]">
|
||||
</div>
|
||||
<div class="flex items-center p-[12px]">
|
||||
<span class="text-[14px] font-500 text-[#171717] leading-[20px]">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
<div class="bg-[#F2F5F8] rounded-[6px] flex items-center ml-[8px] py-[4px] px-[6px]">
|
||||
<RiForbidLine class="w-[16px] h-[16px]" color="#717784" />
|
||||
<span class="text-[12px] font-500 text-[#717784] leading-[20px] ml-[4px]">
|
||||
未绑定
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-[12px] border-t-[1px] border-t-[#E5E8ED]">
|
||||
<el-button plain>绑定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { channel } from '@constant/channel'
|
||||
import { RiForbidLine } from '@remixicon/vue'
|
||||
import TitleSection from '@/components/TitleSection/index.vue'
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template></template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
27
src/renderer/views/setting/components/SystemConfig/index.vue
Normal file
27
src/renderer/views/setting/components/SystemConfig/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div
|
||||
class="w-[136px] h-full box-border border-r-[1px] border-r-[#E5E8EE] py-[12px] px-[8px] flex flex-col gap-[4px] select-none">
|
||||
<div class="text-[12px] text-[#99A0AE] p-[4px]">系统设置</div>
|
||||
|
||||
<div
|
||||
:class="['box-border flex items-center py-[10px] px-[12px] rounded-[6px] cursor-pointer', item.id === currentId ? 'bg-[#EFF6FF]' : '']"
|
||||
v-for="item in systemMenus" :key="item.id" @click="handleClick(item)">
|
||||
<component :is="item.icon" :color="item.id === currentId ? item.activeColor : item.color"
|
||||
class="w-[20px] h-[20px]" />
|
||||
<span class="box-border px-[8px] text-[14px] font-medium text-[#525866]">{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineEmits } from 'vue'
|
||||
import { systemMenus } from '@/constant/system-config'
|
||||
|
||||
const currentId = ref(1)
|
||||
|
||||
const emits = defineEmits(['change'])
|
||||
const handleClick = async (item: any) => {
|
||||
currentId.value = item.id
|
||||
emits('change', item)
|
||||
}
|
||||
</script>
|
||||
16
src/renderer/views/setting/components/Version/index.vue
Normal file
16
src/renderer/views/setting/components/Version/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="flex-1 h-full p-[20px] select-none">
|
||||
<TitleSection title="账号设置" desc="请关联PMS和渠道房型名称,可使用智能对标" />
|
||||
|
||||
<div
|
||||
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE]">
|
||||
<div class="label w-[64px] text-[16px] font-medium text-[#171717] mr-[24px]">当前版本</div>
|
||||
<div class="value text-[16px] font-medium text-[#171717]">1.0.0</div>
|
||||
<el-button type="text" class="ml-auto">检查更新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TitleSection from '@/components/TitleSection/index.vue'
|
||||
</script>
|
||||
25
src/renderer/views/setting/index.vue
Normal file
25
src/renderer/views/setting/index.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="bg-white box-border w-full h-full rounded-[16px] flex">
|
||||
<SystemConfig @change=onChange />
|
||||
|
||||
<component :is="currentComponent" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { shallowRef } from 'vue'
|
||||
import SystemConfig from './components/SystemConfig/index.vue'
|
||||
import AccountSetting from './components/AccountSetting/index.vue'
|
||||
import Version from './components/Version/index.vue'
|
||||
import ChannelSetting from './components/ChannelSetting/index.vue'
|
||||
import RoomTypeSetting from './components/RoomTypeSetting/index.vue'
|
||||
|
||||
const currentComponent = shallowRef(AccountSetting)
|
||||
const components: any = {
|
||||
AccountSetting,
|
||||
ChannelSetting,
|
||||
RoomTypeSetting,
|
||||
Version,
|
||||
}
|
||||
const onChange = ({ componentName }: any) => currentComponent.value = components[componentName]
|
||||
</script>
|
||||
7
src/renderer/views/stock/index.vue
Normal file
7
src/renderer/views/stock/index.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div>订单</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
Reference in New Issue
Block a user