Files
tkNewAdmin/src/views/server/employeehosts/index.vue
2025-08-21 14:36:09 +08:00

604 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<transition name="fade">
<div v-show="isShowSearch">
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
<el-form-item :label="$t('employee.hostsId')" prop="hostsId">
<el-input v-model="queryParams.hostsId" :placeholder="$t('employee.placeHostId')" clearable
@keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item :label="$t('employee.hostsLevel')" prop="hostsLevel">
<el-select v-model="queryParams.hostsLevel" :placeholder="$t('employee.placeHostLevel')" clearable
class="!w-240px" multiple>
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.HOST_LEVEL)" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('employee.hostsCoins')" prop="hostsCoins">
<el-input v-model="queryParams.hostsCoinsMin" :placeholder="$t('employee.min')" clearable
@keyup.enter="handleQuery" class="!w-115px" />
<span>&nbsp;-&nbsp;</span>
<el-input v-model="queryParams.hostsCoinsMax" :placeholder="$t('employee.max')" clearable
@keyup.enter="handleQuery" class="!w-115px" />
</el-form-item>
<el-form-item :label="$t('employee.onlineFans')" prop="onlineFans">
<el-input v-model="queryParams.onlineFansMin" :placeholder="$t('employee.min')" clearable
@keyup.enter="handleQuery" class="!w-115px" />
<span>&nbsp;-&nbsp;</span>
<el-input v-model="queryParams.onlineFansMax" :placeholder="$t('employee.max')" clearable
@keyup.enter="handleQuery" class="!w-115px" />
</el-form-item>
<el-form-item :label="$t('employee.fans')" prop="fans">
<el-input v-model="queryParams.fansMin" :placeholder="$t('employee.min')" clearable
@keyup.enter="handleQuery" class="!w-115px" />
<span>&nbsp;-&nbsp;</span>
<el-input v-model="queryParams.fansMax" :placeholder="$t('employee.max')" clearable
@keyup.enter="handleQuery" class="!w-115px" />
</el-form-item>
<el-form-item :label="$t('employee.fllowernum')" prop="fllowernum">
<el-input v-model="queryParams.fllowernumMin" :placeholder="$t('employee.min')" clearable
@keyup.enter="handleQuery" class="!w-115px" />
<span>&nbsp;-&nbsp;</span>
<el-input v-model="queryParams.fllowernumMax" :placeholder="$t('employee.max')" clearable
@keyup.enter="handleQuery" class="!w-115px" />
</el-form-item>
<el-form-item :label="t('newHosts.hostsCountry')" prop="region">
<el-select v-model="queryParams.region" :placeholder="t('newHosts.placeHostsCountry')" clearable
class="!w-240px" @change="changeCountry(queryParams.region)">
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.COUNTRY_GROUP)" :key="dict.value"
:label="$t(dict.label)" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('newHosts.hostsCountryinfo')" prop="country">
<el-select v-model="queryParams.country" :placeholder="t('newHosts.placeHostsCountry')" clearable
class="!w-240px">
<el-option v-for="dict in countryinfoList" :key="dict.id" :label="dict.countryName"
:value="dict.countryName" />
</el-select>
</el-form-item>
<el-form-item :label="$t('employee.hostsKind')" prop="hostsKind">
<el-input v-model="queryParams.hostsKind" :placeholder="$t('employee.placeHostsKind')" clearable
@keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item :label="$t('employee.createTime')" prop="createTime">
<el-date-picker v-model="queryParams.createTime" value-format="YYYY-MM-DD HH:mm:ss" type="date"
class="!w-240px" />
</el-form-item>
<el-form-item label="旗帜" prop="flag">
<el-select v-model="queryParams.flag" placeholder="请选择状态" clearable class="!w-240px" style="width: 50px">
<el-option label="请选择" :value="''" />
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.FLAG_TYPE)" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
<!-- <el-form-item :label="$t('employee.userId')" prop="userId">
<el-select v-model="queryParams.userId" :placeholder="$t('employee.placeAllocationUser')" clearable
class="!w-240px">
<el-option v-for="(user, index) in allocationUserList" :key="index" :label="user.label"
:value="user.value" />
</el-select>
</el-form-item> -->
<el-form-item :label="$t('employee.invitationType')" prop="invitationType">
<el-select v-model="queryParams.invitationType" :placeholder="$t('employee.placeInvitationType')" clearable
class="!w-240px">
<el-option v-for="dict in getIntDictOptions(DICT_TYPE.HOSTS_INVITATION_TYPE)" :key="dict.value"
:label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('employee.operationStatus')" prop="operationStatus">
<el-select v-model="queryParams.operationStatus" :placeholder="$t('employee.placeOperationStatus')"
clearable class="!w-240px">
<el-option v-for="dict in getIntDictOptions(DICT_TYPE.OPERATION_STATE)" :key="dict.value"
:label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('newHosts.sortName')" prop="sortName">
<el-select v-model="queryParams.sortName" :placeholder="t('newHosts.sortName')" clearable class="!w-240px">
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.SORT_TYPE)" :key="dict.value" :label="t(dict.label)"
:value="dict.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('newHosts.sortType')" prop="sortType">
<el-select v-model="queryParams.sort" :placeholder="t('newHosts.sortType')" clearable class="!w-240px">
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.SORT_STATE)" :key="dict.value"
:label="t(dict.label)" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> {{ $t('employee.search') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> {{ $t('employee.reset') }}
</el-button>
<el-button @click="exportAi">
<Icon icon="ep:copy-document" class="mr-5px" /> {{ $t('employee.exportAi') }}
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
</div>
</transition>
<div class="center-justify" style="margin-top: -15px;">
<el-button type="primary" @click="isShowSearch = !isShowSearch">{{ isShowSearch ? $t('employee.showSearch') :
$t('employee.hideSearch') }}</el-button>
</div>
<!-- 列表区域 -->
<ContentWrap>
<!-- PC 端使用 table -->
<el-table v-if="!isMobile" v-loading="loading" :data="list" :stripe="true"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column :label="$t('employee.hostsId')" align="center" prop="hostsId">
<template #default="scope">
<div style="color: green; text-decoration: underline;" @click="openHtml(scope.row, scope.row.hostsId)">
{{ scope.row.hostsId }}</div>
</template>
</el-table-column>
<el-table-column align="center" prop="hostsId" width="75">
<template #default="scope">
<el-link type="primary" @click="handleCopy(scope.row.hostsId)">复制</el-link>
</template>
</el-table-column>
<el-table-column :label="$t('employee.hostsLevel')" align="center" prop="hostsLevel" />
<el-table-column :label="$t('employee.invitationType')" align="center" prop="invitationType">
<template #default="scope">
<dict-tag :type="DICT_TYPE.HOSTS_INVITATION_TYPE" :value="scope.row.invitationType" />
</template>
</el-table-column>
<el-table-column label="uid" align="center" prop="uid" />
<el-table-column :label="$t('employee.hostsCoins')" align="center" prop="hostsCoins" />
<!-- <el-table-column :label="$t('employee.invitationType')" align="center" prop="invitationType" /> -->
<el-table-column :label="$t('employee.onlineFans')" align="center" prop="onlineFans" />
<el-table-column :label="$t('employee.fans')" align="center" prop="fans" />
<el-table-column :label="$t('employee.fllowernum')" align="center" prop="fllowernum" />
<el-table-column :label="$t('employee.yesterdayCoins')" align="center" prop="yesterdayCoins" />
<el-table-column :label="$t('employee.hostsCountry')" align="center" prop="country" />
<el-table-column :label="$t('employee.hostsKind')" align="center" prop="hostsKind" />
<el-table-column :label="$t('employee.remark')" align="center" prop="remake" />
<el-table-column :label="$t('employee.flag')" align="center" prop="flag">
<template #default="scope">
<dict-tag :type="DICT_TYPE.FLAG_TYPE" :value="scope.row.flag" />
</template>
</el-table-column>
<el-table-column :label="$t('employee.operationStatus')" align="center" prop="operationStatus">
<template #default="scope">
<dict-tag :type="DICT_TYPE.OPERATION_STATE" :value="scope.row.operationStatus" />
</template>
</el-table-column>
<el-table-column :label="$t('employee.createTime')" align="center" prop="createTime" :formatter="dateFormatter"
width="180px" />
<el-table-column :label="$t('employee.updateTime')" align="center" prop="updateTime" :formatter="dateFormatter"
width="180px" />
<el-table-column :label="$t('employee.action')" align="center" min-width="120px">
<template #default="scope">
<el-button link type="primary" @click="openForm('update', scope.row.id)">{{ $t('employee.edit') }}</el-button>
</template>
</el-table-column>
</el-table>
<!-- 移动端使用卡片列表 -->
<div v-else>
<div v-for="(item, index) in list" :key="index" class="mobile-card">
<div class="card-row" style="color:green;">
<b>{{ $t('employee.hostsId') }}</b><span @click="openHtml(item, item.hostsId)"
style=" text-decoration: underline;margin-right: 50px;">{{ item.hostsId }}</span>
<el-link @click="handleCopy(item.hostsId)" type="primary">复制id</el-link>
</div>
<!-- <div class="card-row"><b>{{ $t('employee.userId') }}</b>{{ item.userId }}</div> -->
<div class="card-row"><b>{{ $t('employee.hostsLevel') }}</b>{{ item.hostsLevel }}</div>
<div class="card-row"><b>{{ $t('employee.hostsCoins') }}</b>{{ item.hostsCoins }}</div>
<div class="card-row"><b>{{ $t('employee.fans') }}</b>{{ item.fans }}</div>
<div class="card-row"><b>{{ $t('employee.fllowernum') }}</b>{{ item.fllowernum }}</div>
<div class="card-row"><b>{{ $t('employee.hostsCountry') }}</b>{{ item.country }}</div>
<div class="card-row"><b>{{ $t('employee.hostsKind') }}</b>{{ item.hostsKind }}</div>
<div class="card-row"><b>{{ $t('employee.remark') }}</b>{{ item.remake }}</div>
<div class="card-row"><b>{{ $t('employee.flag') }}</b><dict-tag :type="DICT_TYPE.FLAG_TYPE"
:value="item.flag" />
</div>
<div class="card-row"><b>{{ $t('employee.operationStatus') }}</b>
<dict-tag :type="DICT_TYPE.OPERATION_STATE" :value="item.operationStatus" />
</div>
<div class="card-row"><b>{{ $t('employee.createTime') }}</b>{{ formatTimestamp(item.createTime) }}</div>
<div class="card-row"><b>{{ $t('employee.updateTime') }}</b>{{ formatTimestamp(item.updateTime) }}</div>
<div class="card-row action-row">
<el-button link type="primary" @click="openForm('update', item.id, index)">{{ $t('employee.edit')
}}</el-button>
</div>
</div>
</div>
<MobilePagination v-if="isMobile" :page="queryParams.pageNo" :limit="queryParams.pageSize" :total="total"
@update:page="val => queryParams.pageNo = val" @load="loadList" @loadPre="loadpreviousList" />
<!-- PC 显示分页移动端隐藏 -->
<Pagination v-if="!isMobile" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>
<!-- 表单弹窗 -->
<EmployeeHostsForm ref="formRef" @success="formSuccess" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE, getStrDictOptions } from '@/utils/dict'
import { ElMessage } from 'element-plus'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { EmployeeHostsApi, EmployeeHostsVO } from '@/api/server/employeehosts'
import EmployeeHostsForm from './EmployeeHostsForm.vue'
import { getAllocation, getCountry } from '@/api/system/user'
import { useCache } from '@/hooks/web/useCache'
import { func } from 'vue-types'
import MobilePagination from '@/components/MobilePagination.vue'
const { wsCache } = useCache()
/** 员工分配主播 列表 */
defineOptions({ name: 'EmployeeHosts' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<EmployeeHostsVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 20,
hostsId: undefined,
hostsLevel: undefined,
onlineFansMin: undefined,
onlineFansMax: undefined,
fansMin: undefined,
fansMax: undefined,
hostsCoinsMin: undefined,
hostsCoinsMax: undefined,
fllowernumMin: undefined,
fllowernumMax: undefined,
invitationType: undefined,
yesterdayCoins: undefined,
country: undefined,
region: undefined,
flag: undefined,
hostsKind: undefined,
isAssigned: undefined,
sortName: "createTime", //排序字段
sort: 'desc', //排序方式
createTime: [],
userId: undefined,
operationStatus: undefined,
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
let isShowSearch = ref(true) // 是否显示搜索
let allocationUserList = ref([
{
label: '分配用户1',
value: '1'
}
]) //选中的分配用户
let selectHostList = ref([]) //选中的主播列表
let allocationUser = ref() //选中的分配用户
let countryinfoList = ref([])
/** 查询列表pc直接赋值 */
const getList = async () => {
loading.value = true
try {
const data = await EmployeeHostsApi.getEmployeeHostsPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 查询列表手机叠加赋值 */
const getListPhone = async () => {
loading.value = true
try {
const data = await EmployeeHostsApi.getEmployeeHostsPage(queryParams)
data.list.forEach(element => {
list.value.push(element)
});
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryParams.fansMin = undefined
queryParams.fansMax = undefined
queryParams.hostsCoinsMin = undefined
queryParams.hostsCoinsMax = undefined
queryParams.fllowernumMin = undefined
queryParams.fllowernumMax = undefined
queryParams.onlineFansMin = undefined
queryParams.onlineFansMax = undefined
queryParams.yesterdayCoins = undefined
queryParams.isAssigned = undefined
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number, index?: number) => {
formRef.value.open(type, id, index)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await EmployeeHostsApi.deleteEmployeeHosts(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch { }
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await EmployeeHostsApi.exportEmployeeHosts(queryParams)
download.excel(data, '员工分配主播.xls')
} catch {
} finally {
exportLoading.value = false
}
}
// /** 查询员工 */
// const getAllocationList = async () => {
// loading.value = true
// allocationUserList.value = []
// try {
// const data = await getAllocation(wsCache.get('user').user.deptId)
// console.log('data', data)
// data.forEach((item) => {
// if (wsCache.get('user').user.id == item.id) {
// } else {
// allocationUserList.value.push({
// label: item.nickname,
// value: item.id
// })
// }
// })
// console.log(allocationUserList.value)
// } finally {
// loading.value = false
// }
// }
function openHtml(item, id) {
let data = item
data.operationStatus = 1
EmployeeHostsApi.updateEmployeeHosts({ id: data.id, userId: data.userId, hostsId: data.hostsId, operationStatus: 1 }).then(res => {
getList()
})
window.open(`https://www.tiktok.com/@${id}`)
}
const loadList = () => {
if (queryParams.pageNo < Math.ceil(total.value / queryParams.pageSize)) {
queryParams.pageNo++
getList()
}
}
const loadpreviousList = () => {
if (queryParams.pageNo > 1) {
queryParams.pageNo--
getList()
}
}
function formSuccess(data, index) {
if (isMobile.value) {
console.log(data, index)
list.value[index] = data
} else {
getList()
}
}
function formatTimestamp(milliseconds) {
const date = new Date(milliseconds); // 直接使用毫秒级时间戳
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
//分配按钮操作
const handleSelectionChange = (val) => {
selectHostList.value = val
console.log(selectHostList.value)
}
function exportAi() {
if (selectHostList.value.length === 0) {
ElMessage.error('请选择主播')
return
}
const data = selectHostList.value.map(item => item.hostsId).join('\n')
const copyToClipboard = async (text) => {
// 优先使用 Clipboard API
if (navigator.clipboard && window.isSecureContext) {
try {
await navigator.clipboard.writeText(text)
ElMessage.success('✅ 复制成功 ' + selectHostList.value.length + ' 条数据')
} catch (err) {
ElMessage.error('❌ 复制失败')
console.error(err)
}
} else {
// fallback 兼容方案
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.position = 'fixed' // 避免跳动
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()
try {
const result = document.execCommand('copy')
if (result) {
ElMessage.success('✅(兼容模式)复制成功 ' + selectHostList.value.length + ' 条数据')
} else {
ElMessage.error('❌(兼容模式)复制失败')
}
} catch (err) {
ElMessage.error('❌(兼容模式)复制异常')
console.error(err)
} finally {
document.body.removeChild(textarea)
}
}
}
copyToClipboard(data)
}
function AllocationFun() {
console.log(allocationUser.value)
console.log(selectHostList.value)
}
function handleCopy(text) {
// fallback 兼容方案
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.position = 'fixed' // 避免跳动
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()
try {
const result = document.execCommand('copy')
if (result) {
ElMessage.success('✅复制成功 ')
} else {
ElMessage.error('❌复制失败')
}
} catch (err) {
ElMessage.error('❌复制异常')
console.error(err)
} finally {
document.body.removeChild(textarea)
}
}
const isMobile = ref(window.innerWidth <= 768)
onMounted(() => {
getList()
// getAllocationList()
//实时监听实际还是电脑
window.addEventListener('resize', () => {
if ((window.innerWidth <= 768) != isMobile.value) {
getList()
}
isMobile.value = window.innerWidth <= 768
})
})
function changeCountry(region) {
console.log(region)
getCountry(region).then((res) => {
countryinfoList.value = res
})
}
// /** 初始化 **/
// onMounted(() => {
// getList()
// })
</script>
<style scoped>
.center-align {
display: flex;
justify-content: space-between;
}
.center-justify {
display: flex;
justify-content: space-around;
align-items: center;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
@media screen and (max-width: 768px) {
.mobile-card {
background: #fff;
margin: 12px;
padding: 12px;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
font-size: 14px;
}
.card-row {
margin-bottom: 6px;
word-break: break-word;
}
.card-row b {
color: #555;
font-weight: 500;
margin-right: 4px;
}
.action-row {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 6px;
}
}
</style>