编辑ai人设,新消息提醒
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
# iOS 控制服务
|
# iOS 控制服务
|
||||||
|
# VUE_APP_BASE_LOCAL=http://192.168.1.218:34567/
|
||||||
VUE_APP_BASE_LOCAL=http://127.0.0.1:34567/
|
VUE_APP_BASE_LOCAL=http://127.0.0.1:34567/
|
||||||
# VUE_APP_BASE_LOCAL=http://192.168.1.209:34567/
|
# VUE_APP_BASE_LOCAL=http://192.168.1.209:34567/
|
||||||
|
|
||||||
# 业务后端(开发用内网地址)
|
# 业务后端(开发用内网地址)
|
||||||
# VUE_APP_BASE_REMOTE=https://crawlclient.api.yolozs.com
|
VUE_APP_BASE_REMOTE=https://crawlclient.api.yolozs.com
|
||||||
VUE_APP_BASE_REMOTE=http://192.168.1.144:8101/
|
# VUE_APP_BASE_REMOTE=http://192.168.1.144:8101/
|
||||||
|
|
||||||
# AI 服务
|
# AI 服务
|
||||||
VUE_APP_BASE_SPECIAL=http://ai.zhukeping.com
|
VUE_APP_BASE_SPECIAL=https://ai.yolozs.com
|
||||||
@@ -5,4 +5,4 @@ VUE_APP_BASE_LOCAL=http://127.0.0.1:34567/
|
|||||||
VUE_APP_BASE_REMOTE=https://crawlclient.api.yolozs.com
|
VUE_APP_BASE_REMOTE=https://crawlclient.api.yolozs.com
|
||||||
|
|
||||||
# AI 服务(如支持 HTTPS,最好用 https)
|
# AI 服务(如支持 HTTPS,最好用 https)
|
||||||
VUE_APP_BASE_SPECIAL=http://ai.zhukeping.com
|
VUE_APP_BASE_SPECIAL=https://ai.yolozs.com
|
||||||
@@ -71,4 +71,8 @@ export function anchorList(data) {
|
|||||||
//设置主播列表
|
//设置主播列表
|
||||||
export function deleteAnchorWithIds(data) {
|
export function deleteAnchorWithIds(data) {
|
||||||
return postAxios({ url: 'deleteAnchorWithIds', data })
|
return postAxios({ url: 'deleteAnchorWithIds', data })
|
||||||
|
}
|
||||||
|
//设置经纪人信息
|
||||||
|
export function aiConfig(data) {
|
||||||
|
return postAxios({ url: 'aiConfig', data })
|
||||||
}
|
}
|
||||||
122
src/components/AgentGuildDialog.vue
Normal file
122
src/components/AgentGuildDialog.vue
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog :model-value="modelValue" :title="title" :width="width" :close-on-click-modal="false" append-to-body
|
||||||
|
@open="onOpen" @close="onClose">
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
|
||||||
|
<el-form-item label="经纪人" prop="agentName">
|
||||||
|
<el-input v-model="form.agentName" placeholder="请输入经纪人名字" maxlength="50" show-word-limit clearable />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="公会" prop="guildName">
|
||||||
|
<el-input v-model="form.guildName" placeholder="请输入公会名字" maxlength="50" show-word-limit clearable />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 自由输入的联系工具(可不填) -->
|
||||||
|
<el-form-item label="联系工具" prop="contactTool">
|
||||||
|
<el-input v-model="form.contactTool" placeholder="例如:微信 / Telegram / 邮箱 / WhatsApp / 其它(可不填)"
|
||||||
|
maxlength="30" show-word-limit clearable />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 联系方式:仅当联系工具已填写时才必填 -->
|
||||||
|
<el-form-item label="联系方式" prop="contact">
|
||||||
|
<el-input v-model="form.contact" placeholder="请输入联系方式(当填写了联系工具时必填)" maxlength="100" show-word-limit
|
||||||
|
clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="onCancel">取 消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitting" @click="onConfirm">保 存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: { type: Boolean, default: false },
|
||||||
|
title: { type: String, default: '设置经纪信息' },
|
||||||
|
width: { type: [String, Number], default: '480px' },
|
||||||
|
// 初始值:新增 contactTool 字段(可为空)
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
agentName: '',
|
||||||
|
guildName: '',
|
||||||
|
contactTool: '',
|
||||||
|
contact: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:modelValue', 'save', 'open'])
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
const submitting = ref(false)
|
||||||
|
const form = reactive({
|
||||||
|
agentName: '',
|
||||||
|
guildName: '',
|
||||||
|
contactTool: '',
|
||||||
|
contact: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
function syncFromProps() {
|
||||||
|
form.agentName = props.model?.agentName ?? ''
|
||||||
|
form.guildName = props.model?.guildName ?? ''
|
||||||
|
form.contactTool = props.model?.contactTool ?? ''
|
||||||
|
form.contact = props.model?.contact ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.modelValue, v => { if (v) syncFromProps() })
|
||||||
|
|
||||||
|
function onOpen() {
|
||||||
|
syncFromProps()
|
||||||
|
emits('open')
|
||||||
|
}
|
||||||
|
function onClose() { emits('update:modelValue', false) }
|
||||||
|
function onCancel() { onClose() }
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
agentName: [{ required: true, message: '请输入经纪人名字', trigger: 'blur' }],
|
||||||
|
guildName: [{ required: true, message: '请输入公会名字', trigger: 'blur' }],
|
||||||
|
// contactTool 不必填,不加 required
|
||||||
|
contact: [
|
||||||
|
{
|
||||||
|
validator: (rule, value, cb) => {
|
||||||
|
const tool = (form.contactTool || '').trim()
|
||||||
|
const contact = (value || '').trim()
|
||||||
|
if (tool && !contact) {
|
||||||
|
return cb(new Error('已填写联系工具时,联系方式为必填'))
|
||||||
|
}
|
||||||
|
cb()
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onConfirm() {
|
||||||
|
try {
|
||||||
|
submitting.value = true
|
||||||
|
await formRef.value?.validate()
|
||||||
|
|
||||||
|
// 提交前做一次 trim
|
||||||
|
const payload = {
|
||||||
|
agentName: form.agentName.trim(),
|
||||||
|
guildName: form.guildName.trim(),
|
||||||
|
contactTool: form.contactTool.trim(),
|
||||||
|
contact: form.contact.trim()
|
||||||
|
}
|
||||||
|
emits('save', payload)
|
||||||
|
onClose()
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
:deep(.el-dialog__body) {
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<h3 class="text-lg font-bold mb-4">
|
<h3 class="text-lg font-bold mb-4">
|
||||||
<img style="margin: 0px 15px;" src="@/assets/video/chatMes.png"></img>
|
<img style="margin: 0px 15px;" src="@/assets/video/chatMes.png"></img>
|
||||||
消息内容
|
消息翻译内容
|
||||||
</h3>
|
</h3>
|
||||||
<el-scrollbar class="chat-box">
|
<el-scrollbar class="chat-box">
|
||||||
<div v-for="(msg, index) in messages.filter(m => m.type !== 'time')" :key="index"
|
<div v-for="(msg, index) in messages.filter(m => m.type !== 'time')" :key="index"
|
||||||
@@ -71,6 +71,7 @@ function fallbackCopyTextToClipboard(index, text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
|
margin-top: 5vh;
|
||||||
background: rgb(246, 246, 246);
|
background: rgb(246, 246, 246);
|
||||||
padding: 0px 20px 20px 20px;
|
padding: 0px 20px 20px 20px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -81,7 +82,7 @@ function fallbackCopyTextToClipboard(index, text) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
height: 80vh;
|
height: 35vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-message {
|
.left-message {
|
||||||
|
|||||||
197
src/components/MessageDialogd.vue
Normal file
197
src/components/MessageDialogd.vue
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="visible" class="dialog-overlay" @click.self="close">
|
||||||
|
<div class="dialog-content">
|
||||||
|
<h3 class="text-lg font-bold mb-4">
|
||||||
|
<img style="margin: 0 15px;" src="@/assets/video/chatMes.png" />
|
||||||
|
新消息提醒
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<el-scrollbar class="chat-box">
|
||||||
|
<div v-for="(item, idx) in normalizedMessages" :key="idx" class="msg-item">
|
||||||
|
<div class="meta" @click="onClickMessage(item)">
|
||||||
|
<span class="name">{{ item.sender || '未知用户' }}</span>
|
||||||
|
<span class="sep">·</span>
|
||||||
|
<span class="device">{{ item.device || '未知设备' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bubble" :class="isSeen(item) ? 'seen' : 'unread'" :style="bubbleStyle(item)"
|
||||||
|
@click="onClickMessage(item)">
|
||||||
|
{{ item.text }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps, defineEmits, ref, computed, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: Boolean,
|
||||||
|
messages: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 可自定义颜色
|
||||||
|
reminderColor: {
|
||||||
|
type: String,
|
||||||
|
// Element Plus 警告浅色(看起来像“提醒”)
|
||||||
|
default: 'var(--el-color-warning-light-5, #ffe58f)' // 更深一点
|
||||||
|
},
|
||||||
|
seenColor: {
|
||||||
|
type: String,
|
||||||
|
// EP 填充浅色(更“平静”的已读态)
|
||||||
|
default: 'var(--el-fill-color-light, #f2f3f5)'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['close', 'seen'])
|
||||||
|
const close = () => emit('close')
|
||||||
|
|
||||||
|
// 记录哪些消息已读(用原始索引做 key,避免过滤后索引错位)
|
||||||
|
const seenSet = ref(new Set())
|
||||||
|
|
||||||
|
// 规范化并带上原始索引,过滤掉 type === 'time'
|
||||||
|
const normalizedMessages = computed(() => {
|
||||||
|
const res = []
|
||||||
|
; (props.messages || []).forEach((m, i) => {
|
||||||
|
if (m?.type === 'time') return
|
||||||
|
res.push({
|
||||||
|
origIndex: i,
|
||||||
|
raw: m,
|
||||||
|
sender: m?.sender ?? m?.name ?? m?.user ?? m?.from ?? '',
|
||||||
|
device: m?.device ?? m?.deviceName ?? m?.udid ?? '',
|
||||||
|
text: m?.text ?? m?.content ?? ''
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始化:如果消息本身标注了 seen/read,则置为已读
|
||||||
|
watch(
|
||||||
|
() => props.messages,
|
||||||
|
(list) => {
|
||||||
|
const s = new Set()
|
||||||
|
list?.forEach((m, i) => {
|
||||||
|
if (m && (m.seen === true || m.read === true)) s.add(i)
|
||||||
|
})
|
||||||
|
seenSet.value = s
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
function isSeen(item) {
|
||||||
|
return seenSet.value.has(item.origIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
function markSeen(item) {
|
||||||
|
if (!isSeen(item)) {
|
||||||
|
seenSet.value.add(item.origIndex)
|
||||||
|
// 通知父组件(可选):你也可以在父里把 messages[item.origIndex].seen = true 持久化
|
||||||
|
emit('seen', { index: item.origIndex, message: item.raw })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bubbleStyle(item) {
|
||||||
|
return {
|
||||||
|
backgroundColor: isSeen(item) ? props.seenColor : props.reminderColor,
|
||||||
|
color: '#333'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击:标记已读 + 复制文本
|
||||||
|
function onClickMessage(item) {
|
||||||
|
markSeen(item)
|
||||||
|
fallbackCopyTextToClipboard(item.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兜底复制
|
||||||
|
function fallbackCopyTextToClipboard(text) {
|
||||||
|
// const textArea = document.createElement('textarea')
|
||||||
|
// textArea.value = text || ''
|
||||||
|
// textArea.style.position = 'fixed'
|
||||||
|
// textArea.style.left = '-999999px'
|
||||||
|
// textArea.style.top = '-999999px'
|
||||||
|
// document.body.appendChild(textArea)
|
||||||
|
// textArea.focus()
|
||||||
|
// textArea.select()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// const ok = document.execCommand('copy')
|
||||||
|
// ok ? ElMessage.success('已读') : ElMessage.error('复制失败1')
|
||||||
|
ElMessage.success('已读')
|
||||||
|
} catch (err) {
|
||||||
|
// ElMessage.error('复制失败2')
|
||||||
|
} finally {
|
||||||
|
// document.body.removeChild(textArea)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dialog-overlay {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
margin-top: 3vh;
|
||||||
|
background: rgb(246, 246, 246);
|
||||||
|
padding: 0 20px 20px 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
height: 35vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全部左对齐 */
|
||||||
|
.msg-item {
|
||||||
|
align-self: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
margin: 2px 0 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta .name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta .sep {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta .device {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble {
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-radius: 12px;
|
||||||
|
max-width: 90%;
|
||||||
|
display: inline-block;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 颜色通过内联样式控制(.unread/.seen 仅用于语义钩子) */
|
||||||
|
.bubble.unread {}
|
||||||
|
|
||||||
|
.bubble.seen {}
|
||||||
|
</style>
|
||||||
@@ -20,7 +20,7 @@ function attachInterceptors(instance) {
|
|||||||
instance.interceptors.request.use((config) => {
|
instance.interceptors.request.use((config) => {
|
||||||
// 登录/换租户接口可能不需要 token,根据你的需求放行
|
// 登录/换租户接口可能不需要 token,根据你的需求放行
|
||||||
const urlLast = sliceUrl(config.url || '')
|
const urlLast = sliceUrl(config.url || '')
|
||||||
if ((urlLast === 'prologue' || urlLast === 'comment')) {
|
if ((urlLast === 'prologue' || urlLast === 'comment' || urlLast === 'aiChat-logout')) {
|
||||||
config.headers['vvtoken'] = getToken()
|
config.headers['vvtoken'] = getToken()
|
||||||
}
|
}
|
||||||
// 超时 & 通用头
|
// 超时 & 通用头
|
||||||
@@ -42,6 +42,8 @@ function attachInterceptors(instance) {
|
|||||||
if (data?.code === 0 || data?.code === 200) {
|
if (data?.code === 0 || data?.code === 200) {
|
||||||
// 成功:返回业务数据(没有就回传原 data)
|
// 成功:返回业务数据(没有就回传原 data)
|
||||||
return (data?.data !== undefined) ? data.data : data
|
return (data?.data !== undefined) ? data.data : data
|
||||||
|
} else if (data?.code === 40400) {
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 业务失败:提示 + reject
|
// 业务失败:提示 + reject
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ import { getToken, setToken, setUser, setUserPass, getUserPass } from '@/stores/
|
|||||||
import { ElLoading, ElMessage } from 'element-plus';
|
import { ElLoading, ElMessage } from 'element-plus';
|
||||||
import { passToken } from '@/api/ios';
|
import { passToken } from '@/api/ios';
|
||||||
|
|
||||||
let version = ref('1.1.1');
|
let version = ref('1.1.4');
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<el-scrollbar class="left"> <!-- 左边栏 -->
|
<el-scrollbar class="left"> <!-- 左边栏 -->
|
||||||
|
<div style="position: absolute;left: 20px; top: 20px;">
|
||||||
|
<el-button style="background: linear-gradient(90deg, #60a5fa, #34d399); color: azure;"
|
||||||
|
@click="showMyInfo = true">人设编辑</el-button>
|
||||||
|
</div>
|
||||||
<div class="center-line"> <!-- 左边栏按钮 -->
|
<div class="center-line"> <!-- 左边栏按钮 -->
|
||||||
<div v-for="(btn, index) in buttons" :key="index" style="width: 100%;">
|
<div v-for="(btn, index) in buttons" :key="index" style="width: 100%;">
|
||||||
<div v-if="btn.show?.()" class="left-button" :style="btn.style ? btn.style() : {}" @click="btn.onClick"
|
<div v-if="btn.show?.()" class="left-button" :style="btn.style ? btn.style() : {}" @click="btn.onClick"
|
||||||
@@ -29,6 +33,8 @@
|
|||||||
<div class="input-info" v-show="selectedDevice == index">
|
<div class="input-info" v-show="selectedDevice == index">
|
||||||
|
|
||||||
<div class="app-button" @click="getMesList(device.deviceId)">获取当前聊天记录</div>
|
<div class="app-button" @click="getMesList(device.deviceId)">获取当前聊天记录</div>
|
||||||
|
<div class="app-button" @click="stopScript({ udid: device.deviceId })">停止任务</div>
|
||||||
|
<div class="app-button" @click="runTask(runType, device.deviceId)">开启</div>
|
||||||
<!-- <div class="app-button" @click="mqSend()">mq</div> -->
|
<!-- <div class="app-button" @click="mqSend()">mq</div> -->
|
||||||
<!-- <div class="app-button"
|
<!-- <div class="app-button"
|
||||||
@click="wsActions.clickCopyList(device.deviceId, index); openShowChat = true; istranslate = true">
|
@click="wsActions.clickCopyList(device.deviceId, index); openShowChat = true; istranslate = true">
|
||||||
@@ -52,15 +58,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right center-justify" @click.self="selectedDevice = 999">
|
<div class="right center-line" @click.self="selectedDevice = 999">
|
||||||
<!-- <div style="margin: 30px;"></div> -->
|
<!-- <div style="margin: 30px;"></div> -->
|
||||||
<ChatDialog :visible="openShowChat" :messages="chatList" />
|
<ChatDialog :visible="openShowChat" :messages="chatList" />
|
||||||
|
<MessageDialogd :visible="openShowChat" :messages="MesNewList" />
|
||||||
</div>
|
</div>
|
||||||
<MultiLineInputDialog v-model:visible="showDialog" :initialText='""' :title="dialogTitle" :index="selectedDevice"
|
<MultiLineInputDialog v-model:visible="showDialog" :initialText='""' :title="dialogTitle" :index="selectedDevice"
|
||||||
@confirm="onDialogConfirm" @cancel="stopAll" />
|
@confirm="onDialogConfirm" @cancel="stopAll" />
|
||||||
<HostListManagerDialog v-model:visible="showHostDlg" @save="onHostSaved" />
|
<HostListManagerDialog v-model:visible="showHostDlg" @save="onHostSaved" />
|
||||||
</div>
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- <AgentGuildDialog v-model="showMyInfo" :model="formInit" @save="handleSave" /> -->
|
||||||
|
<AgentGuildDialog v-model="showMyInfo" :model="{
|
||||||
|
agentName: borkerConfig.agentName,
|
||||||
|
guildName: borkerConfig.guildName,
|
||||||
|
contactTool: borkerConfig.contactTool,
|
||||||
|
contact: borkerConfig.contact
|
||||||
|
}" @save="onSave" />
|
||||||
<!-- 定时调度配置弹窗 -->
|
<!-- 定时调度配置弹窗 -->
|
||||||
<el-dialog v-model="showScheduleDlg" title="定时调度(每小时)" width="420px">
|
<el-dialog v-model="showScheduleDlg" title="定时调度(每小时)" width="420px">
|
||||||
<div style="display:grid;grid-template-columns: 100px 1fr; gap:12px; align-items:center;">
|
<div style="display:grid;grid-template-columns: 100px 1fr; gap:12px; align-items:center;">
|
||||||
@@ -106,7 +120,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, onBeforeUnmount, watch, inject, computed } from "vue";
|
import { ref, reactive, onMounted, onUnmounted, onBeforeUnmount, watch, inject, computed } from "vue";
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import {
|
import {
|
||||||
setphoneXYinfo, getphoneXYinfo, getUser,
|
setphoneXYinfo, getphoneXYinfo, getUser,
|
||||||
@@ -118,8 +132,11 @@ import { connectSSE } from '@/utils/sseUtils'
|
|||||||
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
|
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
|
||||||
import { chat, translationToChinese, translation } from "@/api/chat";
|
import { chat, translationToChinese, translation } from "@/api/chat";
|
||||||
import HostListManagerDialog from '@/components/HostListManagerDialog.vue'
|
import HostListManagerDialog from '@/components/HostListManagerDialog.vue'
|
||||||
|
import AgentGuildDialog from '@/components/AgentGuildDialog.vue'
|
||||||
|
|
||||||
import MultiLineInputDialog from '@/components/MultiLineInputDialog.vue'; // 根据实际路径修改
|
import MultiLineInputDialog from '@/components/MultiLineInputDialog.vue'; // 根据实际路径修改
|
||||||
import ChatDialog from '@/components/ChatDialog.vue'
|
import ChatDialog from '@/components/ChatDialog.vue'
|
||||||
|
import MessageDialogd from '@/components/MessageDialogd.vue'
|
||||||
import { pickTikTokBundleId } from '@/utils/arrUtils'
|
import { pickTikTokBundleId } from '@/utils/arrUtils'
|
||||||
import { logout } from '@/api/account';
|
import { logout } from '@/api/account';
|
||||||
import {
|
import {
|
||||||
@@ -137,19 +154,76 @@ import {
|
|||||||
launchApp,
|
launchApp,
|
||||||
getChatTextInfo,
|
getChatTextInfo,
|
||||||
setLoginInfo,
|
setLoginInfo,
|
||||||
|
aiConfig,
|
||||||
|
|
||||||
} from '@/api/ios';
|
} from '@/api/ios';
|
||||||
|
import { set } from "lodash";
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const openShowChat = ref([true])
|
const openShowChat = ref([true])
|
||||||
//主播库
|
//主播库
|
||||||
const showHostDlg = ref(false)
|
const showHostDlg = ref(false)
|
||||||
|
//ai人设
|
||||||
|
const showMyInfo = ref(false)
|
||||||
|
// 假设这是你已有的数据
|
||||||
|
const borkerConfig = reactive({
|
||||||
|
agentName: '',
|
||||||
|
guildName: '',
|
||||||
|
contactTool: '',
|
||||||
|
contact: ''
|
||||||
|
})
|
||||||
|
|
||||||
let hostList = []
|
let hostList = []
|
||||||
//查询列表轮询
|
//查询列表轮询
|
||||||
let getListtimer = null;
|
let getListtimer = null;
|
||||||
let userdata = getUser();
|
let userdata = getUser();
|
||||||
let chatList = ref([])
|
let chatList = ref([])
|
||||||
|
|
||||||
|
let MesNewList = ref([{
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
}, {
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
}, {
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
}, {
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
}, {
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
}, {
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
}, {
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
}, {
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
}, {
|
||||||
|
sender: 'Alice', // 或 name/user/from
|
||||||
|
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
||||||
|
text: '你好呀~', // 或 content
|
||||||
|
type: 'msg' // 可选;有些是 'time' 会被过滤
|
||||||
|
},])
|
||||||
let isImg = ref(true)
|
let isImg = ref(true)
|
||||||
// 引入刷新方法
|
// 引入刷新方法
|
||||||
const reload = inject("reload")
|
const reload = inject("reload")
|
||||||
@@ -172,6 +246,8 @@ let deviceInformation = ref([])
|
|||||||
// 你可以用这种方式声明按钮们
|
// 你可以用这种方式声明按钮们
|
||||||
//联动用作标记
|
//联动用作标记
|
||||||
let batchMode = ref('init'); // 'init' | 'follow'(仅作标记)
|
let batchMode = ref('init'); // 'init' | 'follow'(仅作标记)
|
||||||
|
//停止中
|
||||||
|
let stopLoading = null
|
||||||
|
|
||||||
// 当前是否被其它模式占用(四个互斥按钮专用)
|
// 当前是否被其它模式占用(四个互斥按钮专用)
|
||||||
const isLocked = (type) => !!runType.value && runType.value !== type
|
const isLocked = (type) => !!runType.value && runType.value !== type
|
||||||
@@ -642,20 +718,36 @@ function getMesList(deviceId) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopAll() {
|
async function stopAll() {
|
||||||
|
stopLoading = ElLoading.service({
|
||||||
|
lock: true,
|
||||||
|
text: '停止中',
|
||||||
|
background: 'rgba(0, 0, 0, 0.7)',
|
||||||
|
});
|
||||||
// if (!runType.value) return
|
// if (!runType.value) return
|
||||||
deviceInformation.value.forEach((item) => {
|
|
||||||
stopScript({ udid: item.deviceId }).then((res) => {
|
try {
|
||||||
console.log(`停止成功:${item.deviceId}`, res, printCurrentTime())
|
// 使用 Promise.all 并行处理所有设备的停止操作
|
||||||
ElMessage.success(`停止成功:${item.deviceId}`)
|
await Promise.all(deviceInformation.value.map(async (item) => {
|
||||||
}).catch((res) => {
|
try {
|
||||||
console.log(`停止失败`, printCurrentTime())
|
const res = await stopScript({ udid: item.deviceId })
|
||||||
ElMessage.error(`脚本已停止`)
|
console.log(`停止成功:${item.deviceId}`, res, printCurrentTime())
|
||||||
})
|
ElMessage.success(`停止成功:${item.deviceId}`)
|
||||||
})
|
stopLoading.close()
|
||||||
scheduleEnabled.value = false
|
} catch (error) {
|
||||||
runType.value = ''
|
console.log(`停止失败`, printCurrentTime())
|
||||||
batchMode.value = 'init';
|
ElMessage.error(`脚本已停止`)
|
||||||
|
stopLoading.close()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 所有操作完成后执行以下代码
|
||||||
|
scheduleEnabled.value = false
|
||||||
|
runType.value = ''
|
||||||
|
batchMode.value = 'init'
|
||||||
|
} catch (error) {
|
||||||
|
console.error('批量停止过程中发生错误:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//确认多行文本框内容
|
//确认多行文本框内容
|
||||||
@@ -682,6 +774,8 @@ function onDialogConfirm(result, type, index, isMon) {
|
|||||||
needReply: isMon
|
needReply: isMon
|
||||||
}
|
}
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
|
ElMessage({ type: 'success', message: '任务开启成功' });
|
||||||
|
|
||||||
hostList = []
|
hostList = []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -741,7 +835,6 @@ onMounted(() => {
|
|||||||
{ confirmButtonText: '开始', cancelButtonText: '取消', type: 'success' }
|
{ confirmButtonText: '开始', cancelButtonText: '取消', type: 'success' }
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
ElMessage({ type: 'success', message: '任务开启成功' });
|
|
||||||
// runType.value = 'follow';
|
// runType.value = 'follow';
|
||||||
batchMode.value = 'follow';
|
batchMode.value = 'follow';
|
||||||
|
|
||||||
@@ -749,8 +842,12 @@ onMounted(() => {
|
|||||||
// 不直接发;把这“一波”的主播先塞进 hostList,然后弹出“私信”输入框
|
// 不直接发;把这“一波”的主播先塞进 hostList,然后弹出“私信”输入框
|
||||||
scheduleFlush((items) => {
|
scheduleFlush((items) => {
|
||||||
hostList = (items || []).map(h => ({ id: h.text, country: h.country || '' }))
|
hostList = (items || []).map(h => ({ id: h.text, country: h.country || '' }))
|
||||||
showScheduleDlg.value = true
|
|
||||||
})
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
showScheduleDlg.value = true
|
||||||
|
}, 600)
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// 取消:清理状态,丢弃批次
|
// 取消:清理状态,丢弃批次
|
||||||
@@ -808,6 +905,8 @@ onUnmounted(() => {
|
|||||||
clearInterval(getListtimer)
|
clearInterval(getListtimer)
|
||||||
getListtimer = null
|
getListtimer = null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let isStartLac = false
|
||||||
const getDeviceListFun = () => {
|
const getDeviceListFun = () => {
|
||||||
getDeviceList().then((res) => {
|
getDeviceList().then((res) => {
|
||||||
console.log('返回', res.length)
|
console.log('返回', res.length)
|
||||||
@@ -823,7 +922,12 @@ const getDeviceListFun = () => {
|
|||||||
}
|
}
|
||||||
// deviceInformation.value = ['', '', '', '', '', '',]
|
// deviceInformation.value = ['', '', '', '', '', '',]
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
ElMessage.error(`IOSAI服务错误`)
|
if (isStartLac) {
|
||||||
|
ElMessage.error(`IOSAI服务错误`)
|
||||||
|
isStartLac = true
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -875,15 +979,30 @@ async function uploadLogFile() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function runTask(key) {
|
function runTask(key, deviceId) {
|
||||||
console.log('[schedule] 切换到任务:', key, printCurrentTime())
|
console.log('[schedule] 切换到任务:', key, printCurrentTime())
|
||||||
|
|
||||||
forceActivate(key, () => {
|
forceActivate(key, async () => {
|
||||||
if (key === 'follow') {
|
if (key === 'follow') {
|
||||||
|
|
||||||
|
|
||||||
if (scheduleEnabled.value) {
|
if (scheduleEnabled.value) {
|
||||||
stopAll()
|
if (!deviceId) {
|
||||||
|
await stopAll()
|
||||||
|
} else {
|
||||||
|
passAnchorData(
|
||||||
|
{
|
||||||
|
deviceList: [deviceId],
|
||||||
|
anchorList: [],
|
||||||
|
prologueList: getContentpriList(),
|
||||||
|
needReply: false
|
||||||
|
}
|
||||||
|
).then((res) => {
|
||||||
|
hostList = []
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
runType.value = 'follow'
|
runType.value = 'follow'
|
||||||
|
|
||||||
@@ -903,7 +1022,7 @@ function runTask(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scheduleEnabled.value = true
|
scheduleEnabled.value = true
|
||||||
if (hostList.length <= 0) {
|
if (batchMode.value == 'init') {
|
||||||
dialogTitle.value = '主播ID';
|
dialogTitle.value = '主播ID';
|
||||||
} else {
|
} else {
|
||||||
dialogTitle.value = '私信';
|
dialogTitle.value = '私信';
|
||||||
@@ -912,7 +1031,12 @@ function runTask(key) {
|
|||||||
|
|
||||||
} else if (key === 'like') {
|
} else if (key === 'like') {
|
||||||
|
|
||||||
stopAll()
|
if (!deviceId) {
|
||||||
|
await stopAll()
|
||||||
|
} else {
|
||||||
|
growAccount({ udid: deviceId })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scheduleEnabled.value = true
|
scheduleEnabled.value = true
|
||||||
@@ -921,7 +1045,12 @@ function runTask(key) {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
} else if (key === 'brushLive') {
|
} else if (key === 'brushLive') {
|
||||||
stopAll()
|
if (!deviceId) {
|
||||||
|
await stopAll()
|
||||||
|
} else {
|
||||||
|
watchLiveForGrowth({ udid: deviceId })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scheduleEnabled.value = true
|
scheduleEnabled.value = true
|
||||||
@@ -930,7 +1059,12 @@ function runTask(key) {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
runType.value = 'brushLive'
|
runType.value = 'brushLive'
|
||||||
} else if (key === 'listen') {
|
} else if (key === 'listen') {
|
||||||
stopAll()
|
if (!deviceId) {
|
||||||
|
await stopAll()
|
||||||
|
} else {
|
||||||
|
monitorMessages({ udid: deviceId })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
runType.value = 'listen'
|
runType.value = 'listen'
|
||||||
@@ -1014,6 +1148,14 @@ function printCurrentTime() {
|
|||||||
return now.toLocaleString()
|
return now.toLocaleString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function onSave(payload) {
|
||||||
|
console.log(payload)
|
||||||
|
aiConfig(payload).then((res) => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|||||||
Reference in New Issue
Block a user