上传代码

This commit is contained in:
pengxiaolong
2025-07-01 21:37:07 +08:00
parent 560581f1a4
commit ec8bffa6f0
24 changed files with 1490 additions and 1467 deletions

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

@@ -1,39 +1,67 @@
import { getAxios, postAxios, downFile } from '@/utils/axios.js'
export function getIdByName(name) {
return getAxios({ url: `/api/tenant/get-id-by-name?name=${name}` })
}
//登录
export function login(data) {
return postAxios({ url: '/api/user/doLogin', data })
}
//获取国家
export function getCountryinfo(data) {
return postAxios({ url: '/api/common/country_info', data })
return postAxios({ url: 'api/user/doLogin', data })
}
//查询tk账号查询次数
export function tkaccountuseinfo(accountName) {
return getAxios({ url: `/api/common/accountCount?accountName=${accountName}` })
//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 tkhostdata(data) {
return postAxios({ url: '/api/save_data/hosts_info', 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 apiGetCart() {
return getAxios({ url: '/cgi-bin/cart/latest' })
}
// export function login(data) {
// return postAxios({ url: 'api/account/login', data })
// }
export function cheekalive(data) {
return postAxios({ url: 'api/account/cheekalive', data })
}

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: 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

@@ -1,17 +1,17 @@
<template>
<div class="sidebar">
<div class="logo">
<!-- <img style="margin-right: 10px;" src="@/assets/logo.png"> -->
<!-- <div class="logo">
<img style="margin-right: 10px;" src="@/assets/logo.png">
<img src="@/assets/logotext.png">
</div>
</div> -->
<ul>
<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>

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({

View File

@@ -17,13 +17,13 @@ let baseURL = ''
if (process.env.NODE_ENV === 'development') {
// 生产环境
// baseURL = "https://api.tkpage.yolozs.com"
baseURL = "http://192.168.1.174:8101"
baseURL = "http://49.235.115.212:12025/"
// baseURL = "http://192.168.0.103:8085/"
} else {
// 测试环境
// baseURL = "http://120.26.251.180:8085/"
// 开发环境
baseURL = "https://api.tkpage.yolozs.com"
baseURL = "http://49.235.115.212:12025/"
// baseURL = "http://api.tkpage.vvtiktok.cn"
}
@@ -32,15 +32,15 @@ axios.interceptors.request.use((config) => {
console.log("config", config)
const url = sliceUrl(config.url)
console.log("url", url)
if (!(config.url == 'doLogin' || config.url == 'get-id-by-name')) {
config.headers['vvtoken'] = getToken();
}
// 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)
@@ -48,8 +48,8 @@ axios.interceptors.request.use((config) => {
// 响应拦截器
axios.interceptors.response.use((response) => {
console.log("response", response.data)
if (response.data.code == 0) {
console.log("```````````````response```````````````````", response.data)
if (response.data.code == '200') {
console.log("response", response.data.data)
return response.data.data
} else {
@@ -83,14 +83,12 @@ export function getAxios({ url, params }) {
// axios的post请求
export function postAxios({ url, data }) {
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)
}).catch(err => {

View File

@@ -25,8 +25,8 @@
<!-- logo -->
<div class="logo">
<div class="center-justify" style="height: 80px; width: 300px;">
<!-- <img style="margin-right: 20px;height: 100%;" src="@/assets/logo.png"> -->
<img style="height: 100%;" 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,11 +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.tenantName" placeholder="租户名称"
clearable @keyup.enter="onSubmit" />
</div>
<div class="from-input-item1">
<img src="@/assets/username.png" alt="">
<el-input style="height: 25px;" v-model="formData.userId" placeholder="账号" clearable
@@ -63,7 +58,7 @@
</div>
</div>
</div>
<div class="version center-justify ">版本号{{ version }}</div>
<!-- <div class="version center-justify ">版本号{{ version }}</div> -->
</div>
@@ -80,22 +75,22 @@ 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 { 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,
// tenantName: getUserPass() == null ? '' : getUserPass().tenantName,
userId: getUserPass() == null ? '' : getUserPass().userId,
password: getUserPass() == null ? '' : getUserPass().password,
});
@@ -112,7 +107,7 @@ const onSubmit = () => {
getIdByName(formData.value.tenantName).then((tenantId) => {
console.log(tenantId)
login({
tenantId: Number(tenantId),
// tenantId: Number(tenantId),
username: formData.value.userId,
password: formData.value.password,
}).then((res) => {

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: 160px">
<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.createTime" type="date" value-format="YYYY-MM-DD" placeholder="选择查询时间"
size="large" style="margin-left: 50px;width: 160px;" />
<el-input v-model="searchForm.hostsId" placeholder="请输入主播id" size="large"
style="width: 160px; margin-left: 50px;" clearable />
<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 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="hostlevel" label="等级" width="80">
<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">
</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="filterdialogVisible" width="800px" :before-close="handleClose">
<el-row v-for="(field, index) in fields" :key="index" :gutter="20" style="margin-bottom: 10px">
<el-col :span="4">
<div style="height: 100%; padding-top: 10px" class="center-justify">
{{ field.label }}
</div>
</el-col>
<el-col :span="10">
<div><label>最小值</label></div>
<el-input type="number" :oninput="'if(value.length>9)value=value.slice(0,9)'"
v-model.number="searchForm[field.minModel]" placeholder="请输入最小值" />
</el-col>
<el-col :span="10">
<div><label>最大值</label></div>
<el-input type="number" :oninput="'if(value.length>9)value=value.slice(0,9)'"
v-model.number="searchForm[field.maxModel]" placeholder="请输入最大值" />
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="4">
<!-- <label>选择筛选条件</label> -->
<div style="height: 100%;padding-top: 10px;" class="center-justify">
排序
</div>
</el-col>
<el-col :span="10">
<div><label>排序类型</label></div>
<el-select v-model="sortData.sortType" filterable placeholder="请选择" style="width: 240px">
<el-option v-for="item in sortNameOptions" :key="item.type" :label="item.label" :value="item.type" />
</el-select>
</el-col>
<el-col :span="10">
<div><label>升序/降序</label></div>
<el-select v-model="sortData.sortForm" filterable placeholder="请选择" style="width: 240px">
<el-option v-for="item in [{ label: '升序', value: 'asc' }, { label: '降序', value: 'desc' }]" :key="item.value"
:label="item.label" :value="item.value" />
</el-select>
</el-col>
</el-row>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="reset">
重置
</el-button>
<!-- <el-button @click="filterdialogVisible = false">取消</el-button> -->
<el-button type="primary" @click="handelClick">
确认
</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, accountName } from '@/api/account';
import { usePythonBridge, } from '@/utils/pythonBridge'
import { getUser, setSerch, getSerch } from '@/utils/storage'
import { ref, reactive, onMounted } from 'vue';
import EChartsComponent from '@/components/EChartsComponent.vue';
import { ElMessage, ElMessageBox } from 'element-plus'
import { color } from 'echarts';
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([
{ paramCode: 'country', paramCodeMeaning: '国家' },
{ paramCode: 'createTime', paramCodeMeaning: '创建时间' },
{ paramCode: 'hostsCoins', paramCodeMeaning: '主播金币' },
{ paramCode: 'yesterdayCoins', paramCodeMeaning: '昨日金币' },
{ paramCode: 'fans', paramCodeMeaning: '粉丝数' },
{ paramCode: 'fllowernum', paramCodeMeaning: '关注数' },
{ paramCode: 'onlineFans', paramCodeMeaning: '在线粉丝' },
{ paramCode: 'hostsKind', paramCodeMeaning: '主播类型' },
]);
const tableData = ref([])
//主播列表传参
const searchForm = ref({})
const fields = [
{ label: '粉丝数', minModel: 'fansMin', maxModel: 'fansMax' },
{ label: '在线人数', minModel: 'onlineFansMin', maxModel: 'onlineFansMax' },
{ label: '金币数', minModel: 'hostsCoinsMin', maxModel: 'hostsCoinsMax' },
{ label: '关注数', minModel: 'fllowernumMin', maxModel: 'fllowernumMax' },
]
//排序
let sortData = ref({ sortForm: 'desc', sortType: "createTime" })
//排序类型
let sortNameOptions = ref([
{ label: '创建时间', type: 'createTime' },
{ label: '主播金币', type: 'hostsCoins' },
{ label: '粉丝数', type: 'fans' },
{ label: '昨日金币', type: 'yesterdayCoins' },
{ label: '在线粉丝', type: 'onlineFans' },
{ label: '关注数', type: 'fllowernum' },
])
//员工选择列表
let staffOptions = ref([])
//筛选条件选择列表
//选择的员工
let staffValue = ref('')
//选择的主播列表
let selectHostList = ref([])
//分配弹窗是否弹出
let dialogFormVisible = ref(false)
//分配情况弹窗是否弹出
let hostNameVisible = ref(false)
//备注弹窗是否弹出
let commentVisible = ref(false)
//筛选弹窗是否弹出
let filterdialogVisible = ref(false)
//分配的员工
let staffId = ref({})
//备注信息
let commentInfo = ref('')
//备注信息主播
let commentHost = ref('')
//分页
let pageSize = ref(10)
let page = ref(1)
let total = ref(0)
//是否渲染
const isPopoverVisible = reactive({})
let options = ref([])
let version = ref('0.0.0');
onMounted(() => {
getCountry(); //获取国家
// getSerchStorage();//获取搜索条件
getlist();//获取主播列表
})
function serch() {
page.value = 1
getlist();
}
function exportList() {
if (searchForm.value.dataType == 'InvitationType') {
searchForm.value.dataEnd = searchForm.value.dataStart
}
exportToExcel({})
// //浏览器导出方法
// downList('export/hostsinfo',
// {
// searchTime: searchForm.value.time,
// region: searchForm.value.country,
// pageSize: pageSize.value,
// page: page.value,
// userId: userInfo.value.userId,
// }
// );
}
//分页每页条数
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)
selectHostList.value = []
data.forEach(item => {
selectHostList.value.push(item.hostId)
})
// multipleTableRef.value = data
// console.log(multipleTableRef.value)
}
//获取主播列表
const getlist = () => {
loading.value = true
console.log(searchForm.value)
tkhostdata({
tenantId: Number(userInfo.value.tenantId),
sort: sortData.value.sortForm,//正序倒序
sortName: sortData.value.sortType,//排序类型
"current": page.value,
"pageSize": pageSize.value,
...searchForm.value,//筛选条件
}).then(res => {
loading.value = false
if (res) {
console.log('主播列表', res)
total.value = Number(res.total)
tableData.value = res.records.map(item => ({
hostId: item.hostsId, // 注意:原字段是 hostId你的数据是 hostsId需手动映射
hostlevel: item.hostsLevel, // 原字段 hostlevel 对应你的数据 hostsLevel
country: item.country,
createTime: item.createTime,
fans: item.fans,
fllowernum: item.fllowernum,
hostsCoins: item.hostsCoins,
hostsKind: item.hostsKind,
onlineFans: item.onlineFans,
yesterdayCoins: item.yesterdayCoins,
// 保留原有字段(如 belongBy、useable 等)
belongBy: item.belongBy,
useable: item.useable
}));
}
})
}
function handelClick() {
filterdialogVisible.value = false
}
function reset() {
searchForm.value.fansMin = null
searchForm.value.fansMax = null
searchForm.value.onlineFansMin = null
searchForm.value.onlineFansMax = null
searchForm.value.hostsCoinsMin = null
searchForm.value.hostsCoinsMax = null
searchForm.value.fllowernumMin = null
searchForm.value.fllowernumMax = null
}
function handleClose(done) {
console.log('关闭')
// searchForm.value = {
// dataType: '',
// dataStart: '',
// dataEnd: '',
// }
done()
}
//修改主播维护状态
// 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
// })
// }
function filterTag(value, row) {
console.log(row.useable, value)
return row.useable === value;
}
//获取国家
function getCountry() {
getCountryinfo({}).then(res => {
console.log(res)
res.forEach(item => {
if (item.countryGroupName) {
options.value.push({ value: item.countryGroupName, label: item.countryGroupName })
}
})
console.log(options.value)
}).catch(err => {
console.log('getCountry', err)
})
}
//获取下级员工
// const getStaff = () => {
// getStaffList({
// userId: userInfo.value.userId,
// 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)
// upholdinfo({
// "hostId": id,
// "userId": userInfo.value.userId,
// "tenantId": userInfo.value.tenantId,
// // "comment": "我已经尽力维护,但是失败了",
// "useable": "Y"
// }).then(res => {
// getlist();
// })
}
// function openAccountName(idStr) {
// if (idStr) {
// hostNameVisible.value = true
// accountName(idStr).then(res => {
// staffId.value = JSON.stringify(res).replace(/[{}"]/g, '') // 移除所有 {} 和 "
// .split(',') // 按逗号分割成数组
// console.log(res)
// })
// }
// }
</script>
<style lang="less">
.hostList {
box-sizing: border-box;
// height: 100vh;
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;
}
}
.serch-button {
width: 80px;
height: 47px;
background: @btn-bg-color;
border-radius: 10px;
border: none;
}
.put-button {
width: 132px;
height: 47px;
background: @btn-bg-color;
border-radius: 10px;
border: none;
}
}
.el-dialog {
--el-dialog-font-line-height: 50px;
--el-dialog-width: 600px;
--el-dialog-border-radius: 8px;
// border: 10px solid @bg-color-light;
}
.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 lang="less">
::v-deep(.el-input__wrapper) {
background-color: #F2FAF9;
border: 1px solid @bg-color;
height: 44px;
}
::v-deep(.el-select__wrapper) {
background-color: #F2FAF9;
border: 1px solid @bg-color;
height: 48px;
}
::v-deep(.el-pagination.is-background .el-pager li.is-active) {
background-color: @bg-color;
}
</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,741 +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>运行时间: <span>{{ formattedTime }}</span></div>
</div>
<div class="center-line" style="padding-top: 15vh;">
<el-button class="open-login" type="primary" @click="openTK">开启tk</el-button>
<!-- <el-button class="open-login" type="primary" @click="startTimer">计时开始</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="!(tkData[index].code == 0 && !isLogin[index])"
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="!(tkData[index].code == 0 && !isLogin[index])"
v-model="tkData[index].password" type="password" placeholder="请输入登录密码" show-password />
</div>
<el-button class="open-login" style="margin-left: 60px;"
:disabled="!(tkData[index].code == 0 && !isLogin[index])" type="primary"
@click="loginTK(index)">登录后台</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" @input="handleInputHour" -->
<el-input type='number' v-model="pyData.frequency.hour" placeholder="次/小时"
style="width: 100%" :disabled="!pyData.isStart">
<template #append>/小时</template>
</el-input>
<!-- <el-input type='number' v-model="pyData.frequency.day" @input="handleInputDay" -->
<el-input type='number' v-model="pyData.frequency.day" 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, computed } 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 isLogin = ref([false, false]);
//设置状态轮询定时器
let statusTimer = ref(null);
let statusTimerCopy = ref(null);
//设置次数最大值
let maxCount = ref([
{
hourMax: 50,
dayMax: 300,
},
{
hourMax: 100,
dayMax: 600,
},
]);
//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(true);
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);
ElMessageBox.prompt('请输入将要获取国家的中文名', '获取国家失败', {
confirmButtonText: '确认',
cancelButtonText: '取消',
showClose: false,
closeOnClickModal: false,
showCancelButton: false,
})
.then(({ value }) => {
countryData.value = value
})
// .catch(() => {
// ElMessage({
// type: 'info',
// message: 'Input canceled',
// })
// })
}
};
//提交数据到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;
// }
if (((Number(pyData.value.gold.min) > Number(pyData.value.gold.max)) || (Number(pyData.value.fans.min) > Number(pyData.value.fans.max)))) {
ElMessage.error('请输入正确的区间值');
return;
}
if ((Number(pyData.value.gold.max) <= 0 || Number(pyData.value.fans.max <= 0)) || pyData.value.gold.max == '' || pyData.value.fans.max == '') {
ElMessage.error('请输入正确的区间值');
return;
}
if (Number(pyData.value.frequency.hour) <= 0 || Number(pyData.value.frequency.day) <= 0 || pyData.value.frequency.hour == '' || pyData.value.frequency.day == '') {
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) => {
//开始计时器
startTimer();
//开启查询次数
getHostTimer.value = setInterval(() => {
fetchDataCount().then((res) => {
hostData.value = JSON.parse(res);
tkaccountuse(tkData.value[0].account, 0)
tkaccountuse(tkData.value[1].account, 1)
})
}, 5000);
}).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) => {
pauseTimer();
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) {
isLogin.value[1] = true;
statusTimer = setInterval(() => {
getloginStatus();
}, 2000)
} else if (index == 1) {
isLogin.value[0] = true;
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;
submitting.value = false
isLogin.value[1] = false;
}
})
}
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;
submitting.value = false
isLogin.value[0] = false;
}
})
}
function tkaccountuse(id, index) {
let num = 0;
tkaccountuseinfo(id).then((res) => {
if (res) {
num = res
tkData.value[index].num = num
console.log('账号使用次数', tkData.value[index].num)
}
}).catch((err) => {
console.log('账号使用次数', err)
})
}
const isRunning = ref(false);
const totalSeconds = ref(0);
//定时器
let timerCrawl = null;
const startTimedata = ref(null);
//清空时间 并开始运行
const startTimer = () => {
resetTimer();
if (isRunning.value) return;
isRunning.value = true;
startTimedata.value = Date.now();
timerCrawl = setInterval(() => {
totalSeconds.value = Math.floor((Date.now() - startTimedata.value) / 1000);
}, 1000);
};
//结束运行 暂停
const pauseTimer = () => {
isRunning.value = false;
clearInterval(timerCrawl);
};
//清空时间
const resetTimer = () => {
isRunning.value = false;
clearInterval(timerCrawl);
totalSeconds.value = 0;
};
// 格式化时间为 HH:MM:SS
const formattedTime = computed(() => {
const hours = Math.floor(totalSeconds.value / 3600);
const minutes = Math.floor((totalSeconds.value % 3600) / 60);
const seconds = totalSeconds.value % 60;
return [
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
seconds.toString().padStart(2, '0')
].join(':');
});
function handleInputHour(value) {
console.log(value)
// 替换非数字字符为空字符串
let num = value.replace(/[^\d]/g, '');
// 如果值小于等于0则设置为0
if (Number(num) <= 0) {
num = 0;
}
if ((tkData.value[0].code == 1) && (tkData.value[1].code == 1)) {
if (Number(num) > maxCount.value[1].hourMax) {
num = maxCount.value[1].hourMax;
}
} else if ((tkData.value[0].code == 1) || (tkData.value[1].code == 1)) {
// 如果值大于最大值,则设置为最大值
if (Number(num) > maxCount.value[0].hourMax) {
num = maxCount.value[0].hourMax;
}
} else {
ElMessage.error('请先登录tk后台');
num = 0;
}
// 更新模型
pyData.value.frequency.hour = num;
}
function handleInputDay(value) {
console.log(value)
// 替换非数字字符为空字符串
let num = value.replace(/[^\d]/g, '');
// 如果值小于等于0则设置为0
if (Number(num) <= 0) {
num = 0;
}
if ((tkData.value[0].code == 1) && (tkData.value[1].code == 1)) {
if (Number(num) > maxCount.value[1].dayMax) {
num = maxCount.value[1].dayMax;
}
} else if ((tkData.value[0].code == 1) || (tkData.value[1].code == 1)) {
// 如果值大于最大值,则设置为最大值
if (Number(num) > maxCount.value[0].dayMax) {
num = maxCount.value[0].dayMax;
}
} else {
ElMessage.error('请先登录tk后台');
num = 0;
}
// 更新模型
pyData.value.frequency.day = 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: @btn-bg-color;
border-radius: 10px;
border: none;
}
.reset-button {
width: 132px;
height: 47px;
background: @btn-bg-color;
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: @bg-color;
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 lang="less">
::v-deep(.el-input-group__prepend) {
background: @bg-color-light;
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: @bg-color-light;
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="activeIndexA == 1">
<workbenches />
</div>
<div v-show="activeIndexA == 2">
<hostsList />
</div>
<div style="position: absolute; bottom: 0; right: 0;">{{ version }}</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 { usePythonBridge } from '@/utils/pythonBridge'
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 activeIndexA = ref(1)
function activeIndexFn(data) {
activeIndexA.value = data
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 lang="less">
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: @bg-color;
position: relative;
}
.noneText {
/* 页面无法选中 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.sidebar {
width: 200px;
background-color: @bg-color;
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>

View File

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