33 Commits

Author SHA1 Message Date
李志鹏
3ed5a37e5b 1 2026-05-15 16:25:08 +08:00
李志鹏
5546c71ec0 线稿图手动排序后更新bug 2026-05-15 15:01:17 +08:00
李志鹏
8a7776a4b6 去掉管理员界面的allUser编辑userName 2026-05-15 10:15:18 +08:00
X1627315083@163.com
a1281c8e3f fix 2026-05-14 18:06:01 +08:00
X1627315083@163.com
9a40e69081 fix 2026-05-14 09:27:06 +08:00
X1627315083@163.com
e27b43dc67 管理员页面增加globalAward流量页面 2026-05-13 17:29:05 +08:00
X1627315083@163.com
b6a55a8124 调整选择风格布局 2026-05-12 17:18:16 +08:00
6cace08a51 style: 去除底边距 2026-04-28 17:11:19 +08:00
6207095221 feat: 管理员页面 2026-04-28 17:11:10 +08:00
李志鹏
7bb38bf2e5 处理Safari 不支持requestIdleCallback方法 2026-04-24 17:11:48 +08:00
X1627315083@163.com
743fc762d6 fix 2026-04-23 16:57:03 +08:00
7297e4e7a4 bugfix 2026-04-23 14:08:59 +08:00
3bff1ebb66 feat: 工具函数 2026-04-23 14:05:00 +08:00
0d1656ee0a style: 字体间距 2026-04-23 13:56:54 +08:00
7d0873d874 style: 调整活动页样式 2026-04-23 13:56:47 +08:00
82941bca7c feat: 活动页 2026-04-23 11:22:21 +08:00
949ff9292d feat: 活动页面文案 2026-04-23 11:10:02 +08:00
X1627315083@163.com
11c9de8ced fix 2026-04-22 16:12:23 +08:00
X1627315083@163.com
fd518ad9b3 更新youtube地址 2026-04-22 13:24:23 +08:00
X1627315083@163.com
a2b45e2041 Merge branch 'StableVersion' of ssh://18.167.251.121:10002/aidlab/aida_front into StableVersion 2026-04-17 16:35:14 +08:00
X1627315083@163.com
da64b57c1c 登录页路由加语言设置 2026-04-17 16:35:12 +08:00
李志鹏
7c14b1d831 Merge branch 'StableVersion' of http://18.167.251.121:10003/aidlab/aida_front into StableVersion 2026-04-15 11:37:13 +08:00
李志鹏
8966b52430 红绿图b 2026-04-15 11:37:11 +08:00
X1627315083@163.com
5fa049f73d 隐藏brandDNA功能 2026-04-14 15:30:19 +08:00
X1627315083@163.com
575445f767 fix 2026-04-14 15:27:57 +08:00
李志鹏
f43c56236b 液化苹果浏览器闪屏问题 2026-04-14 10:02:06 +08:00
李志鹏
4352f7c2f4 111 2026-04-13 11:52:24 +08:00
李志鹏
c6b1bdbdf1 红绿图导出问题 2026-04-13 11:20:26 +08:00
X1627315083@163.com
f16aa6ea14 fix 2026-04-10 18:14:20 +08:00
X1627315083@163.com
a25abeb527 fix 2026-04-10 16:54:22 +08:00
X1627315083@163.com
d359cd7763 注释选择信用卡支付和支付宝支付的选项,改为默认信用卡自动续费支付 2026-03-30 10:43:27 +08:00
23085d9a9b Merge branch 'dev_vite' into StableVersion 2026-03-25 10:18:13 +08:00
X1627315083@163.com
35c6dfe29c 印花默认为正片叠底 2026-03-24 14:43:12 +08:00
37 changed files with 5576 additions and 4858 deletions

View File

@@ -9,6 +9,11 @@
"id": 2, "id": 2,
"title": "AiDA X SFT AI Fashion Award 2024", "title": "AiDA X SFT AI Fashion Award 2024",
"imgUrl": "/image/events/Fashion-Award-2024.png" "imgUrl": "/image/events/Fashion-Award-2024.png"
},
{
"id": 3,
"title": "AiDA Global Design Awards 2026",
"imgUrl": "/image/events/award-poster.gif"
} }
], ],
"eventsItem": [ "eventsItem": [
@@ -16,18 +21,19 @@
"id": 3, "id": 3,
"title": "AiDA Global Design Awards 2026", "title": "AiDA Global Design Awards 2026",
"imgUrl": "/image/events/award-poster.gif", "imgUrl": "/image/events/award-poster.gif",
"tips": "For inquiries: awards2026@code-create.com.hk",
"textList": [ "textList": [
{ {
"paragraph": [ "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 CodeCreate, 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 CodeCreate, 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": [ "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."
} }
] ]
} }

View File

@@ -9,6 +9,11 @@
"id": 2, "id": 2,
"title": "AiDA X SFT AI时尚设计比赛2024", "title": "AiDA X SFT AI时尚设计比赛2024",
"imgUrl": "/image/events/Fashion-Award-2024.png" "imgUrl": "/image/events/Fashion-Award-2024.png"
},
{
"id": 3,
"title": "AiDA全球设计奖 2026",
"imgUrl": "/image/events/award-poster-zh.gif"
} }
], ],
"eventsItem": [ "eventsItem": [
@@ -16,18 +21,19 @@
"id": 3, "id": 3,
"title": "AiDA全球设计奖 2026", "title": "AiDA全球设计奖 2026",
"imgUrl": "/image/events/award-poster-zh.gif", "imgUrl": "/image/events/award-poster-zh.gif",
"tips": "如有疑问请联系awards2026@code-create.com.hk",
"textList": [ "textList": [
{ {
"paragraph": [ "paragraph": [
{ {
"text": "秉承推动 AI 赋能创意设计的初衷CodeCreate 举办了「AiDA 全球设计大奖 2026」面向来自香港、中国、新加坡、韩国及全球的设计师,鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。点击“查看详情”按钮获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧!" "text": "秉承推动 AI 赋能创意设计的初衷CodeCreate 举办了「AiDA 全球设计大奖 2026」面向来全球的设计师鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。点击“查看详情”按钮获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧!"
} }
] ]
}, },
{ {
"paragraph": [ "paragraph": [
{ {
"text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅支持,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。" "text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅津贴,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。"
} }
] ]
} }

View File

@@ -2,21 +2,36 @@
<div class="account_systemMessage"> <div class="account_systemMessage">
<div class="account_generalMessage_title modal_title_text"> <div class="account_generalMessage_title modal_title_text">
<!-- <span>系统消息</span> --> <!-- <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>
<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> <a-badge :dot="item.isRead == 0"></a-badge>
<div class="account_generalMessage_item_title"> <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 class="modal_title_text_intro">{{ item.createTime }}</div>
</div> </div>
<div class="modal_title_text_intro"> <div class="modal_title_text_intro">
{{ item.content.content }} {{ 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> </div>
<div class="account_generalMessage_item modal_title_text" style="display:flex;justify-content: center;" v-if="dataList.length == 0 && isNoData"> <div
{{$t('account.dataNull')}} class="account_generalMessage_item modal_title_text"
style="display: flex; justify-content: center"
v-if="dataList.length == 0 && isNoData"
>
{{ $t("account.dataNull") }}
</div> </div>
<div class="page_loading_box" v-show="!isNoData"> <div class="page_loading_box" v-show="!isNoData">
<span class="page_loading" ref="loadingDom" v-show="!isShowMark"></span> <span class="page_loading" ref="loadingDom" v-show="!isShowMark"></span>
@@ -27,34 +42,44 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent,computed,ref,reactive,nextTick,toRefs,createVNode, onMounted} from 'vue' import {
import { Https } from "@/tool/https"; defineComponent,
import { useRouter,useRoute } from 'vue-router' computed,
import { useStore } from "vuex"; ref,
import { useI18n } from 'vue-i18n' 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({ export default defineComponent({
components:{ components: {},
},
// emits:['putListData'], // emits:['putListData'],
props:['setReadStatus','setAllmessage','getHistory'], props: ["setReadStatus", "setAllmessage", "getHistory"],
setup(prop, { emit }) { setup(prop, { emit }) {
const router = useRouter() const router = useRouter()
const store = useStore(); const store = useStore()
let accountMessage = reactive({ let accountMessage = reactive({
dataList: [], dataList: [],
page: 1, page: 1,
size: 10, size: 10,
isNoData: false, isNoData: false,
isShowMark: false, isShowMark: false
}) })
let loadingDom: any = ref(null) let loadingDom: any = ref(null)
let setmessageList = () => { let setmessageList = () => {
accountMessage.isShowMark = true accountMessage.isShowMark = true
let data = { let data = {
page: accountMessage.page, page: accountMessage.page,
size: accountMessage.size, size: accountMessage.size
} }
prop.getHistory(data).then((rv:any)=>{ prop.getHistory(data)
.then((rv: any) => {
accountMessage.isShowMark = false accountMessage.isShowMark = false
if (rv.content.length == 0) { if (rv.content.length == 0) {
@@ -62,58 +87,67 @@ export default defineComponent({
} else { } else {
rv.content.forEach((item: any) => { rv.content.forEach((item: any) => {
item.content = JSON.parse(item.content) item.content = JSON.parse(item.content)
}); })
accountMessage.dataList.push(...rv.content) accountMessage.dataList.push(...rv.content)
} }
}).catch(() => { })
.catch(() => {
accountMessage.isShowMark = false accountMessage.isShowMark = false
accountMessage.isNoData = true accountMessage.isNoData = true
}) })
} }
let setRead = (item: any) => { 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 item.isRead = 1
}).catch((err:any)=>{
}) })
.catch((err: any) => {})
} }
let allRead = () => { let allRead = () => {
// emit('setAllmessage') // emit('setAllmessage')
prop.setAllmessage().then(()=>{ prop.setAllmessage()
.then(() => {
accountMessage.dataList.forEach((item: any) => { accountMessage.dataList.forEach((item: any) => {
item.isRead = 1 item.isRead = 1
}) })
}).catch((err:any)=>{
}) })
.catch((err: any) => {})
} }
// provide('exhibitionList',exhibitionList) // provide('exhibitionList',exhibitionList)
onMounted(() => { onMounted(() => {
accountMessage.isNoData = false accountMessage.isNoData = false
accountMessage.page = 0 accountMessage.page = 0
let imgParent:any = document.querySelector('.account_systemMessage .page_loading') let imgParent: any = document.querySelector(".account_systemMessage .page_loading")
new IntersectionObserver( new IntersectionObserver(
(entries, observer) => { (entries, observer) => {
// 如果不是相交,则直接返回 // 如果不是相交,则直接返回
// console.log(entries[0]); // console.log(entries[0]);
if (!entries[0].intersectionRatio) return; if (!entries[0].intersectionRatio) return
accountMessage.page += 1 accountMessage.page += 1
setmessageList() setmessageList()
}, }
// { root:worksPage } // { root:worksPage }
).observe(loadingDom.value); ).observe(loadingDom.value)
}) })
return { return {
...toRefs(accountMessage), ...toRefs(accountMessage),
setmessageList, setmessageList,
setRead, setRead,
allRead, allRead,
loadingDom, loadingDom
} }
}, },
data() { data() {
return{ return {}
} }
},
}) })
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@@ -133,7 +167,6 @@ export default defineComponent({
} }
.modal_title_text_intro { .modal_title_text_intro {
margin-left: 4rem; margin-left: 4rem;
} }
} }
} }

