Files
tkNewAdmin/src/views/server/manageemployeehosts/index.vue
2025-07-25 14:33:45 +08:00

541 lines
19 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" label-width="68px">
<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">
<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('employee.hostsCountry')" prop="country">
<el-select v-model="queryParams.country" :placeholder="$t('employee.placeHostsCountry')" clearable
class="!w-240px">
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.COUNTRY_GROUP)" :key="dict.value"
:label="dict.label" :value="dict.value" />
</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="$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>
<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="dialogAllocation = true">
<Icon icon="ep:refresh" class="mr-5px" /> {{ t('newHosts.allocation') }}
</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.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 v-infinite-scroll="loadList">
<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>uid</b>{{ item.uid }}</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.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>
<!-- PC 显示分页移动端隐藏 -->
<Pagination v-if="!isMobile" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>
<el-dialog v-model="dialogAllocation" :title="t('newHosts.allocationUser')">
<!-- <div style="padding: 0px 0px 30px 0px ;">
<el-alert title="分配成功数量可能会小于选择数量同id主播无法被重复分配" type="warning" />
</div> -->
<el-select v-model="allocationUser" :placeholder="t('newHosts.placeAllocationUser')" clearable>
<el-option v-for="(user, index) in allocationUserList" :key="index" :label="user.label" :value="user.value" />
</el-select>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogAllocation = false">{{ t('newHosts.cancel') }}</el-button>
<el-button type="primary" @click="AllocationFun">
{{ t('newHosts.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
<!-- 表单弹窗 -->
<EmployeeHostsForm ref="formRef" @success="formSuccess" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE, getStrDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { EmployeeHostsApi, EmployeeHostsVO } from '@/api/server/employeehosts'
import EmployeeHostsForm from './EmployeeHostsForm.vue'
import { getAllocation, getSimpleUserList } from '@/api/system/user'
import { useCache } from '@/hooks/web/useCache'
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: 10,
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,
hostsKind: undefined,
isAssigned: undefined,
createTime: [],
userId: undefined,
operationStatus: undefined,
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
let isShowSearch = ref(true) // 是否显示搜索
let dialogAllocation = ref(false) //分配弹窗
let allocationUserList = ref([
{
label: '分配用户1',
value: '1'
}
]) //选中的分配用户
let selectHostList = ref([]) //选中的主播列表
let allocationUser = ref() //选中的分配用户
/** 查询列表pc直接赋值 */
const getList = async () => {
loading.value = true
try {
const data = await EmployeeHostsApi.getMeangeEmployeeHostsPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 查询列表手机叠加赋值 */
const getListPhone = async () => {
loading.value = true
try {
const data = await EmployeeHostsApi.getMeangeEmployeeHostsPage(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 getSimpleUserList()
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++
getListPhone()
}
}
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 AllocationFun() {
console.log(allocationUser.value)
console.log(selectHostList.value)
if (allocationUser.value == undefined || selectHostList.value.length <= 0) {
message.error('请选择分配用户和主播')
return
}
let data = []
selectHostList.value.forEach(element => {
data.push({
id: element.id,
userId: allocationUser.value,
operationStatus: 0
})
})
EmployeeHostsApi.updateBatchEmployeeHosts(data).then(res => {
console.log(res)
handleQuery()
message.success('分配成功')
})
dialogAllocation.value = false
}
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
})
})
// /** 初始化 **/
// 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>