|
|
|
@@ -437,7 +437,7 @@
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
<script setup lang="ts">
|
|
|
|
import {
|
|
|
|
import {
|
|
|
|
reactive,
|
|
|
|
reactive,
|
|
|
|
ref,
|
|
|
|
ref,
|
|
|
|
onMounted,
|
|
|
|
onMounted,
|
|
|
|
@@ -446,18 +446,18 @@ import {
|
|
|
|
computed,
|
|
|
|
computed,
|
|
|
|
nextTick,
|
|
|
|
nextTick,
|
|
|
|
useTemplateRef
|
|
|
|
useTemplateRef
|
|
|
|
} from "vue"
|
|
|
|
} from "vue"
|
|
|
|
import SelectUser from "@/component/common/SelectUser.vue"
|
|
|
|
import SelectUser from "@/component/common/SelectUser.vue"
|
|
|
|
import { message } from "ant-design-vue"
|
|
|
|
import { message } from "ant-design-vue"
|
|
|
|
import { Https } from "@/tool/https"
|
|
|
|
import { Https } from "@/tool/https"
|
|
|
|
import { formatTime } from "@/tool/util"
|
|
|
|
import { formatTime } from "@/tool/util"
|
|
|
|
import store from "@/store"
|
|
|
|
import store from "@/store"
|
|
|
|
import type { FormInstance, Rule } from "ant-design-vue/es/form"
|
|
|
|
import type { FormInstance, Rule } from "ant-design-vue/es/form"
|
|
|
|
import { debounce } from "lodash-es"
|
|
|
|
import { debounce } from "lodash-es"
|
|
|
|
import dayjs, { Dayjs } from "dayjs"
|
|
|
|
import dayjs, { Dayjs } from "dayjs"
|
|
|
|
|
|
|
|
|
|
|
|
type PlanStatus = "PENDING" | "ACTIVE" | "EXPIRED"
|
|
|
|
type PlanStatus = "PENDING" | "ACTIVE" | "EXPIRED"
|
|
|
|
interface SubscriptionPlan {
|
|
|
|
interface SubscriptionPlan {
|
|
|
|
id: number
|
|
|
|
id: number
|
|
|
|
userId?: string | number
|
|
|
|
userId?: string | number
|
|
|
|
name: string
|
|
|
|
name: string
|
|
|
|
@@ -471,9 +471,9 @@ interface SubscriptionPlan {
|
|
|
|
accountNum?: number
|
|
|
|
accountNum?: number
|
|
|
|
startStamp: number
|
|
|
|
startStamp: number
|
|
|
|
endStamp: number
|
|
|
|
endStamp: number
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface UserInfoRecord {
|
|
|
|
interface UserInfoRecord {
|
|
|
|
id: number
|
|
|
|
id: number
|
|
|
|
userEmail?: string
|
|
|
|
userEmail?: string
|
|
|
|
userName?: string
|
|
|
|
userName?: string
|
|
|
|
@@ -486,12 +486,12 @@ interface UserInfoRecord {
|
|
|
|
browserIdentifiers?: string
|
|
|
|
browserIdentifiers?: string
|
|
|
|
credits?: number
|
|
|
|
credits?: number
|
|
|
|
systemUser?: number | string
|
|
|
|
systemUser?: number | string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const countryList = ref([])
|
|
|
|
const countryList = ref([])
|
|
|
|
const userRef = ref(null)
|
|
|
|
const userRef = ref(null)
|
|
|
|
|
|
|
|
|
|
|
|
const searchForm = reactive({
|
|
|
|
const searchForm = reactive({
|
|
|
|
name: "",
|
|
|
|
name: "",
|
|
|
|
startTime: "",
|
|
|
|
startTime: "",
|
|
|
|
endTime: "",
|
|
|
|
endTime: "",
|
|
|
|
@@ -503,27 +503,27 @@ const searchForm = reactive({
|
|
|
|
page: 1,
|
|
|
|
page: 1,
|
|
|
|
size: 10,
|
|
|
|
size: 10,
|
|
|
|
total: 0
|
|
|
|
total: 0
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const toSeconds = (dateStr: string) => Math.floor(new Date(dateStr).getTime() / 1000)
|
|
|
|
const toSeconds = (dateStr: string) => Math.floor(new Date(dateStr).getTime() / 1000)
|
|
|
|
|
|
|
|
|
|
|
|
const tableData = ref<SubscriptionPlan[]>([])
|
|
|
|
const tableData = ref<SubscriptionPlan[]>([])
|
|
|
|
const tableLoading = ref(false)
|
|
|
|
const tableLoading = ref(false)
|
|
|
|
const userInfoModalVisible = ref(false)
|
|
|
|
const userInfoModalVisible = ref(false)
|
|
|
|
const userInfoLoading = ref(false)
|
|
|
|
const userInfoLoading = ref(false)
|
|
|
|
const userInfoList = ref<UserInfoRecord[]>([])
|
|
|
|
const userInfoList = ref<UserInfoRecord[]>([])
|
|
|
|
const currentUserInfoPlanId = ref<string | number | null>(null)
|
|
|
|
const currentUserInfoPlanId = ref<string | number | null>(null)
|
|
|
|
const userInfoPage = reactive({
|
|
|
|
const userInfoPage = reactive({
|
|
|
|
current: 1,
|
|
|
|
current: 1,
|
|
|
|
size: 10,
|
|
|
|
size: 10,
|
|
|
|
total: 0
|
|
|
|
total: 0
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const modalVisible = ref(false)
|
|
|
|
const modalVisible = ref(false)
|
|
|
|
const confirmLoading = ref(false)
|
|
|
|
const confirmLoading = ref(false)
|
|
|
|
const modalTitle = ref("New Subscription Plan")
|
|
|
|
const modalTitle = ref("New Subscription Plan")
|
|
|
|
const isEditMode = ref(false)
|
|
|
|
const isEditMode = ref(false)
|
|
|
|
const formState = reactive({
|
|
|
|
const formState = reactive({
|
|
|
|
name: "",
|
|
|
|
name: "",
|
|
|
|
currentPeriodStart: "",
|
|
|
|
currentPeriodStart: "",
|
|
|
|
currentPeriodEnd: "",
|
|
|
|
currentPeriodEnd: "",
|
|
|
|
@@ -533,26 +533,26 @@ const formState = reactive({
|
|
|
|
accountNum: null as number | null,
|
|
|
|
accountNum: null as number | null,
|
|
|
|
status: undefined as PlanStatus | undefined,
|
|
|
|
status: undefined as PlanStatus | undefined,
|
|
|
|
countryOrRegion: null as string | null
|
|
|
|
countryOrRegion: null as string | null
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const organizationModalVisible = ref(false)
|
|
|
|
const organizationModalVisible = ref(false)
|
|
|
|
const organizationForm = reactive({
|
|
|
|
const organizationForm = reactive({
|
|
|
|
name: "",
|
|
|
|
name: "",
|
|
|
|
type: undefined as string | undefined
|
|
|
|
type: undefined as string | undefined
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const statusLabelMap: Record<PlanStatus, string> = {
|
|
|
|
const statusLabelMap: Record<PlanStatus, string> = {
|
|
|
|
PENDING: "Pending",
|
|
|
|
PENDING: "Pending",
|
|
|
|
ACTIVE: "Active",
|
|
|
|
ACTIVE: "Active",
|
|
|
|
EXPIRED: "Expired"
|
|
|
|
EXPIRED: "Expired"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const statusColorMap: Record<PlanStatus, string> = {
|
|
|
|
const statusColorMap: Record<PlanStatus, string> = {
|
|
|
|
PENDING: "blue",
|
|
|
|
PENDING: "blue",
|
|
|
|
ACTIVE: "green",
|
|
|
|
ACTIVE: "green",
|
|
|
|
EXPIRED: "red"
|
|
|
|
EXPIRED: "red"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const statusOption = ref([
|
|
|
|
const statusOption = ref([
|
|
|
|
{
|
|
|
|
{
|
|
|
|
label: "Pending",
|
|
|
|
label: "Pending",
|
|
|
|
value: "PENDING"
|
|
|
|
value: "PENDING"
|
|
|
|
@@ -565,27 +565,27 @@ const statusOption = ref([
|
|
|
|
label: "Expired",
|
|
|
|
label: "Expired",
|
|
|
|
value: "EXPIRED"
|
|
|
|
value: "EXPIRED"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
])
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
const disabledDate = (current: Dayjs) => {
|
|
|
|
const disabledDate = (current: Dayjs) => {
|
|
|
|
return current && current < dayjs().subtract(1, "days").endOf("day")
|
|
|
|
return current && current < dayjs().subtract(1, "days").endOf("day")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const disableEndDate = (current: Dayjs) => {
|
|
|
|
const disableEndDate = (current: Dayjs) => {
|
|
|
|
if (isEditMode.value) {
|
|
|
|
if (isEditMode.value) {
|
|
|
|
const specificTime = dayjs(formState.currentPeriodEnd)
|
|
|
|
const specificTime = dayjs(formState.currentPeriodEnd)
|
|
|
|
return current && current < dayjs(formState.currentPeriodEnd * 1000).startOf("day")
|
|
|
|
return current && current < dayjs(formState.currentPeriodEnd * 1000).startOf("day")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return disabledDate(current)
|
|
|
|
return disabledDate(current)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const range = (start: number, end: number) => {
|
|
|
|
const range = (start: number, end: number) => {
|
|
|
|
const result = []
|
|
|
|
const result = []
|
|
|
|
for (let i = start; i < end; i++) {
|
|
|
|
for (let i = start; i < end; i++) {
|
|
|
|
result.push(i)
|
|
|
|
result.push(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const disableEndTime = (date) => {
|
|
|
|
const disableEndTime = (date) => {
|
|
|
|
if (!formState.currentPeriodEnd || !isEditMode.value)
|
|
|
|
if (!formState.currentPeriodEnd || !isEditMode.value)
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
disabledHours: () => [],
|
|
|
|
disabledHours: () => [],
|
|
|
|
@@ -623,17 +623,17 @@ const disableEndTime = (date) => {
|
|
|
|
disabledMinutes: () => [],
|
|
|
|
disabledMinutes: () => [],
|
|
|
|
disabledSeconds: () => []
|
|
|
|
disabledSeconds: () => []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const normalizeStatus = (status?: string): PlanStatus | undefined => {
|
|
|
|
const normalizeStatus = (status?: string): PlanStatus | undefined => {
|
|
|
|
if (!status) return undefined
|
|
|
|
if (!status) return undefined
|
|
|
|
const upper = status.toUpperCase() as PlanStatus
|
|
|
|
const upper = status.toUpperCase() as PlanStatus
|
|
|
|
return upper
|
|
|
|
return upper
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const getStatusColor = (status?: string) =>
|
|
|
|
const getStatusColor = (status?: string) =>
|
|
|
|
statusColorMap[normalizeStatus(status) as PlanStatus] || "default"
|
|
|
|
statusColorMap[normalizeStatus(status) as PlanStatus] || "default"
|
|
|
|
|
|
|
|
|
|
|
|
const columns = [
|
|
|
|
const columns = [
|
|
|
|
{ title: "Name", dataIndex: "name", key: "name", align: "center", width: 180 },
|
|
|
|
{ title: "Name", dataIndex: "name", key: "name", align: "center", width: 180 },
|
|
|
|
{ title: "ID", dataIndex: "id", key: "id", align: "center", width: 80 },
|
|
|
|
{ title: "ID", dataIndex: "id", key: "id", align: "center", width: 80 },
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@@ -690,9 +690,9 @@ const columns = [
|
|
|
|
width: 120
|
|
|
|
width: 120
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{ title: "Operations", key: "actions", width: 160, align: "center", fixed: "right" }
|
|
|
|
{ title: "Operations", key: "actions", width: 160, align: "center", fixed: "right" }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const userInfoColumns = [
|
|
|
|
const userInfoColumns = [
|
|
|
|
{ title: "User Id", dataIndex: "id", key: "id", align: "center", width: 100 },
|
|
|
|
{ title: "User Id", dataIndex: "id", key: "id", align: "center", width: 100 },
|
|
|
|
{
|
|
|
|
{
|
|
|
|
title: "Email",
|
|
|
|
title: "Email",
|
|
|
|
@@ -747,23 +747,23 @@ const userInfoColumns = [
|
|
|
|
align: "center",
|
|
|
|
align: "center",
|
|
|
|
width: 120
|
|
|
|
width: 120
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const systemUserLabelMap: Record<string, string> = {
|
|
|
|
const systemUserLabelMap: Record<string, string> = {
|
|
|
|
"0": "visitor",
|
|
|
|
"0": "visitor",
|
|
|
|
"1": "yearly",
|
|
|
|
"1": "yearly",
|
|
|
|
"2": "monthly",
|
|
|
|
"2": "monthly",
|
|
|
|
"3": "trial",
|
|
|
|
"3": "trial",
|
|
|
|
"4": "userInEvent",
|
|
|
|
"4": "userInEvent",
|
|
|
|
"7": "Edu Admin"
|
|
|
|
"7": "Edu Admin"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getSystemUserLabel = (systemUser?: number | string) => {
|
|
|
|
const getSystemUserLabel = (systemUser?: number | string) => {
|
|
|
|
if (systemUser === undefined || systemUser === null) return ""
|
|
|
|
if (systemUser === undefined || systemUser === null) return ""
|
|
|
|
return systemUserLabelMap[String(systemUser)] || String(systemUser)
|
|
|
|
return systemUserLabelMap[String(systemUser)] || String(systemUser)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const formatUserTime = (value?: number | string) => {
|
|
|
|
const formatUserTime = (value?: number | string) => {
|
|
|
|
if (!value) return ""
|
|
|
|
if (!value) return ""
|
|
|
|
if (typeof value === "number") {
|
|
|
|
if (typeof value === "number") {
|
|
|
|
return formatTime(value / 1000, "YYYY-MM-DD hh:mm:ss")
|
|
|
|
return formatTime(value / 1000, "YYYY-MM-DD hh:mm:ss")
|
|
|
|
@@ -772,16 +772,16 @@ const formatUserTime = (value?: number | string) => {
|
|
|
|
return formatTime(Number(value) / 1000, "YYYY-MM-DD hh:mm:ss")
|
|
|
|
return formatTime(Number(value) / 1000, "YYYY-MM-DD hh:mm:ss")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const normalizeUserInfoList = (res: any): UserInfoRecord[] => {
|
|
|
|
const normalizeUserInfoList = (res: any): UserInfoRecord[] => {
|
|
|
|
if (Array.isArray(res)) return res
|
|
|
|
if (Array.isArray(res)) return res
|
|
|
|
if (Array.isArray(res?.records)) return res.records
|
|
|
|
if (Array.isArray(res?.records)) return res.records
|
|
|
|
if (res) return [res]
|
|
|
|
if (res) return [res]
|
|
|
|
return []
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const buildUserInfoParams = (id: string | number) => ({
|
|
|
|
const buildUserInfoParams = (id: string | number) => ({
|
|
|
|
endTime: "",
|
|
|
|
endTime: "",
|
|
|
|
startTime: "",
|
|
|
|
startTime: "",
|
|
|
|
size: userInfoPage.size,
|
|
|
|
size: userInfoPage.size,
|
|
|
|
@@ -796,9 +796,9 @@ const buildUserInfoParams = (id: string | number) => ({
|
|
|
|
orderBy: "",
|
|
|
|
orderBy: "",
|
|
|
|
userName: "",
|
|
|
|
userName: "",
|
|
|
|
subscriptionPlanId: id
|
|
|
|
subscriptionPlanId: id
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const fetchUserInfo = async () => {
|
|
|
|
const fetchUserInfo = async () => {
|
|
|
|
if (currentUserInfoPlanId.value === null) return
|
|
|
|
if (currentUserInfoPlanId.value === null) return
|
|
|
|
userInfoLoading.value = true
|
|
|
|
userInfoLoading.value = true
|
|
|
|
userInfoList.value = []
|
|
|
|
userInfoList.value = []
|
|
|
|
@@ -817,9 +817,9 @@ const fetchUserInfo = async () => {
|
|
|
|
} finally {
|
|
|
|
} finally {
|
|
|
|
userInfoLoading.value = false
|
|
|
|
userInfoLoading.value = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const openUserInfo = async (record: SubscriptionPlan) => {
|
|
|
|
const openUserInfo = async (record: SubscriptionPlan) => {
|
|
|
|
console.log(record)
|
|
|
|
console.log(record)
|
|
|
|
// debugger
|
|
|
|
// debugger
|
|
|
|
|
|
|
|
|
|
|
|
@@ -827,15 +827,15 @@ const openUserInfo = async (record: SubscriptionPlan) => {
|
|
|
|
userInfoPage.current = 1
|
|
|
|
userInfoPage.current = 1
|
|
|
|
userInfoModalVisible.value = true
|
|
|
|
userInfoModalVisible.value = true
|
|
|
|
await fetchUserInfo()
|
|
|
|
await fetchUserInfo()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const changeUserInfoPage = (pagination: any) => {
|
|
|
|
const changeUserInfoPage = (pagination: any) => {
|
|
|
|
userInfoPage.current = pagination.current
|
|
|
|
userInfoPage.current = pagination.current
|
|
|
|
userInfoPage.size = pagination.pageSize
|
|
|
|
userInfoPage.size = pagination.pageSize
|
|
|
|
fetchUserInfo()
|
|
|
|
fetchUserInfo()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const customPlanRow = (record: SubscriptionPlan) => {
|
|
|
|
const customPlanRow = (record: SubscriptionPlan) => {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
onClick: (event: MouseEvent) => {
|
|
|
|
onClick: (event: MouseEvent) => {
|
|
|
|
const target = event.target as HTMLElement | null
|
|
|
|
const target = event.target as HTMLElement | null
|
|
|
|
@@ -843,19 +843,19 @@ const customPlanRow = (record: SubscriptionPlan) => {
|
|
|
|
openUserInfo(record)
|
|
|
|
openUserInfo(record)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const historyTable = ref<HTMLElement | null>(null)
|
|
|
|
const historyTable = ref<HTMLElement | null>(null)
|
|
|
|
const historyTableHeight = ref(0)
|
|
|
|
const historyTableHeight = ref(0)
|
|
|
|
const minTableBodyHeight = 120
|
|
|
|
const minTableBodyHeight = 120
|
|
|
|
let tableResizeObserver: ResizeObserver | null = null
|
|
|
|
let tableResizeObserver: ResizeObserver | null = null
|
|
|
|
let tableResizeTimer: ReturnType<typeof window.setTimeout> | null = null
|
|
|
|
let tableResizeTimer: ReturnType<typeof window.setTimeout> | null = null
|
|
|
|
|
|
|
|
|
|
|
|
const handleResizeColumn = (w: any, col: any) => {
|
|
|
|
const handleResizeColumn = (w: any, col: any) => {
|
|
|
|
col.width = w
|
|
|
|
col.width = w
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getElementOuterHeight = (element: Element | null) => {
|
|
|
|
const getElementOuterHeight = (element: Element | null) => {
|
|
|
|
if (!element) return 0
|
|
|
|
if (!element) return 0
|
|
|
|
const htmlElement = element as HTMLElement
|
|
|
|
const htmlElement = element as HTMLElement
|
|
|
|
const style = window.getComputedStyle(htmlElement)
|
|
|
|
const style = window.getComputedStyle(htmlElement)
|
|
|
|
@@ -864,9 +864,9 @@ const getElementOuterHeight = (element: Element | null) => {
|
|
|
|
Number.parseFloat(style.marginTop || "0") +
|
|
|
|
Number.parseFloat(style.marginTop || "0") +
|
|
|
|
Number.parseFloat(style.marginBottom || "0")
|
|
|
|
Number.parseFloat(style.marginBottom || "0")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const calculateTableHeight = () => {
|
|
|
|
const calculateTableHeight = () => {
|
|
|
|
if (tableResizeTimer) {
|
|
|
|
if (tableResizeTimer) {
|
|
|
|
window.clearTimeout(tableResizeTimer)
|
|
|
|
window.clearTimeout(tableResizeTimer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -891,13 +891,13 @@ const calculateTableHeight = () => {
|
|
|
|
tableResizeTimer = null
|
|
|
|
tableResizeTimer = null
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}, 50)
|
|
|
|
}, 50)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleResize = () => {
|
|
|
|
const handleResize = () => {
|
|
|
|
calculateTableHeight()
|
|
|
|
calculateTableHeight()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const setupTableResizeObserver = () => {
|
|
|
|
const setupTableResizeObserver = () => {
|
|
|
|
if (!historyTable.value || typeof ResizeObserver === "undefined") return
|
|
|
|
if (!historyTable.value || typeof ResizeObserver === "undefined") return
|
|
|
|
tableResizeObserver?.disconnect()
|
|
|
|
tableResizeObserver?.disconnect()
|
|
|
|
tableResizeObserver = new ResizeObserver(() => {
|
|
|
|
tableResizeObserver = new ResizeObserver(() => {
|
|
|
|
@@ -911,9 +911,9 @@ const setupTableResizeObserver = () => {
|
|
|
|
if (searchCard) {
|
|
|
|
if (searchCard) {
|
|
|
|
tableResizeObserver.observe(searchCard)
|
|
|
|
tableResizeObserver.observe(searchCard)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
onMounted(async () => {
|
|
|
|
await getOrganizationList()
|
|
|
|
await getOrganizationList()
|
|
|
|
await handleSearch()
|
|
|
|
await handleSearch()
|
|
|
|
calculateTableHeight()
|
|
|
|
calculateTableHeight()
|
|
|
|
@@ -921,22 +921,22 @@ onMounted(async () => {
|
|
|
|
window.addEventListener("resize", handleResize)
|
|
|
|
window.addEventListener("resize", handleResize)
|
|
|
|
const list = sessionStorage.getItem("allCountry")
|
|
|
|
const list = sessionStorage.getItem("allCountry")
|
|
|
|
countryList.value = list ? JSON.parse(list) : []
|
|
|
|
countryList.value = list ? JSON.parse(list) : []
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
onActivated(() => {
|
|
|
|
onActivated(() => {
|
|
|
|
calculateTableHeight()
|
|
|
|
calculateTableHeight()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
window.removeEventListener("resize", handleResize)
|
|
|
|
window.removeEventListener("resize", handleResize)
|
|
|
|
tableResizeObserver?.disconnect()
|
|
|
|
tableResizeObserver?.disconnect()
|
|
|
|
if (tableResizeTimer) {
|
|
|
|
if (tableResizeTimer) {
|
|
|
|
window.clearTimeout(tableResizeTimer)
|
|
|
|
window.clearTimeout(tableResizeTimer)
|
|
|
|
tableResizeTimer = null
|
|
|
|
tableResizeTimer = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const handleFetchTableData = async () => {
|
|
|
|
const handleFetchTableData = async () => {
|
|
|
|
tableLoading.value = true
|
|
|
|
tableLoading.value = true
|
|
|
|
return Https.axiosPost(Https.httpUrls.searchAllSubscribePlan, searchForm)
|
|
|
|
return Https.axiosPost(Https.httpUrls.searchAllSubscribePlan, searchForm)
|
|
|
|
.then((res) => {
|
|
|
|
.then((res) => {
|
|
|
|
@@ -947,9 +947,9 @@ const handleFetchTableData = async () => {
|
|
|
|
tableLoading.value = false
|
|
|
|
tableLoading.value = false
|
|
|
|
calculateTableHeight()
|
|
|
|
calculateTableHeight()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const resetFormState = () => {
|
|
|
|
const resetFormState = () => {
|
|
|
|
formState.name = ""
|
|
|
|
formState.name = ""
|
|
|
|
formState.currentPeriodStart = ""
|
|
|
|
formState.currentPeriodStart = ""
|
|
|
|
formState.currentPeriodEnd = ""
|
|
|
|
formState.currentPeriodEnd = ""
|
|
|
|
@@ -959,20 +959,20 @@ const resetFormState = () => {
|
|
|
|
formState.accountNum = null
|
|
|
|
formState.accountNum = null
|
|
|
|
formState.status = undefined
|
|
|
|
formState.status = undefined
|
|
|
|
formState.countryOrRegion = null
|
|
|
|
formState.countryOrRegion = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const changePage = (pagination: any) => {
|
|
|
|
const changePage = (pagination: any) => {
|
|
|
|
searchForm.page = pagination.current
|
|
|
|
searchForm.page = pagination.current
|
|
|
|
searchForm.size = pagination.pageSize
|
|
|
|
searchForm.size = pagination.pageSize
|
|
|
|
handleFetchTableData()
|
|
|
|
handleFetchTableData()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleSearch = () => {
|
|
|
|
const handleSearch = () => {
|
|
|
|
searchForm.page = 1
|
|
|
|
searchForm.page = 1
|
|
|
|
handleFetchTableData()
|
|
|
|
handleFetchTableData()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleReset = () => {
|
|
|
|
const handleReset = () => {
|
|
|
|
searchForm.name = ""
|
|
|
|
searchForm.name = ""
|
|
|
|
searchForm.startTime = ""
|
|
|
|
searchForm.startTime = ""
|
|
|
|
searchForm.endTime = ""
|
|
|
|
searchForm.endTime = ""
|
|
|
|
@@ -982,16 +982,16 @@ const handleReset = () => {
|
|
|
|
searchForm.id = ""
|
|
|
|
searchForm.id = ""
|
|
|
|
searchForm.countryOrRegion = null
|
|
|
|
searchForm.countryOrRegion = null
|
|
|
|
handleSearch()
|
|
|
|
handleSearch()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const openCreate = () => {
|
|
|
|
const openCreate = () => {
|
|
|
|
modalTitle.value = "New Subscription Plan"
|
|
|
|
modalTitle.value = "New Subscription Plan"
|
|
|
|
isEditMode.value = false
|
|
|
|
isEditMode.value = false
|
|
|
|
resetFormState()
|
|
|
|
resetFormState()
|
|
|
|
modalVisible.value = true
|
|
|
|
modalVisible.value = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const openEdit = (record: SubscriptionPlan) => {
|
|
|
|
const openEdit = (record: SubscriptionPlan) => {
|
|
|
|
modalTitle.value = "Edit Subscription Plan"
|
|
|
|
modalTitle.value = "Edit Subscription Plan"
|
|
|
|
isEditMode.value = true
|
|
|
|
isEditMode.value = true
|
|
|
|
formState.name = record.name
|
|
|
|
formState.name = record.name
|
|
|
|
@@ -1009,7 +1009,8 @@ const openEdit = (record: SubscriptionPlan) => {
|
|
|
|
if (record.organizationId) {
|
|
|
|
if (record.organizationId) {
|
|
|
|
const orgExists = organizationOptions.value.some(
|
|
|
|
const orgExists = organizationOptions.value.some(
|
|
|
|
(org: any) =>
|
|
|
|
(org: any) =>
|
|
|
|
org.id === record.organizationId || String(org.id) === String(record.organizationId)
|
|
|
|
org.id === record.organizationId ||
|
|
|
|
|
|
|
|
String(org.id) === String(record.organizationId)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
if (!orgExists) {
|
|
|
|
if (!orgExists) {
|
|
|
|
const orgName = (record as any).organizationName
|
|
|
|
const orgName = (record as any).organizationName
|
|
|
|
@@ -1034,9 +1035,9 @@ const openEdit = (record: SubscriptionPlan) => {
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const validateForm = (): boolean => {
|
|
|
|
const validateForm = (): boolean => {
|
|
|
|
interface FieldRule {
|
|
|
|
interface FieldRule {
|
|
|
|
value: any
|
|
|
|
value: any
|
|
|
|
message: string
|
|
|
|
message: string
|
|
|
|
@@ -1077,9 +1078,9 @@ const validateForm = (): boolean => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleSubmit = async () => {
|
|
|
|
const handleSubmit = async () => {
|
|
|
|
if (!validateForm()) return
|
|
|
|
if (!validateForm()) return
|
|
|
|
confirmLoading.value = true
|
|
|
|
confirmLoading.value = true
|
|
|
|
const params = {
|
|
|
|
const params = {
|
|
|
|
@@ -1107,21 +1108,21 @@ const handleSubmit = async () => {
|
|
|
|
isEditMode.value = false
|
|
|
|
isEditMode.value = false
|
|
|
|
handleSearch()
|
|
|
|
handleSearch()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 防抖包装,避免重复点击
|
|
|
|
// 防抖包装,避免重复点击
|
|
|
|
const handleSubmitDebounced = debounce(handleSubmit, 500, {
|
|
|
|
const handleSubmitDebounced = debounce(handleSubmit, 500, {
|
|
|
|
leading: true,
|
|
|
|
leading: true,
|
|
|
|
trailing: false
|
|
|
|
trailing: false
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const cancelModal = () => {
|
|
|
|
const cancelModal = () => {
|
|
|
|
modalVisible.value = false
|
|
|
|
modalVisible.value = false
|
|
|
|
resetFormState()
|
|
|
|
resetFormState()
|
|
|
|
isEditMode.value = false
|
|
|
|
isEditMode.value = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const removePlan = (id: number) => {
|
|
|
|
const removePlan = (id: number) => {
|
|
|
|
tableLoading.value = true
|
|
|
|
tableLoading.value = true
|
|
|
|
Https.axiosGet(Https.httpUrls.deleteSubscribePlan, { params: { id } })
|
|
|
|
Https.axiosGet(Https.httpUrls.deleteSubscribePlan, { params: { id } })
|
|
|
|
.then((res) => {
|
|
|
|
.then((res) => {
|
|
|
|
@@ -1135,16 +1136,16 @@ const removePlan = (id: number) => {
|
|
|
|
.finally(() => {
|
|
|
|
.finally(() => {
|
|
|
|
tableLoading.value = false
|
|
|
|
tableLoading.value = false
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const organizationOptions = ref([])
|
|
|
|
const organizationOptions = ref([])
|
|
|
|
const organizationParams = reactive({
|
|
|
|
const organizationParams = reactive({
|
|
|
|
page: 1,
|
|
|
|
page: 1,
|
|
|
|
size: 10,
|
|
|
|
size: 10,
|
|
|
|
total: 0
|
|
|
|
total: 0
|
|
|
|
})
|
|
|
|
})
|
|
|
|
const organizationLoading = ref(false)
|
|
|
|
const organizationLoading = ref(false)
|
|
|
|
const getOrganizationList = async (isLoadMore = false) => {
|
|
|
|
const getOrganizationList = async (isLoadMore = false) => {
|
|
|
|
if (organizationLoading.value) return
|
|
|
|
if (organizationLoading.value) return
|
|
|
|
if (isLoadMore) {
|
|
|
|
if (isLoadMore) {
|
|
|
|
const loaded = organizationParams.page * organizationParams.size
|
|
|
|
const loaded = organizationParams.page * organizationParams.size
|
|
|
|
@@ -1179,17 +1180,17 @@ const getOrganizationList = async (isLoadMore = false) => {
|
|
|
|
} finally {
|
|
|
|
} finally {
|
|
|
|
organizationLoading.value = false
|
|
|
|
organizationLoading.value = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const handleOrganizationScroll = (e: any) => {
|
|
|
|
const handleOrganizationScroll = (e: any) => {
|
|
|
|
const target = e?.target
|
|
|
|
const target = e?.target
|
|
|
|
if (!target) return
|
|
|
|
if (!target) return
|
|
|
|
const nearBottom = target.scrollTop + target.clientHeight >= target.scrollHeight - 20
|
|
|
|
const nearBottom = target.scrollTop + target.clientHeight >= target.scrollHeight - 20
|
|
|
|
if (nearBottom) {
|
|
|
|
if (nearBottom) {
|
|
|
|
getOrganizationList(true)
|
|
|
|
getOrganizationList(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleOrganizationSelect = (value: string) => {
|
|
|
|
const handleOrganizationSelect = (value: string) => {
|
|
|
|
if (value === "ADD_ORGANIZATION") {
|
|
|
|
if (value === "ADD_ORGANIZATION") {
|
|
|
|
// 打开添加组织弹窗
|
|
|
|
// 打开添加组织弹窗
|
|
|
|
organizationModalVisible.value = true
|
|
|
|
organizationModalVisible.value = true
|
|
|
|
@@ -1203,9 +1204,9 @@ const handleOrganizationSelect = (value: string) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleOrganizationChange = (value: string) => {
|
|
|
|
const handleOrganizationChange = (value: string) => {
|
|
|
|
// 如果change事件触发时值是"添加组织",立即重置
|
|
|
|
// 如果change事件触发时值是"添加组织",立即重置
|
|
|
|
if (value === "ADD_ORGANIZATION") {
|
|
|
|
if (value === "ADD_ORGANIZATION") {
|
|
|
|
nextTick(() => {
|
|
|
|
nextTick(() => {
|
|
|
|
@@ -1217,15 +1218,15 @@ const handleOrganizationChange = (value: string) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const cancelOrganizationModal = () => {
|
|
|
|
const cancelOrganizationModal = () => {
|
|
|
|
organizationModalVisible.value = false
|
|
|
|
organizationModalVisible.value = false
|
|
|
|
organizationForm.name = ""
|
|
|
|
organizationForm.name = ""
|
|
|
|
organizationForm.type = undefined
|
|
|
|
organizationForm.type = undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleCreateOrganization = async () => {
|
|
|
|
const handleCreateOrganization = async () => {
|
|
|
|
if (!organizationForm.name || !organizationForm.type) {
|
|
|
|
if (!organizationForm.name || !organizationForm.type) {
|
|
|
|
message.warning("Please fill in name and type")
|
|
|
|
message.warning("Please fill in name and type")
|
|
|
|
return
|
|
|
|
return
|
|
|
|
@@ -1252,17 +1253,17 @@ const handleCreateOrganization = async () => {
|
|
|
|
message.error(error.message || "Failed to create organization")
|
|
|
|
message.error(error.message || "Failed to create organization")
|
|
|
|
console.error(error)
|
|
|
|
console.error(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const filterOption = (input: string, option: any) => {
|
|
|
|
const filterOption = (input: string, option: any) => {
|
|
|
|
const label = option?.label ?? option?.children ?? option?.key?.label ?? ""
|
|
|
|
const label = option?.label ?? option?.children ?? option?.key?.label ?? ""
|
|
|
|
return String(label).toLowerCase().includes(input.toLowerCase())
|
|
|
|
return String(label).toLowerCase().includes(input.toLowerCase())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
|
<style lang="less" scoped>
|
|
|
|
.subscription-plan {
|
|
|
|
.subscription-plan {
|
|
|
|
padding: 2rem 2.4rem 3.2rem 0;
|
|
|
|
padding: 2rem 2.4rem 0 0;
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
height: 100%;
|
|
|
|
height: 100%;
|
|
|
|
min-height: 0;
|
|
|
|
min-height: 0;
|
|
|
|
@@ -1285,7 +1286,7 @@ const filterOption = (input: string, option: any) => {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
flex-direction: column;
|
|
|
|
overflow: hidden;
|
|
|
|
overflow: hidden;
|
|
|
|
padding: 2.4rem;
|
|
|
|
padding: 2.4rem 2.4rem 0;
|
|
|
|
min-height: 0;
|
|
|
|
min-height: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1364,15 +1365,15 @@ const filterOption = (input: string, option: any) => {
|
|
|
|
cursor: default;
|
|
|
|
cursor: default;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.user-info-modal {
|
|
|
|
.user-info-modal {
|
|
|
|
:deep(.ant-modal-body) {
|
|
|
|
:deep(.ant-modal-body) {
|
|
|
|
padding: 2.4rem;
|
|
|
|
padding: 2.4rem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.subscriptionPlan_modal) {
|
|
|
|
:deep(.subscriptionPlan_modal) {
|
|
|
|
.ant-modal-body {
|
|
|
|
.ant-modal-body {
|
|
|
|
// height: calc(65rem * 1.2);
|
|
|
|
// height: calc(65rem * 1.2);
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
@@ -1380,9 +1381,9 @@ const filterOption = (input: string, option: any) => {
|
|
|
|
padding: 2.5rem 3rem;
|
|
|
|
padding: 2.5rem 3rem;
|
|
|
|
position: relative;
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.subscriptionPlan_modal {
|
|
|
|
.subscriptionPlan_modal {
|
|
|
|
.form_content {
|
|
|
|
.form_content {
|
|
|
|
width: 100%;
|
|
|
|
width: 100%;
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
@@ -1420,9 +1421,9 @@ const filterOption = (input: string, option: any) => {
|
|
|
|
margin-bottom: 0;
|
|
|
|
margin-bottom: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.search-form) {
|
|
|
|
:deep(.search-form) {
|
|
|
|
--search-label-width: 14rem;
|
|
|
|
--search-label-width: 14rem;
|
|
|
|
--search-control-width: 20rem;
|
|
|
|
--search-control-width: 20rem;
|
|
|
|
align-items: flex-start;
|
|
|
|
align-items: flex-start;
|
|
|
|
@@ -1466,15 +1467,15 @@ const filterOption = (input: string, option: any) => {
|
|
|
|
max-width: none;
|
|
|
|
max-width: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 1600px) {
|
|
|
|
@media (min-width: 1600px) {
|
|
|
|
:deep(.search-form) {
|
|
|
|
:deep(.search-form) {
|
|
|
|
--search-control-width: 22rem;
|
|
|
|
--search-control-width: 22rem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 760px) {
|
|
|
|
@media (max-width: 760px) {
|
|
|
|
:deep(.search-form) {
|
|
|
|
:deep(.search-form) {
|
|
|
|
.ant-form-item {
|
|
|
|
.ant-form-item {
|
|
|
|
flex: 1 1 100%;
|
|
|
|
flex: 1 1 100%;
|
|
|
|
@@ -1486,9 +1487,9 @@ const filterOption = (input: string, option: any) => {
|
|
|
|
max-width: calc(100% - var(--search-label-width));
|
|
|
|
max-width: calc(100% - var(--search-label-width));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.ant-select-dropdown) {
|
|
|
|
:deep(.ant-select-dropdown) {
|
|
|
|
.add-organization-option {
|
|
|
|
.add-organization-option {
|
|
|
|
color: #1890ff !important;
|
|
|
|
color: #1890ff !important;
|
|
|
|
font-weight: 600;
|
|
|
|
font-weight: 600;
|
|
|
|
@@ -1501,9 +1502,9 @@ const filterOption = (input: string, option: any) => {
|
|
|
|
background-color: #e6f4ff !important;
|
|
|
|
background-color: #e6f4ff !important;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.organization_modal) {
|
|
|
|
:deep(.organization_modal) {
|
|
|
|
.ant-modal-body {
|
|
|
|
.ant-modal-body {
|
|
|
|
height: auto;
|
|
|
|
height: auto;
|
|
|
|
min-height: 300px;
|
|
|
|
min-height: 300px;
|
|
|
|
@@ -1523,13 +1524,13 @@ const filterOption = (input: string, option: any) => {
|
|
|
|
.modal_title_text {
|
|
|
|
.modal_title_text {
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.organization_footer {
|
|
|
|
.organization_footer {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
justify-content: flex-end;
|
|
|
|
justify-content: flex-end;
|
|
|
|
column-gap: 3rem;
|
|
|
|
column-gap: 3rem;
|
|
|
|
.footer_btn {
|
|
|
|
.footer_btn {
|
|
|
|
border-radius: 3.3rem;
|
|
|
|
border-radius: 3.3rem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</style>
|
|
|
|
|