Compare commits

...

10 Commits

Author SHA1 Message Date
pengxiaolong
ec8bffa6f0 上传代码 2025-07-01 21:37:07 +08:00
560581f1a4 yolo 2025-06-24 13:35:33 +08:00
17d2251a70 添加 计时器功能 详细筛选功能 分配状态字段 2025-05-15 18:29:53 +08:00
dcd677bbab 近7日数据改为updata 2025-05-12 21:09:32 +08:00
60f6fc4873 版本号更新 2025-05-06 15:38:23 +08:00
5e34aaf402 后端服务地址更新 2025-04-24 22:00:51 +08:00
c5fb3ea0b2 初始化列表 2025-04-24 16:11:22 +08:00
666e27246f 工作台输入框禁用状态,开始按钮禁用状态 2025-04-17 22:36:14 +08:00
91ee2dd7cb 未登录账号禁用爬取按钮 2025-04-17 13:22:24 +08:00
7df29e741c 列表隐藏主播名,添加爬取时间 2025-04-16 19:25:21 +08:00
39 changed files with 1644 additions and 1363 deletions

BIN
YOLO工具箱.zip Normal file

Binary file not shown.

121
package-lock.json generated
View File

@@ -23,12 +23,14 @@
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"file-loader": "^6.2.0",
"less": "^4.2.2",
"less-loader": "^12.2.0",
"postcss-preset-env": "^10.1.5",
"postcss-px-to-viewport": "^1.1.1",
"postcss-px-viewport": "^0.0.4",
"postcss-viewport-units": "^0.1.6"
"postcss-viewport-units": "^0.1.6",
"url-loader": "^4.1.1"
}
},
"node_modules/@achrinza/node-ipc": {
@@ -7018,6 +7020,61 @@
"node": ">=4"
}
},
"node_modules/file-loader": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
"integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
"dev": true,
"license": "MIT",
"dependencies": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"webpack": "^4.0.0 || ^5.0.0"
}
},
"node_modules/file-loader/node_modules/loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/file-loader/node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -13209,6 +13266,68 @@
"punycode": "^2.1.0"
}
},
"node_modules/url-loader": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
"integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==",
"dev": true,
"license": "MIT",
"dependencies": {
"loader-utils": "^2.0.0",
"mime-types": "^2.1.27",
"schema-utils": "^3.0.0"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"file-loader": "*",
"webpack": "^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
"file-loader": {
"optional": true
}
}
},
"node_modules/url-loader/node_modules/loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/url-loader/node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@@ -22,12 +22,14 @@
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"file-loader": "^6.2.0",
"less": "^4.2.2",
"less-loader": "^12.2.0",
"postcss-preset-env": "^10.1.5",
"postcss-px-to-viewport": "^1.1.1",
"postcss-px-viewport": "^0.0.4",
"postcss-viewport-units": "^0.1.6"
"postcss-viewport-units": "^0.1.6",
"url-loader": "^4.1.1"
},
"browserslist": [
"> 1%",

View File

@@ -24,4 +24,10 @@ window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
super(callback)
}
}
</script>
</script>
<!-- <style lang="less">
/*每个页面公共css */
@import "@/static/css/app.less";
</style> -->

View File

