This commit is contained in:
parent
83f725f668
commit
dcebdb399e
@ -1,13 +1,11 @@
|
|||||||
name: Build image
|
name: Build image
|
||||||
on: [push]
|
on: [push]
|
||||||
env:
|
|
||||||
HTTPS_PROXY: "http://192.168.31.55:10809"
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: build
|
- name: build
|
||||||
run: docker build -t ${{ vars.DOCKER_REGISTRY }}/${{ vars.IMAGE_NAME }}:1.3 -f Dockerfile .
|
run: docker build -t ${{ vars.DOCKER_REGISTRY }}/${{ vars.IMAGE_NAME }}:1.3 -f Dockerfile .
|
||||||
- name: tag
|
- name: tag
|
||||||
|
@ -5,19 +5,11 @@ WORKDIR /build
|
|||||||
COPY . /build
|
COPY . /build
|
||||||
RUN CGO_ENABLED=0 go build -trimpath -ldflags '-w -s' -o watcher ./main
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags '-w -s' -o watcher ./main
|
||||||
|
|
||||||
FROM oven/bun:1 as front
|
|
||||||
ARG HTTP_PROXY=http://192.168.31.55:10809
|
|
||||||
ARG HTTPS_PROXY=http://192.168.31.55:10809
|
|
||||||
COPY . /build
|
|
||||||
WORKDIR /build/wwwroot
|
|
||||||
RUN bun install && bun run build
|
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
ARG HTTP_PROXY=http://192.168.31.55:10809
|
ARG HTTP_PROXY=http://192.168.31.55:10809
|
||||||
ARG HTTPS_PROXY=http://192.168.31.55:10809
|
ARG HTTPS_PROXY=http://192.168.31.55:10809
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
COPY --from=back /build/watcher /work/
|
COPY --from=back /build/watcher /work/
|
||||||
COPY --from=front /build/wwwroot/dist /work/dist/
|
|
||||||
ENTRYPOINT ["/work/watcher"]
|
ENTRYPOINT ["/work/watcher"]
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/gofiber/fiber/v3"
|
"github.com/gofiber/fiber/v3"
|
||||||
|
"github.com/gofiber/fiber/v3/middleware/cors"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"haitao_watcher/pkg/database"
|
"haitao_watcher/pkg/database"
|
||||||
"haitao_watcher/pkg/model"
|
"haitao_watcher/pkg/model"
|
||||||
@ -41,8 +42,7 @@ func main() {
|
|||||||
watcherCtl := watcher.NewController(_ctx, db, pool, ch)
|
watcherCtl := watcher.NewController(_ctx, db, pool, ch)
|
||||||
|
|
||||||
r := fiber.New()
|
r := fiber.New()
|
||||||
|
r.Use(cors.New())
|
||||||
r.Static("/", "/work/dist")
|
|
||||||
|
|
||||||
api := r.Group("/api/v1")
|
api := r.Group("/api/v1")
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ func main() {
|
|||||||
server.NewPusherSvcController(pusherCtl).RegistryRouter(api)
|
server.NewPusherSvcController(pusherCtl).RegistryRouter(api)
|
||||||
server.NewProxySvc(pool).RegistryRouter(api)
|
server.NewProxySvc(pool).RegistryRouter(api)
|
||||||
|
|
||||||
if err = r.Listen(":2280"); err != nil {
|
if err = r.Listen(":8080"); err != nil {
|
||||||
glog.Warningf("server over: %v", err)
|
glog.Warningf("server over: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
wwwroot/.gitignore
vendored
24
wwwroot/.gitignore
vendored
@ -1,24 +0,0 @@
|
|||||||
# 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?
|
|
3
wwwroot/.vscode/extensions.json
vendored
3
wwwroot/.vscode/extensions.json
vendored
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": ["Vue.volar"]
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
# 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
|
|
Binary file not shown.
@ -1,13 +0,0 @@
|
|||||||
<!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>
|
|
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
<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>
|
|
@ -1,7 +0,0 @@
|
|||||||
import {mande} from "mande";
|
|
||||||
|
|
||||||
const pushers = mande('/api/v1/proxies')
|
|
||||||
|
|
||||||
export const getProxiesStatus = () => {
|
|
||||||
return pushers.get("/status")
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
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`)
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 271 KiB |
@ -1,10 +0,0 @@
|
|||||||
export const PUSHER = {
|
|
||||||
ANPUSHER: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const WEBSITE_OPTIONS = [
|
|
||||||
{
|
|
||||||
label: 'anPush',
|
|
||||||
value: PUSHER.ANPUSHER
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,15 +0,0 @@
|
|||||||
export const WEBSITES = {
|
|
||||||
UNKNOWN: 0,
|
|
||||||
COACHOUTLET: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const WEBSITE_OPTIONS = [
|
|
||||||
{
|
|
||||||
label: '未知',
|
|
||||||
value: WEBSITES.UNKNOWN
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'coachoutlet',
|
|
||||||
value: WEBSITES.COACHOUTLET
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,6 +0,0 @@
|
|||||||
html,body,#app {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
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')
|
|
@ -1,25 +0,0 @@
|
|||||||
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
|
|
@ -1,178 +0,0 @@
|
|||||||
|
|
||||||
<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>
|
|
@ -1,258 +0,0 @@
|
|||||||
<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>
|
|
@ -1,42 +0,0 @@
|
|||||||
<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>
|
|
@ -1,38 +0,0 @@
|
|||||||
<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>
|
|
@ -1,14 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="h-full w-full bg-[#f0f3f7] flex flex-col">
|
|
||||||
<router-view ></router-view>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,17 +0,0 @@
|
|||||||
<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>
|
|
@ -1,38 +0,0 @@
|
|||||||
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/',
|
|
||||||
changeOrigin: true,
|
|
||||||
secure: false,
|
|
||||||
ws: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': path.resolve(__dirname, "./src")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
Loading…
Reference in New Issue
Block a user