View File

@@ -8,16 +8,11 @@
style="width: 250px" style="width: 250px"
class="range_picker" class="range_picker"
v-model:value="rangePickerValue" v-model:value="rangePickerValue"
:placeholder="[ :placeholder="[$t('HistoryPage.StartDate'), $t('HistoryPage.EndDate')]"
$t('HistoryPage.StartDate'),
$t('HistoryPage.EndDate'),
]"
valueFormat="YYYY-MM-DD" valueFormat="YYYY-MM-DD"
> >
<template #suffixIcon> <template #suffixIcon>
<span <span class="icon iconfont range_picker_icon icon-rili"></span>
class="icon iconfont range_picker_icon icon-rili"
></span>
</template> </template>
</a-range-picker> </a-range-picker>
</div> </div>
@@ -63,38 +58,50 @@
show-search show-search
></a-select> ></a-select>
</div> </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>
<div class="admin_search"> <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 Search
</div> </div>
<div class="admin_search_item" @click="addhHistoryList"> <div class="admin_search_item" @click="addhHistoryList">Add</div>
Add
</div>
</div> </div>
<div class="admin_state_list"> <div class="admin_state_list">
<div <div class="admin_state_list_item" @click="lastGeTrialList('year')">
class="admin_state_list_item"
@click="lastGeTrialList('year')"
>
Nearly a year Nearly a year
</div> </div>
<div <div class="admin_state_list_item" @click="lastGeTrialList('month')">
class="admin_state_list_item"
@click="lastGeTrialList('month')"
>
Last month Last month
</div> </div>
<div <div class="admin_state_list_item" @click="lastGeTrialList('week')">Last week</div>
class="admin_state_list_item"
@click="lastGeTrialList('week')"
>
Last week
</div>
</div> </div>
</div> </div>
<div class="awayOrUnfold" :class="{ active: isAwayOrUnfold }"> <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>
<div class="admin_table_content" ref="historyTable"> <div class="admin_table_content" ref="historyTable">
<a-table <a-table
@@ -104,24 +111,19 @@
:data-source="dataList" :data-source="dataList"
:scroll="{ y: historyTableHeight }" :scroll="{ y: historyTableHeight }"
@change="changePage" @change="changePage"
:showSorterTooltip='false' :showSorterTooltip="false"
:pagination="{ :pagination="{
showSizeChanger: true, showSizeChanger: true,
current: currentPage, current: currentPage,
pageSize: pageSize, pageSize: pageSize,
total: total, total: total,
showQuickJumper: true, showQuickJumper: true,
bordered: false, bordered: false
}" }"
> >
<template #bodyCell="{ column, text, record, index }"> <template #bodyCell="{ column, text, record, index }">
<div class="operate_list" v-if="column?.Operations"> <div class="operate_list" v-if="column?.Operations">
<div <div class="operate_item" @click="setAagree(record)">Edit</div>
class="operate_item"
@click="setAagree(record)"
>
Edit
</div>
<!-- <div <!-- <div
class="operate_item" class="operate_item"
@click="deleteGroup(record, index)" @click="deleteGroup(record, index)"
@@ -132,24 +134,19 @@
</template> </template>
</a-table> </a-table>
</div> </div>
<allUserPoerationsVue ref="allUserPoerationsVue" @searchHistoryList="searchHistoryList"></allUserPoerationsVue> <allUserPoerationsVue
ref="allUserPoerationsVue"
@searchHistoryList="searchHistoryList"
/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { import { defineComponent, ref, createVNode, computed, reactive, toRefs, onMounted } from "vue"
defineComponent, import { formatTime } from "@/tool/util"
ref, import { useStore } from "vuex"
createVNode, import { Https } from "@/tool/https"
computed, import allUserPoerationsVue from "./allUserPoerations.vue"
reactive, import SelectUser from "@/component/common/SelectUser.vue"
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({ export default defineComponent({
components: { allUserPoerationsVue, SelectUser }, components: { allUserPoerationsVue, SelectUser },
setup() { setup() {
@@ -159,7 +156,7 @@ export default defineComponent({
tableLoading: false, tableLoading: false,
allCountry: [], allCountry: [],
isAwayOrUnfold: false isAwayOrUnfold: false
}); })
let filterData: any = reactive({ let filterData: any = reactive({
rangePickerValue: [], rangePickerValue: [],
currentPage: 1, currentPage: 1,
@@ -172,40 +169,41 @@ export default defineComponent({
occupation: "", occupation: "",
systemUser: "", systemUser: "",
order: "", //'Ascending 升序 Descending 降序' order: "", //'Ascending 升序 Descending 降序'
orderBy:'', orderBy: "",
userName: "", userName: "",
}); organizationId: null
})
let state: any = ref([ let state: any = ref([
{ {
label: "all", label: "all",
value: "", value: ""
}, },
{ {
label:'visitor', label: "visitor",
value:'0', value: "0"
}, },
{ {
label:'yearly', label: "yearly",
value:'1', value: "1"
}, },
{ {
label:'monthly', label: "monthly",
value:'2', value: "2"
}, },
{ {
label:'trial', label: "trial",
value:'3', value: "3"
}, },
{ {
label: "userInEvent", label: "userInEvent",
value: "4", value: "4"
}, },
{ {
label: "Edu Admin", label: "Edu Admin",
value: "7", value: "7"
}, }
]); ])
let renameData: any = ref({}); //修改名字选中的数据 let renameData: any = ref({}) //修改名字选中的数据
const columns: any = computed(() => { const columns: any = computed(() => {
return [ return [
{ {
@@ -215,7 +213,7 @@ export default defineComponent({
key: "id", key: "id",
width: 100, width: 100,
fixed: "left", fixed: "left",
sorter: true, sorter: true
}, },
{ {
title: "Email", title: "Email",
@@ -246,7 +244,7 @@ export default defineComponent({
dataIndex: "language", dataIndex: "language",
key: "language", key: "language",
width: 100, width: 100,
ellipsis:true, ellipsis: true
}, },
{ {
title: "Valid Start Time", title: "Valid Start Time",
@@ -256,12 +254,12 @@ export default defineComponent({
width: 200, width: 200,
ellipsis: true, ellipsis: true,
customRender: (record: any) => { customRender: (record: any) => {
let time = '' let time = ""
if (record.text) { 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 return time
}, }
}, },
{ {
title: "Valid End Time", title: "Valid End Time",
@@ -271,19 +269,19 @@ export default defineComponent({
width: 200, width: 200,
ellipsis: true, ellipsis: true,
customRender: (record: any) => { customRender: (record: any) => {
let time = '' let time = ""
if (record.text) { 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 return time
}, }
}, },
{ {
title: "Country or Region", title: "Country or Region",
align: "center", align: "center",
dataIndex: "country", dataIndex: "country",
key: "country", key: "country",
width:200, width: 200
}, },
{ {
title: "Create Date", title: "Create Date",
@@ -291,7 +289,7 @@ export default defineComponent({
dataIndex: "createDate", dataIndex: "createDate",
key: "createDate", key: "createDate",
width: 200, width: 200,
sorter: true, sorter: true
}, },
{ {
title: "Is Beginner", title: "Is Beginner",
@@ -301,21 +299,21 @@ export default defineComponent({
width: 80, width: 80,
ellipsis: true, ellipsis: true,
customRender: (record: any) => { customRender: (record: any) => {
let str; let str
if (record.value == 1) { if (record.value == 1) {
str = "Yes"; str = "Yes"
} else { } else {
str = "No"; str = "No"
}
return str
} }
return str;
},
}, },
{ {
title: 'Machine Room Ip', title: "Machine Room Ip",
align: "center", align: "center",
dataIndex: "browserIdentifiers", dataIndex: "browserIdentifiers",
key: "browserIdentifiers", key: "browserIdentifiers",
width:200, width: 200
}, },
{ {
title: "Credits", title: "Credits",
@@ -327,10 +325,10 @@ export default defineComponent({
dataIndex: "credits", dataIndex: "credits",
key: "credits", key: "credits",
width: 100, width: 100,
sorter: true, sorter: true
}, },
{ {
title: 'User Type', title: "User Type",
align: "center", align: "center",
// width: 150, // width: 150,
// minWidth: 100, // minWidth: 100,
@@ -340,22 +338,22 @@ export default defineComponent({
key: "systemUser", key: "systemUser",
width: 100, width: 100,
customRender: (record: any) => { customRender: (record: any) => {
let str; let str
if (record.value == 0) { if (record.value == 0) {
str = "visitor"; str = "visitor"
} else if (record.value == 1) { } else if (record.value == 1) {
str = "yearly"; str = "yearly"
} else if (record.value == 2) { } else if (record.value == 2) {
str = "monthly"; str = "monthly"
} else if (record.value == 3) { } else if (record.value == 3) {
str = "trial"; str = "trial"
} else if (record.value == 4) { } else if (record.value == 4) {
str = "userInEvent"; str = "userInEvent"
} else if (record.value == 7) { } else if (record.value == 7) {
str = "Edu Admin"; str = "Edu Admin"
}
return str
} }
return str;
},
}, },
{ {
title: "Operations", title: "Operations",
@@ -364,58 +362,58 @@ export default defineComponent({
align: "center", align: "center",
fixed: "right", fixed: "right",
// slots:{customRender:'action'} // slots:{customRender:'action'}
Operations: true, Operations: true
}, }
]; ]
}); })
//改变页码 //改变页码
let changePage = (e: any, filters: any, sorter: any) => { let changePage = (e: any, filters: any, sorter: any) => {
filterData.currentPage = e.current; filterData.currentPage = e.current
filterData.pageSize = e.pageSize; filterData.pageSize = e.pageSize
if (sorter.order) { if (sorter.order) {
if(sorter.columnKey == 'id'){ if (sorter.columnKey == "id") {
filterData.orderBy = 'id' filterData.orderBy = "id"
} else if (sorter.columnKey == "createDate") { } else if (sorter.columnKey == "createDate") {
filterData.orderBy = 'time' filterData.orderBy = "time"
} else if (sorter.columnKey == "credits") { } else if (sorter.columnKey == "credits") {
filterData.orderBy = 'credits' filterData.orderBy = "credits"
} }
} }
if (sorter.order) { if (sorter.order) {
filterData.order = sorter.order == "descend" ? "Descending" : "Ascending"; filterData.order = sorter.order == "descend" ? "Descending" : "Ascending"
} else { } else {
filterData.order = '' filterData.order = ""
}
gettrialList()
} }
gettrialList();
};
//查询列表 //查询列表
let searchHistoryList = () => { let searchHistoryList = () => {
filterData.currentPage = 1; filterData.currentPage = 1
gettrialList(); gettrialList()
}; }
let clearHistoryList = () => { let clearHistoryList = () => {
filterData.rangePickerValue = [], ;((filterData.rangePickerValue = []),
filterData.currentPage = 1, (filterData.currentPage = 1),
filterData.pageSize = 10, (filterData.pageSize = 10),
filterData.total = 0, (filterData.total = 0),
filterData.country = "", (filterData.country = ""),
filterData.email = "", (filterData.email = ""),
filterData.userType = "", (filterData.userType = ""),
filterData.ids = [], (filterData.ids = []),
filterData.occupation = "", (filterData.occupation = ""),
filterData.order = "", //'Ascending 升序 Descending 降序' (filterData.order = ""), //'Ascending 升序 Descending 降序'
filterData.orderBy = "", //'Ascending 升序 Descending 降序' (filterData.orderBy = ""), //'Ascending 升序 Descending 降序'
filterData.systemUser = "", (filterData.systemUser = ""),
filterData.userName = ""; (filterData.userName = ""))
}; }
let setHistoryListData = () => { let setHistoryListData = () => {
let startDate: any = filterData.rangePickerValue?.[0] let startDate: any = filterData.rangePickerValue?.[0]
? filterData.rangePickerValue[0] + " " + "00:00:00" ? filterData.rangePickerValue[0] + " " + "00:00:00"
: ""; : ""
let endDate: any = filterData.rangePickerValue?.[1] let endDate: any = filterData.rangePickerValue?.[1]
? filterData.rangePickerValue[1] + " " + "23:59:59" ? filterData.rangePickerValue[1] + " " + "23:59:59"
: ""; : ""
let data = { let data = {
endTime: endDate, endTime: endDate,
startTime: startDate, startTime: startDate,
@@ -430,63 +428,118 @@ export default defineComponent({
order: filterData.order, order: filterData.order,
orderBy: filterData.orderBy, orderBy: filterData.orderBy,
userName: filterData.userName, userName: filterData.userName,
}; organizationId: filterData.organizationId
return data; }
}; return data
}
//获取列表 //获取列表
let gettrialList = () => { let gettrialList = () => {
filter.tableLoading = true; filter.tableLoading = true
let data = setHistoryListData(); let data = setHistoryListData()
Https.axiosPost(Https.httpUrls.getUserInfo, data).then( Https.axiosPost(Https.httpUrls.getUserInfo, data).then((rv: any) => {
(rv: any) => {
if (rv) { if (rv) {
// this.dataList = rv // this.dataList = rv
filter.dataList = rv.records; filter.dataList = rv.records
filterData.total = rv.total; filterData.total = rv.total
filter.tableLoading = false; filter.tableLoading = false
// this.workspaceItem.position = this.singleTypeList[0].label // this.workspaceItem.position = this.singleTypeList[0].label
} }
})
} }
);
};
let lastGeTrialList = (str: string) => { let lastGeTrialList = (str: string) => {
clearHistoryList(); clearHistoryList()
let currentDate = new Date(); let currentDate = new Date()
let currentTimestamp = Math.floor(currentDate.getTime() / 1000); let currentTimestamp = Math.floor(currentDate.getTime() / 1000)
// 计算30天前的时间戳 // 计算30天前的时间戳
let thirtyDaysAgoTimestamp; let thirtyDaysAgoTimestamp
if (str == "year") { if (str == "year") {
thirtyDaysAgoTimestamp = currentTimestamp - 360 * 24 * 60 * 60; thirtyDaysAgoTimestamp = currentTimestamp - 360 * 24 * 60 * 60
} else if (str == "month") { } else if (str == "month") {
thirtyDaysAgoTimestamp = currentTimestamp - 30 * 24 * 60 * 60; thirtyDaysAgoTimestamp = currentTimestamp - 30 * 24 * 60 * 60
} else if (str == "week") { } 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) => { let filterOption = (input: any, option: any) => {
// 使用 option.label 进行搜索 // 使用 option.label 进行搜索
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0; return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}; }
let addhHistoryList = () => { let addhHistoryList = () => {
allUserPoerationsVue.value.init('Add','') allUserPoerationsVue.value.init("Add", "")
}; }
let allUserPoerationsVue = ref() let allUserPoerationsVue = ref()
let setAagree = (data: any) => { 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(() => { onMounted(() => {
let allCountry: any = sessionStorage.getItem("allCountry"); let allCountry: any = sessionStorage.getItem("allCountry")
if (allCountry) { if (allCountry) {
filter.allCountry = JSON.parse(allCountry); filter.allCountry = JSON.parse(allCountry)
} }
gettrialList(); gettrialList()
}); getOrganizationList()
})
return { return {
...toRefs(filter), ...toRefs(filter),
...toRefs(filterData), ...toRefs(filterData),
@@ -501,22 +554,26 @@ export default defineComponent({
filterOption, filterOption,
allUserPoerationsVue, allUserPoerationsVue,
setAagree, setAagree,
}; handleOrganizationScroll,
getOrganizationList,
organizationOptions,
organizationParams
}
}, },
data() { data() {
return { return {
historyTableHeight: 0, historyTableHeight: 0,
handleResizeColumn: (w: any, col: any) => { handleResizeColumn: (w: any, col: any) => {
col.width = w; col.width = w
}, }
}; }
}, },
mounted() { mounted() {
let historyTable: any = this.$refs.historyTable; let historyTable: any = this.$refs.historyTable
this.historyTableHeight = historyTable.clientHeight - 200; this.historyTableHeight = historyTable.clientHeight - 200
}, },
methods: {}, methods: {}
}); })
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.admin_page .admin_table_search .admin_state { .admin_page .admin_table_search .admin_state {
@@ -524,6 +581,5 @@ export default defineComponent({
flex-wrap: wrap; flex-wrap: wrap;
} }
.admin_page { .admin_page {
} }
</style> </style>

View File

@@ -321,6 +321,7 @@ export default defineComponent({
data = setEditData() data = setEditData()
if (!data.userName || !data.userEmail || !data.validEndTime || !data.systemUser) if (!data.userName || !data.userEmail || !data.validEndTime || !data.systemUser)
return message.warning('Please check the input box marked with *') return message.warning('Please check the input box marked with *')
delete data.userName
Https.axiosPost(Https.httpUrls.modifyUser, {}, { params: data }).then(rv => { Https.axiosPost(Https.httpUrls.modifyUser, {}, { params: data }).then(rv => {
if (rv) { if (rv) {
cancelDsign() cancelDsign()

View 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>

View File

@@ -5,8 +5,8 @@
class="search-form" class="search-form"
layout="inline" layout="inline"
:model="searchForm" :model="searchForm"
:label-col="{ style: { width: '12rem' } }" :label-col="{ style: { width: '14rem' } }"
:wrapper-col="{ style: { width: '22rem' } }" :wrapper-col="{ style: { width: '20rem' } }"
> >
<a-form-item label="ID"> <a-form-item label="ID">
<a-input v-model:value="searchForm.id" allow-clear placeholder="Input the id" /> <a-input v-model:value="searchForm.id" allow-clear placeholder="Input the id" />
@@ -72,7 +72,7 @@
:options="countryList" :options="countryList"
/> />
</a-form-item> </a-form-item>
<a-form-item> <a-form-item class="search-form__actions">
<a-space> <a-space>
<a-button type="primary" @click="handleSearch">Search</a-button> <a-button type="primary" @click="handleSearch">Search</a-button>
<a-button @click="handleReset">Reset</a-button> <a-button @click="handleReset">Reset</a-button>
@@ -92,6 +92,7 @@
:loading="tableLoading" :loading="tableLoading"
:bordered="false" :bordered="false"
row-key="id" row-key="id"
:customRow="customPlanRow"
@change="changePage" @change="changePage"
@resizeColumn="handleResizeColumn" @resizeColumn="handleResizeColumn"
:scroll="{ y: historyTableHeight }" :scroll="{ y: historyTableHeight }"
@@ -107,10 +108,11 @@
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template <template
v-if=" 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>
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
@@ -120,15 +122,15 @@
</template> </template>
<template v-else-if="column.key === 'actions'"> <template v-else-if="column.key === 'actions'">
<a-space> <a-space class="plan-row-actions" @click.stop>
<a @click="openEdit(record)">Edit</a> <a @click.stop="openEdit(record)">Edit</a>
<a-popconfirm <a-popconfirm
title="Confirm to delete this subscription plan?" title="Confirm to delete this subscription plan?"
ok-text="Confirm" ok-text="Confirm"
cancel-text="Cancel" cancel-text="Cancel"
@confirm="removePlan(record.id)" @confirm="removePlan(record.id)"
> >
<a class="danger-text">Delete</a> <a class="danger-text" @click.stop>Delete</a>
</a-popconfirm> </a-popconfirm>
</a-space> </a-space>
</template> </template>
@@ -137,6 +139,50 @@
</div> </div>
</a-card> </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> <div class="subscriptionPlanModal" ref="subscriptionPlanModal"></div>
<a-modal <a-modal
class="subscriptionPlan_modal generalModel" class="subscriptionPlan_modal generalModel"
@@ -213,7 +259,10 @@
@select="handleOrganizationSelect" @select="handleOrganizationSelect"
@change="handleOrganizationChange" @change="handleOrganizationChange"
> >
<a-select-option value="ADD_ORGANIZATION" class="add-organization-option"> <a-select-option
value="ADD_ORGANIZATION"
class="add-organization-option"
>
+ Create Organization + Create Organization
</a-select-option> </a-select-option>
<a-select-option <a-select-option
@@ -393,27 +442,30 @@ import {
ref, ref,
onMounted, onMounted,
onBeforeUnmount, onBeforeUnmount,
onActivated,
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
name: string name: string
currentPeriodStart: string currentPeriodStart: string
currentPeriodEnd: string currentPeriodEnd: string
organizationId: string organizationId: string
adminAccId: string adminAccId: string
adminAccEmail?: string
status: PlanStatus status: PlanStatus
creditLimit: number creditLimit: number
accountNum?: number accountNum?: number
@@ -421,17 +473,32 @@ interface SubscriptionPlan {
endStamp: number 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 countryList = ref([])
const userRef = ref(null) const userRef = ref(null)
const searchForm = reactive({ const searchForm = reactive({
name: '', name: "",
startTime: '', startTime: "",
endTime: '', endTime: "",
organizationId: undefined as string | undefined, organizationId: undefined as string | undefined,
adminAccId: undefined as string | undefined, adminAccId: undefined as string | undefined,
status: [] as PlanStatus[] | [], status: [] as PlanStatus[] | [],
id: '', id: "",
countryOrRegion: null, countryOrRegion: null,
page: 1, page: 1,
size: 10, size: 10,
@@ -442,15 +509,24 @@ const toSeconds = (dateStr: string) => Math.floor(new Date(dateStr).getTime() /
const tableData = ref<SubscriptionPlan[]>([]) const tableData = ref<SubscriptionPlan[]>([])
const tableLoading = ref(false) 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 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: "",
organizationId: undefined as string | undefined, organizationId: undefined as string | undefined,
adminAccId: undefined as string | undefined, adminAccId: undefined as string | undefined,
creditLimit: null as number | null, creditLimit: null as number | null,
@@ -461,44 +537,44 @@ const formState = reactive({
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"
}, },
{ {
label: 'Active', label: "Active",
value: 'ACTIVE' value: "ACTIVE"
}, },
{ {
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)
} }
@@ -509,7 +585,7 @@ const range = (start: number, end: number) => {
} }
return result return result
} }
const disableEndTime = date => { const disableEndTime = (date) => {
if (!formState.currentPeriodEnd || !isEditMode.value) if (!formState.currentPeriodEnd || !isEditMode.value)
return { return {
disabledHours: () => [], disabledHours: () => [],
@@ -519,7 +595,7 @@ const disableEndTime = date => {
const specificTime = dayjs.unix(formState.currentPeriodEnd) const specificTime = dayjs.unix(formState.currentPeriodEnd)
if (date && date.isSame(specificTime, 'day')) { if (date && date.isSame(specificTime, "day")) {
// 如果是指定日期当天,禁用时间戳之前的时间 // 如果是指定日期当天,禁用时间戳之前的时间
const hour = specificTime.hour() const hour = specificTime.hour()
const minute = specificTime.minute() const minute = specificTime.minute()
@@ -527,7 +603,7 @@ const disableEndTime = date => {
return { return {
disabledHours: () => Array.from({ length: hour }, (_, i) => i), // 禁用小时之前 disabledHours: () => Array.from({ length: hour }, (_, i) => i), // 禁用小时之前
disabledMinutes: selectedHour => { disabledMinutes: (selectedHour) => {
if (selectedHour === hour) { if (selectedHour === hour) {
return Array.from({ length: minute }, (_, i) => i) // 同小时,禁用分钟之前 return Array.from({ length: minute }, (_, i) => i) // 同小时,禁用分钟之前
} }
@@ -555,114 +631,328 @@ const normalizeStatus = (status?: string): PlanStatus | undefined => {
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 },
{ {
title: 'Organization', title: "Organization",
dataIndex: 'organizationName', dataIndex: "organizationName",
key: 'organizationName', key: "organizationName",
align: 'center', align: "center",
width: 180 width: 180
}, },
{ {
title: 'Admin Account', title: "Admin Account",
dataIndex: 'adminAccEmail', dataIndex: "adminAccEmail",
key: 'adminAccEmail', key: "adminAccEmail",
align: 'center', align: "center",
width: 180, width: 180,
ellipsis: true ellipsis: true
}, },
{ {
title: 'Sub-Account Num', title: "Sub-Account Num",
dataIndex: 'accountNum', dataIndex: "accountNum",
key: 'accountNum', key: "accountNum",
align: 'center', align: "center",
width: 120, width: 120,
ellipsis: true ellipsis: true
}, },
{ {
title: 'Country or Region', title: "Country or Region",
dataIndex: 'countryOrRegion', dataIndex: "countryOrRegion",
key: 'countryOrRegion', key: "countryOrRegion",
align: 'center', align: "center",
width: 120, width: 120,
ellipsis: true ellipsis: true
}, },
{ {
title: 'Start Time', title: "Start Time",
dataIndex: 'currentPeriodStart', dataIndex: "currentPeriodStart",
key: 'currentPeriodStart', key: "currentPeriodStart",
align: 'center', align: "center",
width: 200 width: 200
}, },
{ {
title: 'End Time', title: "End Time",
dataIndex: 'currentPeriodEnd', dataIndex: "currentPeriodEnd",
key: 'currentPeriodEnd', key: "currentPeriodEnd",
align: 'center', align: "center",
width: 200 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', title: "Credit Limit",
dataIndex: 'creditLimit', dataIndex: "creditLimit",
key: 'creditLimit', key: "creditLimit",
align: 'center', align: "center",
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 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 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) => { const handleResizeColumn = (w: any, col: any) => {
col.width = w col.width = w
} }
const calculateTableHeight = () => { const getElementOuterHeight = (element: Element | null) => {
nextTick(() => { if (!element) return 0
if (historyTable.value) { const htmlElement = element as HTMLElement
historyTableHeight.value = historyTable.value.clientHeight - 200 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 = () => { const handleResize = () => {
calculateTableHeight() 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 () => { onMounted(async () => {
await getOrganizationList() await getOrganizationList()
await handleSearch() await handleSearch()
calculateTableHeight() calculateTableHeight()
window.addEventListener('resize', handleResize) setupTableResizeObserver()
const list = sessionStorage.getItem('allCountry') window.addEventListener("resize", handleResize)
const list = sessionStorage.getItem("allCountry")
countryList.value = list ? JSON.parse(list) : [] countryList.value = list ? JSON.parse(list) : []
}) })
onActivated(() => {
calculateTableHeight()
})
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize) window.removeEventListener("resize", handleResize)
tableResizeObserver?.disconnect()
if (tableResizeTimer) {
window.clearTimeout(tableResizeTimer)
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) => {
tableData.value = res.records tableData.value = res.records
searchForm.total = res.total searchForm.total = res.total
}) })
.finally(() => { .finally(() => {
tableLoading.value = false tableLoading.value = false
calculateTableHeight()
}) })
} }
const resetFormState = () => { const resetFormState = () => {
formState.name = '' formState.name = ""
formState.currentPeriodStart = '' formState.currentPeriodStart = ""
formState.currentPeriodEnd = '' formState.currentPeriodEnd = ""
formState.organizationId = undefined formState.organizationId = undefined
formState.adminAccId = undefined formState.adminAccId = undefined
formState.creditLimit = null formState.creditLimit = null
@@ -683,26 +973,26 @@ const handleSearch = () => {
} }
const handleReset = () => { const handleReset = () => {
searchForm.name = '' searchForm.name = ""
searchForm.startTime = '' searchForm.startTime = ""
searchForm.endTime = '' searchForm.endTime = ""
searchForm.organizationId = undefined searchForm.organizationId = undefined
searchForm.adminAccId = undefined searchForm.adminAccId = undefined
searchForm.status = [] searchForm.status = []
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
formState.currentPeriodStart = String(record.currentPeriodStart) formState.currentPeriodStart = String(record.currentPeriodStart)
@@ -755,25 +1045,25 @@ const validateForm = (): boolean => {
} }
const requiredFields: FieldRule[] = [ const requiredFields: FieldRule[] = [
{ value: formState.currentPeriodStart, message: 'Please select the start time' }, { value: formState.currentPeriodStart, message: "Please select the start time" },
{ value: formState.currentPeriodEnd, message: 'Please select the end time' }, { value: formState.currentPeriodEnd, message: "Please select the end time" },
{ value: formState.adminAccId, message: 'Please select the admin account' }, { value: formState.adminAccId, message: "Please select the admin account" },
{ {
value: formState.creditLimit, value: formState.creditLimit,
message: 'Please input credit limit', message: "Please input credit limit",
checkNull: true checkNull: true
}, },
{ {
value: formState.accountNum, value: formState.accountNum,
message: 'Please input account number', message: "Please input account number",
checkNull: true checkNull: true
} }
] ]
if (!isEditMode.value) { if (!isEditMode.value) {
requiredFields.push( requiredFields.push(
{ value: formState.name, message: 'Please input the name' }, { value: formState.name, message: "Please input the name" },
{ value: formState.organizationId, message: 'Please select organization' } { value: formState.organizationId, message: "Please select organization" }
) )
} }
@@ -806,7 +1096,7 @@ const handleSubmit = async () => {
res = await Https.axiosPost(Https.httpUrls.createSubscribePlan, params) res = await Https.axiosPost(Https.httpUrls.createSubscribePlan, params)
} }
message.success( message.success(
`${isEditMode.value ? 'Subscription plan updated' : 'Subscription plan created'}` `${isEditMode.value ? "Subscription plan updated" : "Subscription plan created"}`
) )
} catch (error: any) { } catch (error: any) {
message.error(error.message) message.error(error.message)
@@ -835,8 +1125,8 @@ const cancelModal = () => {
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) => {
message.success('Subscription plan deleted') message.success("Subscription plan deleted")
handleReset() handleReset()
}) })
.catch((error: any) => { .catch((error: any) => {
@@ -901,15 +1191,15 @@ const handleOrganizationScroll = (e: any) => {
} }
const handleOrganizationSelect = (value: string) => { const handleOrganizationSelect = (value: string) => {
if (value === 'ADD_ORGANIZATION') { if (value === "ADD_ORGANIZATION") {
// 打开添加组织弹窗 // 打开添加组织弹窗
organizationModalVisible.value = true organizationModalVisible.value = true
// 使用nextTick确保值被重置使其不被选中 // 使用nextTick确保值被重置使其不被选中
nextTick(() => { nextTick(() => {
if (searchForm.organizationId === 'ADD_ORGANIZATION') { if (searchForm.organizationId === "ADD_ORGANIZATION") {
searchForm.organizationId = undefined searchForm.organizationId = undefined
} }
if (formState.organizationId === 'ADD_ORGANIZATION') { if (formState.organizationId === "ADD_ORGANIZATION") {
formState.organizationId = undefined formState.organizationId = undefined
} }
}) })
@@ -918,12 +1208,12 @@ const handleOrganizationSelect = (value: string) => {
const handleOrganizationChange = (value: string) => { const handleOrganizationChange = (value: string) => {
// 如果change事件触发时值是"添加组织",立即重置 // 如果change事件触发时值是"添加组织",立即重置
if (value === 'ADD_ORGANIZATION') { if (value === "ADD_ORGANIZATION") {
nextTick(() => { nextTick(() => {
if (searchForm.organizationId === 'ADD_ORGANIZATION') { if (searchForm.organizationId === "ADD_ORGANIZATION") {
searchForm.organizationId = undefined searchForm.organizationId = undefined
} }
if (formState.organizationId === 'ADD_ORGANIZATION') { if (formState.organizationId === "ADD_ORGANIZATION") {
formState.organizationId = undefined formState.organizationId = undefined
} }
}) })
@@ -932,13 +1222,13 @@ 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
} }
try { try {
@@ -948,7 +1238,7 @@ const handleCreateOrganization = async () => {
type: organizationForm.type type: organizationForm.type
} }
}) })
message.success('Organization created successfully') message.success("Organization created successfully")
cancelOrganizationModal() cancelOrganizationModal()
// 刷新组织列表 // 刷新组织列表
await getOrganizationList() await getOrganizationList()
@@ -960,26 +1250,28 @@ const handleCreateOrganization = async () => {
} }
} }
} catch (error: any) { } catch (error: any) {
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;
flex-direction: column; flex-direction: column;
.search-card { .search-card {
margin-bottom: 1.6rem; margin-bottom: 1.6rem;
flex-shrink: 0;
} }
.table-card { .table-card {
@@ -987,13 +1279,15 @@ const filterOption = (input: string, option: any) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
min-height: 0;
:deep(.ant-card-body) { :deep(.ant-card-body) {
flex: 1; flex: 1;
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;
} }
.table-card__header { .table-card__header {
@@ -1007,6 +1301,42 @@ const filterOption = (input: string, option: any) => {
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
min-height: 0; 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 { .danger-text {
@@ -1026,8 +1356,23 @@ const filterOption = (input: string, option: any) => {
:deep(.ant-table-tbody > tr:hover > td) { :deep(.ant-table-tbody > tr:hover > td) {
background: rgb(202, 202, 202); 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) { :deep(.subscriptionPlan_modal) {
.ant-modal-body { .ant-modal-body {
// height: calc(65rem * 1.2); // height: calc(65rem * 1.2);
@@ -1079,11 +1424,69 @@ const filterOption = (input: string, option: any) => {
} }
:deep(.search-form) { :deep(.search-form) {
column-gap: 2rem; --search-label-width: 14rem;
row-gap: 2rem; --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 { .ant-select {
width: 100% !important; 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) { :deep(.ant-select-dropdown) {

View File

@@ -55,6 +55,7 @@ commandManager.setChangeCallback((info) => {
emit("undo-redo-status-changed", { emit("undo-redo-status-changed", {
canUndo: canUndo.value, canUndo: canUndo.value,
canRedo: canRedo.value, canRedo: canRedo.value,
type: info.type,
commandManager, commandManager,
}); });
}); });

View File

@@ -907,7 +907,8 @@
} }
emit("changeCanvas", commandData) emit("changeCanvas", commandData)
canvasManager.changeCanvas() canvasManager.changeCanvas()
if ((command.canUndo || command.canRedo) && props.enabledRedGreenMode) { const type = command.type
if (props.enabledRedGreenMode && (type === "undo" || type === "redo")) {
setTimeout(async () => { setTimeout(async () => {
try { try {
const imageData = await canvasManager.exportImage({ const imageData = await canvasManager.exportImage({
@@ -1040,6 +1041,7 @@
}, },
// 导出图片 // 导出图片
exportImage: async ({ exportImage: async ({
isFrontBackUpdata = false, // 更新红绿图时候需要用,直接更新红绿图
isContainBg = false, // 是否包含背景图层 isContainBg = false, // 是否包含背景图层
isContainFixed = false, // 是否包含固定图层 isContainFixed = false, // 是否包含固定图层
isContainFixedOther = true, // 是否包含其他固定图层--颜色图层 isContainFixedOther = true, // 是否包含其他固定图层--颜色图层
@@ -1056,6 +1058,10 @@
} = {}) => { } = {}) => {
loading.value = true loading.value = true
canvasManager?.canvas?.discardActiveObject() canvasManager?.canvas?.discardActiveObject()
if (isFrontBackUpdata) {
await canvasManager?.setSpecialCliptInfo(true, true)
canvasManager.canvas.renderAll()
}
var base64 = await canvasManager.exportImage({ var base64 = await canvasManager.exportImage({
isContainBg, isContainBg,
isContainFixed, isContainFixed,

View File

@@ -409,7 +409,7 @@ export class BrushIndicator {
// this.show(e.e); // this.show(e.e);
this._mouseEnterHandler && this._mouseEnterHandler(e) this._mouseEnterHandler && this._mouseEnterHandler(e)
} else { } else {
// requestIdleCallback(() => { // setTimeout(() => {
// this.updatePosition(e.e); // this.updatePosition(e.e);
// }); // });

View File

@@ -623,17 +623,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
this.updateMaskPosition(backgroundObject); this.updateMaskPosition(backgroundObject);
} }
// 更新颜色层信息 this.setSpecialCliptInfo(false, true)
// const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
// if(colorObject){
// await this.setObjecCliptInfo(colorObject);
// }
const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
if(groupLayer){
const groupRect = new fabric.Rect({});
await this.setObjecCliptInfo(groupRect);
groupLayer.clippingMask = groupRect.toObject();
}
// 重新渲染画布 // 重新渲染画布
this.canvas.renderAll(); this.canvas.renderAll();
@@ -1556,6 +1546,26 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
this.awaitCanvasRun = null; this.awaitCanvasRun = null;
} }
//设置印花元素颜色的裁剪信息
async setSpecialCliptInfo(isColor = true, isGroup = true) {
// 更新颜色层信息
if (isColor) {
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
if (colorObject) {
await this.setObjecCliptInfo(colorObject);
}
}
// 更新特殊组图层信息
if (isGroup) {
const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
if (groupLayer) {
const groupRect = new fabric.Rect({});
await this.setObjecCliptInfo(groupRect);
groupLayer.clippingMask = groupRect.toObject();
}
}
}
// 设置画布对象的裁剪信息 // 设置画布对象的裁剪信息
async setObjecCliptInfo(tagObject, data) { async setObjecCliptInfo(tagObject, data) {
const fixedLayerObj = this.getFixedLayerObject(); const fixedLayerObj = this.getFixedLayerObject();

View File

@@ -606,7 +606,9 @@ export class ExportManager {
imageSmoothingEnabled: true, imageSmoothingEnabled: true,
}); });
// tempFabricCanvas.setZoom(1); // tempFabricCanvas.setZoom(1);
console.log("==========", fixedLayerObject) const ox = fixedLayerObject.left - fixedLayerObject.width * fixedLayerObject.scaleX / 2
const oy = fixedLayerObject.top - fixedLayerObject.height * fixedLayerObject.scaleY / 2
// console.log("==========", fixedLayerObject, ox, oy)
try { try {
// 克隆并添加所有对象到临时画布,需要调整位置相对于固定图层 // 克隆并添加所有对象到临时画布,需要调整位置相对于固定图层
for (let i = 0; i < objectsToExport.length; i++) { for (let i = 0; i < objectsToExport.length; i++) {
@@ -616,15 +618,20 @@ export class ExportManager {
restoreOpacityInRedGreen && true restoreOpacityInRedGreen && true
); );
if (cloned) { if (cloned) {
let scaleX = cloned.scaleX / fixedLayerObject.scaleX
let scaleY = cloned.scaleY / fixedLayerObject.scaleY
let top = (cloned.top - oy) * scaleY
let left = (cloned.left - ox) * scaleX
if (cloned.originX === "center" && cloned.originY === "center") {
top = canvasHeight / 2
left = canvasWidth / 2
}
cloned.set({ cloned.set({
left: canvasWidth / 2, left: left,
top: canvasHeight / 2, top: top,
scaleX: cloned.scaleX / fixedLayerObject.scaleX, scaleX: scaleX,
scaleY: cloned.scaleY / fixedLayerObject.scaleY, scaleY: scaleY,
originX: "center",
originY: "center",
}); });
console.log("==========", {...cloned})
// 更新对象坐标 // 更新对象坐标
cloned.setCoords(); cloned.setCoords();
tempFabricCanvas.add(cloned); tempFabricCanvas.add(cloned);

View File

@@ -133,11 +133,12 @@ export class RedGreenModeManager {
this.canvas.on("mouse:up", (event) => { this.canvas.on("mouse:up", (event) => {
// 可以在这里添加更多逻辑,比如生成图片或更新状态 // 可以在这里添加更多逻辑,比如生成图片或更新状态
nextTick(() => { nextTick(() => {
requestIdleCallback(async () => { setTimeout(async () => {
if (!this.isInitialized) { if (!this.isInitialized) {
console.warn("红绿图模式未初始化,无法处理鼠标事件"); console.warn("红绿图模式未初始化,无法处理鼠标事件");
return; return;
} }
console.log("鼠标抬起事件触发", this.onImageGenerated);
if (this.onImageGenerated) { if (this.onImageGenerated) {
const imageData = await this.canvasManager.exportImage({ const imageData = await this.canvasManager.exportImage({
restoreOpacityInRedGreen: true, // 恢复红绿图模式下的透明度 restoreOpacityInRedGreen: true, // 恢复红绿图模式下的透明度

View File

@@ -37,7 +37,6 @@ export class ThumbnailManager {
// 延迟执行避免阻塞UI // 延迟执行避免阻塞UI
fabricObjects.length > 0 && fabricObjects.length > 0 &&
requestIdleCallback(() => {
setTimeout(async () => { setTimeout(async () => {
const base64 = await this._generateLayerThumbnailNow(fabricObjects, layer); const base64 = await this._generateLayerThumbnailNow(fabricObjects, layer);
// this.layerThumbnails.set(layerId, base64); // this.layerThumbnails.set(layerId, base64);
@@ -55,7 +54,6 @@ export class ThumbnailManager {
console.error("生成图层缩略图时出错:", error); console.error("生成图层缩略图时出错:", error);
} }
}); });
});
} }
/** /**
@@ -65,7 +63,7 @@ export class ThumbnailManager {
generateAllLayerThumbnails(layers) { generateAllLayerThumbnails(layers) {
if (!layers || !Array.isArray(layers)) return; if (!layers || !Array.isArray(layers)) return;
requestIdleCallback(() => { setTimeout(() => {
setTimeout(() => { setTimeout(() => {
layers.forEach((layer) => { layers.forEach((layer) => {
if (layer && layer.id) { if (layer && layer.id) {

View File

@@ -180,7 +180,7 @@ export class CommandManager {
this._recordPerformance("execute", command.constructor.name, duration); this._recordPerformance("execute", command.constructor.name, duration);
// 通知状态变化 // 通知状态变化
this._notifyStateChange(); this._notifyStateChange("execute");
console.log(`✅ 命令执行成功: ${command.constructor.name}`); console.log(`✅ 命令执行成功: ${command.constructor.name}`);
return result; return result;
@@ -219,7 +219,7 @@ export class CommandManager {
this._recordPerformance("undo", command.constructor.name, duration); this._recordPerformance("undo", command.constructor.name, duration);
// 通知状态变化 // 通知状态变化
this._notifyStateChange(); this._notifyStateChange("undo");
console.log(`✅ 命令撤销成功: ${command.constructor.name}`); console.log(`✅ 命令撤销成功: ${command.constructor.name}`);
return result; return result;
@@ -258,7 +258,7 @@ export class CommandManager {
this._recordPerformance("redo", command.constructor.name, duration); this._recordPerformance("redo", command.constructor.name, duration);
// 通知状态变化 // 通知状态变化
this._notifyStateChange(); this._notifyStateChange("redo");
console.log(`✅ 命令重做成功: ${command.constructor.name}`); console.log(`✅ 命令重做成功: ${command.constructor.name}`);
return result; return result;
@@ -298,7 +298,7 @@ export class CommandManager {
this.undoStack = []; this.undoStack = [];
this.redoStack = []; this.redoStack = [];
this._notifyStateChange(); this._notifyStateChange("clear");
// console.log("📝 命令历史已清空"); // console.log("📝 命令历史已清空");
} }
@@ -417,10 +417,12 @@ export class CommandManager {
* 通知状态变化 * 通知状态变化
* @private * @private
*/ */
_notifyStateChange() { _notifyStateChange(type) {
if (this.onStateChange) { if (this.onStateChange) {
try { try {
this.onStateChange(this.getState()); const obj = this.getState();
obj.type = type;
this.onStateChange(obj);
} catch (error) { } catch (error) {
console.error("状态变化回调执行失败:", error); console.error("状态变化回调执行失败:", error);
} }

View File

@@ -85,7 +85,7 @@ export class LiquifyRealTimeUpdater {
if (isDrawing && this.config.useDirectUpdate) { if (isDrawing && this.config.useDirectUpdate) {
// 拖拽过程中使用快速更新(降低质量以提高性能) // 拖拽过程中使用快速更新(降低质量以提高性能)
this._fastUpdate(imageData); await this._fastUpdate(imageData);
} else { } else {
// 拖拽结束后使用完整更新(最高质量) // 拖拽结束后使用完整更新(最高质量)
await this._fullUpdate(imageData); await this._fullUpdate(imageData);
@@ -124,7 +124,7 @@ export class LiquifyRealTimeUpdater {
* @param {ImageData} imageData 图像数据 * @param {ImageData} imageData 图像数据
* @private * @private
*/ */
_fastUpdate(imageData) { async _fastUpdate(imageData) {
if (!this.targetObject || !this.targetObject._element) { if (!this.targetObject || !this.targetObject._element) {
return; return;
} }
@@ -138,12 +138,14 @@ export class LiquifyRealTimeUpdater {
// 直接更新fabric对象的图像源使用PNG格式保持质量 // 直接更新fabric对象的图像源使用PNG格式保持质量
const targetElement = this.targetObject._element; const targetElement = this.targetObject._element;
// 方案1: 直接设置src属性最高性能 // 方案1: 直接设置src属性最高性能
const dataURL = this.tempCanvas.toDataURL("image/png", quality); const dataURL = this.tempCanvas.toDataURL("image/png", quality);
if (targetElement.src !== dataURL) { if (targetElement.src !== dataURL) {
targetElement.src = dataURL; // targetElement.src = dataURL;
const image = new Image();
image.src = dataURL;
await image.decode();
this.targetObject.setElement(image);
// 关键优化直接设置fabric对象为脏状态但不立即渲染 // 关键优化直接设置fabric对象为脏状态但不立即渲染
// this.targetObject.dirty = false; // 标记为不需要立即渲染 // this.targetObject.dirty = false; // 标记为不需要立即渲染
@@ -153,7 +155,7 @@ export class LiquifyRealTimeUpdater {
// 使用requestAnimationFrame进行批量渲染优化 // 使用requestAnimationFrame进行批量渲染优化
// if (!this.renderingScheduled && !this.config.skipRenderDuringDrag) { // if (!this.renderingScheduled && !this.config.skipRenderDuringDrag) {
// this.renderingScheduled = true; // this.renderingScheduled = true;
// requestIdleCallback(() => { // setTimeout(() => {
// this.canvas.renderAll(); // this.canvas.renderAll();
// this.renderingScheduled = false; // this.renderingScheduled = false;
// }); // });

View File

@@ -447,6 +447,7 @@
</template> </template>
</CanvasEditor> </CanvasEditor>
</div> </div>
<img src="" alt="" id="canvas-test-dom">
</div> </div>
</template> </template>
<style> <style>
@@ -458,6 +459,13 @@
height: 600px !important; height: 600px !important;
z-index: 99999999; z-index: 99999999;
} }
#canvas-test-dom{
position: fixed;
z-index: 9999999999;
top: 0;
left: 0;
pointer-events: none;
}
</style> </style>
<style scoped lang="less"> <style scoped lang="less">
* { * {

View File

@@ -331,7 +331,6 @@ export default defineComponent({
store.commit('DesignDetail/setCurrentDetailType',str) store.commit('DesignDetail/setCurrentDetailType',str)
} }
const setClothes = async (list:any,str:string)=>{ const setClothes = async (list:any,str:string)=>{
console.log(JSON.parse(JSON.stringify(list)))
let clothesList:any = [] let clothesList:any = []
if(detailData.isEditPattern.value == 'editSketch')await detailDom.canvasBox.submitBase64Data().then((rv)=>{ if(detailData.isEditPattern.value == 'editSketch')await detailDom.canvasBox.submitBase64Data().then((rv)=>{
detailData.selectDetail.sketchString = rv detailData.selectDetail.sketchString = rv
@@ -369,7 +368,6 @@ export default defineComponent({
// }else if(isCurrent){ // }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}`:'' 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 gradient = list[i].gradient
if((detailData.currentDetailType == 'sketch' && newData?.sketch) || detailData.isEditPattern.value == 'editSketch'){ if((detailData.currentDetailType == 'sketch' && newData?.sketch) || detailData.isEditPattern.value == 'editSketch'){
@@ -565,11 +563,14 @@ export default defineComponent({
} }
}else{ }else{
//走画布合成图片并且直接分割 //走画布合成图片并且直接分割
if(detailData.isEditPattern.value !== 'canvasEditor' && detailData.isEditPattern.value !== 'redGreenExample'){
if(detailData.isEditPattern.value !== 'canvasEditor'){ if(detailData.isEditPattern.value !== 'canvasEditor'){
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail() if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
}
let otherData = await updateOtherLayers('single') let otherData = await updateOtherLayers('single')
await detailDom.canvasBox.updateOtherLayers(otherData) await detailDom.canvasBox.updateOtherLayers(otherData)
} }
await detailDom.canvasBox.privewDetail() await detailDom.canvasBox.privewDetail()
await upDateFrontBackSketch() await upDateFrontBackSketch()
await uploadSelectDetail() await uploadSelectDetail()
@@ -625,7 +626,7 @@ export default defineComponent({
if(detailData.isEditPattern.value && detailData.isEditPattern.value == str){ if(detailData.isEditPattern.value && detailData.isEditPattern.value == str){
// await detailDom.canvasBox.saveCanvas() // await detailDom.canvasBox.saveCanvas()
await (detailDom.canvasBox as any).privewDetail() 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 = '' detailData.isEditPattern.value = ''
}else{ }else{
// if(detailData.isEditPattern.value && (str == 'canvasEditor' || str == 'redGreenExample')){ // if(detailData.isEditPattern.value && (str == 'canvasEditor' || str == 'redGreenExample')){
@@ -780,8 +781,7 @@ export default defineComponent({
color.gradient = canvasColor.gradient color.gradient = canvasColor.gradient
} }
} }
if(detailData.isEditPattern.value == 'canvasEditor' || detailData.isEditPattern.value == 'redGreenExample'){
if(detailData.isEditPattern.value == 'canvasEditor'){
delete detailData.selectDetail.newDetail delete detailData.selectDetail.newDetail
detailData.selectDetail.trims.prints = allInfo.trims || [] detailData.selectDetail.trims.prints = allInfo.trims || []
detailData.selectDetail.printObject.prints = allInfo.prints || [] detailData.selectDetail.printObject.prints = allInfo.prints || []
@@ -804,7 +804,6 @@ export default defineComponent({
if(detailData.currentDetailType == 'color'){ if(detailData.currentDetailType == 'color'){
detailData.detailLeftColorKey++ detailData.detailLeftColorKey++
} }
} }
const canvasReload = async ()=>{ const canvasReload = async ()=>{
if(detailData.isEditPattern.value){ if(detailData.isEditPattern.value){

View File

@@ -163,7 +163,6 @@ export default defineComponent({
const privewDetail = async (oldSelectDetail = detailData.selectDetail)=>{ const privewDetail = async (oldSelectDetail = detailData.selectDetail)=>{
// if(!detailDom.editCanvas)return // if(!detailDom.editCanvas)return
return new Promise(async (res,reject)=>{ return new Promise(async (res,reject)=>{
console.log(detailDom.editCanvas)
await detailDom.editCanvas.exportImage({ await detailDom.editCanvas.exportImage({
isContainFixed:true, isContainFixed:true,
width:props.sketchSize.width, width:props.sketchSize.width,
@@ -245,17 +244,27 @@ export default defineComponent({
const frontBackChange = async (value:any)=>{ const frontBackChange = async (value:any)=>{
let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex]
store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
await nextTick()
if(!detailData.selectDetail.partialDesign.partialDesignPath && !detailData.selectDetail.partialDesign.partialDesignBase64){ if(!detailData.selectDetail.partialDesign.partialDesignPath && !detailData.selectDetail.partialDesign.partialDesignBase64){
await privewDetail() await privewDetail()
}else{
await detailDom.editCanvas.exportImage({
isFrontBackUpdata: true,
isContainFixed:true,
width:props.sketchSize.width,
height:props.sketchSize.height,
}).then((rv)=>{
if(detailData.selectDetail?.partialDesign)detailData.selectDetail.partialDesign.partialDesignBase64 = rv
})
} }
let full = detailData.selectDetail.partialDesign.partialDesignBase64 || detailData.selectDetail.partialDesign.partialDesignPath || detailData.selectDetail.path let full = detailData.selectDetail.partialDesign.partialDesignBase64 || detailData.selectDetail.partialDesign.partialDesignPath || detailData.selectDetail.path
let size = { let size = {
...detailData.canvasConfig, ...detailData.canvasConfig,
} }
store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
segmentImage(value,full,size).then(async (rv)=>{ segmentImage(value,full,size).then(async (rv)=>{
let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex]
if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl
if(!front?.oldMaskUrl)front.oldMaskUrl = front.maskUrl if(!front?.oldMaskUrl)front.oldMaskUrl = front.maskUrl
if(!back?.oldImageUrl)back.oldImageUrl = back.imageUrl if(!back?.oldImageUrl)back.oldImageUrl = back.imageUrl
@@ -267,7 +276,6 @@ export default defineComponent({
back.imageUrl = rv.targetBackUrl back.imageUrl = rv.targetBackUrl
// store.commit('DesignDetail/updataDetailItem',{maskUrl:value}) // store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
}) })
} }
const editSketchCanvasInit = async (value:any)=>{ const editSketchCanvasInit = async (value:any)=>{
detailData.canvasInstance = value detailData.canvasInstance = value

View File

@@ -393,8 +393,8 @@ export default defineComponent({
angle: 0, angle: 0,
flipX: false, flipX: false,
flipY: false, flipY: false,
// blendMode: "multiply", blendMode: "multiply",
blendMode: "source-over", // blendMode: "source-over",
gapX: 0, gapX: 0,
gapY: 0, gapY: 0,
} }

View File

@@ -124,7 +124,11 @@ export default defineComponent({
const handleResize = ()=>{ const handleResize = ()=>{
clearTimeout(time) clearTimeout(time)
time = setTimeout(()=>{ time = setTimeout(()=>{
store.commit('DesignDetail/setDesignDetail',getDetailListData.designDetail) let data = {
...getDetailListData.designDetail,
fromType:'resize',
}
store.commit('DesignDetail/setDesignDetail',data)
getDetailListDom.position?.updataPosition?.() getDetailListDom.position?.updataPosition?.()
getDetailListDom.modelNav?.setItemPosition?.() getDetailListDom.modelNav?.setItemPosition?.()
getDetailListDom.position?.updateRect?.() getDetailListDom.position?.updateRect?.()

View File

@@ -189,11 +189,11 @@ export default defineComponent({
} }
return { scaleX, scaleY, rotate }; return { scaleX, scaleY, rotate };
} }
const initMoveableForSelected = () => { const initMoveableForSelected = async (isDestroy:boolean = false) => {
// 销毁旧的实例 // 销毁旧的实例
if(selectItem.imgDomIndex == -1)return if(selectItem.imgDomIndex == -1)return
if (moveableInstance.value) { if (moveableInstance?.value?.destroy && !isDestroy) {
moveableInstance.value.destroy(); moveableInstance?.value?.destroy();
} }
const selectedEl = elementRefs.value[selectItem.imgDomIndex]; const selectedEl = elementRefs.value[selectItem.imgDomIndex];
@@ -509,7 +509,7 @@ export default defineComponent({
watch(()=>detailData.frontBack.front.length,(newValue,oldValue)=>{ watch(()=>detailData.frontBack.front.length,(newValue,oldValue)=>{
if(selectItem.selectDetail?.id)selectItem.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == selectItem.selectDetail?.id) if(selectItem.selectDetail?.id)selectItem.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == selectItem.selectDetail?.id)
setTimeout(()=>{ setTimeout(()=>{
initMoveableForSelected() initMoveableForSelected(oldValue == 0)
},100) },100)
}) })
const setRevocation = async ()=>{ const setRevocation = async ()=>{

View File

@@ -21,7 +21,11 @@
{{ $t("event.detail") }} {{ $t("event.detail") }}
</div> </div>
</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_box">
<div <div
class="eventsDetail_content_right_btn" class="eventsDetail_content_right_btn"
@@ -43,6 +47,7 @@
v-detailText="introItem.text" v-detailText="introItem.text"
></div> ></div>
</div> </div>
<div class="tips" v-if="eventsDetail.tips">{{ eventsDetail.tips }}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -120,8 +125,13 @@ export default defineComponent({
}) })
} }
const openDetail = () => { 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}` 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") window.open(url, "_blank")
// router.push("/award/index") // router.push("/award/index")
@@ -233,7 +243,7 @@ export default defineComponent({
} }
.eventsDetail_content_right { .eventsDetail_content_right {
.modal_title_text { .modal_title_text {
letter-spacing: 0.4rem; letter-spacing: 0.3rem;
font-weight: 600; font-weight: 600;
&-header { &-header {
display: flex; display: flex;
@@ -265,18 +275,21 @@ export default defineComponent({
.eventsDetail_content_right_btn_box { .eventsDetail_content_right_btn_box {
display: flex; display: flex;
justify-content: space-evenly; 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 { .modal_title_text:last-child::after {
content: ""; content: "";
display: block; display: block;
border-top: 3px solid; border-top: 3px solid;
height: 6rem; height: 6rem;
} }
.modal_title_text.award:last-child:after {
display: none;
}
} }
} }
} }
@@ -293,4 +306,13 @@ export default defineComponent({
white-space: nowrap; white-space: nowrap;
cursor: pointer; 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> </style>

View File

@@ -531,7 +531,7 @@ export default defineComponent({
} }
) )
const setSpeed = (item: any) => { const setSpeed = (item: any) => {
speed.speedData = item speed.speedData = {...item}
} }
onMounted(() => { onMounted(() => {
if (props.msg == 'Sketchboard') { if (props.msg == 'Sketchboard') {
@@ -734,6 +734,8 @@ export default defineComponent({
let maxImg = 8 let maxImg = 8
if (this.type_.type2 == 'Sketchboard') { if (this.type_.type2 == 'Sketchboard') {
maxImg = 20 maxImg = 20
}else if(this.type_.type2 == 'Printboard'){
maxImg = 16
} }
let parent: any = this.$parent let parent: any = this.$parent
if (parent.isUseGenerate) { if (parent.isUseGenerate) {

View File

@@ -205,7 +205,7 @@
</div> </div>
<div class="payment"> <div class="payment">
<div class="allocation"> <div class="allocation">
<div class="selectType"> <!-- <div class="selectType">
<div class="text">{{ $t('Renew.Payment') }}:</div> <div class="text">{{ $t('Renew.Payment') }}:</div>
<label> <label>
<input <input
@@ -227,7 +227,7 @@
/> />
{{ $t('Renew.Alipay') }} {{ $t('Renew.Alipay') }}
</label> </label>
</div> </div> -->
</div> </div>
<div class="gallery_btn gallery_btn_radius" @click="payment"> <div class="gallery_btn gallery_btn_radius" @click="payment">
{{ $t('upgradePlan.Continue') }} {{ $t('upgradePlan.Continue') }}

View File

@@ -77,17 +77,10 @@ export default defineComponent({
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translate(-50%,-50%); transform: translate(-50%,-50%);
width: 80%;
height: auto;
max-height: 80vh;
position: absolute; position: absolute;
width: max-content;
video{ video{
width: 100%;
max-height:80vh; max-height:80vh;
height: 100%; max-width:80vw;
object-fit: contain;
width: max-content;
} }
.general_video_btn{ .general_video_btn{
color: #fff; color: #fff;

View File

@@ -745,6 +745,12 @@ export default defineComponent({
"userLikeId": likeItem.id "userLikeId": likeItem.id
} }
arrData.push(obj) arrData.push(obj)
designData.selectLikeDesign.forEach((v:any)=>{
if(v.id === likeItem.id){
v.oldSort = v.sort
v.sort = likeItem.sort
}
})
}) })
let data = { let data = {
"userLikeGroupId": userGroupId.value, "userLikeGroupId": userGroupId.value,
@@ -1304,9 +1310,9 @@ export default defineComponent({
}) })
return 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) => { 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 delete parent.oldSort
return { return {
...parent, ...parent,
@@ -1533,7 +1539,7 @@ export default defineComponent({
this.observerData.time = setTimeout(()=>{ this.observerData.time = setTimeout(()=>{
this.setSystemDesigner(0) this.setSystemDesigner(0)
this.setDesignItemStyle() // this.setDesignItemStyle()
},100) },100)
// const { width } = entry.contentRect; // const { width } = entry.contentRect;
} }
@@ -1931,6 +1937,7 @@ export default defineComponent({
this.disLikeLoading = true; this.disLikeLoading = true;
Https.axiosPost(Https.httpUrls.designDislike, data) Https.axiosPost(Https.httpUrls.designDislike, data)
.then((rv: any) => { .then((rv: any) => {
console.log(rv)
if (rv) { if (rv) {
this.recycleDomHidden = true this.recycleDomHidden = true
this.store.commit("addDesignCollectionList", [design]); this.store.commit("addDesignCollectionList", [design]);

View File

@@ -41,10 +41,10 @@
<div class="center">{{ selectObject?.styleName?selectObject?.styleName:$t('Header.All') }}</div> <div class="center">{{ selectObject?.styleName?selectObject?.styleName:$t('Header.All') }}</div>
<div class="gallery_btn" @click="setStyle">{{ $t('Habit.Select') }}</div> <div class="gallery_btn" @click="setStyle">{{ $t('Habit.Select') }}</div>
</div> </div>
<div class="style brand marginBottom"> <!-- <div class="style brand marginBottom">
<div class="text">{{$t('Habit.Brand')}}:</div> <div class="text">{{$t('Habit.Brand')}}:</div>
<div class="gallery_btn" @click="setBrandDNA">{{ $t('Habit.Select') }}</div> <div class="gallery_btn" @click="setBrandDNA">{{ $t('Habit.Select') }}</div>
</div> </div> -->
<div class="brandImg" v-if="selectObject.userBrandDna"><img :src="selectObject.userBrandDnaImg"></div> <div class="brandImg" v-if="selectObject.userBrandDna"><img :src="selectObject.userBrandDnaImg"></div>
<div class="brandDNAStrenght marginBottom" v-if="selectObject.userBrandDna"> <div class="brandDNAStrenght marginBottom" v-if="selectObject.userBrandDna">
<div class="text" style="font-size: 1.6rem;"> <div class="text" style="font-size: 1.6rem;">

View File

@@ -258,6 +258,7 @@ methods: {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
text-align: center;
} }
} }
.content_bottom_item:nth-child(4n){ .content_bottom_item:nth-child(4n){

View File

@@ -178,6 +178,12 @@ const routes: Array<RouteRecordRaw> = [
meta: { enter: "all" }, meta: { enter: "all" },
component: () => import("@/views/HomeRecommend.vue"), component: () => import("@/views/HomeRecommend.vue"),
}, },
{
path: "/Square/:lang",
name: "HomeRecommendLang",
meta: { enter: "all" },
component: () => import("@/views/HomeRecommend.vue"),
},
{ {
path: "/administrator", path: "/administrator",
name: "administrator", name: "administrator",
@@ -331,6 +337,13 @@ const routes: Array<RouteRecordRaw> = [
meta: { enter: 3 }, meta: { enter: 3 },
component: () => component: () =>
import("@/component/Administrator/SE/getGenerateFrequency/index.vue"), import("@/component/Administrator/SE/getGenerateFrequency/index.vue"),
},
{
path: "globalAwardPopularity",
name: "globalAwardPopularity",
meta: { enter: 3 },
component: () =>
import("@/component/Administrator/globalAwardPopularity.vue"),
}, },
], ],
}, },

View File

@@ -87,8 +87,10 @@ const DesignDetail : Module<DesignDetail,RootState> = {
left:0, left:0,
top:0, top:0,
} }
if(data?.fromType !== 'resize'){
v.maskMinioUrl = v.layersObject?.[0]?.maskMinioUrl v.maskMinioUrl = v.layersObject?.[0]?.maskMinioUrl
v.maskUrl = v.layersObject?.[0]?.maskUrl v.maskUrl = v.layersObject?.[0]?.maskUrl
}
v.layersObject[i].designOpenrtionBtn = false v.layersObject[i].designOpenrtionBtn = false
if(v.layersObject[i].imageCategory.indexOf("back") == -1){ if(v.layersObject[i].imageCategory.indexOf("back") == -1){
front[index] = v.layersObject[i] front[index] = v.layersObject[i]

View File

@@ -198,6 +198,13 @@ const all = (t)=>{
route: '/administrator/subscriptionPlan', route: '/administrator/subscriptionPlan',
key: 'sub14', key: 'sub14',
isShow: true isShow: true
},
{
name: 'Global Award Popularity',
icon: 'usetime',
route: '/administrator/globalAwardPopularity',
key: 'sub15',
isShow: true
} }
] ]
} }

View File

@@ -346,6 +346,7 @@ export const Https = {
switchSubscribePlan: '/api/subscription_plan/switchSubscriptionPlan', // 切换管理员订阅计划 switchSubscribePlan: '/api/subscription_plan/switchSubscriptionPlan', // 切换管理员订阅计划
switchSubAccountSubscribePlan: switchSubAccountSubscribePlan:
'/api/subscription_plan/switchSubAccSubscriptionPlan', // 切换子账号订阅计划 '/api/subscription_plan/switchSubAccSubscriptionPlan', // 切换子账号订阅计划
getGlobalAwardPopularity: '/api/global-award/page/visit/count', // 获取global award流量
//云生成 //云生成
designCloud: `/api/design/designCloud`, //创建云生成 designCloud: `/api/design/designCloud`, //创建云生成

View File

@@ -340,12 +340,12 @@ const navTypeList = (t)=>{
value:'Models', value:'Models',
router:'library=Models' router:'library=Models'
}, },
{ // {
icon:'fi-ss-gem', // icon:'fi-ss-gem',
label:t('LibraryPage.brandDNA'), // label:t('LibraryPage.brandDNA'),
value:'MyBrand', // value:'MyBrand',
router:'library=MyBrand' // router:'library=MyBrand'
}, // },
] ]
}, },
// history:{ // history:{

View File

@@ -672,6 +672,17 @@ function sketchToMask(sketchImage) {
img.src = 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 { export {
isEmail, isEmail,
getUploadUrl, getUploadUrl,
@@ -695,5 +706,6 @@ export {
calculateGradientCoordinate, calculateGradientCoordinate,
segmentImage, segmentImage,
UrlToFile, UrlToFile,
sketchToMask sketchToMask,
isValidUrl
} }

View File

@@ -400,7 +400,7 @@
<a href="https://www.facebook.com/CodeCreateAI" target="_blank" > <a href="https://www.facebook.com/CodeCreateAI" target="_blank" >
<img src="@/assets/images/socialMediaLogo/faceBookIcon.svg" alt=""> <img src="@/assets/images/socialMediaLogo/faceBookIcon.svg" alt="">
</a> </a>
<a href="https://www.youtube.com/@AiDA-3.1" target="_blank" > <a href="https://www.youtube.com/@Code-Create_AiDA" target="_blank" >
<img src="@/assets/images/socialMediaLogo/socialIcons.svg" alt=""> <img src="@/assets/images/socialMediaLogo/socialIcons.svg" alt="">
</a> </a>
<a href="https://www.linkedin.com/company/code-create-limited" target="_blank" > <a href="https://www.linkedin.com/company/code-create-limited" target="_blank" >

View File

@@ -62,7 +62,7 @@ import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { setLang } from '@/tool/guide' import { setLang } from '@/tool/guide'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { gsap, TweenMax } from 'gsap' import { gsap, TweenMax } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger' import { ScrollTrigger } from 'gsap/ScrollTrigger'
export default defineComponent({ export default defineComponent({
@@ -71,6 +71,7 @@ export default defineComponent({
const {t, locale} = useI18n() const {t, locale} = useI18n()
const store = useStore() const store = useStore()
const router = useRouter() const router = useRouter()
const route = useRoute()
let registerModel = ref() let registerModel = ref()
let data = reactive({}) let data = reactive({})
@@ -117,7 +118,14 @@ export default defineComponent({
onMounted(() => { onMounted(() => {
window.addEventListener('resize', updataIsMoblie) window.addEventListener('resize', updataIsMoblie)
// 初始化语言设置 // 初始化语言设置
const savedLang = localStorage.getItem('loginLanguage') let savedLang = localStorage.getItem('loginLanguage')
if(route?.params?.lang == 'cn'){
savedLang = 'CHINESE_SIMPLIFIED'
localStorage.setItem('loginLanguage', savedLang)
}{
savedLang = 'ENGLISH'
localStorage.setItem('loginLanguage', savedLang)
}
if (savedLang) { if (savedLang) {
isChinese.value = savedLang === 'CHINESE_SIMPLIFIED' isChinese.value = savedLang === 'CHINESE_SIMPLIFIED'
locale.value = savedLang locale.value = savedLang