@@ -1,17 +1,71 @@
import { getAxios, postAxios, downFile } from '@/utils/axios.js'
export function apiGetCart() {
return getAxios({ url: '/cgi-bin/cart/latest' })
}
//登录
export function login(data) {
return postAxios({ url: 'api/account/login', data })
return postAxios({ url: 'api/user/doLogin', data })
}
//ai
//获取所有话术列表
export function getDialogList(data) {
return postAxios({ url: 'ai/templateList', data })
}
//获取所有语言列表
export function getLanguageList(data) {
return postAxios({ url: 'ai/languageList', data })
}
//添加话术
export function addDialog(data) {
return postAxios({ url: 'ai/addTemplate', data })
}
//编辑话术
export function editDialog(data) {
return postAxios({ url: 'ai/editTemplate', data })
}
//删除话术
export function deleteDialog(data) {
return postAxios({ url: 'ai/deleteTemplate', data })
}
//删除语言
export function deleteLanguage(data) {
return postAxios({ url: 'ai/deleteLanguage', data })
}
//编辑语言
export function editLanguage(data) {
return postAxios({ url: 'ai/updateLanguage', data })
}
//添加语言
export function addLanguage(data) {
return postAxios({ url: 'ai/addLanguage', data })
}
//小程序
//获取配置列表
export function getWxConfigList(data) {
return postAxios({ url: 'pk/configList', data })
}
//添加配置
export function addWxConfig(data) {
return postAxios({ url: 'pk/addNewConfig', data })
}
//编辑配置
export function editWxConfig(data) {
return postAxios({ url: 'pk/updateConfig', data })
}
//删除配置
export function deleteWxConfig(data) {
return postAxios({ url: 'pk/deleteConfig', data })
}
export function cheekalive(data) {
return postAxios({ url: 'api/account/cheekalive', data })
}
export function tkhostdata(data) {
return postAxios({ url: 'api/tkinfo/tkhostdata', data })
}
export function dicts(data) {
return postAxios({ url: 'api/param/dicts', data })
}
@@ -25,10 +79,7 @@ export function exporthosts(data) {
export function downList(url, data) {
return downFile(url, data)
}
//查询tk账号查询次数
export function tkaccountuseinfo(data) {
return postAxios({ url: 'api/tkinfo/tkaccountuseinfo', data })
}
//查询员工
export function getStaffList(data) {
return postAxios({ url: 'api/account/list', data })
@@ -41,7 +92,9 @@ export function managerhosts(data) {
export function upholdinfo(data) {
return postAxios({ url: 'api/tkinfo/upholdinfo', data })
}
//获取
export function getCountryinfo(data) {
return postAxios({ url: 'api/tkinfo/countryinfo', data })
//查看名字
export function accountName(str) {
return postAxios({ url: 'api/account/accountName?accounts=' + str })
}

BIN
src/assets/MiniProgram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
src/assets/ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
src/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 88 KiB

BIN
src/assets/logo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
src/assets/logoBg1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 106 KiB

BIN
src/assets/logotext1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
src/assets/logotext12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

BIN
src/assets/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
src/assets/plus2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 993 B

View File

@@ -38,6 +38,8 @@ export default {
this.inputTime = this.time
this.getTkhostdetail();
console.log(this.time)
console.log(this.getPrevious7Days(this.inputTime))
},
@@ -93,16 +95,6 @@ export default {
res[0][this.getPrevious7Days(this.inputTime)[6]] == null ? 0 : Number(res[0][this.getPrevious7Days(this.inputTime)[6]][this.dataType]),
]
// this.seriesData = {
// [this.getPrevious7Days(this.inputTime)[0]]: res[0][this.getPrevious7Days(this.inputTime)[0]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[0]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[1]]: res[0][this.getPrevious7Days(this.inputTime)[1]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[1]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[2]]: res[0][this.getPrevious7Days(this.inputTime)[2]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[2]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[3]]: res[0][this.getPrevious7Days(this.inputTime)[3]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[3]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[4]]: res[0][this.getPrevious7Days(this.inputTime)[4]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[4]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[5]]: res[0][this.getPrevious7Days(this.inputTime)[5]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[5]][this.dataType],
// [this.getPrevious7Days(this.inputTime)[6]]: res[0][this.getPrevious7Days(this.inputTime)[6]] == null ? 0 : res[0][this.getPrevious7Days(this.inputTime)[6]][this.dataType],
// }
this.initChart();
this.num++
console.log("返回数据", this.seriesData)

View File

@@ -1,17 +1,17 @@
<template>
<div class="sidebar">
<div class="logo">
<!-- <div class="logo">
<img style="margin-right: 10px;" src="@/assets/logo.png">
<img src="@/assets/logotext.png">
</div>
</div> -->
<ul>
<li @click="updateActiveIndex(1)" v-show="userInfo.userType == 3">
<li @click="updateActiveIndex(1)">
<div>
<img v-show="activeIndex == 1" src="@/assets/navAction.png" autoplay loop muted class="background-img">
<div style="display: flex;">
<img v-show="activeIndex == 1" src="@/assets/workAction.png" style="margin-right: 10px;">
<img v-show="activeIndex == 2" src="@/assets/workAction.png" style="margin-right: 10px;">
<div :style="activeIndex == 1 ? 'color: #000' : 'color: #fff'" class="center-justify">工作台</div>
<img v-show="activeIndex == 1" src="@/assets/ai.png" style="margin-right: 10px;width: 24px;height: 24px;">
<img v-show="activeIndex == 2" src="@/assets/ai.png" style="margin-right: 10px;width: 24px;height: 24px;">
<div :style="activeIndex == 1 ? 'color: #000' : 'color: #fff'" class="center-justify">Ai后台管理</div>
</div>
</div>
@@ -21,9 +21,9 @@
<img v-show="activeIndex == 2" src="@/assets/navAction.png" autoplay loop muted class="background-img">
<div style="display: flex;">
<img v-show="activeIndex == 2" src="@/assets/listAction.png" style="margin-right: 10px;">
<img v-show="activeIndex == 1" src="@/assets/listAction.png" style="margin-right: 10px;">
<div :style="activeIndex == 2 ? 'color: #000' : 'color: #fff'" class="center-justify">主播列表</div>
<img v-show="activeIndex == 2" src="@/assets/MiniProgram.png" style="margin-right: 10px;width: 30px;height: 30px;">
<img v-show="activeIndex == 1" src="@/assets/MiniProgram.png" style="margin-right: 10px;width: 30px;height: 30px;">
<div :style="activeIndex == 2 ? 'color: #000' : 'color: #fff'" class="center-justify">小程序后台管理</div>
</div>
</div>
@@ -45,11 +45,13 @@ import { ref, reactive, onMounted } from 'vue';
import { getUser } from '@/utils/storage'
import { defineEmits } from 'vue';
const userInfo = ref(getUser())
let activeIndex = ref(userInfo.value.userType == 3 ? 1 : 2);
let activeIndex = ref(1);
const emit = defineEmits(['update:activeIndex']);
const emit = defineEmits(['activeIndex']);
const updateActiveIndex = (index) => {
@@ -58,23 +60,23 @@ const updateActiveIndex = (index) => {
};
</script>
<style scoped>
<style scoped lang="less">
.sidebar {
position: fixed;
left: 0;
top: 0;
height: 900px;
width: 280px;
background-color: #338F6A;
background-color: @bg-color;
padding: 20px;
box-sizing: border-box;
.logo {
border-bottom: 1px solid #fff;
padding-bottom: 29px;
padding-top: 20px;
img:nth-of-type(1) {
height: 40px;
height: 66px;
}
img:nth-of-type(2) {

View File

@@ -5,11 +5,15 @@ import store from './store'
import { createPinia } from 'pinia';
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'; // 引入中文语言包
// createApp(App).use(store).use(router).mount('#app')
const app = createApp(App);
app.use(ElementPlus, {
locale: zhCn, // 配置中文
});
app.use(ElementPlus) // 注册 ElementPlus
app.use(createPinia()); // 注册 Pinia
app.use(store); // 注册 store

View File

@@ -11,19 +11,35 @@ const routes = [
{
path: '/nav',
name: 'nav',
// redirect: '/nav/hostsList', // 默认跳转
redirect: '/nav/Home', // 默认跳转
component: () => import(/* webpackChunkName: "hostsList" */ '../views/nav.vue'),
children: [
{
path: 'hostsList',
name: 'hostsList',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/hostsList.vue')
path: 'Home',
name: 'Home',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/Home.vue'),
},
{
path: 'workBenches',
name: 'workBenches',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/workbenches.vue')
},]
path: 'scriptManagement',
name: 'scriptManagement',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/ai/scriptManagement.vue'),
},
{
path: 'LanguageManagement',
name: 'LanguageManagement',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/ai/LanguageManagement.vue'),
},
{
path: 'miniAM',
name: 'miniAM',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/mini/miniAM.vue'),
},
{
path: 'miniIntegral',
name: 'miniIntegral',
component: () => import(/* webpackChunkName: "hostsList" */ '../views/hosts/mini/miniIntegral.vue'),
},
]
}
]
const router = createRouter({

4
src/static/css/app.less Normal file
View File

@@ -0,0 +1,4 @@
@bg-color: #022b4e; // 主色
@bg-color-light: #022b4eaf; // 浅主色
@bg-color-light-light: #022b4e1c; // 浅浅主色
@btn-bg-color: #045dac; // 黄色按钮主色

View File

@@ -4,33 +4,43 @@
*/
import axios from 'axios'
import { getToken, getUser } from '@/utils/storage'
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import router from '@/router'
import { ElMessage } from 'element-plus';
import { usePythonBridge, } from '@/utils/pythonBridge'
const { stopScript } = usePythonBridge();
const router = useRouter();
// 请求地址前缀
let baseURL = ''
if (process.env.NODE_ENV === 'development') {
// 生产环境
baseURL = "http://120.26.251.180:8085/"
// baseURL = "http://192.168.0.115:8085/"
// baseURL = "https://api.tkpage.yolozs.com"
baseURL = "http://49.235.115.212:12025/"
// baseURL = "http://192.168.0.103:8085/"
} else {
// 测试环境
// baseURL = "http://120.26.251.180:8085/"
// 开发环境
baseURL = "http://120.26.251.180:8085/"
baseURL = "http://49.235.115.212:12025/"
// baseURL = "http://api.tkpage.vvtiktok.cn"
}
// 请求拦截器
axios.interceptors.request.use((config) => {
// if (getToken()) {
// config.headers['token'] = getToken();
console.log("config", config)
const url = sliceUrl(config.url)
console.log("url", url)
// if (!(config.url == 'templateList' || config.url == 'languageList')) {
// config.headers['vvtoken'] = getToken();
// }
// 请求超时时间 - 毫秒
config.timeout = 60000
config.baseURL = baseURL
// 自定义Content-type
config.headers['Content-type'] = 'application/json'
// config.headers['Content-type'] = 'application/json'
return config;
}, (error) => {
return Promise.reject(error)
@@ -38,7 +48,15 @@ axios.interceptors.request.use((config) => {
// 响应拦截器
axios.interceptors.response.use((response) => {
return response
console.log("```````````````response```````````````````", response.data)
if (response.data.code == '200') {
console.log("response", response.data.data)
return response.data.data
} else {
router.push('/')
ElMessage.error(response.data.code + '' + response.data.message);
}
}, (error) => {
// 可添加请求失败后的处理逻辑
return Promise.reject(error)
@@ -54,10 +72,9 @@ export function getAxios({ url, params }) {
params
// 请求成功将返回的数据传递给resolve函数
}).then(res => {
resolve(res.data)
resolve(res)
// 请求失败将错误信息传递给reject函数
}).catch(err => {
console.log(err)
reject(err)
})
})
@@ -65,37 +82,17 @@ export function getAxios({ url, params }) {
// axios的post请求
export function postAxios({ url, data }) {
if (url != 'api/account/login') {
throttledCheekalive();
}
return new Promise((resolve, reject) => {
console.log("```````````````data```````````````", data);
console.log("````````````````url````````````````", url);
axios.post(
url,
data,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
).then(res => {
resolve(res.data)
resolve(res)
}).catch(err => {
if (err.message == "Network Error") {
// alert("网络错误,请检查网络连接")
ElMessage.error('网络连接错误');
reject('网络连接错误')
} else {
ElMessage.error(err.message);
reject(err.message)
}
// console.log(err)
// reject(err)
reject(err)
})
})
}
@@ -146,6 +143,7 @@ function cheekalive() {
if (res.data) {
} else {
stopScript();
alert("账号在其他地方登录!")
window.location.href = '/';
}
@@ -167,6 +165,18 @@ function throttle(func, limit) {
}
}
const throttledCheekalive = throttle(cheekalive, 5000);
function sliceUrl(url) {
const lastSlash = url.lastIndexOf('/');
const questionMark = url.indexOf('?');
if (questionMark == -1) {
const result = url.slice(lastSlash + 1, url.length);
return result;
} else {
const result = url.slice(lastSlash + 1, questionMark);
return result;
}
}
export default axios

View File

@@ -99,8 +99,23 @@ export function usePythonBridge() {
}
};
const stopScript = () => {
if (bridge.value) {
bridge.value.stopScript();
}
};
//获取版本号
const getVersion = () => {
return new Promise((resolve, reject) => {
if (bridge.value) {
bridge.value.currentVersion(function (result) {
resolve(result);
});
}
});
};
// 在组件挂载时初始化桥接
onMounted(initBridge);
@@ -112,6 +127,8 @@ export function usePythonBridge() {
givePyAnchorId,
backStageloginStatus,
backStageloginStatusCopy,
exportToExcel
exportToExcel,
stopScript,
getVersion
};
}

View File

@@ -42,4 +42,13 @@ export function setTkUser(userdata) {
// 用于获取tk账户密码
export function getTkUser() {
return JSON.parse(localStorage.getItem('tkuser'));
}
// 用于列表筛选条件
export function setSerch(data) {
localStorage.setItem('Serch', JSON.stringify(data));
}
// 用于获取列表筛选条件
export function getSerch() {
return JSON.parse(localStorage.getItem('Serch'));
}

View File

@@ -25,8 +25,8 @@
<!-- logo -->
<div class="logo">
<div class="center-justify" style="height: 80px; width: 300px;">
<img style="margin-right: 20px;" src="@/assets/logo.png">
<img src="@/assets/logotext.png">
<!-- <img style="margin-right: 20px;height: 100%;" src="@/assets/logo.png">
<img style="height: 100%;" src="@/assets/logotext.png"> -->
</div>
</div>
@@ -38,7 +38,6 @@
<div class="from-input">
<el-form label-position="left" label-width="100px" :model="formData">
<div class="from-input-item1">
<img src="@/assets/username.png" alt="">
<el-input style="height: 25px;" v-model="formData.userId" placeholder="账号" clearable
@@ -53,12 +52,16 @@
<div class="from-input-item">
<el-button class="loginButton" color="#8f7ee7" type="primary"
@click="onSubmit">登录</el-button>
</div>
</el-form>
</div>
</div>
</div>
<!-- <div class="version center-justify ">版本号{{ version }}</div> -->
</div>
</div>
</div>
</template>
@@ -66,18 +69,34 @@
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { login } from '@/api/account';
import { login, getIdByName } from '@/api/account';
import { getToken, setToken, setUser, setUserPass, getUserPass } from '@/utils/storage';
import { ElLoading } from 'element-plus';
import { usePythonBridge } from '@/utils/pythonBridge'
// const { getVersion } = usePythonBridge();
// let version = ref('0.0.0');
// onMounted(() => {
// setTimeout(() => {
// getVersion().then((res) => {
// version.value = res;
// })
// }, 500);
// })
const router = useRouter();
const formData = ref({
// tenantName: getUserPass() == null ? '' : getUserPass().tenantName,
userId: getUserPass() == null ? '' : getUserPass().userId,
password: getUserPass() == null ? '' : getUserPass().password,
});
const onSubmit = () => {
const loading = ElLoading.service({
lock: true,
@@ -85,28 +104,24 @@ const onSubmit = () => {
background: 'rgba(0, 0, 0, 0.7)',
});
setUserPass(formData.value);
login({
userId: formData.value.userId,
password: formData.value.password,
}).then((res) => {
loading.close();
console.log(res)
if (res) {
if (res.activeYn == 'Y') {
setToken(res.currcode);
setUser(res);
router.push('/nav');
} else {
alert('账号未启用');
}
getIdByName(formData.value.tenantName).then((tenantId) => {
console.log(tenantId)
login({
// tenantId: Number(tenantId),
username: formData.value.userId,
password: formData.value.password,
}).then((res) => {
loading.close();
console.log(res)
setToken(res.tokenValue);
setUser(res);
router.push('/nav');
}).catch((err) => {
loading.close();
});
})
} else {
alert('账号或密码错误');
}
}).catch((err) => {
loading.close();
});
};
</script>
@@ -141,6 +156,18 @@ const onSubmit = () => {
overflow: hidden;
/* 防止内容溢出 */
.version {
color: #fff;
position: absolute;
font-size: 20px;
bottom: 20px;
left: calc(50% - 50px);
// box-sizing: border-box;
// width: 1600px;
}
.background-video {
position: absolute;
top: 0;
@@ -175,8 +202,8 @@ const onSubmit = () => {
.from {
width: 420px;
height: 320px;
color: #107A4E;
// height: 320px;
color: @bg-color;
background-color: #ffffff44;
border-radius: 20px;
border: 1px solid #fff;
@@ -187,7 +214,7 @@ const onSubmit = () => {
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 24px;
color: #107A4E;
color: @bg-color;
line-height: 37px;
@@ -207,7 +234,7 @@ const onSubmit = () => {
padding: 8px 0;
.from-input-item-title {
color: #107A4E;
color: @bg-color;
font-size: 18px;
font-weight: 500;
width: 80px;
@@ -225,7 +252,7 @@ const onSubmit = () => {
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 18px;
color: #107A4E;
color: @bg-color;
line-height: 37px;
}
}
@@ -234,7 +261,7 @@ const onSubmit = () => {
display: flex;
width: 359px;
height: 50px;
background: rgba(147, 174, 158, 0.37);
background: @bg-color-light-light;
border-radius: 24px;
border: 1px solid #FFFFFF;
padding: 12px 25px 13px 25px;
@@ -277,7 +304,7 @@ const onSubmit = () => {
}
</style>
<style scoped>
<style scoped lang="less">
::v-deep(.el-input__wrapper) {
background-color: rgba(255, 0, 0, 0);
box-shadow: none;
@@ -285,11 +312,11 @@ const onSubmit = () => {
}
::v-deep(.el-input__inner) {
color: #107A4E;
color: #fff;
}
::v-deep(.el-input__inner::placeholder) {
color: #107A4E;
color: @bg-color;
}
</style>

38
src/views/hosts/Home.vue Normal file
View File

@@ -0,0 +1,38 @@
<template>
<div class="home">
</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref('');
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.home {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
}
</style>

View File

@@ -0,0 +1,279 @@
<template>
<div class="language-management">
<div class="content">
<div class="languagetitle">
<div class="title">语言管理</div>
<div class="add" @click="dialogVisible = true">
<img class="add-img" src="@/assets/plus2.png" alt="" />
</div>
</div>
<!-- 语言列表 -->
<div class="languagelist">
<el-table
:data="tableData"
stripe
style="width: 100%; margin-top: 10px"
max-height="700"
>
<el-table-column prop="language" label="语言名称" />
<el-table-column prop="" label="" />
<el-table-column prop="" label="" />
<el-table-column prop="" label="" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">
修改
</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<!-- 编辑弹窗 -->
<el-drawer v-model="dialogVisible" :with-header="false" @close="dialogVisible = false, languageNamedeta = {}">
<div class="dialog-content">
<div class="dialog-title">语言{{languageNamedeta.id? '编辑' : '新增'}}</div>
<el-input
v-model="languageNamedeta.language"
style="width: 100%; margin-top: 20px"
:rows="3"
type="textarea"
placeholder="请输入语言内容"
/>
<div class="dialog-btn">
<div class="dialogSave" @click="languageSave">保存</div>
<div class="dialogCancel" @click="dialogVisible = false">取消</div>
</div>
</div>
</el-drawer>
</template>
<script setup>
import { ElMessage, ElMessageBox } from "element-plus";
import { getLanguageList,deleteLanguage,editLanguage,addLanguage} from "@/api/account";
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref("");
const tableData = ref([]);
const dialogVisible = ref(false);
let languageNamedeta = ref({});// 编辑添加弹窗数据
// 编辑弹窗
function handleEdit(index, row) {
dialogVisible.value = true;
languageNamedeta.value = row;
}
// 保存语言
function languageSave() {
if (languageNamedeta.value.language == null) {
ElMessage({
type: "error",
message: "请输入语言内容",
});
return;
}
if (languageNamedeta.value.id) {
// 修改语言
editLanguage(languageNamedeta.value)
.then(() => {
ElMessage({
type: "success",
message: "修改成功",
});
})
.catch(() => {
ElMessage({
type: "error",
message: "修改失败",
});
});
dialogVisible.value = false;
}else{
// 新增语言
addLanguage(languageNamedeta.value)
.then(() => {
ElMessage({
type: "success",
message: "添加成功",
});
})
.catch(() => {
ElMessage({
type: "error",
message: "添加失败",
});
});
dialogVisible.value = false;
}
}
// 删除语言
function handleDelete(index, row) {
ElMessageBox.confirm("您确认要删除这个语言吗?")
.then(() => {
deleteLanguage({ id: row.id })
.then(() => {
ElMessage({
type: "success",
message: "删除成功",
});
})
.catch(() => {
ElMessage({
type: "error",
message: "删除失败",
});
});
})
.catch(() => {});
}
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// // 组件挂载完成后执行
// // 获取语言列表
// getLanguageList()
// .then((res) => {
// tableData.value = res.data;
// }).catch(() => {
// ElMessage({
// type: "error",
// message: "获取语言列表失败",
// });
// });
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.language-management {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.content {
width: 95%;
height: 95%;
background-color: #f5f5f5;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.3);
}
.languagetitle {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
height: 50px;
background-color: #e2e2e2;
border-radius: 10px;
}
.title {
font-size: 24px;
font-weight: bold;
color: #999999;
margin-left: 40px;
}
.add {
width: 150px;
height: 50px;
background-color: #cccccc;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.add:hover {
background-color: #999999;
}
.add-img {
width: 40px;
height: 40px;
}
.languagelist {
width: 97%;
height: 85%;
background-color: #ffffff;
margin-bottom: 20px;
border-radius: 10px;
overflow: auto;
scrollbar-width: none;
}
.dialog-content {
width: 100%;
height: 100%;
}
.dialog-title {
width: 100%;
height: 50px;
font-size: 25px;
color: #b4b4b4;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
text-align: center;
}
.dialog-btn {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.dialogSave {
width: 45%;
height: 50px;
background-color: #d6d6d6;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #ffffff;
}
.dialogSave:hover {
background-color: #b4b4b4;
}
.dialogCancel {
width: 45%;
height: 50px;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #d6d6d6;
border: 1px solid #d6d6d6;
}
.dialogCancel:hover {
background-color: #e7e6e6;
color: #ffffff;
}
</style>

View File

@@ -0,0 +1,399 @@
<template>
<div class="scriptManagement">
<div class="wordslist">
<!-- 话术列表 -->
<div class="wordscontent" ref="containerRef">
<div
class="words"
v-for="(item, index) in LanguageData"
:key="index"
:id="item.language"
>
<div class="wordsname">{{ item.language }}</div>
<div class="wordscontents">
<div
class="wordscontentstext"
v-for="(items, indexs) in item.content"
:key="index"
>
<div>{{ indexs + 1 + ".&nbsp;" + items.content }}</div>
<div class="wordscontentstextbtn">
<div style="margin-right: 10px" @click="handleEditor(items.id)">编辑</div>
<div @click="handleDelete(items.id)">删除</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="Languagelist">
<div class="Languagecontent">
<!-- 话术添加 -->
<div class="addwords" @click="wordsAddStatus = true">
<img class="addwordsimg" src="@/assets/plus.png" />
</div>
<!-- 语言选择 -->
<el-anchor
class="Languageanchor"
:container="containerRef"
direction="vertical"
type="default"
:offset="30"
@click="handleClick"
>
<el-anchor-link
class="anchorLink"
v-for="(item, index) in getLanguageListData"
:href="'#' + item.language"
:title="item.language"
:key="index"
/>
</el-anchor>
</div>
</div>
</div>
<!-- 话术编辑 -->
<el-drawer v-model="dialogVisible" :with-header="false">
<div class="dialog-content">
<div class="dialog-title">话术编辑</div>
<el-input
v-model="handletextareadeta.content"
style="width: 100%; margin-top: 20px"
:rows="25"
type="textarea"
placeholder="请输入话术内容"
/>
<div class="dialog-btn">
<div class="dialogSave" @click="handleSave">保存</div>
<div class="dialogCancel" @click="dialogVisible = false">取消</div>
</div>
</div>
</el-drawer>
<!-- 话术添加 -->
<el-drawer v-model="wordsAddStatus" :with-header="false">
<div class="dialog-content">
<div class="dialog-title">话术编辑</div>
<el-select v-model="wordsAddData.language" placeholder="请选择语言"></el-select>
<el-input
v-model="wordsAddData.content"
style="width: 100%; margin-top: 20px"
:rows="25"
type="textarea"
placeholder="请输入话术内容"
/>
<div class="dialog-btn">
<div class="dialogSave" @click="handleSaveWordsAdd">保存</div>
<div class="dialogCancel" @click="wordsAddStatus = false">取消</div>
</div>
</div>
</el-drawer>
</template>
<script setup>
import { getDialogList, getLanguageList, editDialog, deleteDialog,addDialog} from "@/api/account";
import { ElMessage, ElMessageBox } from "element-plus";
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref("");
const containerRef = ref(null);
const getDialogListData = ref([]);
const getLanguageListData = ref([]);
const LanguageData = ref([]);
const wordsAddStatus = ref(false);
const wordsAddData = ref({
language: "",
content: "",
});
const handleClick = (e) => {
e.preventDefault();
};
//数据处理
function groupContentByLanguage(arr1, arr2) {
return arr2.map((item) => {
// 过滤出对应语言的内容
const contents = arr1
.filter(({ language }) => language === item.language)
// 按 id 排序
.sort((a, b) => a.id - b.id)
// 转换为所需格式
.map(({ id, content }) => ({ id, content }));
return {
id: item.id,
language: item.language,
content: contents,
};
});
}
const dialogVisible = ref(false);
const handletextareadeta = ref({});
//添加话术
function handleSaveWordsAdd() {
if (wordsAddData.value.language === "" || wordsAddData.value.content === "") {
ElMessage({
type: "error",
message: "请填写完整信息",
});
return;
}
addDialog(wordsAddData.value)
.then(() => {
ElMessage({
type: "success",
message: "添加成功",
});
wordsAddStatus.value = false;
getDialogListData.value = getDialogList();
LanguageData.value = groupContentByLanguage(
getDialogListData.value,
getLanguageListData.value
);
}).catch(() => {
ElMessage({
type: "error",
message: "添加失败",
});
});
}
// 编辑
function handleEditor(id) {
dialogVisible.value = true;
handletextareadeta.value = getDialogListData.value.find((item) => item.id === id);
}
// 保存
function handleSave() {
editDialog({
id: handletextareadeta.value.id,
content: handletextareadeta.value.content,
})
.then(() => {
ElMessage({
type: "success",
message: "保存成功",
});
})
.catch(() => {
ElMessage({
type: "error",
message: "保存失败",
});
});
dialogVisible.value = false;
getDialogListData.value = getDialogList();
}
// 删除
function handleDelete(id) {
ElMessageBox.confirm("您确认要删除这条话术吗?")
.then(() => {
deleteDialog({ id })
.then(() => {
ElMessage({
type: "success",
message: "删除成功",
});
getDialogListData.value = getDialogList();
})
.catch(() => {
ElMessage({
type: "error",
message: "删除失败",
});
});
})
.catch(() => {});
}
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// getDialogListData.value = getDialogList();
// console.log(getDialogListData.value);
// getLanguageListData.value = getLanguageList();
LanguageData.value = groupContentByLanguage(
getDialogListData.value,
getLanguageListData.value
);
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.scriptManagement {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
display: flex;
}
.wordslist {
width: 87%;
height: 97%;
padding: 1% 1%;
/* background-color: #00ff40; */
}
.Languagelist {
height: 97%;
width: 10%;
padding: 1% 1% 1% 0%;
/* background-color: #8d0000; */
}
.wordscontent {
height: 100%;
width: 100%;
border-radius: 10px;
background-color: rgb(243, 243, 243);
overflow-y: auto;
display: flex;
flex-direction: column;
align-items: center;
scrollbar-width: none;
}
.Languagecontent {
height: 100%;
width: 100%;
border-radius: 10px;
background-color: rgb(243, 243, 243);
display: flex;
flex-direction: column;
overflow-y: auto;
scrollbar-width: none; /* 隐藏滚动条 */
}
.addwords {
width: 100%;
height: 50px;
background-color: rgb(226, 226, 226);
display: flex;
justify-content: center;
align-items: center;
}
.addwords:hover{
background-color: rgb(214, 214, 214);
}
.addwordsimg {
width: 30px;
height: 30px;
}
.Language {
height: 50px;
width: 100%;
background-color: rgb(226, 226, 226);
margin-top: 20px;
}
.words {
width: 95%;
margin-top: 20px;
margin-bottom: 20px;
}
.Languageanchor {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
background-color: rgb(243, 243, 243);
}
.anchorLink {
width: 100px;
height: 30px;
margin-top: 20px;
margin-bottom: 20px;
text-align: center;
font-size: 16px;
line-height: 30px;
border-radius: 5px;
margin-left: -0.875vw;
}
.wordsname {
font-size: 18px;
color: #c9c9c9;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
}
.wordscontents {
width: 95%;
/* min-height: 200px; */
/* background-color: #ffffff; */
border-radius: 10px;
margin-left: 30px;
}
.wordscontentstext {
font-size: 16px;
color: #7a7a7a;
line-height: 24px;
margin-top: 10px;
margin-bottom: 10px;
text-align: left;
word-wrap: break-word;
word-break: break-all;
display: flex;
justify-content: space-between;
align-items: center;
}
.wordscontentstextbtn {
display: flex;
}
.dialog-content {
width: 100%;
height: 100%;
}
.dialog-title {
width: 100%;
height: 50px;
font-size: 25px;
color: #b4b4b4;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
text-align: center;
}
.dialog-btn {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.dialogSave {
width: 45%;
height: 50px;
background-color: #d6d6d6;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #ffffff;
}
.dialogSave:hover {
background-color: #b4b4b4;
}
.dialogCancel {
width: 45%;
height: 50px;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #d6d6d6;
border: 1px solid #d6d6d6;
}
.dialogCancel:hover {
background-color: #e7e6e6;
color: #ffffff;
}
/* 样式定义 */
</style>

View File

@@ -1,576 +0,0 @@
<template>
<div class="hostList">
<div>
<div style="display: flex;">
<el-select v-model="searchForm.country" filterable placeholder="选择国家" size="large" style="width: 240px">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<div></div>
<el-date-picker v-model="searchForm.time" type="date" value-format="YYYYMMDD" placeholder="选择查询时间" size="large"
style="margin-left: 50px;" />
<el-button class="serch-button" style="margin-left: 50px;" type="primary" @click="serch">查询</el-button>
<el-button class="put-button" :disabled="tableData.length == 0" type="primary"
@click="exportList">导出Excel数据</el-button>
<el-button v-if="userInfo.userType == 3" class="put-button" type="primary"
@click="dialogFormVisible = true">分配给指定员工</el-button>
<el-button @click="filterdialogVisible = true" style="width: 50px;" class="put-button" type="primary"><img
style="height: 30px;" src="@/assets/filter.png"></el-button>
</div>
<div class="hostTable center-justify">
<el-table ref="multipleTableRef" :data="tableData" stripe v-loading="loading" height="500"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="35" />
<el-table-column fixed prop="hostId" label="主播id" width="160">
<template #default="scope">
<div class="hostIdText" @click="openHTML(scope.row.hostId)"> {{ scope.row.hostId }}</div>
</template>
</el-table-column>
<el-table-column prop="hostName" label="主播名字" min-width="160">
<template #default="scope">
{{ scope.row.hostName }}
</template>
</el-table-column>
<el-table-column prop="hostlevel" label="等级" width="120">
<template #default="scope">
{{ scope.row.hostlevel }}
</template>
</el-table-column>
<el-table-column v-for="label in labelList" :key="label.paramCode" :prop="label.paramCode"
:label="label.paramCodeMeaning" width="120">
<template v-if="label.paramCode != 'createDt'" #default="scope">
<el-popover v-if="!(label.paramCode == 'hostcoins' || label.paramCode == 'ysthostcoins')"
placement="bottom" :width="600" trigger="hover">
<div style="height: 300px;">
<component :is="EChartsComponent" v-if="isPopoverVisible[`${scope.row.hostId}-${label.paramCode}`]"
:title="label.paramCodeMeaning" :id="scope.row.hostId" :dataType="label.paramCode"
:time="searchForm.time"></component>
</div>
<template #reference>
<span @mouseover="openPopover(scope.row.hostId, label.paramCode)"
@mouseout="closePopover(scope.row.hostId, label.paramCode)">
{{ scope.row[label.paramCode] }}
</span>
</template>
</el-popover>
<el-popover v-else placement="bottom" :width="500" trigger="hover">
<div style="height: 300px;">
<component :is="EChartsComponent" v-if="isPopoverVisible[`${scope.row.hostId}-${label.paramCode}`]"
:title="label.paramCodeMeaning" :id="scope.row.hostId" :dataType="label.paramCode"
:time="searchForm.time">
</component>
</div>
<template #reference>
<!-- 鼠标移入时开启 -->
<span @mouseover="openPopover(scope.row.hostId, label.paramCode)"
@mouseout="closePopover(scope.row.hostId, label.paramCode)">
{{ scope.row[label.paramCode] }}
</span>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="120">
<template #default="scope">
<el-popover placement="top-start" :width="200" trigger="hover" :content="scope.row.comment">
<template #reference>
<el-button @click="openComment(scope.row)" link type="primary" size="small">维护情况</el-button>
</template>
</el-popover>
<select @change="handleSelectChange($event, scope.row)" v-model="scope.row.useable" class="m-2"
placeholder="Select" size="small" style="width: 70px">
<option label="未转化" value="N" />
<option label="已转化" value="Y" />
</select>
<el-dialog :modal="false" v-model="commentVisible" title="维护情况" align-center>
<el-input maxlength="80" v-model="commentInfo" placeholder="请输入维护情况" clearable show-word-limit />
<template #footer>
<span class="dialog-footer">
<el-button @click="commentVisible = false">取消</el-button>
<el-button type="primary" @click="uphostcomment()">
提交
</el-button>
</span>
</template>
</el-dialog>
</template>
</el-table-column>
<!-- <el-table-column label="操作">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-button type="primary" @click="getTkhostdetail(scope.row.hostId)">查看</el-button>
</div>
</template>
</el-table-column> -->
</el-table>
</div>
<div class="center-justify" style="margin-top: 30px;">
<el-pagination v-model:current-page="page" v-model:page-size="pageSize" background
layout="sizes, prev, pager, next" :total="total" :page-sizes="[10, 20, 50, 100]"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div>
<el-dialog v-model="dialogFormVisible" title="分配主播" align-center>
<el-select v-model="staffValue" filterable placeholder="Select" style="width: 240px">
<el-option v-for="item in staffOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="allocation">
分配
</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="filterdialogVisible" width="800px">
<el-row :gutter="20">
<el-col :span="8">
<label>选择筛选条件</label>
<el-select v-model="searchForm.dataType" filterable placeholder="筛选条件" style="width: 240px">
<el-option v-for="item in filterOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-col>
<el-col :span="8">
<div v-if="!(searchForm.dataType == 'InvitationType')"><label>最小值</label></div>
<div v-else><label>普票/金票</label></div>
<el-input v-show="!(searchForm.dataType == 'InvitationType')" type="number" v-model="searchForm.dataStart"
placeholder="请输入最小值" />
<el-select v-show="(searchForm.dataType == 'InvitationType')" v-model="searchForm.dataStart" filterable
placeholder="请选择" style="width: 240px">
<el-option v-for="item in [{ label: '普票', value: '1' }, { label: '金票', value: '2' }]" :key="item.value"
:label="item.label" :value="item.value" />
</el-select>
</el-col>
<el-col :span="8">
<div v-show="!(searchForm.dataType == 'InvitationType')"><label>最大值</label></div>
<el-input v-show="!(searchForm.dataType == 'InvitationType')" type="number" v-model="searchForm.dataEnd"
placeholder="请输入最大值" />
</el-col>
</el-row>
<template #footer>
<span class="dialog-footer">
<!-- <el-button @click="filterdialogVisible = false">取消</el-button> -->
<el-button type="primary" @click="filterdialogVisible = false">
确认
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
// import { getToken, setToken, removeToken } from '@/utils/storage'
import { tkhostdata, dicts, tkhostdetail, downList, getStaffList, managerhosts, upholdinfo, getCountryinfo } from '@/api/account';
import { usePythonBridge, } from '@/utils/pythonBridge'
import { getUser } from '@/utils/storage'
import { ref, reactive, onMounted } from 'vue';
import EChartsComponent from '@/components/EChartsComponent.vue';
import { ElMessage, ElMessageBox } from 'element-plus'
const loading = ref(false)
//py方法
const { givePyAnchorId, exportToExcel } = usePythonBridge();
let num = ref(0)
//账号信息
const userInfo = ref(getUser())
//主播列表DOM
const multipleTableRef = ref(null)
let labelList = ref([])
const tableData = ref([])
//选择国家
const searchForm = ref({
country: '',
time: new Date().toISOString().split('T')[0].replace(/-/g, ''),
dataType: '',
dataStart: '',
dataEnd: '',
})
//员工选择列表
let staffOptions = ref([])
//筛选条件选择列表
let filterOptions = ref([])
//选择的员工
let staffValue = ref('')
//选择的主播列表
let selectHostList = ref([])
//分配弹窗是否弹出
let dialogFormVisible = ref(false)
//备注弹窗是否弹出
let commentVisible = ref(false)
//筛选弹窗是否弹出
let filterdialogVisible = ref(false)
//备注信息
let commentInfo = ref('')
//备注信息主播
let commentHost = ref('')
//分页
let pageSize = ref(10)
let page = ref(1)
let total = ref(0)
//是否渲染
const isPopoverVisible = reactive({})
let options = ref([])
onMounted(() => {
//获取字典
getdictionary()
//获取下级员工
getStaff();
//获取国家
getCountry();
})
function serch() {
getlist();
}
function exportList() {
if (searchForm.value.dataType == 'InvitationType') {
searchForm.value.dataEnd = searchForm.value.dataStart
}
exportToExcel({
searchTime: searchForm.value.time,
region: searchForm.value.country,
dataType: searchForm.value.dataType == '' ? null : searchForm.value.dataType,
dataStart: searchForm.value.dataStart == '' ? null : searchForm.value.dataStart,
dataEnd: searchForm.value.dataEnd == '' ? null : searchForm.value.dataEnd,
pageSize: pageSize.value,
page: page.value,
userId: userInfo.value.userId,
userType: userInfo.value.userType
})
// //浏览器导出方法
// downList('export/hostsinfo',
// {
// searchTime: searchForm.value.time,
// region: searchForm.value.country,
// pageSize: pageSize.value,
// page: page.value,
// userId: userInfo.value.userId,
// userType: userInfo.value.userType
// }
// );
}
//分页每页条数
function handleSizeChange(val) {
console.log(`${val} items per page`)
getlist();
}
//分页页数
function handleCurrentChange(val) {
console.log(`current page: ${val}`)
getlist();
}
//选择行
function handleSelectionChange(data) {
console.log(data)
data.forEach(item => {
selectHostList.value.push(item.hostId)
})
// multipleTableRef.value = data
// console.log(multipleTableRef.value)
}
//获取主播列表
const getlist = () => {
if (searchForm.value.dataType == 'InvitationType') {
searchForm.value.dataEnd = searchForm.value.dataStart
}
loading.value = true
console.log(searchForm.value)
tkhostdata({
searchTime: searchForm.value.time,
region: searchForm.value.country,
dataType: searchForm.value.dataType == '' ? null : searchForm.value.dataType,
dataStart: searchForm.value.dataStart == '' ? null : searchForm.value.dataStart,
dataEnd: searchForm.value.dataEnd == '' ? null : searchForm.value.dataEnd,
pageSize: pageSize.value,
page: page.value,
userId: userInfo.value.userId,
userType: userInfo.value.userType
}).then(res => {
loading.value = false
total.value = res.total
tableData.value = []
res.records.forEach(item => {
if (item.infoMap.InvitationType == '1') {
item.infoMap.InvitationType = '普票'
} else if (item.infoMap.InvitationType == '2') {
item.infoMap.InvitationType = '金票'
}
console.log(item.infoMap)
item = { ...item, ...item.infoMap }
tableData.value.push(item)
})
})
}
function openComment(data) {
console.log(data)
commentInfo.value = data.comment
commentHost.value = data.hostId
commentVisible.value = true
}
//修改主播维护状态
function handleSelectChange(event, data) {
upholdinfo({
"hostId": data.hostId,
"userId": userInfo.value.userId,
"tenantId": userInfo.value.tenantId,
// "comment": "我已经尽力维护,但是失败了",
"useable": event.target.value
}).then(res => {
console.log(res)
})
}
//更改主播维护备注
function uphostcomment() {
upholdinfo({
"hostId": commentHost.value,
"userId": userInfo.value.userId,
"tenantId": userInfo.value.tenantId,
"comment": commentInfo.value,
}).then(res => {
console.log(res)
serch()
commentVisible.value = false
})
}
//获取字典
const getdictionary = () => {
dicts({
paramType: "hostsdata",
// page: 1,
offset: 1,
pageSize: 100
}).then(res => {
// labelList.value = res.records
labelList.value = res
console.log(labelList.value)
labelList.value.forEach(item => {
filterOptions.value.push({
value: item.paramCode,
label: item.paramCodeMeaning
})
})
})
}
//获取国家
function getCountry() {
getCountryinfo({}).then(res => {
console.log(res)
res.forEach(item => {
options.value.push({ value: item.countryGroupName, label: item.countryGroupName })
})
console.log(options.value)
})
}
//获取下级员工
const getStaff = () => {
getStaffList({
userId: userInfo.value.userId,
userType: userInfo.value.userType,
activeYn: userInfo.value.activeYn,
tenantId: userInfo.value.tenantId,
}).then(res => {
console.log(res)
res.forEach(item => {
staffOptions.value.push({
value: item.userId,
label: item.userName
})
})
})
}
//分配主播给员工
function allocation() {
managerhosts({
"manaId": userInfo.value.userId,
"userId": staffValue.value,
"tenantId": userInfo.value.tenantId,
"hostIds": selectHostList.value
}).then(res => {
if (res) {
dialogFormVisible.value = false
}
})
}
//获取主播信息
const getTkhostdetail = (id) => {
tkhostdetail({
hostId: id,
// page: 1,
searchTimeStart: '20250401',
searchTimeEnd: '20250403'
}).then(res => {
console.log(labelList.value)
})
}
function openPopover(hostId, paramCode) {
isPopoverVisible[`${hostId}-${paramCode}`] = true;
}
function closePopover(hostId, paramCode) {
// isPopoverVisible[`${hostId}-${paramCode}`] = false;
}
function openHTML(id) {
givePyAnchorId(id)
}
</script>
<style lang="less">
.hostList {
box-sizing: border-box;
// height: 100vh;
width: 100%;
padding: 40px;
/* 页面无法选中 */
// -webkit-user-select: none;
// -moz-user-select: none;
// -ms-user-select: none;
// user-select: none;
.hostTable {
width: 100%;
padding: 40px 0;
.hostIdText {
text-decoration: underline;
cursor: pointer;
color: #0f0092;
}
}
.serch-button {
width: 80px;
height: 47px;
background: #E7CA92;
border-radius: 10px;
border: none;
}
.put-button {
width: 132px;
height: 47px;
background: #E7CA92;
border-radius: 10px;
border: none;
}
}
.el-dialog {
--el-dialog-font-line-height: 50px;
--el-dialog-width: 600px;
--el-dialog-border-radius: 8px;
}
.center-line {
display: flex;
flex-direction: column;
align-items: center;
// justify-content: center;
}
.center-justify {
display: flex;
justify-content: space-around;
align-items: center;
}
.center-align {
display: flex;
justify-content: space-between;
}
.center-flex {
display: flex;
justify-content: center;
align-items: center;
}
</style>
<style scoped>
::v-deep(.el-input__wrapper) {
background-color: #F2FAF9;
border: 1px solid #B7CEC5;
height: 44px;
}
::v-deep(.el-select__wrapper) {
background-color: #F2FAF9;
border: 1px solid #B7CEC5;
height: 48px;
}
::v-deep(.el-pagination.is-background .el-pager li.is-active) {
background-color: #338F6A;
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<div class="miniAM">
小程序账号管理
</div>
</template>
<script setup>
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref('');
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.miniAM {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
}
/* 样式定义 */
</style>

View File

@@ -0,0 +1,311 @@
<template>
<div class="mini-integral">
<div class="content">
<!-- 积分配置列表 -->
<div class="integral-list">
<el-table
:data="tableData"
stripe
style="width: 100%; margin-top: 10px"
max-height="700"
>
<el-table-column prop="functionName" label="事件" />
<el-table-column prop="" label="" />
<el-table-column prop="functionValue" label="积分" />
<el-table-column prop="" label="" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="miniIntegralEdit(scope.$index, scope.row)">
修改
</el-button>
<el-button
size="small"
type="danger"
@click="miniIntegralDelete(scope.$index, scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 积分配置 -->
<div class="integral-title">
<div class="add" @click="dialogVisible = true">
<img class="add-img" src="@/assets/plus2.png" alt="">
</div>
<div class="integral-text">
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
<div class="h1"></div>
</div>
</div>
</div>
</div>
<!-- 新增修改积分配置弹窗 -->
<el-drawer v-model="dialogVisible" :with-header="false" @close="dialogVisible = false, miniIntegraldeta = {}">
<div class="dialog-content">
<div class="dialog-title">事件{{miniIntegraldeta.id? '编辑' : '新增'}}</div>
<el-input
v-if="!miniIntegraldeta.id"
v-model="miniIntegraldeta.functionName"
style="width: 100%; margin-top: 20px"
:rows="3"
type="textarea"
placeholder="请输入事件名称"
/>
<el-input
v-model="miniIntegraldeta.functionValue"
style="width: 100%;height: 50px; margin-top: 20px"
type="number"
placeholder="请输入积分值"
/>
<div class="dialog-btn">
<div class="dialogSave" @click="miniIntegralAddSave">保存</div>
<div class="dialogCancel" @click="dialogVisible = false">取消</div>
</div>
</div>
</el-drawer>
</template>
<script setup>
import { ElMessage, ElMessageBox } from "element-plus";
import {
getWxConfigList,
addWxConfig,
editWxConfig,
deleteWxConfig,
} from "@/api/account";
import {
ref, // 响应式基础
watch, // 侦听器
onMounted, // 组件挂载完成后执行
onUpdated, // 组件更新后执行
onUnmounted, // 组件销毁前执行
} from "vue";
const refname = ref("");
const tableData = ref([
{
id: 1,
functionName: "登录",
functionValue: 10,
},
{
id: 2,
functionName: "分享",
functionValue: 20,
},
]);
const dialogVisible = ref(false);
const miniIntegraldeta = ref({});
//保存积分配置
function miniIntegralAddSave() {
if (!miniIntegraldeta.value.functionName) {
ElMessage({
type: "error",
message: "事件名称不能为空",
});
return;
}
if (!miniIntegraldeta.value.functionValue) {
ElMessage({
type: "error",
message: "积分值不能为空",
});
return;
}
if (miniIntegraldeta.value.id) {
// 修改积分配置
editWxConfig(miniIntegraldeta.value)
.then(() => {
ElMessage({
type: "success",
message: "修改成功",
});
dialogVisible.value = false;
miniIntegraldeta.value = {};
})
.catch(() => {
ElMessage({
type: "error",
message: "修改失败",
});
});
} else {
// 新增积分配置
addWxConfig(miniIntegraldeta.value)
.then(() => {
ElMessage({
type: "success",
message: "新增成功",
});
dialogVisible.value = false;
miniIntegraldeta.value = {};
})
.catch(() => {
ElMessage({
type: "error",
message: "新增失败",
});
});
}
}
// 新增积分配置弹窗
function miniIntegralEdit(index, row) {
dialogVisible.value = true;
miniIntegraldeta.value = row;
console.log(index, row);
}
//删除积分配置
function miniIntegralDelete(index, row) {
ElMessageBox.confirm("您确认要删除这个积分配置吗?")
.then(() => {
deleteWxConfig({ id: row.id })
.then(() => {
ElMessage({
type: "success",
message: "删除成功",
});
})
.catch(() => {
ElMessage({
type: "error",
message: "删除失败",
});
});
})
.catch(() => {});
}
watch(refname, async (newQuestion, oldQuestion) => {
// 变化后执行
});
onMounted(() => {
// 组件挂载完成后执行
});
onUpdated(() => {
// 组件更新后执行
});
onUnmounted(() => {
// 组件销毁前执行
});
</script>
<style scoped>
.mini-integral {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content {
width: 80%;
height: 80%;
background-color: #f5f5f5;
border-radius: 10px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.3);
padding: 40px;
}
.integral-list{
width: 87%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
}
.integral-title{
width: 10%;
height: 100%;
background-color: #ffffff;
border-radius: 10px;
}
.add{
width: 100%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background-color: #c4c4c4;
border-radius: 10px 10px 0 0;
}
.add:hover{
background-color: #a9a9a9;
}
.add-img{
width: 40px;
height: 40px;
}
.integral-text{
width: 100%;
height: 520px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 20px;
color: #c4c4c4;
font-size: 25px;
font-family: "SimSun", "Microsoft YaHei", sans-serif;
}
.h1{
margin-top: 10px;
margin-bottom: 10px;
}
.dialog-title {
width: 100%;
height: 50px;
font-size: 25px;
color: #b4b4b4;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
text-align: center;
}
.dialog-btn {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.dialogSave {
width: 45%;
height: 50px;
background-color: #d6d6d6;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #ffffff;
}
.dialogSave:hover {
background-color: #b4b4b4;
}
.dialogCancel {
width: 45%;
height: 50px;
line-height: 50px;
text-align: center;
font-weight: bold;
border-radius: 5px;
font-size: 16px;
color: #d6d6d6;
border: 1px solid #d6d6d6;
}
.dialogCancel:hover {
background-color: #e7e6e6;
color: #ffffff;
}
/* 样式定义 */
</style>

View File

@@ -1,589 +0,0 @@
<template>
<div class="center-line workbenches">
<div class="center-align" style="width: 100%; margin: 0 20px;">
<div class="box-card-num1 center-line">
<div>总数量: <span>{{ hostData.totalCount }}</span></div>
<div>有效主播: <span>{{ hostData.validAnchorsCount }}</span></div>
<div> 已查询: <span>{{ hostData.checkedDataCount }}</span></div>
<div>可邀请: <span>{{ hostData.canInvitationCount }}</span></div>
</div>
<div class="center-line" style="padding-top: 15vh;">
<el-button class="open-login" type="primary" @click="openTK">开启tk</el-button>
</div>
<div>
<el-card class="box-card-num" v-for="(item, index) in 2" :key="index">
<div class="center-justify">
<div class="from-input-item">
<div class="from-input-item-title center-justify">
公会账号
</div>
<el-input :disabled="!(isTk && tkData[index].code == 0)" v-model="tkData[index].account"
placeholder="请输入登录账号" clearable />
</div>
<div class="from-input-item">
<div class="from-input-item-title center-justify">
公会密码
</div>
<el-input :disabled="!(isTk && tkData[index].code == 0)" v-model="tkData[index].password"
type="password" placeholder="请输入登录密码" show-password />
</div>
<el-button class="open-login" style="margin-left: 60px;"
:disabled="!(isTk && tkData[index].code == 0)" type="primary"
@click="loginTK(index)">登录tk</el-button>
<div v-if="tkData[index].code == 0" class="loginState"></div>
<div v-if="tkData[index].code == 1" class="loginState" style="background-color: green;"></div>
</div>
<div class="todayCount">今日已查询次数{{ tkData[index].num }}</div>
</el-card>
</div>
</div>
<div class="container ">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span class="center-justify"><img src="@/assets/worklogo.png">工作台 </span>
<div style="margin-right: 120px;">当前网络:{{ countryData }}
<!-- <el-button class="reset-button" @click="reset">重置数据</el-button> -->
</div>
</div>
</template>
<el-row :gutter="20">
<el-col :span="8">
<div class="input-group">
<label>设置金币数量</label>
<el-input type='number' v-model="pyData.gold.min" :min="0" :max="pyData.gold.max - 1"
placeholder="最小值" style="width: 100%" :disabled="!pyData.isStart">
<template #prepend>最小金币数</template>
</el-input>
<el-input type='number' v-model="pyData.gold.max" :min="pyData.gold.min + 1" :max="100"
placeholder="最大值" style="width: 100%; margin-top: 10px" :disabled="!pyData.isStart">
<template #prepend>最大金币数</template>
</el-input>
</div>
</el-col>
<el-col :span="8">
<div class="input-group">
<label>设置粉丝数量</label>
<el-input type='number' v-model="pyData.fans.min" :min="0" :max="pyData.fans.max - 1"
placeholder="最小值" style="width: 100%" :disabled="!pyData.isStart">
<template #prepend>最小粉丝数</template>
</el-input>
<el-input type='number' v-model="pyData.fans.max" :min="pyData.fans.min + 1" :max="100"
placeholder="最大值" style="width: 100%; margin-top: 10px" :disabled="!pyData.isStart">
<template #prepend>最大粉丝数</template>
</el-input>
</div>
</el-col>
<el-col :span="8">
<div class="input-group">
<label>后台查询频率</label>
<el-input type='number' v-model="pyData.frequency.hour" :min="0"
:max="pyData.frequency.day - 1" placeholder="次/小时" style="width: 100%"
:disabled="!pyData.isStart">
<template #append>/小时</template>
</el-input>
<el-input type='number' v-model="pyData.frequency.day" :min="pyData.frequency.hour + 1"
:max="100" placeholder="次/24小时" style="width: 100%; margin-top: 10px"
:disabled="!pyData.isStart">
<template #append>/24小时</template>
</el-input>
</div>
</el-col>
</el-row>
<div style="margin-top: 20px; text-align: center">
<el-button class="submit-button" :disabled="submitting" v-show="pyData.isStart" type="primary"
@click="submit">开始获取数据</el-button>
<el-button v-show="!pyData.isStart" type="danger" @click="unsubmit">停止获取</el-button>
</div>
</el-card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { usePythonBridge, } from '@/utils/pythonBridge'
import { setNumData, getNumData, getUser, setTkUser, getTkUser } from '@/utils/storage'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getCountryName } from '@/utils/countryUtil'
import { tkaccountuseinfo } from '@/api/account'
//导入python交互方法
const { fetchDataConfig, fetchDataCount, loginBackStage, loginTikTok, backStageloginStatus, backStageloginStatusCopy } = usePythonBridge();
//ip国家
let countryData = ref('');
//获取主播数量的定时器
let getHostTimer = ref(null);
//获取的主播信息
let hostData = ref({
totalCount: 0,
validAnchorsCount: 0,
canInvitationCount: 0,
checkedDataCount: 0,
});
//是否开启tk
let isTk = ref(true);
//设置状态轮询定时器
let statusTimer = ref(null);
let statusTimerCopy = ref(null);
//tk账号信息
let tkData = ref([
{
account: '',
password: '',
index: 1,
code: 0,
num: 0
},
{
account: '',
password: '',
index: 2,
code: 0,
num: 0
},
]);
//python需要的数据
let pyData = ref({
gold: { min: 0, max: 0 },
fans: { min: 0, max: 0 },
frequency: { hour: 0, day: 0 },
isStart: true,
country: countryData.value,
test: '123',
test1: { test: 123, test12: 123 },
tenantId: getUser().tenantId,
userId: getUser().userId,
});
//按钮提交状态
let submitting = ref(false);
onMounted(() => {
//从缓存获取数据
if (getNumData()) {
pyData.value = getNumData();
}
if (getTkUser()) {
tkData.value = getTkUser();
tkData.value[0].code = 0;
tkData.value[1].code = 0;
}
tkaccountuse(tkData.value[0].account, 0)
tkaccountuse(tkData.value[1].account, 1)
getIpInfo()
//查询次数查询
})
const getIpInfo = async () => {
try {
const response = await fetch('https://ipapi.co/json/');
if (!response.ok) {
throw new Error('请求失败');
}
const data = await response.json();
console.log('IP信息:', data.country);
countryData.value = getCountryName(data.country);
} catch (error) {
console.error('请求出错:', error);
}
};
//提交数据到py
const submit = () => {
pyData.value.country = countryData.value;
console.log('提交的区间值:', pyData.value);
if (tkData.value[0].account == '' || tkData.value[1].account == '') {
ElMessage.error('请输入账号密码');
return;
}
if (tkData.value[0].password == '' || tkData.value[1].password == '') {
ElMessage.error('请输入账号密码');
return;
}
ElMessageBox.confirm(
'确认开始爬取数据?',
'开始',
{
confirmButtonText: '开始',
cancelButtonText: '取消',
type: 'success',
}
)
.then(() => {
// console.log('提交的区间值:', pyData.value.gold, pyData.value.fans, pyData.value.frequency);
//开始按钮的状态 改为禁用
submitting.value = true;
setNumData(pyData.value);
console.error('提交的区间值:', JSON.stringify(pyData.value));
fetchDataConfig(JSON.stringify({
gold: pyData.value.gold,
fans: pyData.value.fans,
frequency: pyData.value.frequency,
isStart: true,
country: countryData.value,
tenantId: getUser().tenantId,
userId: getUser().userId,
})).then((res) => {
getHostTimer.value = setInterval(() => {
fetchDataCount().then((res) => {
hostData.value = JSON.parse(res);
tkaccountuse(tkData.value[0].account, 0)
tkaccountuse(tkData.value[1].account, 1)
})
}, 1000);
}).finally(() => {
setTimeout(() => {
pyData.value.isStart = false;
submitting.value = false;
}, 2000)
})
})
.catch(() => {
})
};
//停止
const unsubmit = () => {
fetchDataConfig(JSON.stringify({
gold: pyData.value.gold,
fans: pyData.value.fans,
frequency: pyData.value.frequency,
isStart: false,
country: countryData.value,
tenantId: getUser().tenantId,
userId: getUser().userId,
})).then((res) => {
pyData.value.isStart = true;
clearInterval(getHostTimer.value);
getHostTimer.value = null;
// ElMessage.sussec('已停止')
}).catch((err) => {
// ElMessage.error('停止失败')
})
};
//重置
const reset = () => {
pyData.value.gold = { min: 0, max: 0 };
pyData.value.fans = { min: 0, max: 0 };
pyData.value.frequency = { hour: 0, day: 0 };
};
const loginTK = (index) => {
setTkUser(tkData.value)
loginBackStage({
account: tkData.value[index].account,
password: tkData.value[index].password,
index: index
})
if (index == 0) {
statusTimer = setInterval(() => {
getloginStatus();
}, 2000)
} else if (index == 1) {
statusTimerCopy = setInterval(() => {
getloginStatusCopy();
}, 2000)
}
}
const openTK = () => {
isTk.value = true;
console.log(isTk.value)
loginTikTok();
}
function getloginStatus() {
backStageloginStatus().then((res) => {
const data = JSON.parse(res);
tkData.value[data.index].code = data.code
if (data.code == 1) {
clearInterval(statusTimer);
statusTimer = null;
}
})
}
function getloginStatusCopy() {
backStageloginStatusCopy().then((res) => {
const data = JSON.parse(res);
tkData.value[data.index].code = data.code
if (data.code == 1) {
clearInterval(statusTimer);
statusTimer = null;
}
})
}
function tkaccountuse(id, index) {
let num = 0;
tkaccountuseinfo({ userId: id }).then((res) => {
num = res
tkData.value[index].num = num
console.log('账号使用次数', tkData.value[index].num)
})
}
</script>
<style scoped lang="less">
.container {
margin: 0 auto;
}
.workbenches {
padding: 45px 29px 22px 27px;
/* 页面无法选中 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.box-card {
// width: 1240px;
height: 436px;
background: #FFFFFF;
box-shadow: 0px 0px 21px 0px rgba(183, 183, 183, 0.33);
border-radius: 24px;
}
.box-card-num1 {
width: 197px;
height: 321px;
background: #FFFFFF;
box-shadow: 0px 0px 21px 0px rgba(183, 183, 183, 0.33);
border-radius: 24px;
padding-top: 60px;
box-sizing: border-box;
div {
height: 20%;
display: flex;
justify-content: space-around;
align-items: center;
color: #8D8E8E;
span {
color: #000;
padding-left: 10px;
}
}
}
.box-card-num {
width: 897px;
height: 145px;
background: #FFFFFF;
box-shadow: 0px 0px 21px 0px rgba(183, 183, 183, 0.33);
border-radius: 24px;
margin-bottom: 30px;
padding-top: 18px;
box-sizing: border-box;
.todayCount {
padding: 15px 21px;
font-size: 14px;
}
}
.from-input-item {
display: flex;
.from-input-item-title {
color: #000000;
font-size: 14px;
font-weight: 500;
width: 100px;
height: 50px;
}
.loginButton {
width: 100%;
height: 40px;
color: #ffffff;
font-size: 16px;
}
}
.loginState {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: #b90000;
margin-left: 15px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
span {
font-family: Source Han Sans SC;
font-weight: 400;
font-size: 20px;
color: #2D2727;
line-height: 37px;
img {
margin-right: 16px;
}
}
}
.input-group {
margin-bottom: 20px;
.el-input {
margin: 22px 0;
}
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #606266;
}
.open-login {
width: 100px;
height: 47px;
background: #E7CA92;
border-radius: 10px;
border: none;
}
.reset-button {
width: 132px;
height: 47px;
background: #E7CA92;
border-radius: 10px;
font-family: Source Han Sans SC;
font-weight: 400;
font-size: 18px;
color: #FFFFFF;
margin-left: 50px;
}
.submit-button {
width: 160px;
height: 47px;
background: #338F6A;
border-radius: 10px;
}
.center-line {
display: flex;
flex-direction: column;
align-items: center;
// justify-content: center;
}
.center-justify {
display: flex;
justify-content: space-around;
align-items: center;
}
.center-align {
display: flex;
justify-content: space-between;
}
.center-flex {
display: flex;
justify-content: center;
align-items: center;
}
</style>
<style scoped>
::v-deep(.el-input-group__prepend) {
background: #84CEB2;
border-radius: 10px 0px 0px 10px;
border: 1px solid #B7CEC5;
font-family: Source Han Sans SC;
font-weight: 400;
font-size: 18px;
color: #FFFFFF;
line-height: 37px;
}
::v-deep(.el-input-group__append) {
background: #84CEB2;
border-radius: 0px 10px 10px 0px;
border: 1px solid #B7CEC5;
font-family: Source Han Sans SC;
font-weight: 400;
font-size: 18px;
color: #FFFFFF;
line-height: 37px;
}
::v-deep(.el-input__wrapper) {
width: 218px;
height: 44px;
}
.el-input {
width: 200px;
height: 48px;
background: #FFFFFF;
border-radius: 10px;
border: 1px solid #B7CEC5;
}
</style>

View File

@@ -1,89 +1,205 @@
<template>
<div class="app-container">
<Sidebar class="noneText" @activeIndex="activeIndexFn" />
<div class="content ">
<div v-show="activeIndex == 1">
<workbenches v-if="openWerk" />
</div>
<div v-show="activeIndex == 2">
<hostsList v-if="openList" />
</div>
<div class="common-layout">
<el-container class="container">
<el-aside :width="asideWidth" class="aside">
<!-- logo -->
<div class="logo" :style="{ width: isCollapse ? '4vw' : '10vw' }">
<img
class="logoimg"
:style="{
width: isCollapse ? '3vw' : '6vw',
height: isCollapse ? '3vw' : '6vw',
}"
src="../assets//icon.png"
alt="图标"
/>
</div>
<!-- 收缩按钮 -->
<div
class="collapse-btn"
:style="{ width: isCollapse ? '4vw' : '10vw' }"
@click="toggleCollapse"
>
<el-icon v-if="isCollapse"><ArrowRightBold /></el-icon
><el-icon v-else><ArrowLeftBold /></el-icon>
</div>
<!-- 菜单 -->
<el-row class="row">
<el-col :span="12">
<el-menu
default-active="/nav"
background-color="#545c64"
router
text-color="#fff"
@open="handleOpen"
@close="handleClose"
@select="handleSelect"
:collapse="isCollapse"
popper-offset="-115"
class="menu"
>
<el-sub-menu index="1" class="sub-menu">
<template #title>
<el-icon><Tickets /></el-icon>
<span>Ai管理</span>
</template>
<el-menu-item-group>
<template #title><span>Ai</span></template>
<el-menu-item index="/nav/scriptManagement">话术管理</el-menu-item>
<el-menu-item index="/nav/LanguageManagement">语言管理</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
<el-sub-menu index="2" class="sub-menu">
<template #title>
<el-icon><Collection /></el-icon>
<span>小程序管理</span>
</template>
<el-menu-item-group>
<template #title><span>小程序</span></template>
<el-menu-item index="/nav/miniAM">账号管理</el-menu-item>
<el-menu-item index="/nav/miniIntegral">积分配置</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
</el-menu>
</el-col>
</el-row>
</el-aside>
</div>
<el-container>
<!-- 顶部 -->
<el-header class="header">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item :to="{ path: '/nav/Home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item
v-for="(item, index) in breadcrumbList"
:key="index"
replace="true"
:to="{ path: handleurl(item.path) }"
>
{{ item.name }}
</el-breadcrumb-item>
</el-breadcrumb>
</el-header>
<!-- 内容 -->
<el-main class="main">
<router-view>
</router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup>
import Sidebar from '../components/Sidebar.vue';
import { RouterLink, RouterView } from 'vue-router'
import hostsList from '@/views/hosts/hostsList.vue'
import workbenches from '@/views/hosts/workbenches.vue'
import { ref } from 'vue'
import { getUser } from '@/utils/storage'
import { ref } from "vue";
import {
Menu as IconMenu,
Message,
Setting,
Tickets,
Collection,
ArrowRightBold,
ArrowLeftBold,
} from "@element-plus/icons-vue";
import { ArrowRight } from "@element-plus/icons-vue";
import { RouterLink, RouterView } from "vue-router";
import { useRoute, useRouter } from "vue-router";
let userType = ref(getUser().userType)
let activeIndex = ref(userType.value == 3 ? 1 : 2)
let openWerk = ref(userType.value == 3 ? true : false)
let openList = ref(userType.value == 3 ? false : true)
console.log("用户等级", getUser().userType)
function activeIndexFn(data) {
activeIndex.value = data
openWerk.value = true
openList.value = true
console.log(data)
const router = useRouter();
const route = useRoute();
const isCollapse = ref(false);
const asideWidth = ref("10vw");
const breadcrumbList = ref([]);
//路由映射
function convertPathsToMenuItems(paths) {
const pathMap = {
1: "ai管理",
2: "小程序管理",
"/nav/scriptManagement": "话术管理",
"/nav/miniAM": "小程序账号管理",
"/nav/miniIntegral": "积分配置",
"/nav/LanguageManagement": "语言管理",
};
return paths.map((path) => {
const name = pathMap[path];
if (!name) throw new Error(`未找到路径 ${path} 对应的名称`);
return { name, path };
});
}
//面包屑
function handleSelect(index, indexPath) {
console.log("select", convertPathsToMenuItems(indexPath));
breadcrumbList.value = convertPathsToMenuItems(indexPath);
}
//面包屑地址判断
function handleurl(indexPath) {
if (indexPath === "1" || indexPath === "2") {
return "";
} else {
return indexPath;
}
}
//菜单展开
function toggleCollapse() {
asideWidth.value = isCollapse.value ? "10vw" : "4vw";
isCollapse.value = isCollapse.value ? false : true;
}
</script>
<style>
body,
html {
margin: 0;
padding: 0;
height: 100%;
<style scoped>
.common-layout {
width: 100vw;
height: 100vh;
}
.app-container {
.aside {
height: 100vh;
background-color: rgb(87, 87, 87);
transition: width 0.3s ease; /* 添加过渡效果 */
}
.header {
width: 85vw;
height: 4vh;
background-color: rgb(255, 255, 255);
display: flex;
width: 1600px;
height: 900px;
background-color: #338F6A;
}
.noneText {
/* 页面无法选中 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.sidebar {
width: 200px;
background-color: #338F6A;
padding: 20px;
/* box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); */
}
.content {
margin-left: 280px;
width: 1304px;
height: 868px;
background: #FFFFFF;
border-radius: 36px;
margin-top: 16px;
}
.center-justify {
display: flex;
justify-content: space-around;
align-items: center;
}
</style>
.main {
width: 100%;
height: 90vh;
background-color: rgb(199, 199, 199);
}
.logo {
margin-top: 10px;
font-size: 20px;
color: white;
text-align: center;
line-height: 10vh;
width: 10vw;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
}
.logoimg {
transition: width 0.3s ease; /* 添加过渡效果 */
transition: height 0.3s ease; /* 添加过渡效果 */
border-radius: 20%;
}
.row {
width: 10vw;
height: 80vh;
}
.menu {
width: 10vw;
font-size: 30px;
}
.collapse-btn {
height: 40px;
display: flex;
justify-content: center;
align-items: center;
transition: width 0.3s ease; /* 添加过渡效果 */
background-color: #3b4046;
}
</style>

Binary file not shown.

View File

@@ -18,7 +18,11 @@ module.exports = defineConfig({
})
]
}
},
less: {
additionalData: `@import "@/static/css/app.less";` // 注入全局变量文件
}
}
}
});
},
});