Compare commits
17 Commits
e4fc51c574
...
StableVers
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ed5a37e5b | ||
|
|
5546c71ec0 | ||
|
|
8a7776a4b6 | ||
|
|
a1281c8e3f | ||
|
|
9a40e69081 | ||
|
|
e27b43dc67 | ||
|
|
b6a55a8124 | ||
| 6cace08a51 | |||
| 6207095221 | |||
|
|
7bb38bf2e5 | ||
|
|
743fc762d6 | ||
| 7297e4e7a4 | |||
| 3bff1ebb66 | |||
| 0d1656ee0a | |||
| 7d0873d874 | |||
| 82941bca7c | |||
| 949ff9292d |
@@ -9,6 +9,11 @@
|
||||
"id": 2,
|
||||
"title": "AiDA X SFT AI Fashion Award 2024",
|
||||
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "AiDA Global Design Awards 2026",
|
||||
"imgUrl": "/image/events/award-poster.gif"
|
||||
}
|
||||
],
|
||||
"eventsItem": [
|
||||
@@ -16,18 +21,19 @@
|
||||
"id": 3,
|
||||
"title": "AiDA Global Design Awards 2026",
|
||||
"imgUrl": "/image/events/award-poster.gif",
|
||||
"tips": "For inquiries: awards2026@code-create.com.hk",
|
||||
"textList": [
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "Click the “View Details” button for more information and to join the competition! The AiDA Global Design Award 2026 is an international design competition hosted by Code‑Create, a globally leading AI fashion solutions provider, celebrating the future of creativity powered by artificial intelligence. Open to designers from Hong Kong, China, Singapore, South Korea, and beyond, the competition brings together global talent, empowering AI as a creative partner—pushing fashion beyond traditional boundaries and unlocking new possibilities where technology amplifies human imagination."
|
||||
"text": "Click the “View Details” button for more information and to join the competition! The AiDA Global Design Award 2026 is an international design competition hosted by Code‑Create, a globally leading AI fashion solutions provider, celebrating the future of creativity powered by artificial intelligence. Open to designers worldwide the competition brings together global talent, empowering AI as a creative partner—pushing fashion beyond traditional boundaries and unlocking new possibilities where technology amplifies human imagination."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "Participants have the opportunity to compete for cash prizes totaling up to US$9,000, gain global media exposure showcased by top international platforms, and connect with designers and industry leaders worldwide. Finalists will also attend an exclusive award ceremony in Hong Kong, with travel support provided, allowing them to showcase their talent, network with professionals, and celebrate their achievements on an international stage."
|
||||
"text": "Participants have the opportunity to compete for cash prizes totaling up to US$9,000, gain global media exposure showcased by top international platforms, and connect with designers and industry leaders worldwide. Finalists will also attend an exclusive award ceremony in Hong Kong, with travel allowance, allowing them to showcase their talent, network with professionals, and celebrate their achievements on an international stage."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
"id": 2,
|
||||
"title": "AiDA X SFT AI时尚设计比赛2024",
|
||||
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "AiDA全球设计奖 2026",
|
||||
"imgUrl": "/image/events/award-poster-zh.gif"
|
||||
}
|
||||
],
|
||||
"eventsItem": [
|
||||
@@ -16,18 +21,19 @@
|
||||
"id": 3,
|
||||
"title": "AiDA全球设计奖 2026",
|
||||
"imgUrl": "/image/events/award-poster-zh.gif",
|
||||
"tips": "如有疑问,请联系:awards2026@code-create.com.hk",
|
||||
"textList": [
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "秉承推动 AI 赋能创意设计的初衷,Code‑Create 举办了「AiDA 全球设计大奖 2026」,面向来自香港、中国、新加坡、韩国及全球的设计师,鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。点击“查看详情”按钮获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧!"
|
||||
"text": "秉承推动 AI 赋能创意设计的初衷,Code‑Create 举办了「AiDA 全球设计大奖 2026」,面向来全球的设计师,鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。点击“查看详情”按钮获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧!"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅支持,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。"
|
||||
"text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅津贴,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,21 +2,36 @@
|
||||
<div class="account_systemMessage">
|
||||
<div class="account_generalMessage_title modal_title_text">
|
||||
<!-- <span>系统消息</span> -->
|
||||
<div class="account_generalMessage_title_setting" @click="allRead">{{$t('account.AllRead')}}</div>
|
||||
<div class="account_generalMessage_title_setting" @click="allRead">
|
||||
{{ $t("account.AllRead") }}
|
||||
</div>
|
||||
<div class="account_generalMessage_item modal_title_text" v-for="item in dataList" :key="item.id" @click="setRead(item)">
|
||||
</div>
|
||||
<div
|
||||
class="account_generalMessage_item modal_title_text"
|
||||
v-for="item in dataList"
|
||||
:key="item.id"
|
||||
@click="setRead(item)"
|
||||
>
|
||||
<a-badge :dot="item.isRead == 0"></a-badge>
|
||||
<div class="account_generalMessage_item_title">
|
||||
<div class="account_generalMessage_item_title_text" :title="item.content">{{ item.content.title }}</div>
|
||||
<div class="account_generalMessage_item_title_text" :title="item.content">
|
||||
{{ item.content.title }}
|
||||
</div>
|
||||
<div class="modal_title_text_intro">{{ item.createTime }}</div>
|
||||
</div>
|
||||
<div class="modal_title_text_intro">
|
||||
{{ item.content.content }}
|
||||
<span v-if="item.content.link" class="account_generalMessage_item_link">{{ item.content.link }}</span>
|
||||
<span v-if="item.content.link" class="account_generalMessage_item_link">{{
|
||||
item.content.link
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="account_generalMessage_item modal_title_text" style="display:flex;justify-content: center;" v-if="dataList.length == 0 && isNoData">
|
||||
{{$t('account.dataNull')}}
|
||||
<div
|
||||
class="account_generalMessage_item modal_title_text"
|
||||
style="display: flex; justify-content: center"
|
||||
v-if="dataList.length == 0 && isNoData"
|
||||
>
|
||||
{{ $t("account.dataNull") }}
|
||||
</div>
|
||||
<div class="page_loading_box" v-show="!isNoData">
|
||||
<span class="page_loading" ref="loadingDom" v-show="!isShowMark"></span>
|
||||
@@ -27,34 +42,44 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent,computed,ref,reactive,nextTick,toRefs,createVNode, onMounted} from 'vue'
|
||||
import { Https } from "@/tool/https";
|
||||
import { useRouter,useRoute } from 'vue-router'
|
||||
import { useStore } from "vuex";
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
reactive,
|
||||
nextTick,
|
||||
toRefs,
|
||||
createVNode,
|
||||
onMounted
|
||||
} from "vue"
|
||||
import { Https } from "@/tool/https"
|
||||
import { useRouter, useRoute } from "vue-router"
|
||||
import { useStore } from "vuex"
|
||||
import { useI18n } from "vue-i18n"
|
||||
import { isValidUrl } from "@/tool/util"
|
||||
export default defineComponent({
|
||||
components:{
|
||||
},
|
||||
components: {},
|
||||
// emits:['putListData'],
|
||||
props:['setReadStatus','setAllmessage','getHistory'],
|
||||
props: ["setReadStatus", "setAllmessage", "getHistory"],
|
||||
setup(prop, { emit }) {
|
||||
const router = useRouter()
|
||||
const store = useStore();
|
||||
const store = useStore()
|
||||
let accountMessage = reactive({
|
||||
dataList: [],
|
||||
page: 1,
|
||||
size: 10,
|
||||
isNoData: false,
|
||||
isShowMark: false,
|
||||
isShowMark: false
|
||||
})
|
||||
let loadingDom: any = ref(null)
|
||||
let setmessageList = () => {
|
||||
accountMessage.isShowMark = true
|
||||
let data = {
|
||||
page: accountMessage.page,
|
||||
size: accountMessage.size,
|
||||
size: accountMessage.size
|
||||
}
|
||||
prop.getHistory(data).then((rv:any)=>{
|
||||
prop.getHistory(data)
|
||||
.then((rv: any) => {
|
||||
accountMessage.isShowMark = false
|
||||
|
||||
if (rv.content.length == 0) {
|
||||
@@ -62,58 +87,67 @@ export default defineComponent({
|
||||
} else {
|
||||
rv.content.forEach((item: any) => {
|
||||
item.content = JSON.parse(item.content)
|
||||
});
|
||||
})
|
||||
accountMessage.dataList.push(...rv.content)
|
||||
}
|
||||
}).catch(() => {
|
||||
})
|
||||
.catch(() => {
|
||||
accountMessage.isShowMark = false
|
||||
accountMessage.isNoData = true
|
||||
})
|
||||
}
|
||||
let setRead = (item: any) => {
|
||||
prop.setReadStatus(item).then((rv:any)=>{
|
||||
let content = item.content.content
|
||||
if (isValidUrl(content)) {
|
||||
if (import.meta.env.VITE_APP_BASE_URL === "https://develop.api.aida.com.hk") {
|
||||
content += "&env=dev"
|
||||
}
|
||||
window.open(content, "_blank")
|
||||
}
|
||||
|
||||
prop.setReadStatus(item)
|
||||
.then((rv: any) => {
|
||||
item.isRead = 1
|
||||
}).catch((err:any)=>{
|
||||
})
|
||||
.catch((err: any) => {})
|
||||
}
|
||||
let allRead = () => {
|
||||
// emit('setAllmessage')
|
||||
prop.setAllmessage().then(()=>{
|
||||
prop.setAllmessage()
|
||||
.then(() => {
|
||||
accountMessage.dataList.forEach((item: any) => {
|
||||
item.isRead = 1
|
||||
})
|
||||
}).catch((err:any)=>{
|
||||
})
|
||||
.catch((err: any) => {})
|
||||
}
|
||||
// provide('exhibitionList',exhibitionList)
|
||||
onMounted(() => {
|
||||
accountMessage.isNoData = false
|
||||
accountMessage.page = 0
|
||||
let imgParent:any = document.querySelector('.account_systemMessage .page_loading')
|
||||
let imgParent: any = document.querySelector(".account_systemMessage .page_loading")
|
||||
new IntersectionObserver(
|
||||
(entries, observer) => {
|
||||
// 如果不是相交,则直接返回
|
||||
// console.log(entries[0]);
|
||||
if (!entries[0].intersectionRatio) return;
|
||||
if (!entries[0].intersectionRatio) return
|
||||
accountMessage.page += 1
|
||||
setmessageList()
|
||||
},
|
||||
}
|
||||
// { root:worksPage }
|
||||
).observe(loadingDom.value);
|
||||
).observe(loadingDom.value)
|
||||
})
|
||||
return {
|
||||
...toRefs(accountMessage),
|
||||
setmessageList,
|
||||
setRead,
|
||||
allRead,
|
||||
loadingDom,
|
||||
loadingDom
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return{
|
||||
|
||||
return {}
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@@ -133,7 +167,6 @@ export default defineComponent({
|
||||
}
|
||||
.modal_title_text_intro {
|
||||
margin-left: 4rem;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,11 @@
|
||||
style="width: 250px"
|
||||
class="range_picker"
|
||||
v-model:value="rangePickerValue"
|
||||
:placeholder="[
|
||||
$t('HistoryPage.StartDate'),
|
||||
$t('HistoryPage.EndDate'),
|
||||
]"
|
||||
:placeholder="[$t('HistoryPage.StartDate'), $t('HistoryPage.EndDate')]"
|
||||
valueFormat="YYYY-MM-DD"
|
||||
>
|
||||
<template #suffixIcon>
|
||||
<span
|
||||
class="icon iconfont range_picker_icon icon-rili"
|
||||
></span>
|
||||
<span class="icon iconfont range_picker_icon icon-rili"></span>
|
||||
</template>
|
||||
</a-range-picker>
|
||||
</div>
|
||||
@@ -63,38 +58,50 @@
|
||||
show-search
|
||||
></a-select>
|
||||
</div>
|
||||
<div class="admin_state_item">
|
||||
<span>Orgnization:</span>
|
||||
<a-select
|
||||
v-model:value="organizationId"
|
||||
placeholder="Select the organization"
|
||||
allow-clear
|
||||
style="width: 250px"
|
||||
@popupScroll="handleOrganizationScroll"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in organizationOptions"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin_search">
|
||||
<div class="admin_search_item" @click="searchHistoryList" :style="{height:isAwayOrUnfold?'4rem':''}">
|
||||
<div
|
||||
class="admin_search_item"
|
||||
@click="searchHistoryList"
|
||||
:style="{ height: isAwayOrUnfold ? '4rem' : '' }"
|
||||
>
|
||||
Search
|
||||
</div>
|
||||
<div class="admin_search_item" @click="addhHistoryList">
|
||||
Add
|
||||
</div>
|
||||
<div class="admin_search_item" @click="addhHistoryList">Add</div>
|
||||
</div>
|
||||
<div class="admin_state_list">
|
||||
<div
|
||||
class="admin_state_list_item"
|
||||
@click="lastGeTrialList('year')"
|
||||
>
|
||||
<div class="admin_state_list_item" @click="lastGeTrialList('year')">
|
||||
Nearly a year
|
||||
</div>
|
||||
<div
|
||||
class="admin_state_list_item"
|
||||
@click="lastGeTrialList('month')"
|
||||
>
|
||||
<div class="admin_state_list_item" @click="lastGeTrialList('month')">
|
||||
Last month
|
||||
</div>
|
||||
<div
|
||||
class="admin_state_list_item"
|
||||
@click="lastGeTrialList('week')"
|
||||
>
|
||||
Last week
|
||||
</div>
|
||||
<div class="admin_state_list_item" @click="lastGeTrialList('week')">Last week</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="awayOrUnfold" :class="{ active: isAwayOrUnfold }">
|
||||
<span class="icon iconfont menu_icon icon-xiala" @click="()=>isAwayOrUnfold = !isAwayOrUnfold"></span>
|
||||
<span
|
||||
class="icon iconfont menu_icon icon-xiala"
|
||||
@click="() => (isAwayOrUnfold = !isAwayOrUnfold)"
|
||||
></span>
|
||||
</div>
|
||||
<div class="admin_table_content" ref="historyTable">
|
||||
<a-table
|
||||
@@ -104,24 +111,19 @@
|
||||
:data-source="dataList"
|
||||
:scroll="{ y: historyTableHeight }"
|
||||
@change="changePage"
|
||||
:showSorterTooltip='false'
|
||||
:showSorterTooltip="false"
|
||||
:pagination="{
|
||||
showSizeChanger: true,
|
||||
current: currentPage,
|
||||
pageSize: pageSize,
|
||||
total: total,
|
||||
showQuickJumper: true,
|
||||
bordered: false,
|
||||
bordered: false
|
||||
}"
|
||||
>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
<div class="operate_list" v-if="column?.Operations">
|
||||
<div
|
||||
class="operate_item"
|
||||
@click="setAagree(record)"
|
||||
>
|
||||
Edit
|
||||
</div>
|
||||
<div class="operate_item" @click="setAagree(record)">Edit</div>
|
||||
<!-- <div
|
||||
class="operate_item"
|
||||
@click="deleteGroup(record, index)"
|
||||
@@ -132,24 +134,19 @@
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
<allUserPoerationsVue ref="allUserPoerationsVue" @searchHistoryList="searchHistoryList"></allUserPoerationsVue>
|
||||
<allUserPoerationsVue
|
||||
ref="allUserPoerationsVue"
|
||||
@searchHistoryList="searchHistoryList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
createVNode,
|
||||
computed,
|
||||
reactive,
|
||||
toRefs,
|
||||
onMounted,
|
||||
} from "vue";
|
||||
import { formatTime } from "@/tool/util";
|
||||
import { useStore } from "vuex";
|
||||
import { Https } from "@/tool/https";
|
||||
import allUserPoerationsVue from "./allUserPoerations.vue";
|
||||
import SelectUser from '@/component/common/SelectUser.vue'
|
||||
import { defineComponent, ref, createVNode, computed, reactive, toRefs, onMounted } from "vue"
|
||||
import { formatTime } from "@/tool/util"
|
||||
import { useStore } from "vuex"
|
||||
import { Https } from "@/tool/https"
|
||||
import allUserPoerationsVue from "./allUserPoerations.vue"
|
||||
import SelectUser from "@/component/common/SelectUser.vue"
|
||||
export default defineComponent({
|
||||
components: { allUserPoerationsVue, SelectUser },
|
||||
setup() {
|
||||
@@ -159,7 +156,7 @@ export default defineComponent({
|
||||
tableLoading: false,
|
||||
allCountry: [],
|
||||
isAwayOrUnfold: false
|
||||
});
|
||||
})
|
||||
let filterData: any = reactive({
|
||||
rangePickerValue: [],
|
||||
currentPage: 1,
|
||||
@@ -172,40 +169,41 @@ export default defineComponent({
|
||||
occupation: "",
|
||||
systemUser: "",
|
||||
order: "", //'Ascending 升序 Descending 降序'
|
||||
orderBy:'',
|
||||
orderBy: "",
|
||||
userName: "",
|
||||
});
|
||||
organizationId: null
|
||||
})
|
||||
let state: any = ref([
|
||||
{
|
||||
label: "all",
|
||||
value: "",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
label:'visitor',
|
||||
value:'0',
|
||||
label: "visitor",
|
||||
value: "0"
|
||||
},
|
||||
{
|
||||
label:'yearly',
|
||||
value:'1',
|
||||
label: "yearly",
|
||||
value: "1"
|
||||
},
|
||||
{
|
||||
label:'monthly',
|
||||
value:'2',
|
||||
label: "monthly",
|
||||
value: "2"
|
||||
},
|
||||
{
|
||||
label:'trial',
|
||||
value:'3',
|
||||
label: "trial",
|
||||
value: "3"
|
||||
},
|
||||
{
|
||||
label: "userInEvent",
|
||||
value: "4",
|
||||
value: "4"
|
||||
},
|
||||
{
|
||||
label: "Edu Admin",
|
||||
value: "7",
|
||||
},
|
||||
]);
|
||||
let renameData: any = ref({}); //修改名字选中的数据
|
||||
value: "7"
|
||||
}
|
||||
])
|
||||
let renameData: any = ref({}) //修改名字选中的数据
|
||||
const columns: any = computed(() => {
|
||||
return [
|
||||
{
|
||||
@@ -215,7 +213,7 @@ export default defineComponent({
|
||||
key: "id",
|
||||
width: 100,
|
||||
fixed: "left",
|
||||
sorter: true,
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: "Email",
|
||||
@@ -246,7 +244,7 @@ export default defineComponent({
|
||||
dataIndex: "language",
|
||||
key: "language",
|
||||
width: 100,
|
||||
ellipsis:true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: "Valid Start Time",
|
||||
@@ -256,12 +254,12 @@ export default defineComponent({
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
customRender: (record: any) => {
|
||||
let time = ''
|
||||
let time = ""
|
||||
if (record.text) {
|
||||
time = formatTime(record.text / 1000, 'YYYY-MM-DD hh:mm:ss')
|
||||
time = formatTime(record.text / 1000, "YYYY-MM-DD hh:mm:ss")
|
||||
}
|
||||
return time
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Valid End Time",
|
||||
@@ -271,19 +269,19 @@ export default defineComponent({
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
customRender: (record: any) => {
|
||||
let time = ''
|
||||
let time = ""
|
||||
if (record.text) {
|
||||
time = formatTime(record.text / 1000, 'YYYY-MM-DD hh:mm:ss')
|
||||
time = formatTime(record.text / 1000, "YYYY-MM-DD hh:mm:ss")
|
||||
}
|
||||
return time
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Country or Region",
|
||||
align: "center",
|
||||
dataIndex: "country",
|
||||
key: "country",
|
||||
width:200,
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: "Create Date",
|
||||
@@ -291,7 +289,7 @@ export default defineComponent({
|
||||
dataIndex: "createDate",
|
||||
key: "createDate",
|
||||
width: 200,
|
||||
sorter: true,
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: "Is Beginner",
|
||||
@@ -301,21 +299,21 @@ export default defineComponent({
|
||||
width: 80,
|
||||
ellipsis: true,
|
||||
customRender: (record: any) => {
|
||||
let str;
|
||||
let str
|
||||
if (record.value == 1) {
|
||||
str = "Yes";
|
||||
str = "Yes"
|
||||
} else {
|
||||
str = "No";
|
||||
str = "No"
|
||||
}
|
||||
return str
|
||||
}
|
||||
return str;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Machine Room Ip',
|
||||
title: "Machine Room Ip",
|
||||
align: "center",
|
||||
dataIndex: "browserIdentifiers",
|
||||
key: "browserIdentifiers",
|
||||
width:200,
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: "Credits",
|
||||
@@ -327,10 +325,10 @@ export default defineComponent({
|
||||
dataIndex: "credits",
|
||||
key: "credits",
|
||||
width: 100,
|
||||
sorter: true,
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: 'User Type',
|
||||
title: "User Type",
|
||||
align: "center",
|
||||
// width: 150,
|
||||
// minWidth: 100,
|
||||
@@ -340,22 +338,22 @@ export default defineComponent({
|
||||
key: "systemUser",
|
||||
width: 100,
|
||||
customRender: (record: any) => {
|
||||
let str;
|
||||
let str
|
||||
if (record.value == 0) {
|
||||
str = "visitor";
|
||||
str = "visitor"
|
||||
} else if (record.value == 1) {
|
||||
str = "yearly";
|
||||
str = "yearly"
|
||||
} else if (record.value == 2) {
|
||||
str = "monthly";
|
||||
str = "monthly"
|
||||
} else if (record.value == 3) {
|
||||
str = "trial";
|
||||
str = "trial"
|
||||
} else if (record.value == 4) {
|
||||
str = "userInEvent";
|
||||
str = "userInEvent"
|
||||
} else if (record.value == 7) {
|
||||
str = "Edu Admin";
|
||||
str = "Edu Admin"
|
||||
}
|
||||
return str
|
||||
}
|
||||
return str;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Operations",
|
||||
@@ -364,58 +362,58 @@ export default defineComponent({
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
// slots:{customRender:'action'}
|
||||
Operations: true,
|
||||
},
|
||||
];
|
||||
});
|
||||
Operations: true
|
||||
}
|
||||
]
|
||||
})
|
||||
//改变页码
|
||||
let changePage = (e: any, filters: any, sorter: any) => {
|
||||
filterData.currentPage = e.current;
|
||||
filterData.pageSize = e.pageSize;
|
||||
filterData.currentPage = e.current
|
||||
filterData.pageSize = e.pageSize
|
||||
if (sorter.order) {
|
||||
if(sorter.columnKey == 'id'){
|
||||
filterData.orderBy = 'id'
|
||||
if (sorter.columnKey == "id") {
|
||||
filterData.orderBy = "id"
|
||||
} else if (sorter.columnKey == "createDate") {
|
||||
filterData.orderBy = 'time'
|
||||
filterData.orderBy = "time"
|
||||
} else if (sorter.columnKey == "credits") {
|
||||
filterData.orderBy = 'credits'
|
||||
filterData.orderBy = "credits"
|
||||
}
|
||||
}
|
||||
if (sorter.order) {
|
||||
filterData.order = sorter.order == "descend" ? "Descending" : "Ascending";
|
||||
filterData.order = sorter.order == "descend" ? "Descending" : "Ascending"
|
||||
} else {
|
||||
filterData.order = ''
|
||||
filterData.order = ""
|
||||
}
|
||||
gettrialList()
|
||||
}
|
||||
gettrialList();
|
||||
};
|
||||
|
||||
//查询列表
|
||||
let searchHistoryList = () => {
|
||||
filterData.currentPage = 1;
|
||||
gettrialList();
|
||||
};
|
||||
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.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 = ""))
|
||||
}
|
||||
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,
|
||||
@@ -430,63 +428,118 @@ export default defineComponent({
|
||||
order: filterData.order,
|
||||
orderBy: filterData.orderBy,
|
||||
userName: filterData.userName,
|
||||
};
|
||||
return data;
|
||||
};
|
||||
organizationId: filterData.organizationId
|
||||
}
|
||||
return data
|
||||
}
|
||||
//获取列表
|
||||
let gettrialList = () => {
|
||||
filter.tableLoading = true;
|
||||
let data = setHistoryListData();
|
||||
Https.axiosPost(Https.httpUrls.getUserInfo, data).then(
|
||||
(rv: any) => {
|
||||
filter.tableLoading = true
|
||||
let data = setHistoryListData()
|
||||
Https.axiosPost(Https.httpUrls.getUserInfo, data).then((rv: any) => {
|
||||
if (rv) {
|
||||
// this.dataList = rv
|
||||
filter.dataList = rv.records;
|
||||
filterData.total = rv.total;
|
||||
filter.tableLoading = false;
|
||||
filter.dataList = rv.records
|
||||
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);
|
||||
clearHistoryList()
|
||||
let currentDate = new Date()
|
||||
let currentTimestamp = Math.floor(currentDate.getTime() / 1000)
|
||||
// 计算30天前的时间戳
|
||||
let thirtyDaysAgoTimestamp;
|
||||
let thirtyDaysAgoTimestamp
|
||||
if (str == "year") {
|
||||
thirtyDaysAgoTimestamp = currentTimestamp - 360 * 24 * 60 * 60;
|
||||
thirtyDaysAgoTimestamp = currentTimestamp - 360 * 24 * 60 * 60
|
||||
} else if (str == "month") {
|
||||
thirtyDaysAgoTimestamp = currentTimestamp - 30 * 24 * 60 * 60;
|
||||
thirtyDaysAgoTimestamp = currentTimestamp - 30 * 24 * 60 * 60
|
||||
} else if (str == "week") {
|
||||
thirtyDaysAgoTimestamp = currentTimestamp - 7 * 24 * 60 * 60;
|
||||
thirtyDaysAgoTimestamp = currentTimestamp - 7 * 24 * 60 * 60
|
||||
}
|
||||
filterData.rangePickerValue[0] = formatTime(thirtyDaysAgoTimestamp, "YYYY-MM-DD")
|
||||
gettrialList()
|
||||
}
|
||||
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;
|
||||
};
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
let addhHistoryList = () => {
|
||||
allUserPoerationsVue.value.init('Add','')
|
||||
};
|
||||
allUserPoerationsVue.value.init("Add", "")
|
||||
}
|
||||
let allUserPoerationsVue = ref()
|
||||
let setAagree = (data: any) => {
|
||||
allUserPoerationsVue.value.init('Edit',data)
|
||||
allUserPoerationsVue.value.init("Edit", data)
|
||||
}
|
||||
|
||||
const organizationOptions = ref([])
|
||||
const organizationParams = reactive({
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
})
|
||||
const organizationLoading = ref(false)
|
||||
const getOrganizationList = async (isLoadMore = false) => {
|
||||
console.log("111111")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
let allCountry: any = sessionStorage.getItem("allCountry");
|
||||
let allCountry: any = sessionStorage.getItem("allCountry")
|
||||
if (allCountry) {
|
||||
filter.allCountry = JSON.parse(allCountry);
|
||||
filter.allCountry = JSON.parse(allCountry)
|
||||
}
|
||||
gettrialList();
|
||||
});
|
||||
gettrialList()
|
||||
getOrganizationList()
|
||||
})
|
||||
return {
|
||||
...toRefs(filter),
|
||||
...toRefs(filterData),
|
||||
@@ -501,22 +554,26 @@ export default defineComponent({
|
||||
filterOption,
|
||||
allUserPoerationsVue,
|
||||
setAagree,
|
||||
};
|
||||
handleOrganizationScroll,
|
||||
getOrganizationList,
|
||||
organizationOptions,
|
||||
organizationParams
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
historyTableHeight: 0,
|
||||
handleResizeColumn: (w: any, col: any) => {
|
||||
col.width = w;
|
||||
},
|
||||
};
|
||||
col.width = w
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let historyTable: any = this.$refs.historyTable;
|
||||
this.historyTableHeight = historyTable.clientHeight - 200;
|
||||
let historyTable: any = this.$refs.historyTable
|
||||
this.historyTableHeight = historyTable.clientHeight - 200
|
||||
},
|
||||
methods: {},
|
||||
});
|
||||
methods: {}
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.admin_page .admin_table_search .admin_state {
|
||||
@@ -524,6 +581,5 @@ export default defineComponent({
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.admin_page {
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -321,6 +321,7 @@ export default defineComponent({
|
||||
data = setEditData()
|
||||
if (!data.userName || !data.userEmail || !data.validEndTime || !data.systemUser)
|
||||
return message.warning('Please check the input box marked with *')
|
||||
delete data.userName
|
||||
Https.axiosPost(Https.httpUrls.modifyUser, {}, { params: data }).then(rv => {
|
||||
if (rv) {
|
||||
cancelDsign()
|
||||
|
||||
99
src/component/Administrator/globalAwardPopularity.vue
Normal file
99
src/component/Administrator/globalAwardPopularity.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="admin_page globalAwardPopularity" ref="adminPage">
|
||||
<div class="admin_table_search">
|
||||
<div class="admin_state">
|
||||
<div class="admin_state_item">
|
||||
<span>Current Time:</span>
|
||||
<span>{{ currentTimeStr }}</span>
|
||||
</div>
|
||||
<div class="admin_state_item">
|
||||
<span>Raw Visi Count:</span>
|
||||
<span>{{ rawVisitCount }}</span>
|
||||
</div>
|
||||
<div class="admin_state_item">
|
||||
<span>Unique Visit Count:</span>
|
||||
<span>{{ uniqueVisitCount }}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="admin_search">
|
||||
<div class="admin_search_item" @click="getGlobalAwardPopularity">
|
||||
<i class="fi fi-br-refresh"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="admin_table_content" ref="questionnaireTable">
|
||||
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, createVNode,toRefs, computed,reactive, onMounted, nextTick } from "vue";
|
||||
import { Https } from "@/tool/https";
|
||||
import type { TableColumnsType } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {},
|
||||
setup() {
|
||||
const currentTime = ref(new Date())
|
||||
const currentTimeStr = computed(()=>{
|
||||
return currentTime.value.toLocaleString()
|
||||
})
|
||||
const rawVisitCount = ref(0)
|
||||
const uniqueVisitCount = ref(0)
|
||||
const getGlobalAwardPopularity = () => {
|
||||
Https.axiosGet(Https.httpUrls.getGlobalAwardPopularity,).then((rv)=>{
|
||||
currentTime.value = new Date()
|
||||
rawVisitCount.value = rv.rawVisitCount
|
||||
uniqueVisitCount.value = rv.uniqueVisitCount
|
||||
})
|
||||
}
|
||||
onMounted(()=>{
|
||||
getGlobalAwardPopularity()
|
||||
})
|
||||
return {
|
||||
currentTimeStr,
|
||||
getGlobalAwardPopularity,
|
||||
rawVisitCount,
|
||||
uniqueVisitCount,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.admin_page.globalAwardPopularity{
|
||||
.admin_table_search{
|
||||
// flex: 1;
|
||||
width: min-content;
|
||||
justify-content: flex-start;
|
||||
border-radius: 2rem;
|
||||
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.2);
|
||||
margin-left: 2rem;
|
||||
flex-wrap: nowrap;
|
||||
gap: 3rem;
|
||||
}
|
||||
.admin_state{
|
||||
flex-direction: column;
|
||||
width: auto;
|
||||
cursor: auto;
|
||||
.admin_state_item{
|
||||
> span{
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.admin_search{
|
||||
i{
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,8 +5,8 @@
|
||||
class="search-form"
|
||||
layout="inline"
|
||||
:model="searchForm"
|
||||
:label-col="{ style: { width: '12rem' } }"
|
||||
:wrapper-col="{ style: { width: '22rem' } }"
|
||||
:label-col="{ style: { width: '14rem' } }"
|
||||
:wrapper-col="{ style: { width: '20rem' } }"
|
||||
>
|
||||
<a-form-item label="ID">
|
||||
<a-input v-model:value="searchForm.id" allow-clear placeholder="Input the id" />
|
||||
@@ -72,7 +72,7 @@
|
||||
:options="countryList"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-form-item class="search-form__actions">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleSearch">Search</a-button>
|
||||
<a-button @click="handleReset">Reset</a-button>
|
||||
@@ -92,6 +92,7 @@
|
||||
:loading="tableLoading"
|
||||
:bordered="false"
|
||||
row-key="id"
|
||||
:customRow="customPlanRow"
|
||||
@change="changePage"
|
||||
@resizeColumn="handleResizeColumn"
|
||||
:scroll="{ y: historyTableHeight }"
|
||||
@@ -107,10 +108,11 @@
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template
|
||||
v-if="
|
||||
column.key === 'currentPeriodStart' || column.key === 'currentPeriodEnd'
|
||||
column.key === 'currentPeriodStart' ||
|
||||
column.key === 'currentPeriodEnd'
|
||||
"
|
||||
>
|
||||
{{ formatTime(record[column.key], 'YYYY-MM-DD hh:mm:ss') }}
|
||||
{{ formatTime(record[column.key], "YYYY-MM-DD hh:mm:ss") }}
|
||||
</template>
|
||||
|
||||
<template v-if="column.key === 'status'">
|
||||
@@ -120,15 +122,15 @@
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'actions'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">Edit</a>
|
||||
<a-space class="plan-row-actions" @click.stop>
|
||||
<a @click.stop="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 class="danger-text" @click.stop>Delete</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
@@ -137,6 +139,50 @@
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="userInfoModalVisible"
|
||||
title="User Info"
|
||||
width="80%"
|
||||
:footer="null"
|
||||
:destroy-on-close="true"
|
||||
class="user-info-modal"
|
||||
>
|
||||
<a-table
|
||||
:columns="userInfoColumns"
|
||||
:data-source="userInfoList"
|
||||
:loading="userInfoLoading"
|
||||
:pagination="{
|
||||
showSizeChanger: true,
|
||||
current: userInfoPage.current,
|
||||
pageSize: userInfoPage.size,
|
||||
total: userInfoPage.total,
|
||||
showQuickJumper: true,
|
||||
bordered: false
|
||||
}"
|
||||
row-key="id"
|
||||
:scroll="{ x: 1280, y: 420 }"
|
||||
@change="changeUserInfoPage"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template
|
||||
v-if="
|
||||
column.key === 'validStartTime' ||
|
||||
column.key === 'validEndTime' ||
|
||||
column.key === 'createDate'
|
||||
"
|
||||
>
|
||||
{{ formatUserTime(record[column.key]) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'systemUser'">
|
||||
{{ getSystemUserLabel(record.systemUser) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'isBeginner'">
|
||||
{{ record.isBeginner == 1 ? "Yes" : "No" }}
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-modal>
|
||||
|
||||
<div class="subscriptionPlanModal" ref="subscriptionPlanModal"></div>
|
||||
<a-modal
|
||||
class="subscriptionPlan_modal generalModel"
|
||||
@@ -213,7 +259,10 @@
|
||||
@select="handleOrganizationSelect"
|
||||
@change="handleOrganizationChange"
|
||||
>
|
||||
<a-select-option value="ADD_ORGANIZATION" class="add-organization-option">
|
||||
<a-select-option
|
||||
value="ADD_ORGANIZATION"
|
||||
class="add-organization-option"
|
||||
>
|
||||
+ Create Organization
|
||||
</a-select-option>
|
||||
<a-select-option
|
||||
@@ -393,27 +442,30 @@ import {
|
||||
ref,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
onActivated,
|
||||
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'
|
||||
} 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'
|
||||
type PlanStatus = "PENDING" | "ACTIVE" | "EXPIRED"
|
||||
interface SubscriptionPlan {
|
||||
id: number
|
||||
userId?: string | number
|
||||
name: string
|
||||
currentPeriodStart: string
|
||||
currentPeriodEnd: string
|
||||
organizationId: string
|
||||
adminAccId: string
|
||||
adminAccEmail?: string
|
||||
status: PlanStatus
|
||||
creditLimit: number
|
||||
accountNum?: number
|
||||
@@ -421,17 +473,32 @@ interface SubscriptionPlan {
|
||||
endStamp: number
|
||||
}
|
||||
|
||||
interface UserInfoRecord {
|
||||
id: number
|
||||
userEmail?: string
|
||||
userName?: string
|
||||
language?: string
|
||||
validStartTime?: number | string
|
||||
validEndTime?: number | string
|
||||
country?: string
|
||||
createDate?: number | string
|
||||
isBeginner?: number
|
||||
browserIdentifiers?: string
|
||||
credits?: number
|
||||
systemUser?: number | string
|
||||
}
|
||||
|
||||
const countryList = ref([])
|
||||
const userRef = ref(null)
|
||||
|
||||
const searchForm = reactive({
|
||||
name: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
name: "",
|
||||
startTime: "",
|
||||
endTime: "",
|
||||
organizationId: undefined as string | undefined,
|
||||
adminAccId: undefined as string | undefined,
|
||||
status: [] as PlanStatus[] | [],
|
||||
id: '',
|
||||
id: "",
|
||||
countryOrRegion: null,
|
||||
page: 1,
|
||||
size: 10,
|
||||
@@ -442,15 +509,24 @@ const toSeconds = (dateStr: string) => Math.floor(new Date(dateStr).getTime() /
|
||||
|
||||
const tableData = ref<SubscriptionPlan[]>([])
|
||||
const tableLoading = ref(false)
|
||||
const userInfoModalVisible = ref(false)
|
||||
const userInfoLoading = ref(false)
|
||||
const userInfoList = ref<UserInfoRecord[]>([])
|
||||
const currentUserInfoPlanId = ref<string | number | null>(null)
|
||||
const userInfoPage = reactive({
|
||||
current: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
})
|
||||
|
||||
const modalVisible = ref(false)
|
||||
const confirmLoading = ref(false)
|
||||
const modalTitle = ref('New Subscription Plan')
|
||||
const modalTitle = ref("New Subscription Plan")
|
||||
const isEditMode = ref(false)
|
||||
const formState = reactive({
|
||||
name: '',
|
||||
currentPeriodStart: '',
|
||||
currentPeriodEnd: '',
|
||||
name: "",
|
||||
currentPeriodStart: "",
|
||||
currentPeriodEnd: "",
|
||||
organizationId: undefined as string | undefined,
|
||||
adminAccId: undefined as string | undefined,
|
||||
creditLimit: null as number | null,
|
||||
@@ -461,44 +537,44 @@ const formState = reactive({
|
||||
|
||||
const organizationModalVisible = ref(false)
|
||||
const organizationForm = reactive({
|
||||
name: '',
|
||||
name: "",
|
||||
type: undefined as string | undefined
|
||||
})
|
||||
|
||||
const statusLabelMap: Record<PlanStatus, string> = {
|
||||
PENDING: 'Pending',
|
||||
ACTIVE: 'Active',
|
||||
EXPIRED: 'Expired'
|
||||
PENDING: "Pending",
|
||||
ACTIVE: "Active",
|
||||
EXPIRED: "Expired"
|
||||
}
|
||||
const statusColorMap: Record<PlanStatus, string> = {
|
||||
PENDING: 'blue',
|
||||
ACTIVE: 'green',
|
||||
EXPIRED: 'red'
|
||||
PENDING: "blue",
|
||||
ACTIVE: "green",
|
||||
EXPIRED: "red"
|
||||
}
|
||||
|
||||
const statusOption = ref([
|
||||
{
|
||||
label: 'Pending',
|
||||
value: 'PENDING'
|
||||
label: "Pending",
|
||||
value: "PENDING"
|
||||
},
|
||||
{
|
||||
label: 'Active',
|
||||
value: 'ACTIVE'
|
||||
label: "Active",
|
||||
value: "ACTIVE"
|
||||
},
|
||||
{
|
||||
label: 'Expired',
|
||||
value: 'EXPIRED'
|
||||
label: "Expired",
|
||||
value: "EXPIRED"
|
||||
}
|
||||
])
|
||||
|
||||
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) => {
|
||||
if (isEditMode.value) {
|
||||
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)
|
||||
}
|
||||
@@ -509,7 +585,7 @@ const range = (start: number, end: number) => {
|
||||
}
|
||||
return result
|
||||
}
|
||||
const disableEndTime = date => {
|
||||
const disableEndTime = (date) => {
|
||||
if (!formState.currentPeriodEnd || !isEditMode.value)
|
||||
return {
|
||||
disabledHours: () => [],
|
||||
@@ -519,7 +595,7 @@ const disableEndTime = date => {
|
||||
|
||||
const specificTime = dayjs.unix(formState.currentPeriodEnd)
|
||||
|
||||
if (date && date.isSame(specificTime, 'day')) {
|
||||
if (date && date.isSame(specificTime, "day")) {
|
||||
// 如果是指定日期当天,禁用时间戳之前的时间
|
||||
const hour = specificTime.hour()
|
||||
const minute = specificTime.minute()
|
||||
@@ -527,7 +603,7 @@ const disableEndTime = date => {
|
||||
|
||||
return {
|
||||
disabledHours: () => Array.from({ length: hour }, (_, i) => i), // 禁用小时之前
|
||||
disabledMinutes: selectedHour => {
|
||||
disabledMinutes: (selectedHour) => {
|
||||
if (selectedHour === hour) {
|
||||
return Array.from({ length: minute }, (_, i) => i) // 同小时,禁用分钟之前
|
||||
}
|
||||
@@ -555,114 +631,328 @@ const normalizeStatus = (status?: string): PlanStatus | undefined => {
|
||||
return upper
|
||||
}
|
||||
const getStatusColor = (status?: string) =>
|
||||
statusColorMap[normalizeStatus(status) as PlanStatus] || 'default'
|
||||
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: "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',
|
||||
title: "Organization",
|
||||
dataIndex: "organizationName",
|
||||
key: "organizationName",
|
||||
align: "center",
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
title: 'Admin Account',
|
||||
dataIndex: 'adminAccEmail',
|
||||
key: 'adminAccEmail',
|
||||
align: 'center',
|
||||
title: "Admin Account",
|
||||
dataIndex: "adminAccEmail",
|
||||
key: "adminAccEmail",
|
||||
align: "center",
|
||||
width: 180,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: 'Sub-Account Num',
|
||||
dataIndex: 'accountNum',
|
||||
key: 'accountNum',
|
||||
align: 'center',
|
||||
title: "Sub-Account Num",
|
||||
dataIndex: "accountNum",
|
||||
key: "accountNum",
|
||||
align: "center",
|
||||
width: 120,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: 'Country or Region',
|
||||
dataIndex: 'countryOrRegion',
|
||||
key: 'countryOrRegion',
|
||||
align: 'center',
|
||||
title: "Country or Region",
|
||||
dataIndex: "countryOrRegion",
|
||||
key: "countryOrRegion",
|
||||
align: "center",
|
||||
width: 120,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: 'Start Time',
|
||||
dataIndex: 'currentPeriodStart',
|
||||
key: 'currentPeriodStart',
|
||||
align: 'center',
|
||||
title: "Start Time",
|
||||
dataIndex: "currentPeriodStart",
|
||||
key: "currentPeriodStart",
|
||||
align: "center",
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: 'End Time',
|
||||
dataIndex: 'currentPeriodEnd',
|
||||
key: 'currentPeriodEnd',
|
||||
align: 'center',
|
||||
title: "End Time",
|
||||
dataIndex: "currentPeriodEnd",
|
||||
key: "currentPeriodEnd",
|
||||
align: "center",
|
||||
width: 200
|
||||
},
|
||||
{ title: 'Status', dataIndex: 'status', key: 'status', align: 'center', width: 100 },
|
||||
{ title: "Status", dataIndex: "status", key: "status", align: "center", width: 100 },
|
||||
{
|
||||
title: 'Credit Limit',
|
||||
dataIndex: 'creditLimit',
|
||||
key: 'creditLimit',
|
||||
align: 'center',
|
||||
title: "Credit Limit",
|
||||
dataIndex: "creditLimit",
|
||||
key: "creditLimit",
|
||||
align: "center",
|
||||
width: 120
|
||||
},
|
||||
{ title: 'Operations', key: 'actions', width: 160, align: 'center', fixed: 'right' }
|
||||
{ title: "Operations", key: "actions", width: 160, align: "center", fixed: "right" }
|
||||
]
|
||||
|
||||
const historyTable = ref()
|
||||
const userInfoColumns = [
|
||||
{ title: "User Id", dataIndex: "id", key: "id", align: "center", width: 100 },
|
||||
{
|
||||
title: "Email",
|
||||
dataIndex: "userEmail",
|
||||
key: "userEmail",
|
||||
align: "center",
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: "User Name",
|
||||
dataIndex: "userName",
|
||||
key: "userName",
|
||||
align: "center",
|
||||
width: 150,
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{
|
||||
title: "Valid Start Time",
|
||||
dataIndex: "validStartTime",
|
||||
key: "validStartTime",
|
||||
align: "center",
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: "Valid End Time",
|
||||
dataIndex: "validEndTime",
|
||||
key: "validEndTime",
|
||||
align: "center",
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: "Country or Region",
|
||||
dataIndex: "country",
|
||||
key: "country",
|
||||
align: "center",
|
||||
width: 180,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: "Credits",
|
||||
dataIndex: "credits",
|
||||
key: "credits",
|
||||
align: "center",
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: "User Type",
|
||||
dataIndex: "systemUser",
|
||||
key: "systemUser",
|
||||
align: "center",
|
||||
width: 120
|
||||
}
|
||||
]
|
||||
|
||||
const systemUserLabelMap: Record<string, string> = {
|
||||
"0": "visitor",
|
||||
"1": "yearly",
|
||||
"2": "monthly",
|
||||
"3": "trial",
|
||||
"4": "userInEvent",
|
||||
"7": "Edu Admin"
|
||||
}
|
||||
|
||||
const getSystemUserLabel = (systemUser?: number | string) => {
|
||||
if (systemUser === undefined || systemUser === null) return ""
|
||||
return systemUserLabelMap[String(systemUser)] || String(systemUser)
|
||||
}
|
||||
|
||||
const formatUserTime = (value?: number | string) => {
|
||||
if (!value) return ""
|
||||
if (typeof value === "number") {
|
||||
return formatTime(value / 1000, "YYYY-MM-DD hh:mm:ss")
|
||||
}
|
||||
if (/^\d+$/.test(value)) {
|
||||
return formatTime(Number(value) / 1000, "YYYY-MM-DD hh:mm:ss")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
const normalizeUserInfoList = (res: any): UserInfoRecord[] => {
|
||||
if (Array.isArray(res)) return res
|
||||
if (Array.isArray(res?.records)) return res.records
|
||||
if (res) return [res]
|
||||
return []
|
||||
}
|
||||
|
||||
const buildUserInfoParams = (id: string | number) => ({
|
||||
endTime: "",
|
||||
startTime: "",
|
||||
size: userInfoPage.size,
|
||||
page: userInfoPage.current,
|
||||
systemUser: "",
|
||||
country: "",
|
||||
email: "",
|
||||
userType: "",
|
||||
ids: [],
|
||||
occupation: "",
|
||||
order: "",
|
||||
orderBy: "",
|
||||
userName: "",
|
||||
subscriptionPlanId: id
|
||||
})
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
if (currentUserInfoPlanId.value === null) return
|
||||
userInfoLoading.value = true
|
||||
userInfoList.value = []
|
||||
|
||||
try {
|
||||
const res = await Https.axiosPost(
|
||||
Https.httpUrls.getUserInfo,
|
||||
buildUserInfoParams(currentUserInfoPlanId.value)
|
||||
)
|
||||
const records = normalizeUserInfoList(res)
|
||||
userInfoList.value = records
|
||||
userInfoPage.total = Number(res?.total ?? records.length)
|
||||
} catch (error: any) {
|
||||
message.error(error.message || "Failed to load user info")
|
||||
console.error(error)
|
||||
} finally {
|
||||
userInfoLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const openUserInfo = async (record: SubscriptionPlan) => {
|
||||
console.log(record)
|
||||
// debugger
|
||||
|
||||
currentUserInfoPlanId.value = record.id
|
||||
userInfoPage.current = 1
|
||||
userInfoModalVisible.value = true
|
||||
await fetchUserInfo()
|
||||
}
|
||||
|
||||
const changeUserInfoPage = (pagination: any) => {
|
||||
userInfoPage.current = pagination.current
|
||||
userInfoPage.size = pagination.pageSize
|
||||
fetchUserInfo()
|
||||
}
|
||||
|
||||
const customPlanRow = (record: SubscriptionPlan) => {
|
||||
return {
|
||||
onClick: (event: MouseEvent) => {
|
||||
const target = event.target as HTMLElement | null
|
||||
if (target?.closest(".plan-row-actions")) return
|
||||
openUserInfo(record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const historyTable = ref<HTMLElement | null>(null)
|
||||
const historyTableHeight = ref(0)
|
||||
const minTableBodyHeight = 120
|
||||
let tableResizeObserver: ResizeObserver | null = null
|
||||
let tableResizeTimer: ReturnType<typeof window.setTimeout> | null = null
|
||||
|
||||
const handleResizeColumn = (w: any, col: any) => {
|
||||
col.width = w
|
||||
}
|
||||
|
||||
const calculateTableHeight = () => {
|
||||
nextTick(() => {
|
||||
if (historyTable.value) {
|
||||
historyTableHeight.value = historyTable.value.clientHeight - 200
|
||||
const getElementOuterHeight = (element: Element | null) => {
|
||||
if (!element) return 0
|
||||
const htmlElement = element as HTMLElement
|
||||
const style = window.getComputedStyle(htmlElement)
|
||||
return (
|
||||
htmlElement.offsetHeight +
|
||||
Number.parseFloat(style.marginTop || "0") +
|
||||
Number.parseFloat(style.marginBottom || "0")
|
||||
)
|
||||
}
|
||||
|
||||
const calculateTableHeight = () => {
|
||||
if (tableResizeTimer) {
|
||||
window.clearTimeout(tableResizeTimer)
|
||||
}
|
||||
tableResizeTimer = window.setTimeout(() => {
|
||||
nextTick(() => {
|
||||
const tableWrapper = historyTable.value
|
||||
if (!tableWrapper) {
|
||||
tableResizeTimer = null
|
||||
return
|
||||
}
|
||||
|
||||
const tableHead = tableWrapper.querySelector(".ant-table-thead") ?? null
|
||||
const pagination = tableWrapper.querySelector(".ant-pagination") ?? null
|
||||
const tableHeadHeight = getElementOuterHeight(tableHead) || 55
|
||||
const paginationHeight = getElementOuterHeight(pagination) || 48
|
||||
const reservedHeight = tableHeadHeight + paginationHeight + 8
|
||||
|
||||
historyTableHeight.value = Math.max(
|
||||
minTableBodyHeight,
|
||||
tableWrapper.clientHeight - reservedHeight
|
||||
)
|
||||
tableResizeTimer = null
|
||||
})
|
||||
}, 50)
|
||||
}
|
||||
|
||||
const handleResize = () => {
|
||||
calculateTableHeight()
|
||||
}
|
||||
|
||||
const setupTableResizeObserver = () => {
|
||||
if (!historyTable.value || typeof ResizeObserver === "undefined") return
|
||||
tableResizeObserver?.disconnect()
|
||||
tableResizeObserver = new ResizeObserver(() => {
|
||||
calculateTableHeight()
|
||||
})
|
||||
tableResizeObserver.observe(historyTable.value)
|
||||
|
||||
const searchCard = historyTable.value
|
||||
.closest(".subscription-plan")
|
||||
?.querySelector(".search-card")
|
||||
if (searchCard) {
|
||||
tableResizeObserver.observe(searchCard)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getOrganizationList()
|
||||
await handleSearch()
|
||||
calculateTableHeight()
|
||||
window.addEventListener('resize', handleResize)
|
||||
const list = sessionStorage.getItem('allCountry')
|
||||
setupTableResizeObserver()
|
||||
window.addEventListener("resize", handleResize)
|
||||
const list = sessionStorage.getItem("allCountry")
|
||||
countryList.value = list ? JSON.parse(list) : []
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
calculateTableHeight()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
window.removeEventListener("resize", handleResize)
|
||||
tableResizeObserver?.disconnect()
|
||||
if (tableResizeTimer) {
|
||||
window.clearTimeout(tableResizeTimer)
|
||||
tableResizeTimer = null
|
||||
}
|
||||
})
|
||||
|
||||
const handleFetchTableData = async () => {
|
||||
tableLoading.value = true
|
||||
return Https.axiosPost(Https.httpUrls.searchAllSubscribePlan, searchForm)
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
tableData.value = res.records
|
||||
searchForm.total = res.total
|
||||
})
|
||||
.finally(() => {
|
||||
tableLoading.value = false
|
||||
calculateTableHeight()
|
||||
})
|
||||
}
|
||||
|
||||
const resetFormState = () => {
|
||||
formState.name = ''
|
||||
formState.currentPeriodStart = ''
|
||||
formState.currentPeriodEnd = ''
|
||||
formState.name = ""
|
||||
formState.currentPeriodStart = ""
|
||||
formState.currentPeriodEnd = ""
|
||||
formState.organizationId = undefined
|
||||
formState.adminAccId = undefined
|
||||
formState.creditLimit = null
|
||||
@@ -683,26 +973,26 @@ const handleSearch = () => {
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
searchForm.name = ''
|
||||
searchForm.startTime = ''
|
||||
searchForm.endTime = ''
|
||||
searchForm.name = ""
|
||||
searchForm.startTime = ""
|
||||
searchForm.endTime = ""
|
||||
searchForm.organizationId = undefined
|
||||
searchForm.adminAccId = undefined
|
||||
searchForm.status = []
|
||||
searchForm.id = ''
|
||||
searchForm.id = ""
|
||||
searchForm.countryOrRegion = null
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
const openCreate = () => {
|
||||
modalTitle.value = 'New Subscription Plan'
|
||||
modalTitle.value = "New Subscription Plan"
|
||||
isEditMode.value = false
|
||||
resetFormState()
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const openEdit = (record: SubscriptionPlan) => {
|
||||
modalTitle.value = 'Edit Subscription Plan'
|
||||
modalTitle.value = "Edit Subscription Plan"
|
||||
isEditMode.value = true
|
||||
formState.name = record.name
|
||||
formState.currentPeriodStart = String(record.currentPeriodStart)
|
||||
@@ -755,25 +1045,25 @@ const validateForm = (): 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.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',
|
||||
message: "Please input credit limit",
|
||||
checkNull: true
|
||||
},
|
||||
{
|
||||
value: formState.accountNum,
|
||||
message: 'Please input account number',
|
||||
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' }
|
||||
{ value: formState.name, message: "Please input the name" },
|
||||
{ value: formState.organizationId, message: "Please select organization" }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -806,7 +1096,7 @@ const handleSubmit = async () => {
|
||||
res = await Https.axiosPost(Https.httpUrls.createSubscribePlan, params)
|
||||
}
|
||||
message.success(
|
||||
`${isEditMode.value ? 'Subscription plan updated' : 'Subscription plan created'}`
|
||||
`${isEditMode.value ? "Subscription plan updated" : "Subscription plan created"}`
|
||||
)
|
||||
} catch (error: any) {
|
||||
message.error(error.message)
|
||||
@@ -835,8 +1125,8 @@ const cancelModal = () => {
|
||||
const removePlan = (id: number) => {
|
||||
tableLoading.value = true
|
||||
Https.axiosGet(Https.httpUrls.deleteSubscribePlan, { params: { id } })
|
||||
.then(res => {
|
||||
message.success('Subscription plan deleted')
|
||||
.then((res) => {
|
||||
message.success("Subscription plan deleted")
|
||||
handleReset()
|
||||
})
|
||||
.catch((error: any) => {
|
||||
@@ -901,15 +1191,15 @@ const handleOrganizationScroll = (e: any) => {
|
||||
}
|
||||
|
||||
const handleOrganizationSelect = (value: string) => {
|
||||
if (value === 'ADD_ORGANIZATION') {
|
||||
if (value === "ADD_ORGANIZATION") {
|
||||
// 打开添加组织弹窗
|
||||
organizationModalVisible.value = true
|
||||
// 使用nextTick确保值被重置,使其不被选中
|
||||
nextTick(() => {
|
||||
if (searchForm.organizationId === 'ADD_ORGANIZATION') {
|
||||
if (searchForm.organizationId === "ADD_ORGANIZATION") {
|
||||
searchForm.organizationId = undefined
|
||||
}
|
||||
if (formState.organizationId === 'ADD_ORGANIZATION') {
|
||||
if (formState.organizationId === "ADD_ORGANIZATION") {
|
||||
formState.organizationId = undefined
|
||||
}
|
||||
})
|
||||
@@ -918,12 +1208,12 @@ const handleOrganizationSelect = (value: string) => {
|
||||
|
||||
const handleOrganizationChange = (value: string) => {
|
||||
// 如果change事件触发时值是"添加组织",立即重置
|
||||
if (value === 'ADD_ORGANIZATION') {
|
||||
if (value === "ADD_ORGANIZATION") {
|
||||
nextTick(() => {
|
||||
if (searchForm.organizationId === 'ADD_ORGANIZATION') {
|
||||
if (searchForm.organizationId === "ADD_ORGANIZATION") {
|
||||
searchForm.organizationId = undefined
|
||||
}
|
||||
if (formState.organizationId === 'ADD_ORGANIZATION') {
|
||||
if (formState.organizationId === "ADD_ORGANIZATION") {
|
||||
formState.organizationId = undefined
|
||||
}
|
||||
})
|
||||
@@ -932,13 +1222,13 @@ const handleOrganizationChange = (value: string) => {
|
||||
|
||||
const cancelOrganizationModal = () => {
|
||||
organizationModalVisible.value = false
|
||||
organizationForm.name = ''
|
||||
organizationForm.name = ""
|
||||
organizationForm.type = undefined
|
||||
}
|
||||
|
||||
const handleCreateOrganization = async () => {
|
||||
if (!organizationForm.name || !organizationForm.type) {
|
||||
message.warning('Please fill in name and type')
|
||||
message.warning("Please fill in name and type")
|
||||
return
|
||||
}
|
||||
try {
|
||||
@@ -948,7 +1238,7 @@ const handleCreateOrganization = async () => {
|
||||
type: organizationForm.type
|
||||
}
|
||||
})
|
||||
message.success('Organization created successfully')
|
||||
message.success("Organization created successfully")
|
||||
cancelOrganizationModal()
|
||||
// 刷新组织列表
|
||||
await getOrganizationList()
|
||||
@@ -960,26 +1250,28 @@ const handleCreateOrganization = async () => {
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error.message || 'Failed to create organization')
|
||||
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 ?? ''
|
||||
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;
|
||||
padding: 2rem 2.4rem 0 0;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
|
||||
.search-card {
|
||||
margin-bottom: 1.6rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
@@ -987,13 +1279,15 @@ const filterOption = (input: string, option: any) => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 2.4rem;
|
||||
padding: 2.4rem 2.4rem 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.table-card__header {
|
||||
@@ -1007,6 +1301,42 @@ const filterOption = (input: string, option: any) => {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
|
||||
:deep(.ant-table-wrapper),
|
||||
:deep(.ant-spin-nested-loading),
|
||||
:deep(.ant-spin-container) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-container) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.ant-table) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.ant-table-container) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.ant-table-content) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-table-body) {
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
:deep(.ant-pagination) {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.danger-text {
|
||||
@@ -1026,8 +1356,23 @@ const filterOption = (input: string, option: any) => {
|
||||
:deep(.ant-table-tbody > tr:hover > td) {
|
||||
background: rgb(202, 202, 202);
|
||||
}
|
||||
|
||||
:deep(.ant-table-tbody > tr) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:deep(.plan-row-actions) {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-info-modal {
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 2.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.subscriptionPlan_modal) {
|
||||
.ant-modal-body {
|
||||
// height: calc(65rem * 1.2);
|
||||
@@ -1079,11 +1424,69 @@ const filterOption = (input: string, option: any) => {
|
||||
}
|
||||
|
||||
:deep(.search-form) {
|
||||
column-gap: 2rem;
|
||||
row-gap: 2rem;
|
||||
--search-label-width: 14rem;
|
||||
--search-control-width: 20rem;
|
||||
align-items: flex-start;
|
||||
column-gap: 1.8rem;
|
||||
row-gap: 1.8rem;
|
||||
|
||||
.ant-form-item {
|
||||
min-width: calc(var(--search-label-width) + var(--search-control-width));
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ant-form-item-label {
|
||||
flex: 0 0 var(--search-label-width);
|
||||
max-width: var(--search-label-width);
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
|
||||
> label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-form-item-control {
|
||||
flex: 0 0 var(--search-control-width);
|
||||
max-width: var(--search-control-width);
|
||||
}
|
||||
|
||||
.ant-input,
|
||||
.ant-input-affix-wrapper,
|
||||
.ant-picker,
|
||||
.ant-select {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.search-form__actions {
|
||||
min-width: auto;
|
||||
|
||||
.ant-form-item-control {
|
||||
flex: 0 0 auto;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1600px) {
|
||||
:deep(.search-form) {
|
||||
--search-control-width: 22rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
:deep(.search-form) {
|
||||
.ant-form-item {
|
||||
flex: 1 1 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.ant-form-item-control {
|
||||
flex: 1 1 auto;
|
||||
max-width: calc(100% - var(--search-label-width));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-select-dropdown) {
|
||||
|
||||
@@ -409,7 +409,7 @@ export class BrushIndicator {
|
||||
// this.show(e.e);
|
||||
this._mouseEnterHandler && this._mouseEnterHandler(e)
|
||||
} else {
|
||||
// requestIdleCallback(() => {
|
||||
// setTimeout(() => {
|
||||
// this.updatePosition(e.e);
|
||||
// });
|
||||
|
||||
|
||||
@@ -133,11 +133,12 @@ export class RedGreenModeManager {
|
||||
this.canvas.on("mouse:up", (event) => {
|
||||
// 可以在这里添加更多逻辑,比如生成图片或更新状态
|
||||
nextTick(() => {
|
||||
requestIdleCallback(async () => {
|
||||
setTimeout(async () => {
|
||||
if (!this.isInitialized) {
|
||||
console.warn("红绿图模式未初始化,无法处理鼠标事件");
|
||||
return;
|
||||
}
|
||||
console.log("鼠标抬起事件触发", this.onImageGenerated);
|
||||
if (this.onImageGenerated) {
|
||||
const imageData = await this.canvasManager.exportImage({
|
||||
restoreOpacityInRedGreen: true, // 恢复红绿图模式下的透明度
|
||||
|
||||
@@ -37,7 +37,6 @@ export class ThumbnailManager {
|
||||
|
||||
// 延迟执行,避免阻塞UI
|
||||
fabricObjects.length > 0 &&
|
||||
requestIdleCallback(() => {
|
||||
setTimeout(async () => {
|
||||
const base64 = await this._generateLayerThumbnailNow(fabricObjects, layer);
|
||||
// this.layerThumbnails.set(layerId, base64);
|
||||
@@ -55,7 +54,6 @@ export class ThumbnailManager {
|
||||
console.error("生成图层缩略图时出错:", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +63,7 @@ export class ThumbnailManager {
|
||||
generateAllLayerThumbnails(layers) {
|
||||
if (!layers || !Array.isArray(layers)) return;
|
||||
|
||||
requestIdleCallback(() => {
|
||||
setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
layers.forEach((layer) => {
|
||||
if (layer && layer.id) {
|
||||
|
||||
@@ -155,7 +155,7 @@ export class LiquifyRealTimeUpdater {
|
||||
// 使用requestAnimationFrame进行批量渲染优化
|
||||
// if (!this.renderingScheduled && !this.config.skipRenderDuringDrag) {
|
||||
// this.renderingScheduled = true;
|
||||
// requestIdleCallback(() => {
|
||||
// setTimeout(() => {
|
||||
// this.canvas.renderAll();
|
||||
// this.renderingScheduled = false;
|
||||
// });
|
||||
|
||||
@@ -331,7 +331,6 @@ export default defineComponent({
|
||||
store.commit('DesignDetail/setCurrentDetailType',str)
|
||||
}
|
||||
const setClothes = async (list:any,str:string)=>{
|
||||
console.log(JSON.parse(JSON.stringify(list)))
|
||||
let clothesList:any = []
|
||||
if(detailData.isEditPattern.value == 'editSketch')await detailDom.canvasBox.submitBase64Data().then((rv)=>{
|
||||
detailData.selectDetail.sketchString = rv
|
||||
@@ -369,7 +368,6 @@ export default defineComponent({
|
||||
// }else if(isCurrent){
|
||||
|
||||
// }
|
||||
console.log(JSON.parse(JSON.stringify(detailData.selectDetail.color)),'=====')
|
||||
color = list[i].color?.rgba?.r != null?`${list[i].color.rgba.r} ${list[i].color.rgba.g} ${list[i].color.rgba.b}`:''
|
||||
gradient = list[i].gradient
|
||||
if((detailData.currentDetailType == 'sketch' && newData?.sketch) || detailData.isEditPattern.value == 'editSketch'){
|
||||
@@ -565,11 +563,14 @@ export default defineComponent({
|
||||
}
|
||||
}else{
|
||||
//走画布合成图片并且直接分割
|
||||
if(detailData.isEditPattern.value !== 'canvasEditor' && detailData.isEditPattern.value !== 'redGreenExample'){
|
||||
if(detailData.isEditPattern.value !== 'canvasEditor'){
|
||||
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
|
||||
}
|
||||
let otherData = await updateOtherLayers('single')
|
||||
await detailDom.canvasBox.updateOtherLayers(otherData)
|
||||
}
|
||||
|
||||
await detailDom.canvasBox.privewDetail()
|
||||
await upDateFrontBackSketch()
|
||||
await uploadSelectDetail()
|
||||
@@ -625,7 +626,7 @@ export default defineComponent({
|
||||
if(detailData.isEditPattern.value && detailData.isEditPattern.value == str){
|
||||
// await detailDom.canvasBox.saveCanvas()
|
||||
await (detailDom.canvasBox as any).privewDetail()
|
||||
if(detailData.isEditPattern.value == 'canvasEditor')await uploadSelectDetail()
|
||||
if(detailData.isEditPattern.value == 'canvasEditor' || detailData.isEditPattern.value == 'redGreenExample')await uploadSelectDetail()
|
||||
detailData.isEditPattern.value = ''
|
||||
}else{
|
||||
// if(detailData.isEditPattern.value && (str == 'canvasEditor' || str == 'redGreenExample')){
|
||||
@@ -780,8 +781,7 @@ export default defineComponent({
|
||||
color.gradient = canvasColor.gradient
|
||||
}
|
||||
}
|
||||
|
||||
if(detailData.isEditPattern.value == 'canvasEditor'){
|
||||
if(detailData.isEditPattern.value == 'canvasEditor' || detailData.isEditPattern.value == 'redGreenExample'){
|
||||
delete detailData.selectDetail.newDetail
|
||||
detailData.selectDetail.trims.prints = allInfo.trims || []
|
||||
detailData.selectDetail.printObject.prints = allInfo.prints || []
|
||||
@@ -804,7 +804,6 @@ export default defineComponent({
|
||||
if(detailData.currentDetailType == 'color'){
|
||||
detailData.detailLeftColorKey++
|
||||
}
|
||||
|
||||
}
|
||||
const canvasReload = async ()=>{
|
||||
if(detailData.isEditPattern.value){
|
||||
|
||||
@@ -124,7 +124,11 @@ export default defineComponent({
|
||||
const handleResize = ()=>{
|
||||
clearTimeout(time)
|
||||
time = setTimeout(()=>{
|
||||
store.commit('DesignDetail/setDesignDetail',getDetailListData.designDetail)
|
||||
let data = {
|
||||
...getDetailListData.designDetail,
|
||||
fromType:'resize',
|
||||
}
|
||||
store.commit('DesignDetail/setDesignDetail',data)
|
||||
getDetailListDom.position?.updataPosition?.()
|
||||
getDetailListDom.modelNav?.setItemPosition?.()
|
||||
getDetailListDom.position?.updateRect?.()
|
||||
|
||||
@@ -189,11 +189,11 @@ export default defineComponent({
|
||||
}
|
||||
return { scaleX, scaleY, rotate };
|
||||
}
|
||||
const initMoveableForSelected = () => {
|
||||
const initMoveableForSelected = async (isDestroy:boolean = false) => {
|
||||
// 销毁旧的实例
|
||||
if(selectItem.imgDomIndex == -1)return
|
||||
if (moveableInstance.value) {
|
||||
moveableInstance.value.destroy();
|
||||
if (moveableInstance?.value?.destroy && !isDestroy) {
|
||||
moveableInstance?.value?.destroy();
|
||||
}
|
||||
|
||||
const selectedEl = elementRefs.value[selectItem.imgDomIndex];
|
||||
@@ -509,7 +509,7 @@ export default defineComponent({
|
||||
watch(()=>detailData.frontBack.front.length,(newValue,oldValue)=>{
|
||||
if(selectItem.selectDetail?.id)selectItem.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == selectItem.selectDetail?.id)
|
||||
setTimeout(()=>{
|
||||
initMoveableForSelected()
|
||||
initMoveableForSelected(oldValue == 0)
|
||||
},100)
|
||||
})
|
||||
const setRevocation = async ()=>{
|
||||
|
||||
@@ -21,7 +21,11 @@
|
||||
{{ $t("event.detail") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal_title_text content" v-for="item in eventsDetail.textList">
|
||||
<div
|
||||
class="modal_title_text content"
|
||||
v-for="item in eventsDetail.textList"
|
||||
:class="{ award: eventsDetail.id === 3 }"
|
||||
>
|
||||
<div class="eventsDetail_content_right_btn_box">
|
||||
<div
|
||||
class="eventsDetail_content_right_btn"
|
||||
@@ -43,6 +47,7 @@
|
||||
v-detailText="introItem.text"
|
||||
></div>
|
||||
</div>
|
||||
<div class="tips" v-if="eventsDetail.tips">{{ eventsDetail.tips }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -120,8 +125,13 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
const openDetail = () => {
|
||||
let language = locale.value === "ENGLISH" ? "en" : "zh"
|
||||
let language = locale.value === "ENGLISH" ? "en" : "cn"
|
||||
let url = `https://aida-global-design-awards.com.hk/${language}`
|
||||
// 如果是dev环境把域名换成http://192.168.31.198
|
||||
|
||||
if (import.meta.env.VITE_APP_BASE_URL === "https://develop.api.aida.com.hk") {
|
||||
url += "?env=dev"
|
||||
}
|
||||
window.open(url, "_blank")
|
||||
|
||||
// router.push("/award/index")
|
||||
@@ -233,7 +243,7 @@ export default defineComponent({
|
||||
}
|
||||
.eventsDetail_content_right {
|
||||
.modal_title_text {
|
||||
letter-spacing: 0.4rem;
|
||||
letter-spacing: 0.3rem;
|
||||
font-weight: 600;
|
||||
&-header {
|
||||
display: flex;
|
||||
@@ -265,18 +275,21 @@ export default defineComponent({
|
||||
.eventsDetail_content_right_btn_box {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
.eventsDetail_content_right_btn {
|
||||
// .eventsDetail_content_right_btn {
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
.modal_title_text:last-child {
|
||||
}
|
||||
// .modal_title_text:last-child {
|
||||
// }
|
||||
.modal_title_text:last-child::after {
|
||||
content: "";
|
||||
display: block;
|
||||
border-top: 3px solid;
|
||||
height: 6rem;
|
||||
}
|
||||
.modal_title_text.award:last-child:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,4 +306,13 @@ export default defineComponent({
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tips{
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: var(--aida-fsize1-4);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.3rem;
|
||||
}
|
||||
.modal_title_text.content.award{
|
||||
line-height: 1.3;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -734,6 +734,8 @@ export default defineComponent({
|
||||
let maxImg = 8
|
||||
if (this.type_.type2 == 'Sketchboard') {
|
||||
maxImg = 20
|
||||
}else if(this.type_.type2 == 'Printboard'){
|
||||
maxImg = 16
|
||||
}
|
||||
let parent: any = this.$parent
|
||||
if (parent.isUseGenerate) {
|
||||
|
||||
@@ -745,6 +745,12 @@ export default defineComponent({
|
||||
"userLikeId": likeItem.id
|
||||
}
|
||||
arrData.push(obj)
|
||||
designData.selectLikeDesign.forEach((v:any)=>{
|
||||
if(v.id === likeItem.id){
|
||||
v.oldSort = v.sort
|
||||
v.sort = likeItem.sort
|
||||
}
|
||||
})
|
||||
})
|
||||
let data = {
|
||||
"userLikeGroupId": userGroupId.value,
|
||||
@@ -1304,9 +1310,9 @@ export default defineComponent({
|
||||
})
|
||||
return
|
||||
}
|
||||
const parents = designData.selectLikeDesign.filter((item:any) => item.resultType === 'Design');
|
||||
const parents = designData.selectLikeDesign.filter((item:any) => item.resultType === 'Design').filter((item:any) => likeDesignCollectionList.value.some((v:any) => (v.id === item.id)));
|
||||
parents.map((parent:any) => {
|
||||
parent.sort = parent.oldSort||parent.sort
|
||||
parent.sort = likeDesignCollectionList.value.find((v:any) => v.id === parent.id)?.sort || parent.oldSort||parent.sort
|
||||
delete parent.oldSort
|
||||
return {
|
||||
...parent,
|
||||
@@ -1533,7 +1539,7 @@ export default defineComponent({
|
||||
this.observerData.time = setTimeout(()=>{
|
||||
|
||||
this.setSystemDesigner(0)
|
||||
this.setDesignItemStyle()
|
||||
// this.setDesignItemStyle()
|
||||
},100)
|
||||
// const { width } = entry.contentRect;
|
||||
}
|
||||
@@ -1931,6 +1937,7 @@ export default defineComponent({
|
||||
this.disLikeLoading = true;
|
||||
Https.axiosPost(Https.httpUrls.designDislike, data)
|
||||
.then((rv: any) => {
|
||||
console.log(rv)
|
||||
if (rv) {
|
||||
this.recycleDomHidden = true
|
||||
this.store.commit("addDesignCollectionList", [design]);
|
||||
|
||||
@@ -258,6 +258,7 @@ methods: {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.content_bottom_item:nth-child(4n){
|
||||
|
||||
@@ -337,6 +337,13 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: { enter: 3 },
|
||||
component: () =>
|
||||
import("@/component/Administrator/SE/getGenerateFrequency/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "globalAwardPopularity",
|
||||
name: "globalAwardPopularity",
|
||||
meta: { enter: 3 },
|
||||
component: () =>
|
||||
import("@/component/Administrator/globalAwardPopularity.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -87,8 +87,10 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
left:0,
|
||||
top:0,
|
||||
}
|
||||
if(data?.fromType !== 'resize'){
|
||||
v.maskMinioUrl = v.layersObject?.[0]?.maskMinioUrl
|
||||
v.maskUrl = v.layersObject?.[0]?.maskUrl
|
||||
}
|
||||
v.layersObject[i].designOpenrtionBtn = false
|
||||
if(v.layersObject[i].imageCategory.indexOf("back") == -1){
|
||||
front[index] = v.layersObject[i]
|
||||
|
||||
@@ -198,6 +198,13 @@ const all = (t)=>{
|
||||
route: '/administrator/subscriptionPlan',
|
||||
key: 'sub14',
|
||||
isShow: true
|
||||
},
|
||||
{
|
||||
name: 'Global Award Popularity',
|
||||
icon: 'usetime',
|
||||
route: '/administrator/globalAwardPopularity',
|
||||
key: 'sub15',
|
||||
isShow: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -346,6 +346,7 @@ export const Https = {
|
||||
switchSubscribePlan: '/api/subscription_plan/switchSubscriptionPlan', // 切换管理员订阅计划
|
||||
switchSubAccountSubscribePlan:
|
||||
'/api/subscription_plan/switchSubAccSubscriptionPlan', // 切换子账号订阅计划
|
||||
getGlobalAwardPopularity: '/api/global-award/page/visit/count', // 获取global award流量
|
||||
|
||||
//云生成
|
||||
designCloud: `/api/design/designCloud`, //创建云生成
|
||||
|
||||
@@ -672,6 +672,17 @@ function sketchToMask(sketchImage) {
|
||||
img.src = sketchImage;
|
||||
});
|
||||
}
|
||||
|
||||
function isValidUrl(string) {
|
||||
try {
|
||||
const url = new URL(string)
|
||||
// 通常我们只需要 http 或 https 协议
|
||||
return url.protocol === "http:" || url.protocol === "https:"
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
isEmail,
|
||||
getUploadUrl,
|
||||
@@ -695,5 +706,6 @@ export {
|
||||
calculateGradientCoordinate,
|
||||
segmentImage,
|
||||
UrlToFile,
|
||||
sketchToMask
|
||||
sketchToMask,
|
||||
isValidUrl
|
||||
}
|
||||
Reference in New Issue
Block a user