init
24
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
dist
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
75
frontend/README.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Nuxt Minimal Starter
|
||||
|
||||
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||
|
||||
## Setup
|
||||
|
||||
Make sure to install dependencies:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install
|
||||
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# bun
|
||||
bun install
|
||||
```
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on `http://localhost:3000`:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run dev
|
||||
|
||||
# pnpm
|
||||
pnpm dev
|
||||
|
||||
# yarn
|
||||
yarn dev
|
||||
|
||||
# bun
|
||||
bun run dev
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run build
|
||||
|
||||
# pnpm
|
||||
pnpm build
|
||||
|
||||
# yarn
|
||||
yarn build
|
||||
|
||||
# bun
|
||||
bun run build
|
||||
```
|
||||
|
||||
Locally preview production build:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run preview
|
||||
|
||||
# pnpm
|
||||
pnpm preview
|
||||
|
||||
# yarn
|
||||
yarn preview
|
||||
|
||||
# bun
|
||||
bun run preview
|
||||
```
|
||||
|
||||
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
||||
11
frontend/app/app.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
<style></style>
|
||||
93
frontend/app/assets/fonts/OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com),
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
frontend/app/assets/fonts/nunito-v16-latin-regular.woff2
Normal file
BIN
frontend/app/assets/images/info-card-1.jpg
Normal file
|
After Width: | Height: | Size: 217 KiB |
BIN
frontend/app/assets/images/info-card-2.jpg
Normal file
|
After Width: | Height: | Size: 368 KiB |
BIN
frontend/app/assets/images/info-card-3.jpg
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
frontend/app/assets/images/logo-universal.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
53
frontend/app/components/CardBarChart.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
|
||||
<!-- Active Users Card -->
|
||||
<a-card :bordered="false" class="dashboard-bar-chart" style="margin-bottom: 20px;">
|
||||
<chart-bar :height="220" :data="barChartData"></chart-bar>
|
||||
<div class="card-title">
|
||||
<h2>Active Users</h2>
|
||||
<p>than last week <span class="text-success">+23%</span></p>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>We have created multiple options for you to put together and customise into pixel perfect pages.</p>
|
||||
</div>
|
||||
<a-row class="card-footer" type="flex" justify="center" align="top">
|
||||
<a-col :span="6">
|
||||
<h4>3,6K</h4>
|
||||
<span>Users</span>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<h4>2m</h4>
|
||||
<span>Clicks</span>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<h4>$772</h4>
|
||||
<span>Sales</span>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<h4>82</h4>
|
||||
<span>Items</span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
<!-- Active Users Card -->
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
const barChartData = {
|
||||
labels: ["01", "02", "03", "04", "05", "06", "07", "08", "09"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Sales",
|
||||
backgroundColor: '#fff',
|
||||
borderWidth: 0,
|
||||
borderSkipped: false,
|
||||
borderRadius: 6,
|
||||
data: [850, 600, 500, 620, 900, 500, 900, 630, 900],
|
||||
maxBarThickness: 20
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
</script>
|
||||
44
frontend/app/components/CardLineChart.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<a-card :bordered="false" class="dashboard-bar-line header-solid">
|
||||
<template #title>
|
||||
<h2>Sales Overview</h2>
|
||||
<p>than last year <span class="text-success">+20%</span></p>
|
||||
</template>
|
||||
|
||||
<template #extra>
|
||||
<a-badge color="primary" class="badge-dot-primary" text="Traffic" />
|
||||
<a-badge color="primary" class="badge-dot-secondary" text="Sales" />
|
||||
</template>
|
||||
|
||||
<chart-line :height="310" :data="lineChartData" />
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 定义折线图数据
|
||||
const lineChartData = {
|
||||
labels: ["Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Mobile apps",
|
||||
tension: 0.4,
|
||||
borderWidth: 0,
|
||||
pointRadius: 0,
|
||||
borderColor: "#1890FF",
|
||||
borderWidth: 3,
|
||||
data: [50, 40, 300, 220, 500, 250, 400, 230, 500],
|
||||
maxBarThickness: 6
|
||||
},
|
||||
{
|
||||
label: "Websites",
|
||||
tension: 0.4,
|
||||
borderWidth: 0,
|
||||
pointRadius: 0,
|
||||
borderColor: "#B37FEB",
|
||||
borderWidth: 3,
|
||||
data: [30, 90, 40, 140, 290, 290, 340, 230, 400],
|
||||
maxBarThickness: 6
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
101
frontend/app/components/ChartBar.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div>
|
||||
<canvas ref="chartRef" :style="{ height: height + 'px' }"></canvas>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Chart, registerables } from 'chart.js'
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
|
||||
// 注册 Chart.js 所有组件
|
||||
Chart.register(...registerables)
|
||||
|
||||
// 定义 props
|
||||
const props = defineProps<{
|
||||
data: any
|
||||
height: number
|
||||
}>()
|
||||
|
||||
// chart 实例引用
|
||||
const chartRef = ref<HTMLCanvasElement | null>(null)
|
||||
let chartInstance: Chart | null = null
|
||||
|
||||
onMounted(() => {
|
||||
if (chartRef.value) {
|
||||
const ctx = chartRef.value.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
chartInstance = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: props.data,
|
||||
options: {
|
||||
layout: {
|
||||
padding: { top: 30, right: 15, left: 10, bottom: 5 }
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false }
|
||||
},
|
||||
// Chart.js v3+ 的 tooltip 配置在 plugins.tooltip
|
||||
// 如果你用的是 v4,tooltips 配置需要改为 plugins.tooltip
|
||||
// 这里保留原写法
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
grid: {
|
||||
display: true,
|
||||
color: 'rgba(255, 255, 255, .2)',
|
||||
borderDash: [6],
|
||||
borderDashOffset: 6
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 0,
|
||||
suggestedMax: 1000,
|
||||
display: true,
|
||||
color: '#fff',
|
||||
font: {
|
||||
size: 14,
|
||||
lineHeight: 1.5,
|
||||
weight: '600',
|
||||
family: 'Open Sans'
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: {
|
||||
display: true,
|
||||
color: '#fff',
|
||||
font: {
|
||||
size: 14,
|
||||
lineHeight: 1.5,
|
||||
weight: '600',
|
||||
family: 'Open Sans'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.destroy()
|
||||
chartInstance = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
canvas {
|
||||
background-image: linear-gradient(to right, #00369E, #005CFD, #A18DFF);
|
||||
}
|
||||
</style>
|
||||
98
frontend/app/components/ChartLine.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div>
|
||||
<canvas ref="chartRef" :style="{ height: height + 'px' }"></canvas>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Chart, registerables } from 'chart.js'
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
|
||||
// 注册 Chart.js 所有组件
|
||||
Chart.register(...registerables)
|
||||
|
||||
// 定义 props
|
||||
const props = defineProps<{
|
||||
data: any
|
||||
height: number
|
||||
}>()
|
||||
|
||||
// DOM 引用
|
||||
const chartRef = ref<HTMLCanvasElement | null>(null)
|
||||
// Chart 实例
|
||||
let chartInstance: Chart | null = null
|
||||
|
||||
onMounted(() => {
|
||||
if (!chartRef.value) return
|
||||
const ctx = chartRef.value.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
chartInstance = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: props.data,
|
||||
options: {
|
||||
layout: {
|
||||
padding: { top: 30, right: 15, left: 10, bottom: 5 }
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false }
|
||||
},
|
||||
// Chart.js v3+ 的 tooltip 配置在 plugins.tooltip
|
||||
// 如果你用的是 v4,需要改成 plugins.tooltip
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
grid: {
|
||||
display: true,
|
||||
color: 'rgba(0, 0, 0, .2)',
|
||||
borderDash: [6],
|
||||
borderDashOffset: 6
|
||||
},
|
||||
ticks: {
|
||||
suggestedMin: 0,
|
||||
suggestedMax: 1000,
|
||||
display: true,
|
||||
color: '#8C8C8C',
|
||||
font: {
|
||||
size: 14,
|
||||
lineHeight: 1.8,
|
||||
weight: '600',
|
||||
family: 'Open Sans'
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: {
|
||||
display: true,
|
||||
color: '#8C8C8C',
|
||||
font: {
|
||||
size: 14,
|
||||
lineHeight: 1.5,
|
||||
weight: '600',
|
||||
family: 'Open Sans'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.destroy()
|
||||
chartInstance = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 你可以在这里加样式 */
|
||||
</style>
|
||||
34
frontend/app/components/Header.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="header-outside">
|
||||
<a-layout-header class="header">
|
||||
|
||||
<div class="header-menu">
|
||||
<div></div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</a-layout-header>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DownOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.header-menu{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.header-outside {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
/* z-index: 9999; */
|
||||
}
|
||||
|
||||
</style>
|
||||
49
frontend/app/components/HelloWorld.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import {GreetService} from "../../bindings/go-site-clone";
|
||||
import {Events} from "@wailsio/runtime";
|
||||
|
||||
const name = ref('')
|
||||
const result = ref('Please enter your name below 👇')
|
||||
const time = ref('Listening for Time event...')
|
||||
|
||||
const doGreet = () => {
|
||||
let localName = name.value;
|
||||
if (!localName) {
|
||||
localName = 'anonymous';
|
||||
}
|
||||
GreetService.Greet(localName).then((resultValue) => {
|
||||
result.value = resultValue;
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
Events.On('time', (timeValue) => {
|
||||
time.value = timeValue.data;
|
||||
});
|
||||
})
|
||||
|
||||
defineProps({
|
||||
msg: String,
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="result">{{ result }}</div>
|
||||
<div class="card">
|
||||
<div class="input-box">
|
||||
<input class="input" v-model="name" type="text" autocomplete="off"/>
|
||||
<button class="btn" @click="doGreet">Greet</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div><p>Click on the Wails logo to learn more</p></div>
|
||||
<div><p>{{ time }}</p></div>
|
||||
</div>
|
||||
</template>
|
||||
72
frontend/app/components/SiderMenu.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="menu-out">
|
||||
<a-layout-sider v-model:collapsed="collapsed" collapsible style="height: 100%;">
|
||||
<div class="logo" />
|
||||
<a-menu v-model:selectedKeys="selectedKeys" theme="dark" mode="inline" @click="menuClick">
|
||||
<a-menu-item key="">
|
||||
<pie-chart-outlined />
|
||||
<span>控制台</span>
|
||||
</a-menu-item>
|
||||
<a-sub-menu key="device">
|
||||
<template #title>
|
||||
<span>
|
||||
<user-outlined />
|
||||
<span>设备管理</span>
|
||||
</span>
|
||||
</template>
|
||||
<a-menu-item key="list">设备列表</a-menu-item>
|
||||
</a-sub-menu>
|
||||
</a-menu>
|
||||
</a-layout-sider>
|
||||
</div>
|
||||
<div>
|
||||
<a-layout-sider v-model:collapsed="collapsed" collapsible style="height: 100%;"></a-layout-sider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
PieChartOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { ref } from 'vue';
|
||||
const collapsed = ref<boolean>(false);
|
||||
const selectedKeys = ref<string[]>(['']);
|
||||
function menuClick(e: any) {
|
||||
console.log(e);
|
||||
const keyList = e.keyPath
|
||||
let path = ''
|
||||
for (let i = 0; i < keyList.length; i++) {
|
||||
const element = keyList[i];
|
||||
path += "/"+element
|
||||
}
|
||||
navigateTo(path)
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.logo {
|
||||
height: 32px;
|
||||
margin: 16px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.site-layout .site-layout-background {
|
||||
background: #fff;
|
||||
}
|
||||
[data-theme='dark'] .site-layout .site-layout-background {
|
||||
background: #141414;
|
||||
}
|
||||
|
||||
.menu-out{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
35
frontend/app/components/WidgetCounter.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<a-card :bordered="false" class="widget-1" style="margin-bottom: 20px;">
|
||||
<a-statistic
|
||||
:title="title"
|
||||
:value="value"
|
||||
:prefix="prefix"
|
||||
:suffix="suffix"
|
||||
:precision="0"
|
||||
class="text-success"
|
||||
:class="'text-' + status"
|
||||
/>
|
||||
<div class="icon" v-html="icon"></div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
title?: string
|
||||
value?: number
|
||||
prefix?: string
|
||||
suffix?: string
|
||||
icon?: string
|
||||
status?: string
|
||||
}>()
|
||||
|
||||
// 默认值处理(可选)
|
||||
const {
|
||||
title = '',
|
||||
value = 0,
|
||||
prefix = '',
|
||||
suffix = '',
|
||||
icon = '',
|
||||
status = 'success'
|
||||
} = props
|
||||
</script>
|
||||
26
frontend/app/layouts/default.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-layout style="min-height: 100vh">
|
||||
<SiderMenu />
|
||||
<a-layout>
|
||||
<Header></Header>
|
||||
<a-layout-content style="margin: 0 16px">
|
||||
<div style="min-height: calc(100vh - 155px)">
|
||||
<slot />
|
||||
</div>
|
||||
</a-layout-content>
|
||||
<a-layout-footer style="text-align: center">
|
||||
<!-- Ant Design ©2018 Created by Ant UED -->
|
||||
</a-layout-footer>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
onMounted(() => {
|
||||
|
||||
});
|
||||
</script>
|
||||
79
frontend/app/page/index.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-breadcrumb style="margin: 10px 0">
|
||||
<a-breadcrumb-item>控制台</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
<div>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="24" :lg="12" :xl="6" class="mb-24" v-for="(stat, index) in stats" :key="index">
|
||||
<WidgetCounter :title="stat.title" :value="stat.value" :prefix="stat.prefix" :suffix="stat.suffix"
|
||||
:icon="stat.icon" :status="stat.status"></WidgetCounter>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24" type="flex" align="stretch">
|
||||
<a-col :span="24" :lg="10" class="mb-24">
|
||||
<CardBarChart></CardBarChart>
|
||||
</a-col>
|
||||
<a-col :span="24" :lg="14" class="mb-24">
|
||||
<CardLineChart></CardLineChart>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
// Counter Widgets stats
|
||||
const stats = [
|
||||
{
|
||||
title: "Today’s Sales",
|
||||
value: 53000,
|
||||
prefix: "$",
|
||||
suffix: "+30%",
|
||||
icon: `
|
||||
<svg width="22" height="22" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.43338 7.41784C8.58818 7.31464 8.77939 7.2224 9 7.15101L9.00001 8.84899C8.77939 8.7776 8.58818 8.68536 8.43338 8.58216C8.06927 8.33942 8 8.1139 8 8C8 7.8861 8.06927 7.66058 8.43338 7.41784Z" fill="#111827"/>
|
||||
<path d="M11 12.849L11 11.151C11.2206 11.2224 11.4118 11.3146 11.5666 11.4178C11.9308 11.6606 12 11.8861 12 12C12 12.1139 11.9308 12.3394 11.5666 12.5822C11.4118 12.6854 11.2206 12.7776 11 12.849Z" fill="#111827"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2C5.58172 2 2 5.58172 2 10C2 14.4183 5.58172 18 10 18ZM11 5C11 4.44772 10.5523 4 10 4C9.44772 4 9 4.44772 9 5V5.09199C8.3784 5.20873 7.80348 5.43407 7.32398 5.75374C6.6023 6.23485 6 7.00933 6 8C6 8.99067 6.6023 9.76515 7.32398 10.2463C7.80348 10.5659 8.37841 10.7913 9.00001 10.908L9.00002 12.8492C8.60902 12.7223 8.31917 12.5319 8.15667 12.3446C7.79471 11.9275 7.16313 11.8827 6.74599 12.2447C6.32885 12.6067 6.28411 13.2382 6.64607 13.6554C7.20855 14.3036 8.05956 14.7308 9 14.9076L9 15C8.99999 15.5523 9.44769 16 9.99998 16C10.5523 16 11 15.5523 11 15L11 14.908C11.6216 14.7913 12.1965 14.5659 12.676 14.2463C13.3977 13.7651 14 12.9907 14 12C14 11.0093 13.3977 10.2348 12.676 9.75373C12.1965 9.43407 11.6216 9.20873 11 9.09199L11 7.15075C11.391 7.27771 11.6808 7.4681 11.8434 7.65538C12.2053 8.07252 12.8369 8.11726 13.254 7.7553C13.6712 7.39335 13.7159 6.76176 13.354 6.34462C12.7915 5.69637 11.9405 5.26915 11 5.09236V5Z" fill="#111827"/>
|
||||
</svg>`,
|
||||
},
|
||||
{
|
||||
title: "Today’s Users",
|
||||
value: 3200,
|
||||
suffix: "+20%",
|
||||
icon: `
|
||||
<svg width="22" height="22" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 6C9 7.65685 7.65685 9 6 9C4.34315 9 3 7.65685 3 6C3 4.34315 4.34315 3 6 3C7.65685 3 9 4.34315 9 6Z" fill="#111827"/>
|
||||
<path d="M17 6C17 7.65685 15.6569 9 14 9C12.3431 9 11 7.65685 11 6C11 4.34315 12.3431 3 14 3C15.6569 3 17 4.34315 17 6Z" fill="#111827"/>
|
||||
<path d="M12.9291 17C12.9758 16.6734 13 16.3395 13 16C13 14.3648 12.4393 12.8606 11.4998 11.6691C12.2352 11.2435 13.0892 11 14 11C16.7614 11 19 13.2386 19 16V17H12.9291Z" fill="#111827"/>
|
||||
<path d="M6 11C8.76142 11 11 13.2386 11 16V17H1V16C1 13.2386 3.23858 11 6 11Z" fill="#111827"/>
|
||||
</svg>`,
|
||||
},
|
||||
{
|
||||
title: "New Clients",
|
||||
value: 1200,
|
||||
prefix: "+",
|
||||
status: "danger",
|
||||
suffix: "-20%",
|
||||
icon: `
|
||||
<svg width="22" height="22" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.17157 5.17157C4.73367 3.60948 7.26633 3.60948 8.82843 5.17157L10 6.34315L11.1716 5.17157C12.7337 3.60948 15.2663 3.60948 16.8284 5.17157C18.3905 6.73367 18.3905 9.26633 16.8284 10.8284L10 17.6569L3.17157 10.8284C1.60948 9.26633 1.60948 6.73367 3.17157 5.17157Z" fill="#111827"/>
|
||||
</svg>`,
|
||||
},
|
||||
{
|
||||
title: "New Orders",
|
||||
value: 13200,
|
||||
prefix: "$",
|
||||
suffix: "+10%",
|
||||
icon: `
|
||||
<svg width="22" height="22" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C7.79086 2 6 3.79086 6 6V7H5C4.49046 7 4.06239 7.38314 4.00612 7.88957L3.00612 16.8896C2.97471 17.1723 3.06518 17.455 3.25488 17.6669C3.44458 17.8789 3.71556 18 4 18H16C16.2844 18 16.5554 17.8789 16.7451 17.6669C16.9348 17.455 17.0253 17.1723 16.9939 16.8896L15.9939 7.88957C15.9376 7.38314 15.5096 7 15 7H14V6C14 3.79086 12.2091 2 10 2ZM12 7V6C12 4.89543 11.1046 4 10 4C8.89543 4 8 4.89543 8 6V7H12ZM6 10C6 9.44772 6.44772 9 7 9C7.55228 9 8 9.44772 8 10C8 10.5523 7.55228 11 7 11C6.44772 11 6 10.5523 6 10ZM13 9C12.4477 9 12 9.44772 12 10C12 10.5523 12.4477 11 13 11C13.5523 11 14 10.5523 14 10C14 9.44772 13.5523 9 13 9Z" fill="#111827"/>
|
||||
</svg>`,
|
||||
},
|
||||
];
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
8
frontend/app/plugins/ant-design-vue.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
import Antd, { notification } from 'ant-design-vue'
|
||||
import 'ant-design-vue/dist/reset.css' // v4 使用新样式重置文件
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.vueApp.use(Antd)
|
||||
nuxtApp.provide('notification', notification)
|
||||
})
|
||||
15
frontend/bindings/go-site-clone/greetservice.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {$CancellablePromise<string>}
|
||||
*/
|
||||
export function Greet(name) {
|
||||
return $Call.ByID(1411160069, name);
|
||||
}
|
||||
8
frontend/bindings/go-site-clone/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as GreetService from "./greetservice.js";
|
||||
export {
|
||||
GreetService
|
||||
};
|
||||
18
frontend/nuxt.config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: { enabled: true },
|
||||
ssr: false, // 关闭 SSR,改为 CSR
|
||||
nitro: {
|
||||
preset: 'static',
|
||||
output: {
|
||||
publicDir: './dist' // 相对 Nuxt3 项目根目录的路径
|
||||
}
|
||||
},
|
||||
app: {
|
||||
baseURL: './' // 确保相对路径,方便打包到 Wails
|
||||
},
|
||||
plugins: [
|
||||
'~/plugins/ant-design-vue',
|
||||
],
|
||||
})
|
||||
9655
frontend/package-lock.json
generated
Normal file
20
frontend/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wailsio/runtime": "latest",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"chart.js": "^4.5.1",
|
||||
"nuxt": "^4.1.2",
|
||||
"vue": "^3.5.21",
|
||||
"vue-router": "^4.5.1"
|
||||
}
|
||||
}
|
||||
BIN
frontend/public/Inter-Medium.ttf
Normal file
BIN
frontend/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
2
frontend/public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-Agent: *
|
||||
Disallow:
|
||||
145
frontend/public/style.css
Normal file
@@ -0,0 +1,145 @@
|
||||
:root {
|
||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: rgba(27, 38, 54, 1);
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(""),
|
||||
url("./Inter-Medium.ttf") format("truetype");
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 3em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
margin: 0 0 0 20px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 1.5rem auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1rem;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.input-box .btn:hover {
|
||||
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.input-box .input {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
color: black;
|
||||
background-color: rgba(240, 240, 240, 1);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.input-box .input:hover {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.input-box .input:focus {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
1
frontend/public/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
BIN
frontend/public/wails.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
18
frontend/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.server.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.shared.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||