first commit
This commit is contained in:
commit
57ce531be5
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
6
.prettierrc.json
Normal file
6
.prettierrc.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
6
.vscode/extensions.json
vendored
Normal file
6
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"Vue.volar",
|
||||||
|
"esbenp.prettier-vscode"
|
||||||
|
]
|
||||||
|
}
|
29
README.md
Normal file
29
README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# tg_user_h5
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||||
|
|
||||||
|
## Customize configuration
|
||||||
|
|
||||||
|
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Vite App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
8
jsconfig.json
Normal file
8
jsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
4428
package-lock.json
generated
Normal file
4428
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
package.json
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "tg_user_h5",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"format": "prettier --write src/"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vueuse/core": "^13.5.0",
|
||||||
|
"@wangeditor/editor": "^5.1.23",
|
||||||
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
|
"axios": "^1.10.0",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"pinia": "^3.0.3",
|
||||||
|
"vant": "^4.9.20",
|
||||||
|
"vue": "^3.5.17",
|
||||||
|
"vue-clipboard3": "^2.0.0",
|
||||||
|
"vue-router": "^4.5.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
|
"prettier": "3.5.3",
|
||||||
|
"sass": "^1.89.2",
|
||||||
|
"sass-loader": "^16.0.5",
|
||||||
|
"unplugin-vue-components": "^28.8.0",
|
||||||
|
"vite": "npm:rolldown-vite@latest",
|
||||||
|
"vite-plugin-vue-devtools": "^7.7.7"
|
||||||
|
}
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
11
src/App.vue
Normal file
11
src/App.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup>
|
||||||
|
import { RouterLink, RouterView } from 'vue-router'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
|
||||||
|
<RouterView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style></style>
|
86
src/assets/base.css
Normal file
86
src/assets/base.css
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/* color palette from <https://github.com/vuejs/theme> */
|
||||||
|
:root {
|
||||||
|
--vt-c-white: #ffffff;
|
||||||
|
--vt-c-white-soft: #f8f8f8;
|
||||||
|
--vt-c-white-mute: #f2f2f2;
|
||||||
|
|
||||||
|
--vt-c-black: #181818;
|
||||||
|
--vt-c-black-soft: #222222;
|
||||||
|
--vt-c-black-mute: #282828;
|
||||||
|
|
||||||
|
--vt-c-indigo: #2c3e50;
|
||||||
|
|
||||||
|
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||||
|
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||||
|
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||||
|
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||||
|
|
||||||
|
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||||
|
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||||
|
--vt-c-text-dark-1: var(--vt-c-white);
|
||||||
|
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* semantic color variables for this project */
|
||||||
|
:root {
|
||||||
|
--color-background: var(--vt-c-white);
|
||||||
|
--color-background-soft: var(--vt-c-white-soft);
|
||||||
|
--color-background-mute: var(--vt-c-white-mute);
|
||||||
|
|
||||||
|
--color-border: var(--vt-c-divider-light-2);
|
||||||
|
--color-border-hover: var(--vt-c-divider-light-1);
|
||||||
|
|
||||||
|
--color-heading: var(--vt-c-text-light-1);
|
||||||
|
--color-text: var(--vt-c-text-light-1);
|
||||||
|
|
||||||
|
--section-gap: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--color-background: var(--vt-c-black);
|
||||||
|
--color-background-soft: var(--vt-c-black-soft);
|
||||||
|
--color-background-mute: var(--vt-c-black-mute);
|
||||||
|
|
||||||
|
--color-border: var(--vt-c-divider-dark-2);
|
||||||
|
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||||
|
|
||||||
|
--color-heading: var(--vt-c-text-dark-1);
|
||||||
|
--color-text: var(--vt-c-text-dark-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-background);
|
||||||
|
transition:
|
||||||
|
color 0.5s,
|
||||||
|
background-color 0.5s;
|
||||||
|
line-height: 1.6;
|
||||||
|
font-family:
|
||||||
|
Inter,
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
'Segoe UI',
|
||||||
|
Roboto,
|
||||||
|
Oxygen,
|
||||||
|
Ubuntu,
|
||||||
|
Cantarell,
|
||||||
|
'Fira Sans',
|
||||||
|
'Droid Sans',
|
||||||
|
'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
font-size: 15px;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
1
src/assets/logo.svg
Normal file
1
src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
After Width: | Height: | Size: 276 B |
35
src/assets/main.css
Normal file
35
src/assets/main.css
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
@import './base.css';
|
||||||
|
|
||||||
|
#app {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
.green {
|
||||||
|
text-decoration: none;
|
||||||
|
color: hsla(160, 100%, 37%, 1);
|
||||||
|
transition: 0.4s;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
a:hover {
|
||||||
|
background-color: hsla(160, 100%, 37%, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
}
|
BIN
src/assets/pay-code.jpeg
Normal file
BIN
src/assets/pay-code.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
67
src/axios/api.js
Normal file
67
src/axios/api.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import { showSuccessToast, showFailToast } from 'vant'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
const router = useRouter()
|
||||||
|
const instance = axios.create({
|
||||||
|
baseURL: import.meta.env.VITE_API_URL,
|
||||||
|
timeout: 1000 * 30,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleGetParams = (params) => {
|
||||||
|
let result = { ...params }
|
||||||
|
|
||||||
|
Object.entries(params || {}).map(([key, value]) => {
|
||||||
|
if (value instanceof Date) {
|
||||||
|
result[key] = getTimestamp(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
const token = localStorage.getItem('customeraccessToken')
|
||||||
|
if (config.method?.toLowerCase() === 'get') {
|
||||||
|
if (config.params) {
|
||||||
|
config.params = handleGetParams(config.params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`
|
||||||
|
}
|
||||||
|
console.log('config', config)
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.log(error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
instance.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
const data = response.data
|
||||||
|
if (data.code === 200) {
|
||||||
|
return data.data
|
||||||
|
} else {
|
||||||
|
showFailToast(data.msg)
|
||||||
|
return Promise.reject(data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.log('errror---------->', error)
|
||||||
|
if (error.status == 401) {
|
||||||
|
showFailToast('登录已过期,请重新登录')
|
||||||
|
localStorage.removeItem('customeraccessToken')
|
||||||
|
window.location.href = '/h5/login'
|
||||||
|
console.log('router')
|
||||||
|
// router.replace({
|
||||||
|
// path: '/login',
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
export default instance
|
189
src/axios/request.js
Normal file
189
src/axios/request.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import request from './api'
|
||||||
|
export const getTaskList = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/tasks/list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 添加任务
|
||||||
|
export const addTask = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/tasks/add',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const changeStatus = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/tasks/changeStatus',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 修改任务
|
||||||
|
export const editTask = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/tasks/edit',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 删除任务
|
||||||
|
export const delTask = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/tasks/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 获取群组
|
||||||
|
export const getGroupsList = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/groups/list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 获取模板列表
|
||||||
|
export const getTemplatesList = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/templates/list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export const getLoginToken = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/robot/get-login-token',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export const checkLoginToken = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/robot/check-login-token',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export const userLogin = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/login',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const uploadFileApi = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/uploadImg',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取收款账号
|
||||||
|
export const getUsdtInfo = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/tasks/get-usd-info',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传交易hash
|
||||||
|
export const updatePayHash = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/tasks/updatePayHash',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 获取订单列表
|
||||||
|
export const getOrderList = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/order/list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 获取订单状态列表
|
||||||
|
export const getOrderStatusList = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/order/get-status-list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 申请退款
|
||||||
|
export const confirmConversionRefundRequest = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/order/ConfirmConversionRefundRequest',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 获取用户信息
|
||||||
|
export const getUserinfo = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/getUserinfo',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取统计信息
|
||||||
|
export const getDashboard = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/dashboard/index',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 更新用户信息
|
||||||
|
export const UpdateInfo = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/updateinfo',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 保存密码
|
||||||
|
export const ChangePassword = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/changePassword',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 群发记录
|
||||||
|
export const getMassSend = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/send_log/list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//消息记录
|
||||||
|
export const getMessageLog = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/message_log/list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 敏感词校验
|
||||||
|
export const checkWord = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/sensitiveWords/detect',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
220
src/components/DateTime.vue
Normal file
220
src/components/DateTime.vue
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 弹出层 -->
|
||||||
|
<van-popup v-model:show="data.isPicker" position="bottom" round @close="confirmOn">
|
||||||
|
<van-picker ref="picker" title="请选择时间" :columns="data.columns" @change="onChange" @cancel="cancelOn"
|
||||||
|
@confirm="onConfirm" v-model="data.selectedValues" />
|
||||||
|
</van-popup>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { reactive, watch, getCurrentInstance } from "vue";
|
||||||
|
|
||||||
|
const customFieldName = {
|
||||||
|
text: "value",
|
||||||
|
value: "values",
|
||||||
|
children: ""
|
||||||
|
};
|
||||||
|
const data = reactive({
|
||||||
|
isPicker: false, //是否显示弹出层
|
||||||
|
columns: [], //所有时间列
|
||||||
|
selectedValues: [] //控件选择的时间值
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 传入的显影状态
|
||||||
|
showPicker: {
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
// 传入的值
|
||||||
|
values: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//定义要向父组件传递的事件
|
||||||
|
const emit = defineEmits(["changeValue", "confirm"]);
|
||||||
|
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.showPicker,
|
||||||
|
val => {
|
||||||
|
data.isPicker = val;
|
||||||
|
data.columns = [];
|
||||||
|
getcolumns();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true//立即监听--进入就会执行一次 监听显影状态
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
function onChange() {
|
||||||
|
// 无用的方法
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getcolumns() {
|
||||||
|
let strtime = props.values; //传入的时间
|
||||||
|
//console.log(strtime); 2023-09-05 19:28:00
|
||||||
|
let date = new Date(strtime.replace(/-/g, "/"));
|
||||||
|
// console.log(date); Wed Aug 09 2023 14:53:15 GMT+0800 (中国标准时间)
|
||||||
|
let timeVaules = date.getTime();
|
||||||
|
|
||||||
|
let dateVaules;
|
||||||
|
if (props.values != "") {
|
||||||
|
dateVaules = new Date(timeVaules);
|
||||||
|
} else {
|
||||||
|
dateVaules = new Date(); //没有传入时间则默认当前时刻
|
||||||
|
}
|
||||||
|
|
||||||
|
let Y = dateVaules.getFullYear();
|
||||||
|
let M = dateVaules.getMonth();
|
||||||
|
let D = dateVaules.getDate();
|
||||||
|
let h = dateVaules.getHours();
|
||||||
|
let m = dateVaules.getMinutes();
|
||||||
|
let s = dateVaules.getSeconds();
|
||||||
|
|
||||||
|
let year = []; //获取前后十年数组
|
||||||
|
year.values = [];
|
||||||
|
let Currentday = new Date().getFullYear();
|
||||||
|
for (let i = Currentday - 10; i < Currentday + 10; i++) {
|
||||||
|
year.push({ text: i.toString(), value: i });
|
||||||
|
}
|
||||||
|
year.defaultIndex = year.values.indexOf(Y); //设置默认选项当前年
|
||||||
|
|
||||||
|
// 个位数补0
|
||||||
|
const _M = M < 10 ? `0${M + 1}` : M.toString(); //月份比实际获取的少1,所以要加1
|
||||||
|
const _D = D < 10 ? `0${D}` : D.toString();
|
||||||
|
const _h = h < 10 ? `0${h}` : h.toString();
|
||||||
|
const _m = m < 10 ? `0${m}` : m.toString();
|
||||||
|
const _s = s < 10 ? `0${s}` : s.toString();
|
||||||
|
|
||||||
|
// 生成年月日时分秒时间值
|
||||||
|
data.selectedValues.push(Y);
|
||||||
|
data.selectedValues.push(_M);
|
||||||
|
data.selectedValues.push(_D);
|
||||||
|
data.selectedValues.push(_h);
|
||||||
|
data.selectedValues.push(_m);
|
||||||
|
data.selectedValues.push(_s);
|
||||||
|
|
||||||
|
data.columns.push(year); //生成年列
|
||||||
|
|
||||||
|
let month = []; //获取12月数组
|
||||||
|
month = Object.keys(Array.apply(null, { length: 13 })).map(function (item) {
|
||||||
|
if (+item + 1 <= 10) {
|
||||||
|
return { text: "0" + item, value: "0" + item };
|
||||||
|
} else if (+item + 1 == 11) {
|
||||||
|
return { text: +item, value: +item };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
text: (+item + 0).toString(),
|
||||||
|
value: (+item + 0).toString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
month.splice(0, 1);
|
||||||
|
data.columns.push(month); //生成月列
|
||||||
|
|
||||||
|
//获取当月的天数
|
||||||
|
let days = getCountDays(Y, M + 1);
|
||||||
|
let day = []; //创建当月天数数组
|
||||||
|
day = Object.keys(Array.apply(null, { length: days + 1 })).map(function (
|
||||||
|
item
|
||||||
|
) {
|
||||||
|
if (+item + 1 <= 10) {
|
||||||
|
return { text: "0" + item, value: "0" + item };
|
||||||
|
} else if (+item + 1 == 11) {
|
||||||
|
return { text: +item, value: +item };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
text: (+item + 0).toString(),
|
||||||
|
value: (+item + 0).toString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
day.splice(0, 1);
|
||||||
|
data.columns.push(day); //生成日列
|
||||||
|
|
||||||
|
let hour = []; //创建小时数组
|
||||||
|
hour = Object.keys(Array.apply(null, { length: 24 })).map(function (item) {
|
||||||
|
if (+item + 1 <= 10) {
|
||||||
|
return { text: "0" + item, value: "0" + item };
|
||||||
|
} else if (+item + 1 == 11) {
|
||||||
|
return { text: +item, value: +item };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
text: (+item + 0).toString(),
|
||||||
|
value: (+item + 0).toString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.columns.push(hour); //生成小时列
|
||||||
|
|
||||||
|
let mi = []; //创建分钟数组
|
||||||
|
mi = Object.keys(Array.apply(null, { length: 60 })).map(function (item) {
|
||||||
|
if (+item + 1 <= 10) {
|
||||||
|
return { text: "0" + item, value: "0" + item };
|
||||||
|
} else if (+item + 1 == 11) {
|
||||||
|
return { text: +item, value: +item };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
text: (+item + 0).toString(),
|
||||||
|
value: (+item + 0).toString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.columns.push(mi);//生成分钟列
|
||||||
|
|
||||||
|
let ss = []; //创建秒数数组
|
||||||
|
ss = Object.keys(Array.apply(null, { length: 60 })).map(function (item) {
|
||||||
|
if (+item + 1 <= 10) {
|
||||||
|
return { text: "0" + item, value: "0" + item };
|
||||||
|
} else if (+item + 1 == 11) {
|
||||||
|
return { text: +item, value: +item };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
text: (+item + 0).toString(),
|
||||||
|
value: (+item + 0).toString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.columns.push(ss);//生成秒钟列
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getCountDays(year, month) {
|
||||||
|
//获取某年某月多少天
|
||||||
|
let day = new Date(year, month, 0);
|
||||||
|
return day.getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹框
|
||||||
|
function confirmOn() {
|
||||||
|
emit("changeValue");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//时间选择器关闭 值不改变并关闭弹框
|
||||||
|
function cancelOn({ selectedValues }) {
|
||||||
|
confirmOn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间选择器确定 值改变
|
||||||
|
function onConfirm({ selectedValues }) {
|
||||||
|
let endval =
|
||||||
|
selectedValues[0] +
|
||||||
|
"-" +
|
||||||
|
selectedValues[1] +
|
||||||
|
"-" +
|
||||||
|
selectedValues[2] +
|
||||||
|
" " +
|
||||||
|
selectedValues[3] +
|
||||||
|
":" +
|
||||||
|
selectedValues[4] +
|
||||||
|
":" +
|
||||||
|
selectedValues[5];
|
||||||
|
|
||||||
|
confirmOn()
|
||||||
|
emit("confirm", endval);
|
||||||
|
}
|
||||||
|
</script>
|
172
src/components/Pay.vue
Normal file
172
src/components/Pay.vue
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<van-dialog v-model:show="show" title="标题" show-cancel-button>
|
||||||
|
<template #title>
|
||||||
|
<div class="my-header">
|
||||||
|
<div>充值网络USDT-TRC20</div>
|
||||||
|
<div style="font-size: 12px;color: #999;">请选择TRC20网络充值,否则可能造成资产丢失</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="pay-code">
|
||||||
|
<div style="margin:10px 0;">支付金额:<span type="danger" style="font-size: 16px;font-weight: 600;"> ${{
|
||||||
|
current_order.pay_usdt_amount }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img v-if="showImg" :src="data.payCode" class="pay-code-img"></img>
|
||||||
|
<span v-if="!showImg" type="warning">订单已过期</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showImg" style="margin: 10px 0;">
|
||||||
|
<span>订单有效期:</span><span type="primary" style="font-size: 16px;font-weight: 600;"> {{ get_time
|
||||||
|
}}</span>
|
||||||
|
<div>
|
||||||
|
<span type="danger">超过有效期订单自动取消,请尽快完成支付</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showImg" @click="copy(data.payHash)">
|
||||||
|
<div style="color: #999;font-size: 12px;">钱包地址(点击复制): </div>
|
||||||
|
<span style="margin: 0 10px; color: aqua;">{{ data.payHash }} </span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<van-button @click="closeFun" size="small" style="margin-right: 8px;">关闭</van-button>
|
||||||
|
<van-button type="primary" size="small" @click="confirmPay" v-if="showImg">
|
||||||
|
支付完成
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref, watch, } from 'vue'
|
||||||
|
import payCodeImg from '@/assets/pay-code.jpeg';
|
||||||
|
import useClipboard from 'vue-clipboard3'
|
||||||
|
|
||||||
|
import { getUsdtInfo } from '@/axios/request';
|
||||||
|
import { showSuccessToast, showFailToast } from 'vant';
|
||||||
|
const props = defineProps({
|
||||||
|
showDialog: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
current_order: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('props', props)
|
||||||
|
const show = ref(false);
|
||||||
|
const emit = defineEmits(["update:showDialog", "handleConfrimPay", 'closePay']);
|
||||||
|
const data = reactive({
|
||||||
|
payCode: payCodeImg,
|
||||||
|
payHash: '',
|
||||||
|
});;
|
||||||
|
const copy = async (msg) => {
|
||||||
|
const { toClipboard } = useClipboard()
|
||||||
|
try {
|
||||||
|
// 复制
|
||||||
|
console.log('复制', msg)
|
||||||
|
await toClipboard(msg)
|
||||||
|
showSuccessToast('复制成功');
|
||||||
|
// 复制成功
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
// 复制失败
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch(() => props.showDialog, (val) => {
|
||||||
|
console.log('showDialog', props.showDialog)
|
||||||
|
show.value = props.showDialog;
|
||||||
|
if (props.showDialog) {
|
||||||
|
|
||||||
|
setTime()
|
||||||
|
getInfo()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const get_time = ref('')
|
||||||
|
const showImg = ref(false)
|
||||||
|
const updateCountdown = (creacteTime, targetTime) => {
|
||||||
|
const target = new Date(targetTime).getTime();
|
||||||
|
const now = new Date().getTime();
|
||||||
|
const diff = target - now;
|
||||||
|
if (diff <= 0) {
|
||||||
|
clearInterval(timer)
|
||||||
|
showImg.value = false
|
||||||
|
get_time.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
|
||||||
|
|
||||||
|
get_time.value = ` ${minutes}分钟 ${seconds}秒`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 每秒更新一次
|
||||||
|
var timer = null
|
||||||
|
const setTime = () => {
|
||||||
|
clearInterval(timer)
|
||||||
|
let cureateTime = props.current_order.order_created_at
|
||||||
|
let targetTime = props.current_order.expired_at
|
||||||
|
if (new Date(cureateTime).getTime() < new Date(targetTime).getTime()) {
|
||||||
|
showImg.value = true
|
||||||
|
timer = setInterval(() => updateCountdown(cureateTime, targetTime), 1000);
|
||||||
|
} else {
|
||||||
|
showImg.value = false
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const closeFun = () => {
|
||||||
|
data.hash = ''
|
||||||
|
emit("update:showDialog", false);
|
||||||
|
emit("closePay");
|
||||||
|
};
|
||||||
|
const confirmPay = () => {
|
||||||
|
|
||||||
|
emit("handleConfrimPay");
|
||||||
|
emit("update:showDialog", false);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const getInfo = () => {
|
||||||
|
getUsdtInfo().then((res) => {
|
||||||
|
data.payHash = res.USDT_ACCOUNT;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.pay-code {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.pay-code-img {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-hash {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
94
src/components/WangEditor.vue
Normal file
94
src/components/WangEditor.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
|
||||||
|
<Editor style="height: 500px; overflow-y: hidden;" v-model="modelValue" :defaultConfig="editorConfig" :mode="mode"
|
||||||
|
@onCreated="handleCreated" />
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||||
|
import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
|
||||||
|
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||||
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
|
import { uploadFileApi } from "@/axios/request";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: [String],
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 编辑器实例,必须用 shallowRef
|
||||||
|
const editorRef = shallowRef()
|
||||||
|
const emit = defineEmits(["update:modelValue"]);
|
||||||
|
const modelValue = useVModel(props, "modelValue", emit);
|
||||||
|
const mode = ref("default");
|
||||||
|
|
||||||
|
const toolbarConfig = ref({
|
||||||
|
excludeKeys: [
|
||||||
|
'uploadVideo', // 隐藏上传视频按钮
|
||||||
|
'insertVideo', // 隐藏插入视频按钮
|
||||||
|
'group-video', // 禁用视频功能'
|
||||||
|
'insertTable',
|
||||||
|
'blockquote',
|
||||||
|
'header1',
|
||||||
|
'header2',
|
||||||
|
'header3',
|
||||||
|
'bulletedList',
|
||||||
|
'numberedList',
|
||||||
|
'todo',
|
||||||
|
'codeBlock',
|
||||||
|
'fontSize',
|
||||||
|
'fontFamily',
|
||||||
|
'lineHeight',
|
||||||
|
'group-indent',
|
||||||
|
'divider',
|
||||||
|
'headerSelect'
|
||||||
|
|
||||||
|
]
|
||||||
|
}); // 工具条配置
|
||||||
|
|
||||||
|
// 内容 HTML
|
||||||
|
const valueHtml = ref('<p>hello</p>')
|
||||||
|
|
||||||
|
// 模拟 ajax 异步获取内容
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const editorConfig = ref({
|
||||||
|
placeholder: "请输入内容...",
|
||||||
|
MENU_CONF: {
|
||||||
|
uploadImage: {
|
||||||
|
// 自定义图片上传
|
||||||
|
async customUpload(file, insertFn) {
|
||||||
|
uploadFileApi(file).then((response) => {
|
||||||
|
const url = response.data.url;
|
||||||
|
insertFn(url);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件销毁时,也及时销毁编辑器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (editor == null) return
|
||||||
|
editor.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCreated = (editor) => {
|
||||||
|
editorRef.value = editor // 记录 editor 实例,重要!
|
||||||
|
if (props.disabled) {
|
||||||
|
editor.disable();
|
||||||
|
} else {
|
||||||
|
editor.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
7
src/components/icons/IconCommunity.vue
Normal file
7
src/components/icons/IconCommunity.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
7
src/components/icons/IconDocumentation.vue
Normal file
7
src/components/icons/IconDocumentation.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
7
src/components/icons/IconEcosystem.vue
Normal file
7
src/components/icons/IconEcosystem.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
7
src/components/icons/IconSupport.vue
Normal file
7
src/components/icons/IconSupport.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
19
src/components/icons/IconTooling.vue
Normal file
19
src/components/icons/IconTooling.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
aria-hidden="true"
|
||||||
|
role="img"
|
||||||
|
class="iconify iconify--mdi"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
18
src/main.js
Normal file
18
src/main.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import './assets/main.css'
|
||||||
|
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
// import { DateTime } from 'vant'
|
||||||
|
import 'vant/lib/index.css'
|
||||||
|
import 'vant/es/toast/style'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(createPinia())
|
||||||
|
app.use(router)
|
||||||
|
// app.use(DateTime)
|
||||||
|
|
||||||
|
app.mount('#app')
|
44
src/router/index.js
Normal file
44
src/router/index.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/h5',
|
||||||
|
name: 'home',
|
||||||
|
component: () => import('../views/HomeView.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/h5/login',
|
||||||
|
name: 'login',
|
||||||
|
component: () => import('../views/login.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/h5/task',
|
||||||
|
name: 'task',
|
||||||
|
component: () => import('../views/task/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/h5/addtask',
|
||||||
|
name: 'addtask',
|
||||||
|
component: () => import('../views/task/add.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/h5/detail',
|
||||||
|
name: 'detail',
|
||||||
|
component: () => import('../views/user/detail.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/h5/mass_send',
|
||||||
|
name: 'mass_send',
|
||||||
|
component: () => import('../views/user/mass_send.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/h5/notice',
|
||||||
|
name: 'notice',
|
||||||
|
component: () => import('../views/user/notice.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
12
src/stores/counter.js
Normal file
12
src/stores/counter.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useCounterStore = defineStore('counter', () => {
|
||||||
|
const count = ref(0)
|
||||||
|
const doubleCount = computed(() => count.value * 2)
|
||||||
|
function increment() {
|
||||||
|
count.value++
|
||||||
|
}
|
||||||
|
|
||||||
|
return { count, doubleCount, increment }
|
||||||
|
})
|
57
src/views/HomeView.vue
Normal file
57
src/views/HomeView.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div>
|
||||||
|
<Task v-if="activeName == 'a'"></Task>
|
||||||
|
<PayRecord v-if="activeName == 'b'"></PayRecord>
|
||||||
|
<User v-if="activeName == 'c'"></User>
|
||||||
|
<div class="footer">
|
||||||
|
<van-tabs v-model:active="activeName" @change="changeTab">
|
||||||
|
<van-tab title="任务管理" name="a"></van-tab>
|
||||||
|
<van-tab title="支付管理" name="b"></van-tab>
|
||||||
|
<van-tab title="用户管理" name="c"></van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import Task from './task/index.vue'
|
||||||
|
import PayRecord from './pay/index.vue';
|
||||||
|
import User from './user/index.vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
let activeName = ref('a');
|
||||||
|
let token = ref('');
|
||||||
|
const router = useRouter();
|
||||||
|
const changeTab = (name) => {
|
||||||
|
console.log(name);
|
||||||
|
activeName.value = name;
|
||||||
|
localStorage.setItem('activeName', name);
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
console.log('HomeView', localStorage.getItem('activeName'));
|
||||||
|
token.value = localStorage.getItem('customeraccessToken')
|
||||||
|
if (localStorage.getItem('activeName')) {
|
||||||
|
activeName.value = localStorage.getItem('activeName');
|
||||||
|
}
|
||||||
|
if (!token.value) {
|
||||||
|
router.push('/h5/login');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
</style>
|
188
src/views/login.vue
Normal file
188
src/views/login.vue
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<van-nav-bar title="用户登录" />
|
||||||
|
<div class="login_box">
|
||||||
|
<van-tabs v-model:active="activeName" @change="changeTab">
|
||||||
|
<van-tab title="TG登录/注册" name="a">
|
||||||
|
<div class="type_login">
|
||||||
|
<span class="tips">复制信息跳转八方官方机器人粘贴完成登录</span>
|
||||||
|
<div class="tg-btn" @click="copyTg()">
|
||||||
|
<div class="tg-btn-span"> {{ `/login ${tg_login}` }}</div>
|
||||||
|
<van-icon name="notes-o" />
|
||||||
|
</div>
|
||||||
|
<van-button round block type="primary" @click="goPage" style="margin-top: 12px;">
|
||||||
|
跳转TG机器人
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</van-tab>
|
||||||
|
<van-tab title="账号密码登录" name="b">
|
||||||
|
<div class="type_login">
|
||||||
|
<van-form>
|
||||||
|
<van-cell-group inset>
|
||||||
|
<van-field v-model="username" name="usernmae" label="用户名" placeholder="用户名"
|
||||||
|
:rules="[{ required: true, message: '请填写用户名' }]" />
|
||||||
|
<van-field v-model="password" type="password" name="password" label="密码"
|
||||||
|
placeholder="密码" :rules="[{ required: true, message: '请填写密码' }]" />
|
||||||
|
</van-cell-group>
|
||||||
|
<div style="margin: 16px;">
|
||||||
|
<van-button round block type="primary" @click="onSubmit">
|
||||||
|
提交
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</van-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { getLoginToken, checkLoginToken, userLogin } from '@/axios/request';
|
||||||
|
// 导入插件
|
||||||
|
import useClipboard from 'vue-clipboard3'
|
||||||
|
import { showSuccessToast, showFailToast } from 'vant';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const username = ref('');
|
||||||
|
const password = ref('');
|
||||||
|
const activeName = ref('a');
|
||||||
|
const tg_login = ref('');
|
||||||
|
const tg_expires_at = ref('')
|
||||||
|
const onSubmit = () => {
|
||||||
|
console.log('submit');
|
||||||
|
userLogin({ username: username.value, password: password.value }).then(res => {
|
||||||
|
console.log(res);
|
||||||
|
localStorage.setItem("customeraccessToken", res.token);
|
||||||
|
successPageSkip();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const getTgToken = () => {
|
||||||
|
getLoginToken().then((res) => {
|
||||||
|
console.log(res)
|
||||||
|
tg_login.value = res.token
|
||||||
|
tg_expires_at.value = res.expires_at
|
||||||
|
checkLoginTokenTimer()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let tgTimer = null
|
||||||
|
const checkLoginTokenTimer = () => {
|
||||||
|
let time = new Date(tg_expires_at.value).getTime() - Date.now();
|
||||||
|
if (time <= 0) {
|
||||||
|
clearInterval(tgTimer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clearInterval(tgTimer)
|
||||||
|
tgTimer = setInterval(() => {
|
||||||
|
checkLoginToken({
|
||||||
|
token: tg_login.value
|
||||||
|
}).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
if (res.logged_in) {
|
||||||
|
localStorage.setItem("customeraccessToken", res.loginToken); // eyJhbGciOiJIUzI1NiJ9.xxx.xxx
|
||||||
|
successPageSkip();
|
||||||
|
clearInterval(tgTimer)
|
||||||
|
} else if (!res.valid) {
|
||||||
|
console.log('未登录');
|
||||||
|
showFailToast("授权认证过期,请刷新界面重新获取认证");
|
||||||
|
clearInterval(tgTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
}).catch((err) => {
|
||||||
|
clearInterval(tgTimer)
|
||||||
|
})
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
const goPage = () => {
|
||||||
|
window.open('https://t.me/bfbf')
|
||||||
|
}
|
||||||
|
const copyTg = () => {
|
||||||
|
let data = `/login ${tg_login.value}`
|
||||||
|
copy(data)
|
||||||
|
}
|
||||||
|
const router = useRouter();
|
||||||
|
const successPageSkip = () => {
|
||||||
|
|
||||||
|
router.push('/h5')
|
||||||
|
}
|
||||||
|
|
||||||
|
const copy = async (msg) => {
|
||||||
|
const { toClipboard } = useClipboard()
|
||||||
|
try {
|
||||||
|
// 复制
|
||||||
|
console.log('复制', msg)
|
||||||
|
await toClipboard(msg)
|
||||||
|
showSuccessToast('复制成功');
|
||||||
|
// 复制成功
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
// 复制失败
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const changeTab = (tab) => {
|
||||||
|
console.log(tab)
|
||||||
|
if (tab == 'a') {
|
||||||
|
getTgToken()
|
||||||
|
} else {
|
||||||
|
clearInterval(tgTimer)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
console.log('login mounted');
|
||||||
|
if (activeName.value === 'a') {
|
||||||
|
getTgToken()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.login_box {
|
||||||
|
width: 100vw;
|
||||||
|
height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.type_login {
|
||||||
|
height: 200px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
display: block;
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tg-btn {
|
||||||
|
// 超出长度隐藏
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #1989fa;
|
||||||
|
border-radius: 24px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 75vw;
|
||||||
|
|
||||||
|
.tg-btn-span {
|
||||||
|
display: block;
|
||||||
|
width: 60vw;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #1989fa;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.van-button {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
94
src/views/pay/fee-standards.vue
Normal file
94
src/views/pay/fee-standards.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<van-search v-model="search" placeholder="请输入模板名称进行搜索" @search="onSearch" @clear="onSearch" />
|
||||||
|
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
|
||||||
|
<div v-for="item in list" :key="item.id" class="item">
|
||||||
|
<div>模板名称:{{ item.name }}</div>
|
||||||
|
<div>模板类型:{{ item.type === 1 ? '预设模板' : '定制模板' }}</div>
|
||||||
|
<div>
|
||||||
|
<span>群组:</span>
|
||||||
|
<van-tag plain type="primary" style="margin-right: 4px;"
|
||||||
|
@click="goPage(group_options?.find(i => i.value == citem)?.url)"
|
||||||
|
v-for="(citem, index) in JSON.parse(item.group_ids)" :key="index">
|
||||||
|
{{group_options?.find(i => i.value == citem)?.label}}
|
||||||
|
</van-tag>
|
||||||
|
</div>
|
||||||
|
<div>频率:{{ item.times }}小时一次</div>
|
||||||
|
<div>周期:{{ item.cycle }}天</div>
|
||||||
|
<div>utsd金额:{{ item.pay_usdt_amount }}</div>
|
||||||
|
<div>创建时间:{{ item.created_at }}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, onBeforeMount } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { getGroupsList, getTemplatesList } from '@/axios/request';
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const task_title = ref('费用标准')
|
||||||
|
const search = ref('')
|
||||||
|
const group_options = ref([])
|
||||||
|
const list = ref([])
|
||||||
|
const finished = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
let currentPage = 1
|
||||||
|
const onLoad = async () => {
|
||||||
|
let res = await getTemplatesList({
|
||||||
|
page: currentPage,
|
||||||
|
per_page: 10,
|
||||||
|
name: search.value
|
||||||
|
})
|
||||||
|
console.log('list', res)
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list.value = res.data
|
||||||
|
} else {
|
||||||
|
list.value = list.value.concat(res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
finished.value = currentPage * 10 >= res.total
|
||||||
|
currentPage = currentPage + 1
|
||||||
|
loading.value = false
|
||||||
|
console.log('finished', finished, currentPage)
|
||||||
|
|
||||||
|
}
|
||||||
|
const onSearch = () => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
}
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
let group = await getGroupsList({})
|
||||||
|
console.log('onMounted', group)
|
||||||
|
group_options.value = []
|
||||||
|
group.data.map((item) => {
|
||||||
|
group_options.value.push({
|
||||||
|
label: item.tg_name,
|
||||||
|
value: item.id,
|
||||||
|
url: item.tg_url
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
const goPage = (url) => {
|
||||||
|
console.log('goPage', url)
|
||||||
|
window.open(url)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.container {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin: 12px;
|
||||||
|
background-color: #f7f8fa;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
27
src/views/pay/index.vue
Normal file
27
src/views/pay/index.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<van-nav-bar title="支付管理" />
|
||||||
|
<van-tabs v-model:active="activeName">
|
||||||
|
<van-tab title="支付记录" name="a">
|
||||||
|
<payRecord></payRecord>
|
||||||
|
</van-tab>
|
||||||
|
<van-tab title="收费标准" name="b">
|
||||||
|
<feeStandards></feeStandards>
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import feeStandards from './fee-standards.vue';
|
||||||
|
import payRecord from './pay-records.vue';
|
||||||
|
const activeName = ref('a');
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const router = useRouter();
|
||||||
|
const onClickLeft = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
147
src/views/pay/pay-records.vue
Normal file
147
src/views/pay/pay-records.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<van-search v-model="search" placeholder="请输入订单ID进行搜索" @search="onSearch" @clear="onSearch" />
|
||||||
|
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
|
||||||
|
<div v-for="item in list" :key="item.id" class="item">
|
||||||
|
<div>订单ID:{{ item.order_no }}</div>
|
||||||
|
<div>任务名称:{{ item?.task?.title }}</div>
|
||||||
|
<div>支付金额:{{ item.pay_usdt_amount }}</div>
|
||||||
|
<div> <span>订单状态:</span>
|
||||||
|
<van-tag plain :type="getStatusType(item.status)">
|
||||||
|
{{order_status_options?.find(s => s.value == item.status)?.label}}</van-tag>
|
||||||
|
</div>
|
||||||
|
<div>创建时间:{{ item.created_at }}</div>
|
||||||
|
<div class="item-btns">
|
||||||
|
|
||||||
|
<van-button type="primary" size="small" style="margin-left: 12px;"
|
||||||
|
v-if="item.status == 0 || item.status == 8" @click="payTask(item)">支付</van-button>
|
||||||
|
<!-- <van-button type="primary" size="small" style="margin-left: 12px;"
|
||||||
|
v-if="item.status == 2 || item.status == 7" @click="handleRefund(item)">退款</van-button> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
<Pay v-model:showDialog="showPayCodeDialog" v-model:current_order="current_order"
|
||||||
|
@handleConfrimPay="handleConfrimPay"></Pay>
|
||||||
|
|
||||||
|
<van-dialog v-model:show="show" title="退款申请" show-cancel-button @confirm="handleRefundPay">
|
||||||
|
<van-form>
|
||||||
|
<van-cell-group inset>
|
||||||
|
<van-field v-model="order_info.order_no" name="订单ID" label="订单ID" placeholder="订单ID" disabled />
|
||||||
|
<van-field v-model="order_info.pay_usdt_amount" name="订单金额" label="订单金额" placeholder="订单金额"
|
||||||
|
disabled />
|
||||||
|
<van-field v-model="order_info.refund_request_remark" name="退款原因" label="退款原因" placeholder="退款原因" />
|
||||||
|
</van-cell-group>
|
||||||
|
</van-form>
|
||||||
|
</van-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, onBeforeMount } from 'vue';
|
||||||
|
|
||||||
|
import { getOrderList, getOrderStatusList, updatePayHash, confirmConversionRefundRequest } from '@/axios/request';
|
||||||
|
import Pay from '@/components/Pay.vue';
|
||||||
|
|
||||||
|
|
||||||
|
const task_title = ref('费用标准')
|
||||||
|
const search = ref('')
|
||||||
|
const order_status_options = ref([])
|
||||||
|
const list = ref([])
|
||||||
|
const finished = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const showPayCodeDialog = ref(false)
|
||||||
|
const current_order = ref({})
|
||||||
|
const show = ref(false)
|
||||||
|
const order_info = ref({})
|
||||||
|
let currentPage = 1
|
||||||
|
const onLoad = async () => {
|
||||||
|
let res = await getOrderList({
|
||||||
|
page: currentPage,
|
||||||
|
per_page: 10,
|
||||||
|
order_no: search.value
|
||||||
|
})
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list.value = res.data
|
||||||
|
} else {
|
||||||
|
list.value = list.value.concat(res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
finished.value = currentPage * 10 >= res.total
|
||||||
|
currentPage = currentPage + 1
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
const onSearch = () => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
}
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
let order_status = await getOrderStatusList({})
|
||||||
|
|
||||||
|
order_status_options.value = []
|
||||||
|
order_status.map((item, index) => {
|
||||||
|
order_status_options.value.push({
|
||||||
|
label: item,
|
||||||
|
value: index
|
||||||
|
})
|
||||||
|
});
|
||||||
|
console.log('onMounted', order_status_options.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getStatusType = (val) => {
|
||||||
|
if (val == 0 || val == 6) {
|
||||||
|
return 'primary'
|
||||||
|
} else if (val == 2 || val == 5) {
|
||||||
|
return 'success'
|
||||||
|
} else if (val == 3 || val == 4 || val == 7 || val == 8) {
|
||||||
|
return 'danger'
|
||||||
|
} else if (val == 1) {
|
||||||
|
return 'warning'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleConfrimPay = (val = '', type) => {
|
||||||
|
console.log('handleConfrimPay', current_order)
|
||||||
|
updatePayHash({
|
||||||
|
order_id: current_order.value.id.toString(),
|
||||||
|
hash: val
|
||||||
|
}).then(res => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
showPayCodeDialog.value = false;
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
const payTask = (row) => {
|
||||||
|
console.log('payTask', row)
|
||||||
|
current_order.value = row
|
||||||
|
showPayCodeDialog.value = true
|
||||||
|
}
|
||||||
|
const handleRefund = (row) => {
|
||||||
|
order_info.value = row
|
||||||
|
show.value = true
|
||||||
|
}
|
||||||
|
const handleRefundPay = () => {
|
||||||
|
const data = {
|
||||||
|
id: order_info.value.id.toString(),
|
||||||
|
refund_request_remark: order_info.value.refund_request_remark
|
||||||
|
}
|
||||||
|
confirmConversionRefundRequest(data).then(res => {
|
||||||
|
show.value = false
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.container {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin: 12px;
|
||||||
|
background-color: #f7f8fa;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
262
src/views/task/add.vue
Normal file
262
src/views/task/add.vue
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<van-nav-bar :title="task_title" left-text="返回" left-arrow @click-left="onClickLeft" />
|
||||||
|
<van-form @submit="onSubmit">
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="form.title" name="title" label="任务名称" :disabled="isEdit" placeholder="请输入任务名称"
|
||||||
|
:rules="[{ required: true, message: '请输入任务名称' }]" />
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="form.template" is-link readonly name="template" :disabled="isEdit" label="选择模板"
|
||||||
|
:rules="[{ required: true, message: '请选择模板' }]" placeholder="请选择模板" @click="showPicker = true" />
|
||||||
|
<van-popup v-model:show="showPicker" position="bottom">
|
||||||
|
<van-picker :columns="template_options" @confirm="onConfirm" @cancel="showPicker = false" />
|
||||||
|
</van-popup>
|
||||||
|
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="form.group" disabled label="群组" />
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="form.times" disabled label="评率" />
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="form.cycle" disabled label="周期" />
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="form.price" disabled label="USDT价格" />
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="form.type" is-link readonly name="picker" :disabled="isEdit" label="执行方式"
|
||||||
|
:rules="[{ required: true, message: '请选择执行方式' }]" placeholder="请选择执行方式"
|
||||||
|
@click="showTypePicker = true" />
|
||||||
|
<van-popup v-model:show="showTypePicker" position="bottom">
|
||||||
|
<van-picker :columns="start_type_options" @confirm="onTypeConfirm"
|
||||||
|
@cancel="showTypePicker = false" />
|
||||||
|
</van-popup>
|
||||||
|
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group v-if="form.exec_type == 0">
|
||||||
|
<van-field v-model="form.next_run_time" is-link readonly name="picker" :disabled="isEdit" label="执行时间"
|
||||||
|
placeholder="点击选择执行时间" @click="showDatePicker = true" />
|
||||||
|
<DataTime :values="startTime" @changeValue="showDatePicker = false" :showPicker="showDatePicker"
|
||||||
|
@confirm="onDateConfirm" />
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="form.tg_title" label="发送标题" placeholder="请输入标题" />
|
||||||
|
</van-cell-group>
|
||||||
|
<van-cell-group>
|
||||||
|
<WangEditor v-model:modelValue="form.content"></WangEditor>
|
||||||
|
</van-cell-group>
|
||||||
|
|
||||||
|
|
||||||
|
</van-form>
|
||||||
|
<div class="footer">
|
||||||
|
<van-button type="primary" class="footer-button" @click="onSubmit">提交</van-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Pay v-model:showDialog="showPayCodeDialog" v-model:current_order="current_order"
|
||||||
|
@handleConfrimPay="handleConfrimPay" @closePay="closePay"></Pay>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted, onBeforeMount, watch } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { getTemplatesList, getGroupsList, addTask, editTask, checkWord, updatePayHash } from '@/axios/request';
|
||||||
|
import DataTime from "@/components/DateTime.vue";
|
||||||
|
import WangEditor from '@/components/WangEditor.vue';
|
||||||
|
import Pay from "@/components/Pay.vue";
|
||||||
|
|
||||||
|
import { showSuccessToast, showFailToast } from 'vant';
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const form = reactive({
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
exec_type: 1,
|
||||||
|
type: '立即执行',
|
||||||
|
next_run_time: '',
|
||||||
|
id: '',
|
||||||
|
})
|
||||||
|
let template_options = reactive([])
|
||||||
|
const showPicker = ref(false)
|
||||||
|
const showTypePicker = ref(false)
|
||||||
|
const showDatePicker = ref(false)
|
||||||
|
const startTime = ref(""); //值定义
|
||||||
|
const showPayCodeDialog = ref(false)
|
||||||
|
let current_order = reactive({})
|
||||||
|
let start_type_options = [
|
||||||
|
{
|
||||||
|
text: "立即执行",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "定时执行",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
let task_title = ref("添加任务")
|
||||||
|
let template_info = {}
|
||||||
|
let group_info = {}
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const onClickLeft = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
const onConfirm = ({ selectedOptions }) => {
|
||||||
|
console.log(selectedOptions);
|
||||||
|
form.template = selectedOptions[0]?.text;
|
||||||
|
form.temp_id = selectedOptions[0]?.value;
|
||||||
|
showPicker.value = false;
|
||||||
|
let template = template_info.find(item => item.id == selectedOptions[0]?.value)
|
||||||
|
|
||||||
|
console.log(template);
|
||||||
|
let group_id = JSON.parse(template?.group_ids)
|
||||||
|
form.times = template?.times + '小时1次'
|
||||||
|
form.cycle = template?.cycle + '天'
|
||||||
|
form.price = template?.price
|
||||||
|
let filter_groups = group_id.map(id => {
|
||||||
|
return group_info.find(item => item.id == id).tg_name
|
||||||
|
})
|
||||||
|
form.group = filter_groups.join(',');
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTypeConfirm = ({ selectedOptions }) => {
|
||||||
|
console.log('onTypeConfirm', selectedOptions);
|
||||||
|
form.type = selectedOptions[0]?.text;
|
||||||
|
form.exec_type = selectedOptions[0]?.value;
|
||||||
|
console.log(form.exec_type);
|
||||||
|
showTypePicker.value = false;
|
||||||
|
};
|
||||||
|
const onDateConfirm = selectedValues => {
|
||||||
|
console.log(selectedValues);
|
||||||
|
startTime.value = selectedValues;
|
||||||
|
form.next_run_time = selectedValues
|
||||||
|
showPicker.value = false;
|
||||||
|
};
|
||||||
|
const clearFrom = () => {
|
||||||
|
form.title = '';
|
||||||
|
form.content = '';
|
||||||
|
form.next_run_time = ''
|
||||||
|
form.exec_type = ''
|
||||||
|
form.type = ''
|
||||||
|
startTime.value = ''
|
||||||
|
form.id = ''
|
||||||
|
form.tg_title = ''
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
let params = {
|
||||||
|
title: form.title,
|
||||||
|
content: form.content,
|
||||||
|
exec_type: form.exec_type,
|
||||||
|
next_run_time: form.next_run_time,
|
||||||
|
temp_id: form.temp_id,
|
||||||
|
tg_title: form.tg_title,
|
||||||
|
id: form.id || '',
|
||||||
|
}
|
||||||
|
let tg_title_check = checkWord({ content: params.tg_title })
|
||||||
|
let tg_content_check = checkWord({ content: params.content })
|
||||||
|
console.log('onSubmit', tg_title_check, tg_content_check)
|
||||||
|
Promise.all([tg_title_check, tg_content_check]).then((values) => {
|
||||||
|
if (params.id) {
|
||||||
|
editTask(params).then(res => {
|
||||||
|
showSuccessToast('提交成功')
|
||||||
|
setTimeout(() => {
|
||||||
|
clearFrom()
|
||||||
|
router.push('/h5')
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (params.exec_type == 0 && params.next_run_time != "") {
|
||||||
|
let msg = checkTime(params.next_run_time)
|
||||||
|
if (msg) {
|
||||||
|
return showFailToast(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addTask(params).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
showSuccessToast('提交成功')
|
||||||
|
current_order = res
|
||||||
|
setTimeout(() => {
|
||||||
|
clearFrom()
|
||||||
|
showPayCodeDialog.value = true
|
||||||
|
// router.push('/')
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.log('err', err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const checkTime = (value) => {
|
||||||
|
let msg = ''
|
||||||
|
if (!value) {
|
||||||
|
msg = '请选择时间'
|
||||||
|
}
|
||||||
|
if (new Date(value).getTime() <= Date.now()) {
|
||||||
|
msg = '必须选择当前时间之后的时间'
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
let group = await getGroupsList({ pagination: 'off' })
|
||||||
|
|
||||||
|
// console.log('onMounted', group)
|
||||||
|
group_info = group
|
||||||
|
let templates = await getTemplatesList({ pagination: 'off' })
|
||||||
|
console.log('templates', templates)
|
||||||
|
template_options = []
|
||||||
|
template_info = templates
|
||||||
|
templates.map((item) => {
|
||||||
|
template_options.push({
|
||||||
|
text: item.name,
|
||||||
|
value: item.id,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let current_task_info = localStorage.getItem('current_task_info') != '' ? JSON.parse(localStorage.getItem('current_task_info')) : {}
|
||||||
|
console.log('current_task_info', current_task_info)
|
||||||
|
if (current_task_info != null) {
|
||||||
|
form.id = current_task_info.id
|
||||||
|
form.title = current_task_info.title
|
||||||
|
form.temp_id = current_task_info.temp_id
|
||||||
|
form.template = template_options?.find(item => item.value == current_task_info.temp_id)?.text
|
||||||
|
onConfirm({ selectedOptions: [{ text: template_options.find(item => item.value == current_task_info.temp_id)?.text, value: current_task_info.temp_id }] })
|
||||||
|
console.log('form', start_type_options?.find(item => item.value == current_task_info.exec_type))
|
||||||
|
onTypeConfirm({ selectedOptions: [{ text: start_type_options?.find(item => item.value == current_task_info.exec_type)?.text, value: current_task_info.exec_type }] })
|
||||||
|
form.content = current_task_info.content
|
||||||
|
form.next_run_time = current_task_info.next_run_time
|
||||||
|
form.tg_title = current_task_info.tg_title
|
||||||
|
isEdit.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleConfrimPay = (val = '', type) => {
|
||||||
|
console.log('handleConfrimPay', current_order)
|
||||||
|
updatePayHash({
|
||||||
|
order_id: current_order.id.toString(),
|
||||||
|
hash: val
|
||||||
|
}).then(res => {
|
||||||
|
router.push('/h5')
|
||||||
|
}).finally(() => {
|
||||||
|
router.push('/h5')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const closePay = () => {
|
||||||
|
router.push('/h5')
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 12px;
|
||||||
|
width: calc(100vw - 24px);
|
||||||
|
|
||||||
|
.footer-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
283
src/views/task/index.vue
Normal file
283
src/views/task/index.vue
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
<template>
|
||||||
|
<van-nav-bar title="任务管理" />
|
||||||
|
<div class="container">
|
||||||
|
<van-search v-model="search" placeholder="请输入任务名称进行搜索" @search="onSearch" @clear="onSearch" />
|
||||||
|
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
|
||||||
|
<div v-for="item in list" :key="item.id" class="item">
|
||||||
|
<div>任务名称:{{ item.title }}</div>
|
||||||
|
<div>模板名称:{{ item.temp_name }}</div>
|
||||||
|
<div>模板类型:{{ item.temp_type === 1 ? '预设模板' : '定制模板' }}</div>
|
||||||
|
<div>
|
||||||
|
<span>群组:</span>
|
||||||
|
<van-tag plain type="primary" style="margin-right: 4px;"
|
||||||
|
@click="goPage(group_options?.find(i => i.value == citem)?.url)"
|
||||||
|
v-for="(citem, index) in JSON.parse(item.temp_groups)" :key="index">
|
||||||
|
{{group_options?.find(i => i.value == citem)?.label}}
|
||||||
|
</van-tag>
|
||||||
|
</div>
|
||||||
|
<div>频率:{{ item.times }}小时一次</div>
|
||||||
|
<div>周期:{{ item.cycle }}天</div>
|
||||||
|
<div>utsd金额:{{ item.pay_usdt_amount }}</div>
|
||||||
|
<div> <span>任务状态:</span>
|
||||||
|
<van-tag plain type="warning" v-if="item.status === 0 || item.status == 5">
|
||||||
|
{{status_options?.find(s => s.value == item.status)?.label}}</van-tag>
|
||||||
|
<van-tag plain type="danger" v-if="item.status == 3 || item.status == 6">
|
||||||
|
{{status_options?.find(s => s.value == item.status)?.label}}</van-tag>
|
||||||
|
<van-tag plain type="primary" v-if="item.status == 1 || item.status == 2">
|
||||||
|
{{status_options?.find(s => s.value == item.status)?.label}}</van-tag>
|
||||||
|
<van-tag plaing type="success" v-if="item.status == 4">
|
||||||
|
{{status_options?.find(s => s.value == item.status)?.label}}</van-tag>
|
||||||
|
</div>
|
||||||
|
<div>创建时间:{{ item.created_at }}</div>
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
支付状态:
|
||||||
|
</span>
|
||||||
|
<van-tag type="primary" v-if="item.order_status == 0 || item.order_status == 6">
|
||||||
|
{{order_status?.find(o => o.value == item.order_status)?.label}}</van-tag>
|
||||||
|
<van-tag type="danger"
|
||||||
|
v-if="item.order_status == 3 || item.order_status == 4 || item.order_status == 7">
|
||||||
|
{{order_status?.find(o => o.value == item.order_status)?.label}}</van-tag>
|
||||||
|
<van-tag type="warning" v-if="item.order_status == 1">
|
||||||
|
{{order_status?.find(o => o.value == item.order_status)?.label}}</van-tag>
|
||||||
|
<van-tag type="success" v-if="item.order_status == 2 || item.order_status == 5">
|
||||||
|
{{order_status?.find(o => o.value == item.order_status)?.label}}</van-tag>
|
||||||
|
</div>
|
||||||
|
<div class="item-btns">
|
||||||
|
<van-button type="primary"
|
||||||
|
v-if="item.status == 0 || item.status == 1 || item.status == 3 || item.status == 5" size="small"
|
||||||
|
style="margin-left: 12px;" @click="addTask(item)">编辑</van-button>
|
||||||
|
<van-button type="primary" size="small" style="margin-left: 12px;"
|
||||||
|
v-if="item.order_status == 0 || item.order_status == 8" @click="payTask(item)">支付</van-button>
|
||||||
|
<van-button type="primary" size="small" style="margin-left: 12px;" v-if="item.status == 2"
|
||||||
|
@click="handleTask(item.id, '2')">执行</van-button>
|
||||||
|
<van-button type="primary" size="small" style="margin-left: 12px;"
|
||||||
|
@click="handleTask(item.id, '5')">暂停</van-button>
|
||||||
|
<van-button type="danger" size="small" style="margin-left: 12px;"
|
||||||
|
v-if="item.status == 6 || item.status == 0" @click="deleteTask(item.id)">删除</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
<div class="footer">
|
||||||
|
<van-button type="primary" size="small" class="footer-button" @click="addTask('')">创建任务</van-button>
|
||||||
|
</div>
|
||||||
|
<Pay v-model:showDialog="showPayCodeDialog" v-model:current_order="current_order"
|
||||||
|
@handleConfrimPay="handleConfrimPay"></Pay>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onBeforeMount, ref } from 'vue'
|
||||||
|
import { getTaskList, getGroupsList, getTemplatesList, updatePayHash, delTask, changeStatus } from '@/axios/request';
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import Pay from '@/components/Pay.vue';
|
||||||
|
import { showSuccessToast, showConfirmDialog } from 'vant';
|
||||||
|
const router = useRouter()
|
||||||
|
let search = ref('')
|
||||||
|
let list = ref([])
|
||||||
|
let loading = ref(false)
|
||||||
|
let finished = ref(false)
|
||||||
|
let group_options = ref([])
|
||||||
|
let template_info = ref([])
|
||||||
|
let showPayCodeDialog = ref(false)
|
||||||
|
let current_order = ref({})
|
||||||
|
let order_status = [{
|
||||||
|
label: "未支付",
|
||||||
|
value: 0,
|
||||||
|
}, {
|
||||||
|
label: "待确认",
|
||||||
|
value: 1,
|
||||||
|
}, {
|
||||||
|
label: "已支付",
|
||||||
|
value: 2,
|
||||||
|
}, {
|
||||||
|
label: "已关闭",
|
||||||
|
value: 3,
|
||||||
|
}, {
|
||||||
|
label: "超时关闭",
|
||||||
|
value: 4,
|
||||||
|
}, {
|
||||||
|
label: "已退款",
|
||||||
|
value: 5,
|
||||||
|
}, {
|
||||||
|
label: "用户申请退款",
|
||||||
|
value: 6,
|
||||||
|
}, {
|
||||||
|
label: "退款被拒绝",
|
||||||
|
value: 7,
|
||||||
|
}]
|
||||||
|
const status_options = [ //0未开始,1待审核,2进行中(审核通过),3审核拒绝,4已完成,5暂停,6已取消
|
||||||
|
{
|
||||||
|
label: "未开始",
|
||||||
|
value: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "待审核",
|
||||||
|
value: "1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "进行中",
|
||||||
|
value: "2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "审核拒绝",
|
||||||
|
value: "3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "已完成",
|
||||||
|
value: "4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "暂停",
|
||||||
|
value: "5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "已取消",
|
||||||
|
value: "6",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
let currentPage = 1
|
||||||
|
const onLoad = async () => {
|
||||||
|
let res = await getTaskList({
|
||||||
|
page: currentPage,
|
||||||
|
per_page: 10,
|
||||||
|
task_name: search.value
|
||||||
|
})
|
||||||
|
console.log('list', res)
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list.value = res.data
|
||||||
|
} else {
|
||||||
|
list.value = list.value.concat(res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
finished.value = currentPage * 10 >= res.total
|
||||||
|
currentPage = currentPage + 1
|
||||||
|
loading.value = false
|
||||||
|
console.log('finished', finished, currentPage)
|
||||||
|
|
||||||
|
}
|
||||||
|
const onSearch = () => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
}
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
let group = await getGroupsList({})
|
||||||
|
let templates = await getTemplatesList({})
|
||||||
|
console.log('onMounted', group, templates)
|
||||||
|
group_options.value = []
|
||||||
|
group.data.map((item) => {
|
||||||
|
group_options.value.push({
|
||||||
|
label: item.tg_name,
|
||||||
|
value: item.id,
|
||||||
|
url: item.tg_url
|
||||||
|
})
|
||||||
|
});
|
||||||
|
console.log('group_options', group_options)
|
||||||
|
template_info.value = templates.data
|
||||||
|
})
|
||||||
|
|
||||||
|
const goPage = (url) => {
|
||||||
|
console.log('goPage', url)
|
||||||
|
window.open(url)
|
||||||
|
}
|
||||||
|
const addTask = (row = '') => {
|
||||||
|
console.log('addTask', row)
|
||||||
|
if (row.id) {
|
||||||
|
localStorage.setItem('current_task_info', JSON.stringify(row))
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('current_task_info')
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/h5/addTask',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const payTask = (row) => {
|
||||||
|
console.log('payTask', row)
|
||||||
|
current_order.value = row
|
||||||
|
showPayCodeDialog.value = true
|
||||||
|
}
|
||||||
|
const handleConfrimPay = (val = '', type) => {
|
||||||
|
console.log('handleConfrimPay', current_order)
|
||||||
|
updatePayHash({
|
||||||
|
order_id: current_order.value.order_id.toString(),
|
||||||
|
hash: val
|
||||||
|
}).then(res => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
showPayCodeDialog.value = false;
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTask = ((id, status) => {
|
||||||
|
let title = status == 2 ? '执行任务' : '暂停任务'
|
||||||
|
let message = status == 2 ? '确定执行此任务' : '确定暂停此任务'
|
||||||
|
showConfirmDialog({
|
||||||
|
title: title,
|
||||||
|
message: message
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
changeTask(id, status)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// on cancel
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
const changeTask = (id, status) => {
|
||||||
|
changeStatus({ id: id, status: parseInt(status) }).then(res => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
}).finally(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const deleteTask = (id) => {
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '删除任务',
|
||||||
|
message: '确定删除此任务吗?'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
delTask({ id: id }).then((res) => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// on cancel
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.container {
|
||||||
|
padding-bottom: 60px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin: 12px;
|
||||||
|
background-color: #f7f8fa;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
.item-btns {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 60px;
|
||||||
|
left: 12px;
|
||||||
|
width: calc(100vw - 24px);
|
||||||
|
|
||||||
|
.footer-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
142
src/views/user/detail.vue
Normal file
142
src/views/user/detail.vue
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="detail">
|
||||||
|
<van-nav-bar title="用户信息" left-text="返回" left-arrow @click-left="onClickLeft" />
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="userinfo.telegram_id" name="Telegram ID" label="Telegram ID" placeholder=" Telegram ID"
|
||||||
|
label-width="120px" disabled />
|
||||||
|
<van-field v-model="userinfo.nikename" name="用户昵称" label="用户昵称" placeholder="用户昵称" label-width="120px" />
|
||||||
|
<van-field v-model="userinfo.telegram_tel" name="Telegram联系方式" label="Telegram联系方式" label-width="120px"
|
||||||
|
placeholder="Telegram联系方式" />
|
||||||
|
<van-field v-model="userinfo.email" name="Email" label="Email" placeholder="Email" label-width="120px" />
|
||||||
|
<van-field v-model="userinfo.usdt_address" name="USFT钱包地址" label="USFT钱包地址" placeholder="USFT钱包地址"
|
||||||
|
label-width="120px" />
|
||||||
|
<van-field v-model="userinfo.username" name="登录账号" label="登录账号" placeholder="登录账号" disabled
|
||||||
|
label-width="120px" />
|
||||||
|
</van-cell-group>
|
||||||
|
<div class="tips">TG首次登录默认密码:<span class="default">{{ defaultPassword }}</span><span class="view"
|
||||||
|
@click="viewPassword">查看</span>,请尽快修改!!!</div>
|
||||||
|
|
||||||
|
<div class="btns">
|
||||||
|
<van-button type="primary" size="small" @click="saveInfo">保存用户信息</van-button>
|
||||||
|
<van-button type="warning" size="small" @click="changePassword">修改密码</van-button>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<van-button class="footer-button" type="primary" size="small" @click="logout">登出用户</van-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<van-dialog v-model:show="showModify" title="修改密码" show-cancel-button @confirm="modifyPassword">
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field v-model="password.oldPassword" name="旧密码" label="旧密码" placeholder="旧密码" type="password"
|
||||||
|
label-width="120px" />
|
||||||
|
<van-field v-model="password.newPassword" name="新密码" label="新密码" placeholder="新密码" type="password"
|
||||||
|
label-width="120px" />
|
||||||
|
<van-field v-model="password.confirmPassword" name="确认密码" label="确认密码" placeholder="确认密码"
|
||||||
|
type="password" label-width="120px" />
|
||||||
|
</van-cell-group>
|
||||||
|
</van-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, reactive } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { getUserinfo, UpdateInfo, ChangePassword } from '@/axios/request'
|
||||||
|
import { showSuccessToast, showFailToast } from 'vant'
|
||||||
|
const router = useRouter()
|
||||||
|
const defaultPassword = ref('******')
|
||||||
|
const userinfo = ref({})
|
||||||
|
const showModify = ref(false)
|
||||||
|
const password = reactive({
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
})
|
||||||
|
onMounted(async () => {
|
||||||
|
userinfo.value = await getUserinfo()
|
||||||
|
})
|
||||||
|
const onClickLeft = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
const viewPassword = () => {
|
||||||
|
defaultPassword.value = '123456'
|
||||||
|
setTimeout(() => {
|
||||||
|
defaultPassword.value = '******'
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
const saveInfo = async () => {
|
||||||
|
let param = {
|
||||||
|
nikename: userinfo.value.nikename,
|
||||||
|
telegram_tel: userinfo.value.telegram_tel,
|
||||||
|
email: userinfo.value.email,
|
||||||
|
usdt_address: userinfo.value.usdt_address
|
||||||
|
}
|
||||||
|
UpdateInfo(param).then(res => {
|
||||||
|
showSuccessToast('保存成功')
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
const changePassword = async () => {
|
||||||
|
showModify.value = true
|
||||||
|
}
|
||||||
|
const modifyPassword = async () => {
|
||||||
|
if (password.oldPassword === password.newPassword) {
|
||||||
|
showFailToast("新密码不能与旧密码相同")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (password.newPassword !== password.confirmPassword) {
|
||||||
|
showFailToast('密码不一致');
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
let param = {
|
||||||
|
old_password: password.oldPassword,
|
||||||
|
new_password: password.newPassword,
|
||||||
|
}
|
||||||
|
changePassword(param).then(res => {
|
||||||
|
showSuccessToast('修改成功');
|
||||||
|
showModify = false;
|
||||||
|
}).finally(() => {
|
||||||
|
password.oldPassword = ''
|
||||||
|
password.newPassword = ''
|
||||||
|
password.confirmPassword = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const logout = () => {
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
router.push('/h5/login')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.tips {
|
||||||
|
color: #999;
|
||||||
|
margin: 10px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.default {
|
||||||
|
color: red;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view {
|
||||||
|
color: #1998fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 20px;
|
||||||
|
|
||||||
|
.footer-button {
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
136
src/views/user/index.vue
Normal file
136
src/views/user/index.vue
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<template>
|
||||||
|
<van-nav-bar title="用户信息" />
|
||||||
|
<div class="container">
|
||||||
|
<div class="userinfo">
|
||||||
|
<div class="title">用户昵称:<span>{{ userInfo.nikename }}</span></div>
|
||||||
|
<div class="title">Telegram ID:<span>{{ userInfo.telegram_id }}</span></div>
|
||||||
|
<div class="title">Telegram联系方式:<span>{{ userInfo.telegram_tel }}</span></div>
|
||||||
|
<div class="title">Email:<span>{{ userInfo.email }}</span></div>
|
||||||
|
<div class="edit" @click="editUserInfo()"><van-icon name="edit" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard">
|
||||||
|
<div class="box">
|
||||||
|
<div class="account">{{ dashboard.taskCount }}</div>
|
||||||
|
<div class="title">总任务数</div>
|
||||||
|
</div>
|
||||||
|
<div class="shu"></div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="account">{{ dashboard.orderMoney }}</div>
|
||||||
|
<div class="title">USDT付款总额</div>
|
||||||
|
</div>
|
||||||
|
<div class="shu"></div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="account">{{ dashboard.taskUnpayCount }}</div>
|
||||||
|
<div class="title">待支付任务数</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tool">
|
||||||
|
<div class="tool-box" @click="goPage('/h5/mass_send')">
|
||||||
|
<div><van-icon name="notes-o" class="icon" /></div>
|
||||||
|
<div class="title">群发记录</div>
|
||||||
|
</div>
|
||||||
|
<div class="tool-box" @click="goPage('/h5/notice')">
|
||||||
|
<div><van-icon name="other-pay" class="icon" /></div>
|
||||||
|
<div class="title">通知历史</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, computed, watch, onMounted, onBeforeUnmount } from 'vue'
|
||||||
|
import { getUserinfo, getDashboard } from '@/axios/request'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
const router = useRouter()
|
||||||
|
const userInfo = ref({})
|
||||||
|
const dashboard = ref({})
|
||||||
|
onMounted(async () => {
|
||||||
|
userInfo.value = await getUserinfo()
|
||||||
|
dashboard.value = await getDashboard()
|
||||||
|
console.log('userInfo', userInfo.value, dashboard.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const editUserInfo = () => {
|
||||||
|
router.push('/h5/detail')
|
||||||
|
}
|
||||||
|
const goPage = (url) => {
|
||||||
|
router.push(url)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.container {
|
||||||
|
background-color: #f5f8fa !important;
|
||||||
|
height: 100vh;
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userinfo {
|
||||||
|
margin: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 12px;
|
||||||
|
color: #666;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 0;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
position: absolute;
|
||||||
|
top: 45%;
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
margin: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.shu {
|
||||||
|
width: 1px;
|
||||||
|
height: 40px;
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool {
|
||||||
|
margin: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.tool-box {
|
||||||
|
width: 30%;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
95
src/views/user/mass_send.vue
Normal file
95
src/views/user/mass_send.vue
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<van-nav-bar :title="task_title" left-text="返回" left-arrow @click-left="onClickLeft" />
|
||||||
|
<div class="container">
|
||||||
|
<van-search v-model="search" placeholder="请输入任务名称进行搜索" @search="onSearch" @clear="onSearch" />
|
||||||
|
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
|
||||||
|
<div v-for="item in list" :key="item.id" class="item">
|
||||||
|
<div><span> 任务ID:</span>{{ item.task_id }}</div>
|
||||||
|
<div> <span>任务名称:</span>{{ item.task_name }}</div>
|
||||||
|
<div><span>任务状态:</span> <van-tag plain :type="item.status == 1 ? 'success' : 'danger'">{{ item.status ==
|
||||||
|
1 ? '成功' : '失败'
|
||||||
|
}}</van-tag></div>
|
||||||
|
<div><span>发送时间:</span>{{ dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss') }}</div>
|
||||||
|
<div><span>发送群组:</span>{{ item.group_name }}</div>
|
||||||
|
<div @click="goPage(item.send_url)"><span>发送链接:</span>{{ item.send_url }}</div>
|
||||||
|
<div><span>失败原因:</span> {{ showReason(item.reason) }}</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, onBeforeMount } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { getMassSend } from '@/axios/request';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const task_title = ref('群发记录')
|
||||||
|
const search = ref('')
|
||||||
|
const group_options = ref([])
|
||||||
|
const list = ref([])
|
||||||
|
const finished = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
let currentPage = 1
|
||||||
|
const onLoad = async () => {
|
||||||
|
let res = await getMassSend({
|
||||||
|
page: currentPage,
|
||||||
|
per_page: 10,
|
||||||
|
task_id: search.value
|
||||||
|
})
|
||||||
|
console.log('list', res)
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list.value = res.data
|
||||||
|
} else {
|
||||||
|
list.value = list.value.concat(res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
finished.value = currentPage * 10 >= res.total
|
||||||
|
currentPage = currentPage + 1
|
||||||
|
loading.value = false
|
||||||
|
console.log('finished', finished, currentPage)
|
||||||
|
|
||||||
|
}
|
||||||
|
const onSearch = () => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const goPage = (url) => {
|
||||||
|
console.log('goPage', url)
|
||||||
|
if (!url) return
|
||||||
|
window.open(url)
|
||||||
|
}
|
||||||
|
const onClickLeft = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
const showReason = (reason) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(reason)?.code == 400 ? JSON.parse(reason)?.msg : ''
|
||||||
|
} catch (error) {
|
||||||
|
return reason || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.container {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin: 12px;
|
||||||
|
background-color: #f7f8fa;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 12px;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
80
src/views/user/notice.vue
Normal file
80
src/views/user/notice.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<van-nav-bar :title="task_title" left-text="返回" left-arrow @click-left="onClickLeft" />
|
||||||
|
<div class="container">
|
||||||
|
<van-search v-model="search" placeholder="请输入模板名称进行搜索" @search="onSearch" @clear="onSearch" />
|
||||||
|
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
|
||||||
|
<div v-for="item in list" :key="item.id" class="item">
|
||||||
|
<div><span> 标题:</span>{{ item.title }}</div>
|
||||||
|
<div> <span>内容:</span>{{ item.content }}</div>
|
||||||
|
<div><span>发送地址:</span> {{ item.addr }}</div>
|
||||||
|
<div><span>发送时间:</span>{{ item.updated_at }}</div>
|
||||||
|
<div><span>状态:</span>{{ item.status == 1 ? '成功' : '失败' }}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, onBeforeMount } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { getMessageLog } from '@/axios/request';
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const task_title = ref('通知历史')
|
||||||
|
const search = ref('')
|
||||||
|
const group_options = ref([])
|
||||||
|
const list = ref([])
|
||||||
|
const finished = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
let currentPage = 1
|
||||||
|
const onLoad = async () => {
|
||||||
|
let res = await getMessageLog({
|
||||||
|
page: currentPage,
|
||||||
|
per_page: 10,
|
||||||
|
name: search.value
|
||||||
|
})
|
||||||
|
console.log('list', res)
|
||||||
|
if (currentPage == 1) {
|
||||||
|
list.value = res.data
|
||||||
|
} else {
|
||||||
|
list.value = list.value.concat(res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
finished.value = currentPage * 10 >= res.total
|
||||||
|
currentPage = currentPage + 1
|
||||||
|
loading.value = false
|
||||||
|
console.log('finished', finished, currentPage)
|
||||||
|
|
||||||
|
}
|
||||||
|
const onSearch = () => {
|
||||||
|
currentPage = 1
|
||||||
|
onLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
const goPage = (url) => {
|
||||||
|
console.log('goPage', url)
|
||||||
|
window.open(url)
|
||||||
|
}
|
||||||
|
const onClickLeft = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.container {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin: 12px;
|
||||||
|
background-color: #f7f8fa;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 12px;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
32
vite.config.js
Normal file
32
vite.config.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import { VantResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
// import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
// vueDevTools(),
|
||||||
|
Components({
|
||||||
|
resolvers: [VantResolver()],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://192.168.2.79',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, '/api'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
Loading…
x
Reference in New Issue
Block a user