This commit is contained in:
commit
43e2dff6a3
18
.gitea/workflows/build-push.yml
Normal file
18
.gitea/workflows/build-push.yml
Normal file
@ -0,0 +1,18 @@
|
||||
name: Build image
|
||||
on: [push]
|
||||
env:
|
||||
HTTPS_PROXY: "http://192.168.31.55:10809"
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: build
|
||||
run: docker build -t ${{ vars.DOCKER_REGISTRY }}/${{ vars.IMAGE_NAME }}:1.3 -f Dockerfile .
|
||||
- name: tag
|
||||
run: docker tag ${{ vars.DOCKER_REGISTRY }}/${{ vars.IMAGE_NAME }}:1.3 ${{ vars.DOCKER_REGISTRY }}/${{ vars.IMAGE_NAME }}:latest
|
||||
- name: push 1.3
|
||||
run: docker push ${{ vars.DOCKER_REGISTRY }}/${{ vars.IMAGE_NAME }}:1.3
|
||||
- name: push latest
|
||||
run: docker push ${{ vars.DOCKER_REGISTRY }}/${{ vars.IMAGE_NAME }}:latest
|
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@ -0,0 +1,13 @@
|
||||
FROM oven/bun:1 as front
|
||||
COPY . /build
|
||||
WORKDIR /build/wwwroot
|
||||
RUN bun install && bun run build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=front /build/wwwroot/dist /usr/share/
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
# 将自定义配置文件nginx.conf复制到容器内/etc/nginx/conf.d/目录
|
||||
ADD ./nginx.conf /etc/nginx/conf.d/
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously Volar) and disable Vetur
|
2
bunfig.toml
Normal file
2
bunfig.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[install]
|
||||
registry = "https://registry.npmmirror.com/"
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" />-->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>可达鸭海淘</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
21
nginx.conf
Normal file
21
nginx.conf
Normal file
@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name ht.timerzz.com;
|
||||
|
||||
#access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/dist;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location /api/v1 {
|
||||
resolver 10.43.0.10 valid=10s; # 6.6.6.6 为自建DNS
|
||||
proxy_pass http://ht-watcher;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
26
package.json
Normal file
26
package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "wwwroot",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"ant-design-vue": "4.x",
|
||||
"mande": "^2.0.8",
|
||||
"moment": "^2.30.1",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.5",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"unocss": "^0.59.0",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.2.0"
|
||||
}
|
||||
}
|
15
src/App.vue
Normal file
15
src/App.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<a-config-provider :locale="zhCN">
|
||||
<layout></layout>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
<script setup>
|
||||
import Layout from '@/views/layout/index.vue'
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
7
src/api/proxies.js
Normal file
7
src/api/proxies.js
Normal file
@ -0,0 +1,7 @@
|
||||
import {mande} from "mande";
|
||||
|
||||
const pushers = mande('/api/v1/proxies')
|
||||
|
||||
export const getProxiesStatus = () => {
|
||||
return pushers.get("/status")
|
||||
}
|
11
src/api/pusher.js
Normal file
11
src/api/pusher.js
Normal file
@ -0,0 +1,11 @@
|
||||
import {mande} from "mande";
|
||||
|
||||
const pushers = mande('/api/v1/pushers')
|
||||
|
||||
export const ListPushers = (query) => {
|
||||
return pushers.get({query:query})
|
||||
}
|
||||
|
||||
export const AddPusher = (opt)=>{
|
||||
return pushers.post(opt)
|
||||
}
|
22
src/api/watcher.js
Normal file
22
src/api/watcher.js
Normal file
@ -0,0 +1,22 @@
|
||||
import {mande} from "mande";
|
||||
|
||||
const watchers = mande('/api/v1/watchers')
|
||||
|
||||
export const ListWatchers = (query) => {
|
||||
return watchers.get({query:query})
|
||||
}
|
||||
|
||||
export const CreateWatcher = (opt)=>{
|
||||
return watchers.post(opt)
|
||||
}
|
||||
|
||||
export const DeleteWatcher = (uid)=>{
|
||||
return watchers.delete(`/${encodeURIComponent(uid)}`)
|
||||
}
|
||||
|
||||
export const StopWatcher = (uid)=>{
|
||||
return watchers.delete(`/${encodeURIComponent(uid)}/status`)
|
||||
}
|
||||
export const StartWatcher = (uid)=>{
|
||||
return watchers.post(`/${encodeURIComponent(uid)}/status`)
|
||||
}
|
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 271 KiB |
10
src/constants/pusher.js
Normal file
10
src/constants/pusher.js
Normal file
@ -0,0 +1,10 @@
|
||||
export const PUSHER = {
|
||||
ANPUSHER: 1,
|
||||
}
|
||||
|
||||
export const WEBSITE_OPTIONS = [
|
||||
{
|
||||
label: 'anPush',
|
||||
value: PUSHER.ANPUSHER
|
||||
}
|
||||
]
|
15
src/constants/website.js
Normal file
15
src/constants/website.js
Normal file
@ -0,0 +1,15 @@
|
||||
export const WEBSITES = {
|
||||
UNKNOWN: 0,
|
||||
COACHOUTLET: 1,
|
||||
}
|
||||
|
||||
export const WEBSITE_OPTIONS = [
|
||||
{
|
||||
label: '未知',
|
||||
value: WEBSITES.UNKNOWN
|
||||
},
|
||||
{
|
||||
label: 'coachoutlet',
|
||||
value: WEBSITES.COACHOUTLET
|
||||
}
|
||||
]
|
6
src/css/base.css
Normal file
6
src/css/base.css
Normal file
@ -0,0 +1,6 @@
|
||||
html,body,#app {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
9
src/main.js
Normal file
9
src/main.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { createApp } from 'vue'
|
||||
import './css/base.css'
|
||||
import App from './App.vue'
|
||||
import 'virtual:uno.css'
|
||||
import router from "@/routers/index.js";
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
app.mount('#app')
|
25
src/routers/index.js
Normal file
25
src/routers/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
import {createRouter, createWebHashHistory} from "vue-router";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/watcher'
|
||||
},
|
||||
{
|
||||
path: '/watcher',
|
||||
name: 'watcher',
|
||||
component: ()=>import('@/views/Watcher/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/pusher',
|
||||
name: 'pusher',
|
||||
component: ()=>import('@/views/Pusher/index.vue')
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
178
src/views/Pusher/index.vue
Normal file
178
src/views/Pusher/index.vue
Normal file
@ -0,0 +1,178 @@
|
||||
|
||||
<template>
|
||||
<div class="h-full m-4 bg-white rounded-2 shadow-lg p-8 flex flex-col justify-between space-y-4">
|
||||
<div class="flex justify-between">
|
||||
<a-button type="primary" @click="addModal.visible=true" :disabled="loading">添加</a-button>
|
||||
<div class="flex space-x-4">
|
||||
<a-input placeholder="请输入关键词" v-model:value="query.keyword"></a-input>
|
||||
<a-button type="primary" :disabled="loading" @click="list">搜索</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-full border-0 border-t-1 border-solid border-gray-300 pt-4">
|
||||
<a-spin :spinning="loading" :indicator="indicator">
|
||||
<a-table :dataSource="data.list" :columns="columns" :pagination="false">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'type'">
|
||||
<span>{{WEBSITE_OPTIONS.find(w => w.value === record.type).label}}</span>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'updatedAt'">
|
||||
<span>{{moment(record.updatedAt).format('YYYY-MM-DD HH:mm:ss')}}</span>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'option'">
|
||||
<span>{{record.option}}</span>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-pagination :disabled="loading" class="text-right" v-model:current="query.page" :total="data.total" show-less-items />
|
||||
</div>
|
||||
<a-modal v-model:open="addModal.visible" title="添加推送通知" @ok="handleOk" >
|
||||
<a-spin :spinning="addModal.loading" :indicator="indicator">
|
||||
<a-form
|
||||
:model="addModal.data"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
autocomplete="off"
|
||||
@finish="closeAddModal"
|
||||
@finishFailed="closeAddModal"
|
||||
>
|
||||
<a-form-item label="名称" name="name" :rules="[{ required: true, message: '请填写推送名称' }]">
|
||||
<a-input v-model:value="addModal.data.name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="token" name="option.token" :rules="[{ required: false, message: '请填写token' }]">
|
||||
<a-input v-model:value="addModal.data.option.token" />
|
||||
</a-form-item>
|
||||
<a-form-item label="channel" name="option.channel" :rules="[{ required: false, message: '请填写channel' }]">
|
||||
<a-input v-model:value="addModal.data.option.channel" />
|
||||
</a-form-item>
|
||||
<a-form-item label="备注" name="remark">
|
||||
<a-textarea v-model:value="addModal.data.remark" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
import {h, onMounted, reactive, ref} from "vue";
|
||||
import {WEBSITE_OPTIONS} from "@/constants/website.js";
|
||||
import moment from "moment/moment.js";
|
||||
import {LoadingOutlined} from "@ant-design/icons-vue";
|
||||
import {AddPusher, ListPushers} from "@/api/pusher.js";
|
||||
import {PUSHER} from "@/constants/pusher.js";
|
||||
import {message} from "ant-design-vue";
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
onMounted(()=>{
|
||||
list()
|
||||
})
|
||||
|
||||
|
||||
const query = reactive({
|
||||
// Website database.WebsiteType `query:"website,omitempty"` //是什么网站
|
||||
// Watch *bool `query:"watch,omitempty"`
|
||||
// Orderable *bool `query:"orderable,omitempty"`
|
||||
// Keyword string `query:"keyword,omitempty"`
|
||||
keyword: '',
|
||||
page: 1,
|
||||
size:10
|
||||
})
|
||||
|
||||
|
||||
const data = ref({
|
||||
total: 0,
|
||||
list:[]
|
||||
})
|
||||
|
||||
const list = ()=>{
|
||||
loading.value = true
|
||||
ListPushers(query).then(res=>{
|
||||
data.value = res
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const addModal = reactive({
|
||||
visible: false,
|
||||
data: {
|
||||
type:PUSHER.ANPUSHER,
|
||||
name:'',
|
||||
remark:'',
|
||||
option:{
|
||||
token:'',
|
||||
channel: ''
|
||||
}
|
||||
},
|
||||
loading: false
|
||||
})
|
||||
|
||||
const closeAddModal = ()=>{
|
||||
addModal.visible = false
|
||||
addModal.data = {
|
||||
type:PUSHER.ANPUSHER,
|
||||
name:'',
|
||||
remark:'',
|
||||
option:{
|
||||
token:'',
|
||||
channel: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleOk = ()=>{
|
||||
AddPusher(addModal.data).then(res=>{
|
||||
message.success("添加成功")
|
||||
}).catch(err => {
|
||||
message.error("添加失败")
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
list(false)
|
||||
closeAddModal()
|
||||
})
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
},
|
||||
{
|
||||
title: '配置',
|
||||
dataIndex: 'option',
|
||||
key: 'option',
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updatedAt',
|
||||
key: 'updatedAt',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
},
|
||||
]
|
||||
|
||||
const indicator = h(LoadingOutlined, {
|
||||
style: {
|
||||
fontSize: '32px',
|
||||
},
|
||||
spin: true,
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
258
src/views/Watcher/index.vue
Normal file
258
src/views/Watcher/index.vue
Normal file
@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<div class="h-full m-4 bg-white rounded-2 shadow-lg p-8 flex flex-col justify-between space-y-4">
|
||||
<div class="flex justify-between">
|
||||
<a-button type="primary" @click="addModal.visible=true" :disabled="loading">添加</a-button>
|
||||
<div class="flex space-x-4">
|
||||
<a-input placeholder="请输入关键词" v-model:value="query.keyword"></a-input>
|
||||
<a-button type="primary" :disabled="loading" @click="list">搜索</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-full border-0 border-t-1 border-solid border-gray-300 pt-4">
|
||||
<a-spin :spinning="loading" :indicator="indicator">
|
||||
<a-table :dataSource="data.list" :columns="columns" :pagination="false">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'website'">
|
||||
<span>{{WEBSITE_OPTIONS.find(w => w.value === record.website).label}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'watch'">
|
||||
<a-switch :checked="record.watch" @click="watcherStatusChange(!record.watch,record.uid)"/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'name'">
|
||||
<a v-if="record.name !== '' " :href="record.link" target="_blank">{{record.name}}</a>
|
||||
<span v-else>正在抓取信息</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'updatedAt'">
|
||||
<span>{{moment(record.updatedAt).format('YYYY-MM-DD HH:mm:ss')}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'pusherIds'">
|
||||
<template v-if="record.pusherIds" v-for="id in record.pusherIds">
|
||||
<a-tag :bordered="false" color="processing">{{pusher.list.find(p =>p.id === id)?.name}}</a-tag>
|
||||
</template>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'opt'">
|
||||
<a-button type="link" danger @click="onDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-pagination :disabled="loading" class="text-right" v-model:current="query.page" :total="data.total" show-less-items />
|
||||
</div>
|
||||
<a-modal v-model:open="addModal.visible" title="添加监听任务" @ok="handleOk" >
|
||||
<a-spin :spinning="addModal.loading" :indicator="indicator">
|
||||
<a-form
|
||||
:model="addModal.data"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
autocomplete="off"
|
||||
@finish="closeAddModal"
|
||||
@finishFailed="closeAddModal"
|
||||
>
|
||||
<a-form-item label="ID" name="pid" :rules="[{ required: true, message: '请填写商品id' }]">
|
||||
<a-input v-model:value="addModal.data.pid" />
|
||||
</a-form-item>
|
||||
<a-form-item label="推送" name="pusherIds" :rules="[{ required: true, message: '请选择通知推送' }]">
|
||||
<a-select
|
||||
v-model:value="addModal.data.pusherIds"
|
||||
mode="multiple"
|
||||
@dropdown-visible-change="getPushers"
|
||||
placeholder="请选择通知推送" :fieldNames="{label:'name',value:'id'}"
|
||||
:options="pusher.list"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注" name="remark">
|
||||
<a-textarea v-model:value="addModal.data.remark" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup>
|
||||
import moment from "moment";
|
||||
import {h, onMounted, reactive, ref} from "vue";
|
||||
import {ListWatchers, CreateWatcher, DeleteWatcher, StartWatcher, StopWatcher} from "@/api/watcher.js";
|
||||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import {message, Modal} from 'ant-design-vue';
|
||||
import {WEBSITE_OPTIONS} from "@/constants/website.js";
|
||||
import {onBeforeRouteLeave} from "vue-router";
|
||||
import {ListPushers} from "@/api/pusher.js";
|
||||
|
||||
let ticker = null
|
||||
onMounted(()=>{
|
||||
list(false)
|
||||
getPushers()
|
||||
ticker = setInterval(list, 2000, true)
|
||||
})
|
||||
|
||||
onBeforeRouteLeave(()=>{
|
||||
if(ticker){
|
||||
clearInterval(ticker)
|
||||
}
|
||||
})
|
||||
|
||||
const query = reactive({
|
||||
// Website database.WebsiteType `query:"website,omitempty"` //是什么网站
|
||||
// Watch *bool `query:"watch,omitempty"`
|
||||
// Orderable *bool `query:"orderable,omitempty"`
|
||||
// Keyword string `query:"keyword,omitempty"`
|
||||
page: 1,
|
||||
size:10
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const data = ref({
|
||||
total: 0,
|
||||
list:[]
|
||||
})
|
||||
|
||||
const list = (silent)=>{
|
||||
if(!silent){
|
||||
loading.value = true
|
||||
}
|
||||
ListWatchers(query).then(res=>{
|
||||
data.value = res
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
if(!silent){
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const addModal = reactive({
|
||||
visible: false,
|
||||
data: {
|
||||
pid:'',
|
||||
remark:'',
|
||||
pusherIds:[]
|
||||
},
|
||||
loading: false
|
||||
})
|
||||
|
||||
const closeAddModal = ()=>{
|
||||
addModal.visible = false
|
||||
addModal.data = {
|
||||
pid:'',
|
||||
remark:'',
|
||||
pusherIds: []
|
||||
}
|
||||
}
|
||||
|
||||
const handleOk = ()=>{
|
||||
CreateWatcher(addModal.data).then(res=>{
|
||||
message.success("添加成功")
|
||||
}).catch(err => {
|
||||
message.error("添加失败")
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
list(false)
|
||||
closeAddModal()
|
||||
})
|
||||
}
|
||||
|
||||
const pusher = reactive({
|
||||
list: [],
|
||||
query: {
|
||||
keyword:'',
|
||||
all: true
|
||||
}
|
||||
})
|
||||
|
||||
const getPushers = ()=>{
|
||||
ListPushers(pusher.query).then(res=>{
|
||||
pusher.list = res.list || []
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
const onDelete = ({name, uid})=>{
|
||||
Modal.confirm({
|
||||
title: '确认',
|
||||
content: `确定删除 ${name} 监听?`,
|
||||
centered: true,
|
||||
onOk() {
|
||||
DeleteWatcher(uid).then(res=>{
|
||||
message.success("删除成功")
|
||||
list(false)
|
||||
}).catch(err=>{
|
||||
message.error("删除失败")
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const watcherStatusChange=(changed, uid)=>{
|
||||
const api = changed ? StartWatcher:StopWatcher
|
||||
loading.value = true
|
||||
api(uid).then(res=>{
|
||||
message.success(`${changed?'开启':'关闭'}成功`)
|
||||
}).catch(err=>{
|
||||
message.error(`${changed?'开启':'关闭'}失败`)
|
||||
}).finally(list)
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '货号',
|
||||
dataIndex: 'pid',
|
||||
key: 'pid',
|
||||
},
|
||||
{
|
||||
title: '品牌',
|
||||
dataIndex: 'brand',
|
||||
key: 'brand',
|
||||
},
|
||||
{
|
||||
title: '网站',
|
||||
dataIndex: 'website',
|
||||
key: 'website',
|
||||
},
|
||||
{
|
||||
title: '正在蹲货',
|
||||
dataIndex: 'watch',
|
||||
key: 'watch',
|
||||
},
|
||||
{
|
||||
title: '抓取时间',
|
||||
dataIndex: 'updatedAt',
|
||||
key: 'updatedAt',
|
||||
},
|
||||
{
|
||||
title: '推送',
|
||||
key: 'pusherIds',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'opt',
|
||||
},
|
||||
]
|
||||
|
||||
const indicator = h(LoadingOutlined, {
|
||||
style: {
|
||||
fontSize: '32px',
|
||||
},
|
||||
spin: true,
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
42
src/views/layout/Aside.vue
Normal file
42
src/views/layout/Aside.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<a-menu
|
||||
@click="onclick"
|
||||
id="aside"
|
||||
style="width: 256px"
|
||||
mode="inline"
|
||||
:items="items"
|
||||
></a-menu>
|
||||
</template>
|
||||
<script setup>
|
||||
import {AccountBookOutlined, BellOutlined} from "@ant-design/icons-vue";
|
||||
import {h} from "vue";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: 'watcher',
|
||||
icon: () => h(AccountBookOutlined),
|
||||
label: '蹲货',
|
||||
title: '蹲货',
|
||||
},
|
||||
{
|
||||
key: 'pusher',
|
||||
icon: () => h(BellOutlined),
|
||||
label: '推送',
|
||||
title: '推送',
|
||||
},
|
||||
]
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const onclick = ({key}) => {
|
||||
router.push({
|
||||
name: key
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
38
src/views/layout/Header.vue
Normal file
38
src/views/layout/Header.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="h-[100px] shadow-lg flex items-center justify-between px-12 z-10">
|
||||
<div class="flex space-x-4 items-center">
|
||||
<img class="h-[60px]" src="@/assets/logo.png" alt="">
|
||||
<div class="text-[24px] font-bold">
|
||||
可达鸭海淘蹲货
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>当前代理个数: {{proxiesInfo.list.length}}</div>
|
||||
<div class="text-[14px]">代理更新时间:{{moment(proxiesInfo.updated).format('YYYY-MM-DD HH:mm:ss')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {onMounted, reactive} from "vue";
|
||||
import {getProxiesStatus} from "@/api/proxies.js";
|
||||
import moment from "moment";
|
||||
|
||||
const proxiesInfo = reactive({
|
||||
list:[],
|
||||
updated: ''
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
loadProxiesInfo()
|
||||
})
|
||||
const loadProxiesInfo = ()=>{
|
||||
getProxiesStatus().then(res=>{
|
||||
proxiesInfo.list = res.list
|
||||
proxiesInfo.updated = res.updated
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
14
src/views/layout/Main.vue
Normal file
14
src/views/layout/Main.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="h-full w-full bg-[#f0f3f7] flex flex-col">
|
||||
<router-view ></router-view>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
17
src/views/layout/index.vue
Normal file
17
src/views/layout/index.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="h-full w-full flex flex-col">
|
||||
<Header></Header>
|
||||
<div class="h-full w-full flex">
|
||||
<Aside></Aside>
|
||||
<Main></Main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import Header from "@/views/layout/Header.vue";
|
||||
import Aside from "@/views/layout/Aside.vue";
|
||||
import Main from "@/views/layout/Main.vue";
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
39
vite.config.js
Normal file
39
vite.config.js
Normal file
@ -0,0 +1,39 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import UnoCSS from 'unocss/vite'
|
||||
import path from 'path'
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
|
||||
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
UnoCSS(),
|
||||
Components({
|
||||
resolvers: [
|
||||
AntDesignVueResolver({
|
||||
importStyle: false, // css in js
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
server:{
|
||||
open: true,
|
||||
proxy: {
|
||||
'/api/v1': {
|
||||
target: 'http://172.25.168.160:2280/',
|
||||
// target: 'http://192.168.31.55:2280/',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, "./src")
|
||||
}
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue
Block a user