Files
aida_front/src/component/Administrator/subscriptionPlan.vue

1134 lines
30 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="subscription-plan">
<a-card class="search-card" :bordered="false">
<a-form
class="search-form"
layout="inline"
:model="searchForm"
:label-col="{ style: { width: '12rem' } }"
:wrapper-col="{ style: { width: '22rem' } }"
>
<a-form-item label="ID">
<a-input v-model:value="searchForm.id" allow-clear placeholder="Input the id" />
</a-form-item>
<!-- <a-form-item label="Name">
<a-input
v-model:value="searchForm.name"
allow-clear
placeholder="Input the name"
/>
</a-form-item> -->
<a-form-item label="Time Range">
<a-range-picker
v-model:value="searchForm.dateRange"
value-format="X"
allow-clear
/>
</a-form-item>
<a-form-item label="Organization">
<a-select
v-model:value="searchForm.organizationId"
allow-clear
placeholder="Select Organization"
style="width: 180px"
@popupScroll="handleOrganizationScroll"
@select="handleOrganizationSelect"
@change="handleOrganizationChange"
>
<a-select-option value="ADD_ORGANIZATION" class="add-organization-option">
+ Create Organization
</a-select-option>
<a-select-option
v-for="item in organizationOptions"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="Admin Account">
<SelectUser v-model="searchForm.adminAccId" labelKey="email" />
</a-form-item>
<a-form-item label="Status">
<a-select
v-model:value="searchForm.status"
mode="multiple"
allow-clear
placeholder="Select Status"
style="width: 220px"
:options="statusOption"
/>
</a-form-item>
<a-form-item label="Country or Region">
<a-select
v-model:value="searchForm.countryOrRegion"
:allowClear="true"
show-search
style="width: 250px"
:filter-option="filterOption"
placeholder="Select the country or region"
max-tag-count="responsive"
:options="countryList"
/>
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">Search</a-button>
<a-button @click="handleReset">Reset</a-button>
</a-space>
</a-form-item>
</a-form>
</a-card>
<a-card class="table-card" :bordered="false">
<div class="table-card__header">
<a-button type="primary" @click="openCreate">New Subscription Plan</a-button>
</div>
<div ref="historyTable" class="table-wrapper">
<a-table
:data-source="tableData"
:columns="columns"
:loading="tableLoading"
:bordered="false"
row-key="id"
@change="changePage"
@resizeColumn="handleResizeColumn"
:scroll="{ y: historyTableHeight }"
:pagination="{
showSizeChanger: true,
current: searchForm.page,
pageSize: searchForm.size,
total: searchForm.total,
showQuickJumper: true,
bordered: false
}"
>
<template #bodyCell="{ column, record }">
<template
v-if="
column.key === 'currentPeriodStart' || column.key === 'currentPeriodEnd'
"
>
{{ formatTime(record[column.key], 'YYYY-MM-DD hh:mm:ss') }}
</template>
<template v-if="column.key === 'status'">
<a-tag :color="getStatusColor(record.status)">
{{ record.status }}
</a-tag>
</template>
<template v-else-if="column.key === 'actions'">
<a-space>
<a @click="openEdit(record)">Edit</a>
<a-popconfirm
title="Confirm to delete this subscription plan?"
ok-text="Confirm"
cancel-text="Cancel"
@confirm="removePlan(record.id)"
>
<a class="danger-text">Delete</a>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
</div>
</a-card>
<div class="subscriptionPlanModal" ref="subscriptionPlanModal"></div>
<a-modal
class="subscriptionPlan_modal generalModel"
v-model:visible="modalVisible"
:footer="null"
:get-container="() => $refs.subscriptionPlanModal"
width="50%"
:maskClosable="false"
:centered="true"
:closable="false"
:mask="true"
wrapClassName="#app"
:keyboard="false"
destroy-on-close
>
<div class="generalModel_btn">
<div class="generalModel_closeIcon" @click.stop="cancelModal">
<svg
width="100%"
height="100%"
viewBox="0 0 46 46"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="23" cy="23" r="23" fill="#000" fill-opacity="0.3" />
<rect
x="32.5063"
y="12"
width="3"
height="29"
rx="1.5"
transform="rotate(45 32.5063 12)"
fill="white"
/>
<rect
x="34.6274"
y="32.5059"
width="3"
height="29"
rx="1.5"
transform="rotate(135 34.6274 32.5059)"
fill="white"
/>
</svg>
</div>
</div>
<div class="modal_title_text">
<div>{{ modalTitle }}</div>
</div>
<div class="subscriptionPlan_center admin_page">
<div class="form_content">
<div class="admin_state_item" v-if="!isEditMode">
<span>
Name:
<span>*</span>
</span>
<a-input
v-model:value="formState.name"
placeholder="Input the name"
style="width: 250px"
/>
</div>
<div class="admin_state_item" v-if="!isEditMode">
<span>
Organization:
<span>*</span>
</span>
<a-select
v-model:value="formState.organizationId"
placeholder="Select the organization"
allow-clear
style="width: 250px"
@popupScroll="handleOrganizationScroll"
@select="handleOrganizationSelect"
@change="handleOrganizationChange"
>
<a-select-option value="ADD_ORGANIZATION" class="add-organization-option">
+ Create Organization
</a-select-option>
<a-select-option
v-for="item in organizationOptions"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</a-select-option>
</a-select>
</div>
<div class="admin_state_item">
<span>
Country Or Region:
<span>*</span>
</span>
<a-select
v-model:value="formState.countryOrRegion"
placeholder="Select the country or region"
allow-clear
show-search
:filter-option="filterOption"
style="width: 250px"
:options="countryList"
></a-select>
</div>
<div class="admin_state_item">
<span>
Admin Account:
<span>*</span>
</span>
<SelectUser ref="userRef" v-model="formState.adminAccId" labelKey="email" />
</div>
<div class="admin_state_item">
<span>
Start Time:
<span>*</span>
</span>
<a-date-picker
v-model:value="formState.currentPeriodStart"
value-format="X"
:disabled="isEditMode && formState.status !== 'PENDING'"
style="width: 250px"
:disabledDate="disabledDate"
:show-time="{ format: 'HH:mm' }"
class="range_picker"
placeholder="Select the start time"
>
<template #suffixIcon>
<span class="icon iconfont range_picker_icon icon-rili"></span>
</template>
</a-date-picker>
</div>
<div class="admin_state_item">
<span>
End Time:
<span>*</span>
</span>
<a-date-picker
v-model:value="formState.currentPeriodEnd"
value-format="X"
:show-time="{ format: 'HH:mm' }"
:disabledDate="disableEndDate"
:disabledTime="disableEndTime"
:show-now="!isEditMode"
style="width: 250px"
class="range_picker"
placeholder="Select the end time"
>
<template #suffixIcon>
<span class="icon iconfont range_picker_icon icon-rili"></span>
</template>
</a-date-picker>
</div>
<div class="admin_state_item">
<span>
Sub-Account Num:
<span>*</span>
</span>
<a-input-number
v-model:value="formState.accountNum"
:min="0"
style="width: 250px"
placeholder="Input the account number"
/>
</div>
<div class="admin_state_item">
<span>
Credit Limit:
<span>*</span>
</span>
<a-input-number
v-model:value="formState.creditLimit"
:min="0"
style="width: 250px"
placeholder="Input the credit limit"
/>
</div>
<div class="admin_state_item" v-if="!isEditMode">
<span>Status:</span>
<a-select
v-model:value="formState.status"
placeholder="Select status"
allow-clear
style="width: 250px"
:options="statusOption"
/>
</div>
</div>
</div>
<div class="subscriptionPlan_btn admin_page">
<div class="admin_search_item" @click="cancelModal">Close</div>
<div class="admin_search_item" @click="handleSubmitDebounced">OK</div>
</div>
</a-modal>
<div class="organizationModal" ref="organizationModal"></div>
<a-modal
class="organization_modal"
v-model:visible="organizationModalVisible"
:footer="null"
:get-container="() => $refs.organizationModal"
:maskClosable="false"
:centered="true"
:mask="true"
wrapClassName="#app"
:keyboard="false"
destroy-on-close
>
<div class="modal_title_text">
<div>Create Organization</div>
</div>
<div class="subscriptionPlan_center admin_page">
<div class="form_content">
<div class="admin_state_item">
<span>
Name:
<span>*</span>
</span>
<a-input
v-model:value="organizationForm.name"
placeholder="Input the name"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>
Type:
<span>*</span>
</span>
<a-select
v-model:value="organizationForm.type"
placeholder="Select type"
style="width: 250px"
>
<a-select-option value="Enterprise">Enterprise</a-select-option>
<a-select-option value="Education">Education</a-select-option>
</a-select>
</div>
</div>
</div>
<div class="organization_footer">
<div class="footer_btn ant-btn ant-btn-primary" @click="cancelOrganizationModal">
Close
</div>
<div class="footer_btn ant-btn ant-btn-primary" @click="handleCreateOrganization">
OK
</div>
</div>
</a-modal>
</div>
</template>
<script setup lang="ts">
import {
reactive,
ref,
onMounted,
onBeforeUnmount,
computed,
nextTick,
useTemplateRef
} from 'vue'
import SelectUser from '@/component/common/SelectUser.vue'
import { message } from 'ant-design-vue'
import { Https } from '@/tool/https'
import { formatTime } from '@/tool/util'
import store from '@/store'
import type { FormInstance, Rule } from 'ant-design-vue/es/form'
import { debounce } from 'lodash-es'
import dayjs, { Dayjs } from 'dayjs'
type PlanStatus = 'PENDING' | 'ACTIVE' | 'EXPIRED'
interface SubscriptionPlan {
id: number
name: string
currentPeriodStart: string
currentPeriodEnd: string
organizationId: string
adminAccId: string
status: PlanStatus
creditLimit: number
accountNum?: number
startStamp: number
endStamp: number
}
const countryList = ref([])
const userRef = ref(null)
const searchForm = reactive({
name: '',
startTime: '',
endTime: '',
organizationId: undefined as string | undefined,
adminAccId: undefined as string | undefined,
status: [] as PlanStatus[] | [],
id: '',
countryOrRegion: null,
page: 1,
size: 10,
total: 0
})
const toSeconds = (dateStr: string) => Math.floor(new Date(dateStr).getTime() / 1000)
const tableData = ref<SubscriptionPlan[]>([])
const tableLoading = ref(false)
const modalVisible = ref(false)
const confirmLoading = ref(false)
const modalTitle = ref('New Subscription Plan')
const isEditMode = ref(false)
const formState = reactive({
name: '',
currentPeriodStart: '',
currentPeriodEnd: '',
organizationId: undefined as string | undefined,
adminAccId: undefined as string | undefined,
creditLimit: null as number | null,
accountNum: null as number | null,
status: undefined as PlanStatus | undefined,
countryOrRegion: null as string | null
})
const organizationModalVisible = ref(false)
const organizationForm = reactive({
name: '',
type: undefined as string | undefined
})
const statusLabelMap: Record<PlanStatus, string> = {
PENDING: 'Pending',
ACTIVE: 'Active',
EXPIRED: 'Expired'
}
const statusColorMap: Record<PlanStatus, string> = {
PENDING: 'blue',
ACTIVE: 'green',
EXPIRED: 'red'
}
const statusOption = ref([
{
label: 'Pending',
value: 'PENDING'
},
{
label: 'Active',
value: 'ACTIVE'
},
{
label: 'Expired',
value: 'EXPIRED'
}
])
const disabledDate = (current: Dayjs) => {
return current && current < dayjs().subtract(1, 'days').endOf('day')
}
const disableEndDate = (current: Dayjs) => {
if (isEditMode.value) {
const specificTime = dayjs(formState.currentPeriodEnd)
return current && current < dayjs(formState.currentPeriodEnd * 1000).startOf('day')
}
return disabledDate(current)
}
const range = (start: number, end: number) => {
const result = []
for (let i = start; i < end; i++) {
result.push(i)
}
return result
}
const disableEndTime = date => {
if (!formState.currentPeriodEnd || !isEditMode.value)
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => []
}
const specificTime = dayjs.unix(formState.currentPeriodEnd)
if (date && date.isSame(specificTime, 'day')) {
// 如果是指定日期当天,禁用时间戳之前的时间
const hour = specificTime.hour()
const minute = specificTime.minute()
const second = specificTime.second()
return {
disabledHours: () => Array.from({ length: hour }, (_, i) => i), // 禁用小时之前
disabledMinutes: selectedHour => {
if (selectedHour === hour) {
return Array.from({ length: minute }, (_, i) => i) // 同小时,禁用分钟之前
}
return []
},
disabledSeconds: (selectedHour, selectedMinute) => {
if (selectedHour === hour && selectedMinute === minute) {
return Array.from({ length: second }, (_, i) => i) // 同小时分钟,禁用秒之前
}
return []
}
}
}
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => []
}
}
const normalizeStatus = (status?: string): PlanStatus | undefined => {
if (!status) return undefined
const upper = status.toUpperCase() as PlanStatus
return upper
}
const getStatusColor = (status?: string) =>
statusColorMap[normalizeStatus(status) as PlanStatus] || 'default'
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name', align: 'center', width: 180 },
{ title: 'ID', dataIndex: 'id', key: 'id', align: 'center', width: 80 },
{
title: 'Organization',
dataIndex: 'organizationName',
key: 'organizationName',
align: 'center',
width: 180
},
{
title: 'Admin Account',
dataIndex: 'adminAccEmail',
key: 'adminAccEmail',
align: 'center',
width: 180,
ellipsis: true
},
{
title: 'Sub-Account Num',
dataIndex: 'accountNum',
key: 'accountNum',
align: 'center',
width: 120,
ellipsis: true
},
{
title: 'Country or Region',
dataIndex: 'countryOrRegion',
key: 'countryOrRegion',
align: 'center',
width: 120,
ellipsis: true
},
{
title: 'Start Time',
dataIndex: 'currentPeriodStart',
key: 'currentPeriodStart',
align: 'center',
width: 200
},
{
title: 'End Time',
dataIndex: 'currentPeriodEnd',
key: 'currentPeriodEnd',
align: 'center',
width: 200
},
{ title: 'Status', dataIndex: 'status', key: 'status', align: 'center', width: 100 },
{
title: 'Credit Limit',
dataIndex: 'creditLimit',
key: 'creditLimit',
align: 'center',
width: 120
},
{ title: 'Operations', key: 'actions', width: 160, align: 'center', fixed: 'right' }
]
const historyTable = ref()
const historyTableHeight = ref(0)
const handleResizeColumn = (w: any, col: any) => {
col.width = w
}
const calculateTableHeight = () => {
nextTick(() => {
if (historyTable.value) {
historyTableHeight.value = historyTable.value.clientHeight - 200
}
})
}
const handleResize = () => {
calculateTableHeight()
}
onMounted(async () => {
await getOrganizationList()
await handleSearch()
calculateTableHeight()
window.addEventListener('resize', handleResize)
const list = sessionStorage.getItem('allCountry')
countryList.value = list ? JSON.parse(list) : []
})
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
})
const handleFetchTableData = async () => {
tableLoading.value = true
return Https.axiosPost(Https.httpUrls.searchAllSubscribePlan, searchForm)
.then(res => {
tableData.value = res.records
searchForm.total = res.total
})
.finally(() => {
tableLoading.value = false
})
}
const resetFormState = () => {
formState.name = ''
formState.currentPeriodStart = ''
formState.currentPeriodEnd = ''
formState.organizationId = undefined
formState.adminAccId = undefined
formState.creditLimit = null
formState.accountNum = null
formState.status = undefined
formState.countryOrRegion = null
}
const changePage = (pagination: any) => {
searchForm.page = pagination.current
searchForm.size = pagination.pageSize
handleFetchTableData()
}
const handleSearch = () => {
searchForm.page = 1
handleFetchTableData()
}
const handleReset = () => {
searchForm.name = ''
searchForm.startTime = ''
searchForm.endTime = ''
searchForm.organizationId = undefined
searchForm.adminAccId = undefined
searchForm.status = []
searchForm.id = ''
searchForm.countryOrRegion = null
handleSearch()
}
const openCreate = () => {
modalTitle.value = 'New Subscription Plan'
isEditMode.value = false
resetFormState()
modalVisible.value = true
}
const openEdit = (record: SubscriptionPlan) => {
modalTitle.value = 'Edit Subscription Plan'
isEditMode.value = true
formState.name = record.name
formState.currentPeriodStart = String(record.currentPeriodStart)
formState.currentPeriodEnd = String(record.currentPeriodEnd)
formState.organizationId = record.organizationId
formState.adminAccId = record.adminAccId
formState.creditLimit = record.creditLimit
formState.accountNum = (record as any).accountNum || null
formState.status = record.status
formState.id = record.id
formState.countryOrRegion = (record as any).countryOrRegion || null
// 检查组织ID是否在已加载的组织列表中如果不在则添加临时项
if (record.organizationId) {
const orgExists = organizationOptions.value.some(
(org: any) =>
org.id === record.organizationId ||
String(org.id) === String(record.organizationId)
)
if (!orgExists) {
const orgName = (record as any).organizationName
if (orgName) {
organizationOptions.value = [
{
id: record.organizationId,
name: orgName
},
...organizationOptions.value
]
}
}
}
modalVisible.value = true
if (record.adminAccId) {
nextTick(() => {
userRef.value.patchList({
label: record.name,
value: record.adminAccId,
email: record.adminAccEmail
})
})
}
}
const validateForm = (): boolean => {
interface FieldRule {
value: any
message: string
checkNull?: boolean
}
const requiredFields: FieldRule[] = [
{ value: formState.currentPeriodStart, message: 'Please select the start time' },
{ value: formState.currentPeriodEnd, message: 'Please select the end time' },
{ value: formState.adminAccId, message: 'Please select the admin account' },
{
value: formState.creditLimit,
message: 'Please input credit limit',
checkNull: true
},
{
value: formState.accountNum,
message: 'Please input account number',
checkNull: true
}
]
if (!isEditMode.value) {
requiredFields.push(
{ value: formState.name, message: 'Please input the name' },
{ value: formState.organizationId, message: 'Please select organization' }
)
}
for (const field of requiredFields) {
const isEmpty = field.checkNull
? field.value === null || field.value === undefined
: !field.value
if (isEmpty) {
message.warning(field.message)
return false
}
}
return true
}
const handleSubmit = async () => {
if (!validateForm()) return
confirmLoading.value = true
const params = {
...formState,
currentPeriodStart: Number(formState.currentPeriodStart),
currentPeriodEnd: Number(formState.currentPeriodEnd)
}
let res = null
try {
if (isEditMode.value) {
res = await Https.axiosPost(Https.httpUrls.updateSubscribePlan, params)
} else {
res = await Https.axiosPost(Https.httpUrls.createSubscribePlan, params)
}
message.success(
`${isEditMode.value ? 'Subscription plan updated' : 'Subscription plan created'}`
)
} catch (error: any) {
message.error(error.message)
console.error(error)
} finally {
confirmLoading.value = false
modalVisible.value = false
resetFormState()
isEditMode.value = false
handleSearch()
}
}
// 防抖包装,避免重复点击
const handleSubmitDebounced = debounce(handleSubmit, 500, {
leading: true,
trailing: false
})
const cancelModal = () => {
modalVisible.value = false
resetFormState()
isEditMode.value = false
}
const removePlan = (id: number) => {
tableLoading.value = true
Https.axiosGet(Https.httpUrls.deleteSubscribePlan, { params: { id } })
.then(res => {
message.success('Subscription plan deleted')
handleReset()
})
.catch((error: any) => {
message.error(error.message)
console.error(error)
})
.finally(() => {
tableLoading.value = false
})
}
const organizationOptions = ref([])
const organizationParams = reactive({
page: 1,
size: 10,
total: 0
})
const organizationLoading = ref(false)
const getOrganizationList = async (isLoadMore = false) => {
if (organizationLoading.value) return
if (isLoadMore) {
const loaded = organizationParams.page * organizationParams.size
if (organizationParams.total && loaded >= organizationParams.total) return
organizationParams.page += 1
} else {
organizationParams.page = 1
organizationOptions.value = []
}
organizationLoading.value = true
try {
const { total, ...requestParams } = organizationParams
const rv: any = await Https.axiosPost(Https.httpUrls.queryOrganization, requestParams)
if (rv) {
const newRecords = rv.records || []
// 遍历新数据,如果已存在则覆盖,不存在则追加
newRecords.forEach((newOrg: any) => {
const newOrgId = String(newOrg.id)
const existingIndex = organizationOptions.value.findIndex(
(org: any) => String(org.id) === newOrgId
)
if (existingIndex !== -1) {
// 如果已存在,用新数据覆盖旧项
organizationOptions.value[existingIndex] = newOrg
} else {
// 如果不存在,追加到末尾
organizationOptions.value.push(newOrg)
}
})
organizationParams.total = rv.total || 0
}
} finally {
organizationLoading.value = false
}
}
const handleOrganizationScroll = (e: any) => {
const target = e?.target
if (!target) return
const nearBottom = target.scrollTop + target.clientHeight >= target.scrollHeight - 20
if (nearBottom) {
getOrganizationList(true)
}
}
const handleOrganizationSelect = (value: string) => {
if (value === 'ADD_ORGANIZATION') {
// 打开添加组织弹窗
organizationModalVisible.value = true
// 使用nextTick确保值被重置使其不被选中
nextTick(() => {
if (searchForm.organizationId === 'ADD_ORGANIZATION') {
searchForm.organizationId = undefined
}
if (formState.organizationId === 'ADD_ORGANIZATION') {
formState.organizationId = undefined
}
})
}
}
const handleOrganizationChange = (value: string) => {
// 如果change事件触发时值是"添加组织",立即重置
if (value === 'ADD_ORGANIZATION') {
nextTick(() => {
if (searchForm.organizationId === 'ADD_ORGANIZATION') {
searchForm.organizationId = undefined
}
if (formState.organizationId === 'ADD_ORGANIZATION') {
formState.organizationId = undefined
}
})
}
}
const cancelOrganizationModal = () => {
organizationModalVisible.value = false
organizationForm.name = ''
organizationForm.type = undefined
}
const handleCreateOrganization = async () => {
if (!organizationForm.name || !organizationForm.type) {
message.warning('Please fill in name and type')
return
}
try {
const res: any = await Https.axiosGet(Https.httpUrls.addOrganization, {
params: {
name: organizationForm.name,
type: organizationForm.type
}
})
message.success('Organization created successfully')
cancelOrganizationModal()
// 刷新组织列表
await getOrganizationList()
// 如果是在编辑/新建弹窗中,自动选择新创建的组织
if (modalVisible.value) {
const newOrgId = res?.id || res?.data?.id || res
if (newOrgId) {
formState.organizationId = String(newOrgId)
}
}
} catch (error: any) {
message.error(error.message || 'Failed to create organization')
console.error(error)
}
}
const filterOption = (input: string, option: any) => {
const label = option?.label ?? option?.children ?? option?.key?.label ?? ''
return String(label).toLowerCase().includes(input.toLowerCase())
}
</script>
<style lang="less" scoped>
.subscription-plan {
padding: 2rem 2.4rem 3.2rem 0;
display: flex;
height: 100%;
flex-direction: column;
.search-card {
margin-bottom: 1.6rem;
}
.table-card {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
:deep(.ant-card-body) {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 2.4rem;
}
.table-card__header {
display: flex;
justify-content: flex-end;
margin-bottom: 2.6rem;
flex-shrink: 0;
}
.table-wrapper {
flex: 1;
overflow: hidden;
min-height: 0;
}
.danger-text {
color: #ff4d4f;
}
:deep(.ant-table-cell::before) {
display: none;
}
:deep(.ant-table-thead > tr > th) {
border-bottom: none;
}
:deep(.ant-table-tbody > tr > td) {
border: none;
}
:deep(.ant-table-tbody > tr:hover > td) {
background: rgb(202, 202, 202);
}
}
}
:deep(.subscriptionPlan_modal) {
.ant-modal-body {
// height: calc(65rem * 1.2);
display: flex;
flex-direction: column;
padding: 2.5rem 3rem;
position: relative;
}
}
.subscriptionPlan_modal {
.form_content {
width: 100%;
display: flex;
flex-wrap: wrap;
row-gap: 2rem;
}
.admin_state_item {
> span {
width: 17rem !important;
}
}
.modal_title_text {
margin-bottom: 2rem;
flex-shrink: 0;
}
.subscriptionPlan_center {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
padding: 2rem 0;
}
.subscriptionPlan_btn {
display: flex;
flex-direction: row;
height: auto;
justify-content: flex-end;
padding: 1.5rem 0 0 0;
margin-top: auto;
flex-shrink: 0;
.admin_search_item {
margin-bottom: 0;
}
}
}
:deep(.search-form) {
column-gap: 2rem;
row-gap: 2rem;
.ant-select {
width: 100% !important;
}
}
:deep(.ant-select-dropdown) {
.add-organization-option {
color: #1890ff !important;
font-weight: 600;
background-color: #f0f7ff !important;
border-bottom: 1px solid #e6f4ff;
margin-bottom: 4px;
padding-bottom: 4px;
&:hover {
background-color: #e6f4ff !important;
}
}
}
:deep(.organization_modal) {
.ant-modal-body {
height: auto;
min-height: 300px;
display: flex;
flex-direction: column;
padding: 2rem 2.5rem;
position: relative;
}
.subscriptionPlan_center {
flex: 0 0 auto;
overflow: visible;
padding: 1rem 0;
min-height: auto;
}
.modal_title_text {
margin-bottom: 1.5rem;
}
}
.organization_footer {
display: flex;
justify-content: flex-end;
column-gap: 3rem;
.footer_btn {
border-radius: 3.3rem;
}
}
</style>