Files
aida_front/src/component/Administrator/SE/allUser/index.vue

778 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>
<div class="admin_page all-user">
<div class="admin_table_search">
<div class="admin_state">
<div class="admin_state_item">
<span>{{ $t('admin.CreateTime') }}:</span>
<a-range-picker
style="width: 230px"
class="range_picker"
v-model:value="rangePickerValue"
:placeholder="[$t('HistoryPage.StartDate'), $t('HistoryPage.EndDate')]"
valueFormat="YYYY-MM-DD"
>
<template #suffixIcon>
<span class="icon iconfont range_picker_icon icon-rili"></span>
</template>
</a-range-picker>
</div>
<div class="admin_state_item">
<span>{{ $t('admin.UserName') }}:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 230px"
:field-names="{ label: 'label', value: 'label' }"
:filter-option="filterOption"
:placeholder="$t('admin.selectUserName')"
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
</div>
</div>
<div class="admin_search">
<div class="admin_search_item" @click="searchHistoryList">
{{ $t('admin.search') }}
</div>
<div class="admin_search_item" @click="addhHistoryList">
{{ $t('admin.add') }}
</div>
<div
class="admin_search_item"
style="width: auto; padding: 0 2rem"
@click="downloadTemplate"
>
{{ $t('admin.DownloadTemplate') }}
</div>
<div
class="admin_search_item"
style="width: auto; padding: 0 2rem"
@click="uploadTemplate"
>
{{ $t('admin.UploadTemplate') }}
</div>
<div
class="admin_search_item"
style="width: auto; padding: 0 2rem"
@click="ExportAccountData"
>
{{ $t('admin.ExportAccountData') }}
</div>
<div
class="admin_search_item"
style="width: auto; padding: 0 2rem"
@click="deleteList"
>
{{ $t('admin.Delete') }}
</div>
</div>
<div class="admin_state_list">
<div class="admin_state_list_item" @click="lastGeTrialList('year')">
{{ $t('admin.NearlyAYear') }}
</div>
<div class="admin_state_list_item" @click="lastGeTrialList('month')">
{{ $t('admin.LastMonth') }}
</div>
<div class="admin_state_list_item" @click="lastGeTrialList('week')">
{{ $t('admin.LastWeek') }}
</div>
</div>
</div>
<div class="admin_table_content" ref="historyTable">
<div class="admin_state_list plan_list">
<div
v-for="plan in planFilterOptions"
:key="plan.id"
class="plan_item"
:class="{
active: subscriptionPlanId === plan.id,
disabled: plan.status === 'PENDING'
}"
@click="plan.status !== 'PENDING' && selectPlanFilter(plan.id)"
>
<span class="plan_name">{{ plan.name }}</span>
<MoreOutlined
class="plan_more_icon"
@click.stop="plan.status !== 'PENDING' && openPlanRenameModal(plan)"
/>
</div>
</div>
<a-table
@resizeColumn="handleResizeColumn"
:loading="tableLoading"
:columns="columns"
:data-source="dataList"
:row-selection="rowSelection"
rowKey="id"
:scroll="{ y: historyTableHeight }"
@change="changePage"
:showSorterTooltip="false"
:pagination="{
showSizeChanger: true,
current: currentPage,
pageSize: pageSize,
total: total,
showQuickJumper: true,
bordered: false
}"
>
<template #bodyCell="{ column, text, record, index }">
<div class="operate_list" v-if="column?.Operations">
<div
class="operate_item"
@click="setAagree(record)"
style="margin-right: 2rem"
>
{{ $t('admin.Edit') }}
</div>
<div class="operate_item" @click="deleteAagree(record)">
{{ $t('admin.Delete') }}
</div>
</div>
</template>
</a-table>
</div>
<allUserPoerationsVue
ref="allUserPoerationsVue"
@searchHistoryList="searchHistoryList"
:plan-options="planFilterOptions"
></allUserPoerationsVue>
<div class="renamePlanModal" ref="renamePlanModal"></div>
<!-- 重命名订阅计划弹窗 -->
<a-modal
v-model:visible="renamePlanModalVisible"
:title="$t('admin.RenamePlan')"
@ok="confirmRenamePlan"
@cancel="cancelRenamePlan"
:ok-text="$t('admin.OK')"
:cancel-text="$t('admin.Cancel')"
:get-container="() => $refs.renamePlanModal"
>
<div class="rename-plan-form">
<div class="admin_state_item">
<span>{{ $t('admin.PlanName') }}:</span>
<a-input
v-model:value="renamePlanForm.planName"
:placeholder="$t('admin.InputPlanName')"
style="width: 250px"
@pressEnter="confirmRenamePlan"
/>
</div>
</div>
</a-modal>
</div>
</template>
<script lang="ts">
import {
defineComponent,
ref,
createVNode,
computed,
reactive,
toRefs,
unref,
watch
} from 'vue'
import { formatTime } from '@/tool/util'
import { useStore } from 'vuex'
import { Https } from '@/tool/https'
import { Modal, message, Input } from 'ant-design-vue'
import { ExclamationCircleOutlined, MoreOutlined } from '@ant-design/icons-vue'
import allUserPoerationsVue from './addAllUser.vue'
import { useI18n } from 'vue-i18n'
export default defineComponent({
components: { allUserPoerationsVue, MoreOutlined },
setup() {
const store: any = useStore()
const currentOrganizationId = computed(
() => store.state.UserHabit.userDetail.organizationId
)
const selectedRowKeys = ref([]) as any
const onSelectChange = (changableRowKeys: string[]) => {
selectedRowKeys.value = changableRowKeys
}
let filter: any = reactive({
dataList: [],
tableLoading: false,
allUserList: computed(() => {
return store.state.adminPage.allUserList
}),
allCountry: [],
rowSelection: computed(() => {
return {
selectedRowKeys: unref(selectedRowKeys),
onChange: onSelectChange
}
})
})
const { t } = useI18n()
let filterData: any = reactive({
rangePickerValue: [],
currentPage: 1,
pageSize: 10,
total: 0,
country: '',
email: '',
userType: '',
ids: [],
occupation: '',
systemUser: '',
order: '', //'Ascending 升序 Descending 降序'
orderBy: '',
userName: '',
subscriptionPlanId: ''
})
let renameData: any = ref({}) //修改名字选中的数据
const renamePlanModalVisible = ref(false)
const renamePlanForm = reactive({
planId: null as number | null,
planName: ''
})
const columns: any = computed(() => {
return [
{
title: t('admin.UserId'),
align: 'center',
dataIndex: 'id',
key: 'id',
width: 100,
fixed: 'left',
sorter: true
},
{
title: t('admin.Email'),
align: 'center',
dataIndex: 'userEmail',
key: 'userEmail',
width: 200,
ellipsis: true
},
{
title: t('admin.UserName'),
align: 'center',
dataIndex: 'userName',
key: 'userName',
width: 150,
ellipsis: true
},
{
title: t('admin.language'),
align: 'center',
dataIndex: 'language',
key: 'language',
width: 100,
ellipsis: true,
customRender: (record: any) => {
return t(`admin.${record.text}`)
}
},
{
title: t('admin.CreateDate'),
align: 'center',
dataIndex: 'createDate',
key: 'createDate',
width: 200,
sorter: true
},
{
title: t('admin.Credits'),
align: 'center',
dataIndex: 'credits',
key: 'credits',
width: 100,
sorter: true
},
{
title: t('admin.CreditsUsage'),
align: 'center',
dataIndex: 'creditsUsage',
key: 'creditsUsage',
width: 150,
sorter: true
},
{
title: t('admin.CreditsUsageLimit'),
align: 'center',
dataIndex: 'creditsUsageLimit',
key: 'creditsUsageLimit',
width: 200,
sorter: true
},
{
title: t('admin.Operations'),
key: 'operation',
width: 120,
align: 'center',
fixed: 'right',
Operations: true
}
]
})
//改变页码
let changePage = (e: any, filters: any, sorter: any) => {
filterData.currentPage = e.current
filterData.pageSize = e.pageSize
if (sorter.order) {
if (sorter.columnKey == 'id') {
filterData.orderBy = 'id'
} else if (sorter.columnKey == 'createDate') {
filterData.orderBy = 'time'
} else if (sorter.columnKey == 'credits') {
filterData.orderBy = 'credits'
}
}
if (sorter.order) {
filterData.order = sorter.order == 'descend' ? 'Descending' : 'Ascending'
} else {
filterData.order = ''
}
gettrialList()
}
//查询列表
let searchHistoryList = () => {
filterData.currentPage = 1
gettrialList()
}
let clearHistoryList = () => {
;(filterData.rangePickerValue = []),
(filterData.currentPage = 1),
(filterData.pageSize = 10),
(filterData.total = 0),
(filterData.country = ''),
(filterData.email = ''),
(filterData.userType = ''),
(filterData.ids = []),
(filterData.occupation = ''),
(filterData.order = ''), //'Ascending 升序 Descending 降序'
(filterData.orderBy = ''), //'Ascending 升序 Descending 降序'
(filterData.systemUser = ''),
(filterData.userName = ''),
(filterData.subscriptionPlanId = '')
}
let setHistoryListData = () => {
let startDate: any = filterData.rangePickerValue?.[0]
? filterData.rangePickerValue[0] + ' ' + '00:00:00'
: ''
let endDate: any = filterData.rangePickerValue?.[1]
? filterData.rangePickerValue[1] + ' ' + '23:59:59'
: ''
let data = {
endTime: endDate,
startTime: startDate,
size: filterData.pageSize,
page: filterData.currentPage,
systemUser: filterData.systemUser,
country: filterData.country,
// email: filterData.email.trim(),
userType: filterData.userType,
ids: [],
occupation: filterData.occupation,
order: filterData.order,
orderBy: filterData.orderBy,
// userName: filterData.userName,
userName: filterData.ids,
subscriptionPlanId: filterData.subscriptionPlanId
}
return data
}
//获取列表
const gettrialList = () => {
filter.tableLoading = true
let data = setHistoryListData()
Https.axiosPost(Https.httpUrls.subAccountList, data).then((rv: any) => {
if (rv) {
filter.dataList = rv.content
filterData.total = rv.total
filter.tableLoading = false
// this.workspaceItem.position = this.singleTypeList[0].label
}
})
}
let lastGeTrialList = (str: string) => {
clearHistoryList()
let currentDate = new Date()
let currentTimestamp = Math.floor(currentDate.getTime() / 1000)
// 计算30天前的时间戳
let thirtyDaysAgoTimestamp
if (str == 'year') {
thirtyDaysAgoTimestamp = currentTimestamp - 360 * 24 * 60 * 60
} else if (str == 'month') {
thirtyDaysAgoTimestamp = currentTimestamp - 30 * 24 * 60 * 60
} else if (str == 'week') {
thirtyDaysAgoTimestamp = currentTimestamp - 7 * 24 * 60 * 60
}
filterData.rangePickerValue[0] = formatTime(thirtyDaysAgoTimestamp, 'YYYY-MM-DD')
gettrialList()
}
let filterOption = (input: any, option: any) => {
// 使用 option.label 进行搜索
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
// 订阅计划筛选(按钮点击)
const selectPlanFilter = async (planId: string) => {
filterData.subscriptionPlanId = planId
// 切换管理员订阅计划
Https.axiosGet(Https.httpUrls.switchSubscribePlan, {
params: {
targetSubscriptionPlanId: planId,
adminAccId: store.state.UserHabit.userDetail.id
}
}).then((res: any) => {
console.log(res)
})
searchHistoryList()
}
const planFilterOptions = ref([])
const fetchSubscribePlanList = () => {
const orgId = currentOrganizationId.value
if (!orgId) return
Https.axiosPost(Https.httpUrls.searchSubscribeByOrg, {
organizationId: orgId,
status: ['ACTIVE', 'PENDING']
}).then(res => {
// 将与当前用户 subscriptionPlanId 相同的订阅计划放到第一个
const userSubscriptionPlanId = store.state.UserHabit.userDetail.subscriptionPlanId
if (userSubscriptionPlanId && Array.isArray(res)) {
const sortedList = [...res].sort((a: any, b: any) => {
const isAUserPlan = a.id == userSubscriptionPlanId
const isBUserPlan = b.id == userSubscriptionPlanId
if (isAUserPlan && !isBUserPlan) return -1
if (!isAUserPlan && isBUserPlan) return 1
return 0
})
planFilterOptions.value = sortedList
} else {
planFilterOptions.value = res
}
})
}
// 监听组织ID获取到值后再拉取订阅计划
watch(
() => currentOrganizationId.value,
orgId => {
if (orgId) {
fetchSubscribePlanList()
const userSubscriptionPlanId =
store.state.UserHabit.userDetail.subscriptionPlanId
if (userSubscriptionPlanId) {
selectPlanFilter(userSubscriptionPlanId)
}
}
},
{ immediate: true }
)
// 打开重命名弹窗(基于当前点击的计划)
const openPlanRenameModal = plan => {
renamePlanForm.planId = plan.id
renamePlanForm.planName = plan?.name || ''
renamePlanModalVisible.value = true
console.log(renamePlanForm)
}
let addhHistoryList = () => {
allUserPoerationsVue.value.init({ value: 'Add', label: t('admin.add') }, '')
}
let allUserPoerationsVue = ref()
let setAagree = (data: any) => {
allUserPoerationsVue.value.init({ value: 'Edit', label: t('admin.Edit') }, data)
}
const downloadTemplate = () => {
Https.axiosGet(Https.httpUrls.subAccountImportExcelDownload, {
responseType: 'blob',
env: { binary: true }
}).then((rv: any) => {
const link = document.createElement('a')
link.href = rv.url
link.download = 'file.xlsx' // 设置正确的文件扩展名
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// 释放 URL 对象
URL.revokeObjectURL(link.href)
})
}
const ExportAccountData = () => {
Https.axiosGet(Https.httpUrls.exportAccountsToExcel, {
headers: { responseType: 'blob' },
env: {
binary: true,
binaryType:
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8'
}
}).then((rv: any) => {
const link = document.createElement('a')
link.href = rv.url
link.download = 'file.xlsx' // 设置正确的文件扩展名
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// 释放 URL 对象
URL.revokeObjectURL(link.href)
})
}
const uploadTemplate = () => {
const fileInput = document.createElement('input')
fileInput.type = 'file'
fileInput.accept = '.xlsx' // 只接受 .xlsx 文件
fileInput.onchange = (event: any) => {
const file = event.target.files[0] // 获取选择的文件
if (file) {
let param = new FormData()
param.append('file', file)
let config: any = {
headers: { 'Content-Type': 'multipart/form-data', 'Accept': '*/*' }
}
Https.axiosPost(Https.httpUrls.subAccountImport, param, config).then(
(rv: any) => {
gettrialList()
}
)
}
}
fileInput.click()
}
const confirmDelete = () => {
return new Promise<void>((resolve, reject) => {
Modal.confirm({
title: t('admin.jsDelete'),
icon: createVNode(ExclamationCircleOutlined),
okText: 'Yes',
cancelText: 'No',
centered: true,
onOk() {
resolve(true)
},
onCancel() {
resolve(false)
}
})
})
}
const deleteList = async () => {
console.log(selectedRowKeys.value)
if (selectedRowKeys.value.length == 0) return
let boolean: any = await confirmDelete()
if (!boolean) {
return
}
Https.axiosPost(Https.httpUrls.deleteSubAccount, {
deleteIdList: selectedRowKeys.value
}).then((rv: any) => {
gettrialList()
})
}
const deleteAagree = async (event: any) => {
let boolean: any = await confirmDelete()
if (!boolean) {
return
}
const value = {
deleteIdList: [event.id]
}
Https.axiosPost(Https.httpUrls.deleteSubAccount, value).then((rv: any) => {
gettrialList()
})
}
// 确认重命名
const confirmRenamePlan = () => {
if (!renamePlanForm.planName || !renamePlanForm.planName.trim()) {
message.warning(t('admin.PlanNameRequired'))
return
}
Https.axiosPost(Https.httpUrls.updateSubscribePlan, {
id: renamePlanForm.planId,
name: renamePlanForm.planName.trim()
})
.then((rv: any) => {
message.success(t('admin.RenamePlanSuccess'))
renamePlanModalVisible.value = false
fetchSubscribePlanList()
})
.catch((error: any) => {
message.error(error.message || t('admin.RenamePlanFailed'))
})
}
// 取消重命名
const cancelRenamePlan = () => {
renamePlanModalVisible.value = false
renamePlanForm.planId = null
renamePlanForm.planName = ''
}
onMounted(() => {
let allCountry: any = sessionStorage.getItem('allCountry')
if (allCountry) {
filter.allCountry = JSON.parse(allCountry)
}
gettrialList()
})
return {
...toRefs(filter),
...toRefs(filterData),
columns,
renameData,
changePage,
searchHistoryList,
addhHistoryList,
lastGeTrialList,
gettrialList,
filterOption,
allUserPoerationsVue,
setAagree,
downloadTemplate,
ExportAccountData,
uploadTemplate,
deleteList,
deleteAagree,
planFilterOptions,
selectPlanFilter,
openPlanRenameModal,
renamePlanModalVisible,
renamePlanForm,
confirmRenamePlan,
cancelRenamePlan,
fetchSubscribePlanList
}
},
data() {
return {
historyTableHeight: 0,
handleResizeColumn: (w: any, col: any) => {
col.width = w
}
}
},
mounted() {
this.updateTableHeight()
window.addEventListener('resize', this.updateTableHeight)
},
beforeUnmount() {
window.removeEventListener('resize', this.updateTableHeight)
},
methods: {
updateTableHeight() {
const historyTable: any = this.$refs.historyTable
if (historyTable) {
// 为底部分页器预留固定空间,使表格部分高度固定且分页器始终可见
this.historyTableHeight = historyTable.clientHeight - 200
}
}
}
})
</script>
<style lang="less" scoped>
.admin_page {
display: flex;
flex-direction: column;
height: 100%;
}
.admin_table_content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.admin_page .admin_table_search .admin_state {
display: flex;
width: 70%;
flex-wrap: wrap;
align-content: flex-start;
}
.admin_page .admin_table_search .admin_search {
display: flex;
flex-wrap: wrap;
width: 30%;
> .admin_search_item {
height: 4rem;
font-size: 1.6rem;
}
}
.plan_list {
margin-top: 1rem;
display: flex;
// flex-wrap: wrap;
padding-left: 2.8rem;
column-gap: 0.6rem;
margin-bottom: 2rem;
.plan_item {
height: 4rem;
width: auto;
min-width: 10rem;
display: flex;
align-items: center;
justify-content: center;
// font-size: 1.8rem;
font-weight: 600;
border-radius: 1.3rem;
color: #fff;
cursor: pointer;
border: 1.8px solid #000;
background-color: #000;
padding: 0 1rem 0 2rem;
&:hover {
color: #000;
background-color: #fff;
}
&.active {
background: #ffffff;
color: #000000;
}
&.disabled {
opacity: 0.5;
cursor: not-allowed;
background-color: #d9d9d9;
border-color: #d9d9d9;
color: #999;
&:hover {
background-color: #d9d9d9;
color: #999;
}
}
}
.plan_item {
column-gap: 0.6rem;
}
.plan_more_icon {
font-size: 1.6rem;
cursor: pointer;
}
}
.subscription-plan-cell {
display: flex;
align-items: center;
justify-content: center;
}
.rename-plan-form {
padding: 2rem 0;
.admin_state_item {
display: flex;
align-items: center;
> span {
width: 10rem;
margin-right: 1rem;
}
}
}
.all-user {
.admin_table_content {
:deep(.ant-table-wrapper) {
overflow: hidden;
}
}
}
</style>