Compare commits
25 Commits
main
...
260db8e896
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
260db8e896 | ||
|
|
b5f393ceb7 | ||
| b1bea281ec | |||
| c9b65b6090 | |||
| 0ac6d6e93f | |||
| 743b3f0ef6 | |||
| 17f0045dbe | |||
| b16c5c3263 | |||
| 8ed58d37d8 | |||
|
|
84175e94d1 | ||
|
|
ed83044f81 | ||
| 9cb6be3098 | |||
| 4ab4578081 | |||
|
|
652d89d3be | ||
|
|
17edeef461 | ||
|
|
aad6919ec3 | ||
|
|
baf161e695 | ||
|
|
51751f6b5e | ||
|
|
c1b051a185 | ||
| 1a5e285f09 | |||
| 911d1d8477 | |||
| e0261d4a37 | |||
| 50cb33ac43 | |||
| 261064bd23 | |||
| a1e8f3295e |
@@ -1,56 +0,0 @@
|
|||||||
name: AiDA WEB-Node.js Develop 分支构建部署123
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- dev_vite
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: "contains(github.event.head_commit.message, '[run build]')"
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [ 18.18.0 ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
REMOTE_DEPLOY_PATH: /workspace/workspace_aida/DevelopVersion/develop-aida-web-front
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: 1.检出代码
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: 2.设置 Node.js 环境
|
|
||||||
uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run build:dev
|
|
||||||
- run: ls -l
|
|
||||||
|
|
||||||
- name: 3.同步文件到远程服务器
|
|
||||||
uses: appleboy/scp-action@v0.1.7
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.SERVER_HOST }}
|
|
||||||
username: ${{ secrets.SERVER_USER }}
|
|
||||||
key: ${{ secrets.SSH_KEY }}
|
|
||||||
source: "./dist/*"
|
|
||||||
target: ${{ env.REMOTE_DEPLOY_PATH }}
|
|
||||||
ssh_options: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
|
||||||
strip_components: 0
|
|
||||||
|
|
||||||
- name: 4. 远程重载 Nginx 配置
|
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.SERVER_HOST }}
|
|
||||||
username: ${{ secrets.SERVER_USER }}
|
|
||||||
key: ${{ secrets.SSH_KEY }}
|
|
||||||
# 核心:执行 Nginx 重载命令
|
|
||||||
script: |
|
|
||||||
echo "尝试重载 Nginx 服务..."
|
|
||||||
# 💡 注意:执行此命令需要服务器用户具有 sudo 权限,并且配置了 NOPASSWD。
|
|
||||||
# 否则工作流可能会因为权限不足而失败。
|
|
||||||
sudo systemctl reload nginx
|
|
||||||
echo "Nginx 重载命令已发送。"
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
name: 手动触发 AiDA WEB-Node.js Develop 分支构建部署
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [ 18.18.0 ]
|
|
||||||
env:
|
|
||||||
REMOTE_DEPLOY_PATH: /workspace/workspace_aida/DevelopVersion/develop-aida-web-front
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: 1.检出代码
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: 2.设置 Node.js 环境
|
|
||||||
uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run build:dev
|
|
||||||
- run: ls -l
|
|
||||||
|
|
||||||
- name: 3.同步文件到远程服务器
|
|
||||||
uses: appleboy/scp-action@v0.1.7
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.SERVER_HOST }}
|
|
||||||
username: ${{ secrets.SERVER_USER }}
|
|
||||||
key: ${{ secrets.SSH_KEY }}
|
|
||||||
source: "./dist/*"
|
|
||||||
target: ${{ env.REMOTE_DEPLOY_PATH }}
|
|
||||||
ssh_options: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
|
||||||
strip_components: 0
|
|
||||||
|
|
||||||
- name: 4. 远程重载 Nginx 配置
|
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.SERVER_HOST }}
|
|
||||||
username: ${{ secrets.SERVER_USER }}
|
|
||||||
key: ${{ secrets.SSH_KEY }}
|
|
||||||
# 核心:执行 Nginx 重载命令
|
|
||||||
script: |
|
|
||||||
echo "尝试重载 Nginx 服务..."
|
|
||||||
# 💡 注意:执行此命令需要服务器用户具有 sudo 权限,并且配置了 NOPASSWD。
|
|
||||||
# 否则工作流可能会因为权限不足而失败。
|
|
||||||
sudo systemctl reload nginx
|
|
||||||
echo "Nginx 重载命令已发送。"
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
name: AiDA WEB-Node.js StableVersion 分支构建部署
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [ 18.18.0 ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: 1.检出代码
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: StableVersion
|
|
||||||
|
|
||||||
- name: 2.打印当前分支信息
|
|
||||||
run: |
|
|
||||||
echo "Current branch being deployed is: $(git rev-parse --abbrev-ref HEAD)"
|
|
||||||
echo "The code is from the 'main' branch, as specified in 'actions/checkout'."
|
|
||||||
|
|
||||||
- name: 3.设置 Node.js 环境 ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run build
|
|
||||||
- run: ls -l
|
|
||||||
|
|
||||||
- name: 3.5. 手动安装 AWS CLI v2 # 新增步骤:确保 aws 命令可用
|
|
||||||
run: |
|
|
||||||
echo "安装 AWS CLI V2..."
|
|
||||||
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
|
||||||
unzip awscliv2.zip
|
|
||||||
sudo ./aws/install --update
|
|
||||||
aws --version
|
|
||||||
echo "AWS CLI V2 安装完成。"
|
|
||||||
|
|
||||||
- name: 4.配置 AWS 凭证
|
|
||||||
uses: aws-actions/configure-aws-credentials@main
|
|
||||||
with:
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
aws-region: 'ap-east-1'
|
|
||||||
|
|
||||||
- name: 5.同步 dist 目录到 S3
|
|
||||||
run: |
|
|
||||||
aws s3 sync dist/* s3://${{ secrets.S3_BUCKET_NAME }}/ --acl public-read
|
|
||||||
|
|
||||||
- name: 6.部署完成
|
|
||||||
run: echo "构建和部署到 S3 任务完成。"
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
name: AiDA WEB-Node.js StableVersion 分支构建部署
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# cron为UTC时区,构建时间=部署时间-8小时 {*分 (-8)时 *日 *月 *周} ---
|
|
||||||
# 示例: 1月1日22点22分触发构建 cron写作 - '22 14 1 1 *'
|
|
||||||
- cron: '22 14 1 1 *'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [ 18.18.0 ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: 1.检出代码
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: StableVersion
|
|
||||||
|
|
||||||
- name: 2.打印当前分支信息
|
|
||||||
run: |
|
|
||||||
echo "Current branch being deployed is: $(git rev-parse --abbrev-ref HEAD)"
|
|
||||||
echo "The code is from the 'main' branch, as specified in 'actions/checkout'."
|
|
||||||
|
|
||||||
- name: 3.设置 Node.js 环境 ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run build
|
|
||||||
- run: ls -l
|
|
||||||
|
|
||||||
- name: 3.5. 手动安装 AWS CLI v2 # 新增步骤:确保 aws 命令可用
|
|
||||||
run: |
|
|
||||||
echo "安装 AWS CLI V2..."
|
|
||||||
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
|
||||||
unzip awscliv2.zip
|
|
||||||
sudo ./aws/install --update
|
|
||||||
aws --version
|
|
||||||
echo "AWS CLI V2 安装完成。"
|
|
||||||
|
|
||||||
- name: 4.配置 AWS 凭证
|
|
||||||
uses: aws-actions/configure-aws-credentials@main
|
|
||||||
with:
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
aws-region: 'ap-east-1'
|
|
||||||
|
|
||||||
- name: 5.同步 dist 目录到 S3
|
|
||||||
run: |
|
|
||||||
aws s3 sync dist/* s3://${{ secrets.S3_BUCKET_NAME }}/ --acl public-read
|
|
||||||
|
|
||||||
- name: 6.部署完成
|
|
||||||
run: echo "构建和部署到 S3 任务完成。"
|
|
||||||
23
src/App.vue
23
src/App.vue
@@ -1,7 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
|
<div class="loading" v-show="loading"><a-spin :delay="0.5" /></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
const store = useStore();
|
||||||
|
const loading = computed(() => store.state.loading || store.state.view_loading);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
#app {
|
#app {
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
@@ -9,7 +18,19 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.loading{
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0,0,0,0.4);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 999999999999;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
.ipad{
|
.ipad{
|
||||||
*{
|
*{
|
||||||
-webkit-touch-callout:none;
|
-webkit-touch-callout:none;
|
||||||
|
|||||||
@@ -1250,10 +1250,14 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
|
|||||||
background: #000 !important;
|
background: #000 !important;
|
||||||
border-color: #000 !important;
|
border-color: #000 !important;
|
||||||
}
|
}
|
||||||
|
.ant-spin .ant-spin-dot {
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
.ant-spin-dot-item {
|
.ant-spin-dot-item {
|
||||||
background-color: #000000 !important;
|
background-color: #000000 !important;
|
||||||
width: 9px !important;
|
width: 0.9em !important;
|
||||||
height: 9px !important;
|
height: 0.9em !important;
|
||||||
}
|
}
|
||||||
.ant-spin {
|
.ant-spin {
|
||||||
color: #000;
|
color: #000;
|
||||||
|
|||||||
@@ -1378,10 +1378,14 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//loding样式
|
//loding样式
|
||||||
|
.ant-spin .ant-spin-dot{
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
.ant-spin-dot-item{
|
.ant-spin-dot-item{
|
||||||
background-color: #000000 !important;
|
background-color: #000000 !important;
|
||||||
width: 9px !important;
|
width: .9em !important;
|
||||||
height: 9px !important;
|
height: .9em !important;
|
||||||
}
|
}
|
||||||
.ant-spin{
|
.ant-spin{
|
||||||
color: #000;
|
color: #000;
|
||||||
|
|||||||
848
src/component/Administrator/subscriptionPlan.vue
Normal file
848
src/component/Administrator/subscriptionPlan.vue
Normal file
@@ -0,0 +1,848 @@
|
|||||||
|
<template>
|
||||||
|
<div class="subscription-plan">
|
||||||
|
<a-card class="search-card" :bordered="false">
|
||||||
|
<a-form
|
||||||
|
class="search-form"
|
||||||
|
layout="inline"
|
||||||
|
:model="searchForm"
|
||||||
|
:label-col="{ style: { width: '12rem' } }"
|
||||||
|
:wrapper-col="{ style: { width: '22rem' } }"
|
||||||
|
>
|
||||||
|
<a-form-item label="ID">
|
||||||
|
<a-input v-model:value="searchForm.id" allow-clear placeholder="Input the id" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Name">
|
||||||
|
<a-input
|
||||||
|
v-model:value="searchForm.name"
|
||||||
|
allow-clear
|
||||||
|
placeholder="Input the name"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Time Range">
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="searchForm.dateRange"
|
||||||
|
value-format="X"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Organization">
|
||||||
|
<a-select
|
||||||
|
v-model:value="searchForm.organizationId"
|
||||||
|
allow-clear
|
||||||
|
placeholder="Select Organization"
|
||||||
|
style="width: 180px"
|
||||||
|
@popupScroll="handleOrganizationScroll"
|
||||||
|
@select="handleOrganizationSelect"
|
||||||
|
@change="handleOrganizationChange"
|
||||||
|
>
|
||||||
|
<a-select-option value="ADD_ORGANIZATION" class="add-organization-option">
|
||||||
|
+ 添加组织
|
||||||
|
</a-select-option>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in organizationOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Admin Account">
|
||||||
|
<a-select
|
||||||
|
v-model:value="searchForm.adminAccId"
|
||||||
|
allow-clear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
placeholder="Select Account"
|
||||||
|
style="width: 180px"
|
||||||
|
:options="allUserList"
|
||||||
|
></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="handleSearch">Search</a-button>
|
||||||
|
<a-button @click="handleReset">Reset</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card class="table-card" :bordered="false">
|
||||||
|
<div class="table-card__header">
|
||||||
|
<div class="table-card__title">Subscription Plan</div>
|
||||||
|
<a-button type="primary" @click="openCreate">New Subscription Plan</a-button>
|
||||||
|
</div>
|
||||||
|
<a-table
|
||||||
|
:data-source="tableData"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="tableLoading"
|
||||||
|
row-key="id"
|
||||||
|
:pagination="{
|
||||||
|
showSizeChanger: true,
|
||||||
|
current: searchForm.page,
|
||||||
|
pageSize: searchForm.size,
|
||||||
|
total: searchForm.total,
|
||||||
|
showQuickJumper: true,
|
||||||
|
bordered: false
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
column.key === 'currentPeriodStart' || column.key === 'currentPeriodEnd'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ formatTime(record[column.key], 'YYYY-MM-DD hh:mm:ss') }}
|
||||||
|
</template>
|
||||||
|
<!-- <template v-if="column.key === 'organizationId'">
|
||||||
|
{{ organizationOptions.find(item => item.id === record[column.key]).name }}
|
||||||
|
</template> -->
|
||||||
|
|
||||||
|
<template v-if="column.key === 'status'">
|
||||||
|
<a-tag :color="statusColorMap[record.status]">
|
||||||
|
{{ statusLabelMap[record.status] }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'adminAccId'">
|
||||||
|
{{ allUserList.find(item => item.value === record.adminAccId)?.label }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="column.key === 'actions'">
|
||||||
|
<a-space>
|
||||||
|
<a @click="openEdit(record)">Edit</a>
|
||||||
|
<a-popconfirm
|
||||||
|
title="Confirm to delete this subscription plan?"
|
||||||
|
ok-text="Confirm"
|
||||||
|
cancel-text="Cancel"
|
||||||
|
@confirm="removePlan(record.id)"
|
||||||
|
>
|
||||||
|
<a class="danger-text">Delete</a>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<div class="subscriptionPlanModal" ref="subscriptionPlanModal"></div>
|
||||||
|
<a-modal
|
||||||
|
class="subscriptionPlan_modal generalModel"
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:footer="null"
|
||||||
|
:get-container="() => $refs.subscriptionPlanModal"
|
||||||
|
width="50%"
|
||||||
|
:maskClosable="false"
|
||||||
|
:centered="true"
|
||||||
|
:closable="false"
|
||||||
|
:mask="true"
|
||||||
|
wrapClassName="#app"
|
||||||
|
:keyboard="false"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<div class="generalModel_btn">
|
||||||
|
<div class="generalModel_closeIcon" @click.stop="cancelModal">
|
||||||
|
<svg
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
viewBox="0 0 46 46"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<circle cx="23" cy="23" r="23" fill="#000" fill-opacity="0.3" />
|
||||||
|
<rect
|
||||||
|
x="32.5063"
|
||||||
|
y="12"
|
||||||
|
width="3"
|
||||||
|
height="29"
|
||||||
|
rx="1.5"
|
||||||
|
transform="rotate(45 32.5063 12)"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="34.6274"
|
||||||
|
y="32.5059"
|
||||||
|
width="3"
|
||||||
|
height="29"
|
||||||
|
rx="1.5"
|
||||||
|
transform="rotate(135 34.6274 32.5059)"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal_title_text">
|
||||||
|
<div>{{ modalTitle }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="subscriptionPlan_center admin_page">
|
||||||
|
<div class="form_content">
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
Name:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formState.name"
|
||||||
|
placeholder="Input the name"
|
||||||
|
style="width: 250px"
|
||||||
|
:disabled="isEditMode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
Organization:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
v-model:value="formState.organizationId"
|
||||||
|
placeholder="Select the organization"
|
||||||
|
allow-clear
|
||||||
|
style="width: 250px"
|
||||||
|
@popupScroll="handleOrganizationScroll"
|
||||||
|
@select="handleOrganizationSelect"
|
||||||
|
@change="handleOrganizationChange"
|
||||||
|
:disabled="isEditMode"
|
||||||
|
>
|
||||||
|
<a-select-option value="ADD_ORGANIZATION" class="add-organization-option">
|
||||||
|
+ 添加组织
|
||||||
|
</a-select-option>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in organizationOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
Admin Account:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
v-model:value="formState.adminAccId"
|
||||||
|
placeholder="Select the admin account"
|
||||||
|
allow-clear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
style="width: 250px"
|
||||||
|
:options="allUserList"
|
||||||
|
></a-select>
|
||||||
|
</div>
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
Start Time:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="formState.currentPeriodStart"
|
||||||
|
value-format="X"
|
||||||
|
style="width: 250px"
|
||||||
|
class="range_picker"
|
||||||
|
placeholder="Select the start time"
|
||||||
|
>
|
||||||
|
<template #suffixIcon>
|
||||||
|
<span class="icon iconfont range_picker_icon icon-rili"></span>
|
||||||
|
</template>
|
||||||
|
</a-date-picker>
|
||||||
|
</div>
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
End Time:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="formState.currentPeriodEnd"
|
||||||
|
value-format="X"
|
||||||
|
style="width: 250px"
|
||||||
|
class="range_picker"
|
||||||
|
placeholder="Select the end time"
|
||||||
|
>
|
||||||
|
<template #suffixIcon>
|
||||||
|
<span class="icon iconfont range_picker_icon icon-rili"></span>
|
||||||
|
</template>
|
||||||
|
</a-date-picker>
|
||||||
|
</div>
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
Account Num:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="formState.accountNum"
|
||||||
|
:min="0"
|
||||||
|
style="width: 250px"
|
||||||
|
placeholder="Input the account number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
Credit Limit:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="formState.creditLimit"
|
||||||
|
:min="0"
|
||||||
|
style="width: 250px"
|
||||||
|
placeholder="Input the credit limit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="subscriptionPlan_btn admin_page">
|
||||||
|
<div class="admin_search_item" @click="cancelModal">Close</div>
|
||||||
|
<div class="admin_search_item" @click="handleSubmit">OK</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<div class="organizationModal" ref="organizationModal"></div>
|
||||||
|
<a-modal
|
||||||
|
class="organization_modal"
|
||||||
|
v-model:visible="organizationModalVisible"
|
||||||
|
:footer="null"
|
||||||
|
:get-container="() => $refs.organizationModal"
|
||||||
|
:maskClosable="false"
|
||||||
|
:centered="true"
|
||||||
|
:mask="true"
|
||||||
|
wrapClassName="#app"
|
||||||
|
:keyboard="false"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<div class="modal_title_text">
|
||||||
|
<div>Create Organization</div>
|
||||||
|
</div>
|
||||||
|
<div class="subscriptionPlan_center admin_page">
|
||||||
|
<div class="form_content">
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
Name:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-input
|
||||||
|
v-model:value="organizationForm.name"
|
||||||
|
placeholder="Input the name"
|
||||||
|
style="width: 250px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="admin_state_item">
|
||||||
|
<span>
|
||||||
|
Type:
|
||||||
|
<span>*</span>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
v-model:value="organizationForm.type"
|
||||||
|
placeholder="Select type"
|
||||||
|
style="width: 250px"
|
||||||
|
>
|
||||||
|
<a-select-option value="Enterprise">Enterprise</a-select-option>
|
||||||
|
<a-select-option value="Education">Education</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="organization_footer">
|
||||||
|
<div class="footer_btn ant-btn ant-btn-primary" @click="cancelOrganizationModal">
|
||||||
|
Close
|
||||||
|
</div>
|
||||||
|
<div class="footer_btn ant-btn ant-btn-primary" @click="handleCreateOrganization">
|
||||||
|
OK
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import { Https } from '@/tool/https'
|
||||||
|
import { formatTime } from '@/tool/util'
|
||||||
|
import store from '@/store'
|
||||||
|
import type { FormInstance, Rule } from 'ant-design-vue/es/form'
|
||||||
|
|
||||||
|
type PlanStatus = 'active' | 'paused' | 'ended'
|
||||||
|
interface SubscriptionPlan {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
currentPeriodStart: string
|
||||||
|
currentPeriodEnd: string
|
||||||
|
organizationId: string
|
||||||
|
adminAccId: string
|
||||||
|
status: PlanStatus
|
||||||
|
creditLimit: number
|
||||||
|
accountNum?: number
|
||||||
|
startStamp: number
|
||||||
|
endStamp: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchForm = reactive({
|
||||||
|
name: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
organizationId: undefined as string | undefined,
|
||||||
|
adminAccId: undefined as string | undefined,
|
||||||
|
id: '',
|
||||||
|
page: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const toSeconds = (dateStr: string) => Math.floor(new Date(dateStr).getTime() / 1000)
|
||||||
|
|
||||||
|
const tableData = ref<SubscriptionPlan[]>([])
|
||||||
|
const tableLoading = ref(false)
|
||||||
|
|
||||||
|
const modalVisible = ref(false)
|
||||||
|
const confirmLoading = ref(false)
|
||||||
|
const modalTitle = ref('New Subscription Plan')
|
||||||
|
const isEditMode = ref(false)
|
||||||
|
const formState = reactive({
|
||||||
|
name: '',
|
||||||
|
currentPeriodStart: '',
|
||||||
|
currentPeriodEnd: '',
|
||||||
|
organizationId: undefined as string | undefined,
|
||||||
|
adminAccId: undefined as string | undefined,
|
||||||
|
creditLimit: null as number | null,
|
||||||
|
accountNum: null as number | null
|
||||||
|
})
|
||||||
|
|
||||||
|
const organizationModalVisible = ref(false)
|
||||||
|
const organizationForm = reactive({
|
||||||
|
name: '',
|
||||||
|
type: undefined as string | undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const statusLabelMap: Record<PlanStatus, string> = {
|
||||||
|
active: 'Active',
|
||||||
|
paused: 'Paused',
|
||||||
|
ended: 'Ended'
|
||||||
|
}
|
||||||
|
const statusColorMap: Record<PlanStatus, string> = {
|
||||||
|
active: 'green',
|
||||||
|
paused: 'orange',
|
||||||
|
ended: 'red'
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: 'Name', dataIndex: 'name', key: 'name' },
|
||||||
|
{ title: 'ID', dataIndex: 'id', key: 'id' },
|
||||||
|
{ title: 'Organization', dataIndex: 'organizationName', key: 'organizationName' },
|
||||||
|
{ title: 'Admin Account', dataIndex: 'adminAccId', key: 'adminAccId' },
|
||||||
|
{ title: 'Account Num', dataIndex: 'accountNum', key: 'accountNum' },
|
||||||
|
{
|
||||||
|
title: 'Start Time',
|
||||||
|
dataIndex: 'currentPeriodStart',
|
||||||
|
key: 'currentPeriodStart'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'End Time',
|
||||||
|
dataIndex: 'currentPeriodEnd',
|
||||||
|
key: 'currentPeriodEnd'
|
||||||
|
},
|
||||||
|
{ title: 'Status', dataIndex: 'status', key: 'status' },
|
||||||
|
{ title: 'Credit Limit', dataIndex: 'creditLimit', key: 'creditLimit' },
|
||||||
|
{ title: 'Operations', key: 'actions', width: 160 }
|
||||||
|
]
|
||||||
|
onMounted(async () => {
|
||||||
|
await getOrganizationList()
|
||||||
|
await handleSearch()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleFetchTableData = async () => {
|
||||||
|
tableLoading.value = true
|
||||||
|
return Https.axiosPost(Https.httpUrls.searchAllSubscribePlan, searchForm)
|
||||||
|
.then(res => {
|
||||||
|
tableData.value = res.records.map(item => {
|
||||||
|
const organization = organizationOptions.value.find(
|
||||||
|
el => el.id === item.organizationId
|
||||||
|
) || { name: '' }
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
organizationName: organization.name || ''
|
||||||
|
}
|
||||||
|
debugger
|
||||||
|
})
|
||||||
|
searchForm.total = res.total
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
tableLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetFormState = () => {
|
||||||
|
formState.name = ''
|
||||||
|
formState.currentPeriodStart = ''
|
||||||
|
formState.currentPeriodEnd = ''
|
||||||
|
formState.organizationId = undefined
|
||||||
|
formState.adminAccId = undefined
|
||||||
|
formState.creditLimit = null
|
||||||
|
formState.accountNum = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
searchForm.page = 1
|
||||||
|
handleFetchTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
searchForm.name = ''
|
||||||
|
searchForm.startTime = ''
|
||||||
|
searchForm.endTime = ''
|
||||||
|
searchForm.organizationId = undefined
|
||||||
|
searchForm.adminAccId = undefined
|
||||||
|
searchForm.id = ''
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
const allUserList = computed(() => {
|
||||||
|
return store.state.adminPage.allUserList
|
||||||
|
})
|
||||||
|
const openCreate = () => {
|
||||||
|
modalTitle.value = 'New Subscription Plan'
|
||||||
|
isEditMode.value = false
|
||||||
|
resetFormState()
|
||||||
|
modalVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const openEdit = (record: SubscriptionPlan) => {
|
||||||
|
modalTitle.value = 'Edit Subscription Plan'
|
||||||
|
isEditMode.value = true
|
||||||
|
formState.name = record.name
|
||||||
|
formState.currentPeriodStart = record.currentPeriodStart
|
||||||
|
formState.currentPeriodEnd = record.currentPeriodEnd
|
||||||
|
formState.organizationId = record.organizationId
|
||||||
|
formState.adminAccId = record.adminAccId
|
||||||
|
formState.creditLimit = record.creditLimit
|
||||||
|
formState.accountNum = (record as any).accountNum || null
|
||||||
|
modalVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateForm = (): boolean => {
|
||||||
|
interface FieldRule {
|
||||||
|
value: any
|
||||||
|
message: string
|
||||||
|
checkNull?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const requiredFields: FieldRule[] = [
|
||||||
|
{ value: formState.currentPeriodStart, message: 'Please select the start time' },
|
||||||
|
{ value: formState.currentPeriodEnd, message: 'Please select the end time' },
|
||||||
|
{ value: formState.adminAccId, message: 'Please select the admin account' },
|
||||||
|
{
|
||||||
|
value: formState.creditLimit,
|
||||||
|
message: 'Please input credit limit',
|
||||||
|
checkNull: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: formState.accountNum,
|
||||||
|
message: 'Please input account number',
|
||||||
|
checkNull: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!isEditMode.value) {
|
||||||
|
requiredFields.push(
|
||||||
|
{ value: formState.name, message: 'Please input the name' },
|
||||||
|
{ value: formState.organizationId, message: 'Please select organization' }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const field of requiredFields) {
|
||||||
|
const isEmpty = field.checkNull
|
||||||
|
? field.value === null || field.value === undefined
|
||||||
|
: !field.value
|
||||||
|
if (isEmpty) {
|
||||||
|
message.warning(field.message)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!validateForm()) return
|
||||||
|
confirmLoading.value = true
|
||||||
|
const params = {
|
||||||
|
...formState,
|
||||||
|
currentPeriodStart: Number(formState.currentPeriodStart),
|
||||||
|
currentPeriodEnd: Number(formState.currentPeriodEnd)
|
||||||
|
}
|
||||||
|
let res = null
|
||||||
|
try {
|
||||||
|
if (isEditMode.value) {
|
||||||
|
res = await Https.axiosPost(Https.httpUrls.updateSubscribePlan, params)
|
||||||
|
} else {
|
||||||
|
res = await Https.axiosPost(Https.httpUrls.createSubscribePlan, params)
|
||||||
|
}
|
||||||
|
message.success(
|
||||||
|
`${isEditMode.value ? 'Subscription plan updated' : 'Subscription plan created'}`
|
||||||
|
)
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message)
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
confirmLoading.value = false
|
||||||
|
modalVisible.value = false
|
||||||
|
resetFormState()
|
||||||
|
isEditMode.value = false
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelModal = () => {
|
||||||
|
modalVisible.value = false
|
||||||
|
resetFormState()
|
||||||
|
isEditMode.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const removePlan = (id: number) => {
|
||||||
|
tableLoading.value = true
|
||||||
|
Https.axiosGet(Https.httpUrls.deleteSubscribePlan, { params: { id } })
|
||||||
|
.then(res => {
|
||||||
|
message.success('Subscription plan deleted')
|
||||||
|
handleReset()
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
message.error(error.message)
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
tableLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const organizationOptions = ref([])
|
||||||
|
const organizationParams = reactive({
|
||||||
|
page: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
const organizationLoading = ref(false)
|
||||||
|
const getOrganizationList = async (isLoadMore = false) => {
|
||||||
|
if (organizationLoading.value) return
|
||||||
|
if (isLoadMore) {
|
||||||
|
const loaded = organizationParams.page * organizationParams.size
|
||||||
|
if (organizationParams.total && loaded >= organizationParams.total) return
|
||||||
|
organizationParams.page += 1
|
||||||
|
} else {
|
||||||
|
organizationParams.page = 1
|
||||||
|
organizationOptions.value = []
|
||||||
|
}
|
||||||
|
organizationLoading.value = true
|
||||||
|
try {
|
||||||
|
const rv: any = await Https.axiosPost(
|
||||||
|
Https.httpUrls.queryOrganization,
|
||||||
|
organizationParams
|
||||||
|
)
|
||||||
|
if (rv) {
|
||||||
|
organizationOptions.value = [...organizationOptions.value, ...(rv.records || [])]
|
||||||
|
organizationParams.total = rv.total || 0
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
organizationLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleOrganizationScroll = (e: any) => {
|
||||||
|
const target = e?.target
|
||||||
|
if (!target) return
|
||||||
|
const nearBottom = target.scrollTop + target.clientHeight >= target.scrollHeight - 20
|
||||||
|
if (nearBottom) {
|
||||||
|
getOrganizationList(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOrganizationSelect = (value: string) => {
|
||||||
|
if (value === 'ADD_ORGANIZATION') {
|
||||||
|
// 打开添加组织弹窗
|
||||||
|
organizationModalVisible.value = true
|
||||||
|
// 使用nextTick确保值被重置,使其不被选中
|
||||||
|
nextTick(() => {
|
||||||
|
if (searchForm.organizationId === 'ADD_ORGANIZATION') {
|
||||||
|
searchForm.organizationId = undefined
|
||||||
|
}
|
||||||
|
if (formState.organizationId === 'ADD_ORGANIZATION') {
|
||||||
|
formState.organizationId = undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOrganizationChange = (value: string) => {
|
||||||
|
// 如果change事件触发时值是"添加组织",立即重置
|
||||||
|
if (value === 'ADD_ORGANIZATION') {
|
||||||
|
nextTick(() => {
|
||||||
|
if (searchForm.organizationId === 'ADD_ORGANIZATION') {
|
||||||
|
searchForm.organizationId = undefined
|
||||||
|
}
|
||||||
|
if (formState.organizationId === 'ADD_ORGANIZATION') {
|
||||||
|
formState.organizationId = undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelOrganizationModal = () => {
|
||||||
|
organizationModalVisible.value = false
|
||||||
|
organizationForm.name = ''
|
||||||
|
organizationForm.type = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreateOrganization = async () => {
|
||||||
|
if (!organizationForm.name || !organizationForm.type) {
|
||||||
|
message.warning('Please fill in name and type')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res: any = await Https.axiosGet(Https.httpUrls.addOrganization, {
|
||||||
|
params: {
|
||||||
|
name: organizationForm.name,
|
||||||
|
type: organizationForm.type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
message.success('Organization created successfully')
|
||||||
|
cancelOrganizationModal()
|
||||||
|
// 刷新组织列表
|
||||||
|
await getOrganizationList()
|
||||||
|
// 如果是在编辑/新建弹窗中,自动选择新创建的组织
|
||||||
|
if (modalVisible.value) {
|
||||||
|
const newOrgId = res?.id || res?.data?.id || res
|
||||||
|
if (newOrgId) {
|
||||||
|
formState.organizationId = String(newOrgId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || 'Failed to create organization')
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterOption = (input: string, option: any) => {
|
||||||
|
const label = option?.label ?? option?.children ?? option?.key?.label ?? ''
|
||||||
|
return String(label).toLowerCase().includes(input.toLowerCase())
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.subscription-plan {
|
||||||
|
padding: 20px 24px 32px 0;
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
.table-card__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.table-card__title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger-text {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.subscriptionPlan_modal) {
|
||||||
|
.ant-modal-body {
|
||||||
|
height: calc(65rem * 1.2);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 2.5rem 3rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscriptionPlan_modal {
|
||||||
|
> .admin_state_item {
|
||||||
|
> span {
|
||||||
|
width: 15rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal_title_text {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.subscriptionPlan_center {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2rem 0;
|
||||||
|
}
|
||||||
|
.subscriptionPlan_btn {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: auto;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 1.5rem 0 0 0;
|
||||||
|
margin-top: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
.admin_search_item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.search-form) {
|
||||||
|
column-gap: 2rem;
|
||||||
|
row-gap: 2rem;
|
||||||
|
.ant-select {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-select-dropdown) {
|
||||||
|
.add-organization-option {
|
||||||
|
color: #1890ff !important;
|
||||||
|
font-weight: 600;
|
||||||
|
background-color: #f0f7ff !important;
|
||||||
|
border-bottom: 1px solid #e6f4ff;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #e6f4ff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.organization_modal) {
|
||||||
|
.ant-modal-body {
|
||||||
|
height: auto;
|
||||||
|
min-height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 2rem 2.5rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscriptionPlan_center {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
overflow: visible;
|
||||||
|
padding: 1rem 0;
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal_title_text {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.organization_footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
column-gap: 3rem;
|
||||||
|
.footer_btn {
|
||||||
|
border-radius: 3.3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -34,8 +34,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layout_centent" :class="{active:flex_direction}" id="layoutCentent">
|
<div class="layout_centent" :class="{active:flex_direction}" id="layoutCentent">
|
||||||
<div v-for="item,index in layoutList" :key="item" :class="moodbClassName[index]" class="modal_imgItem" v-layout="item" @mousedown="setpitch(item,index)" @touchstart="setpitch(item,index)" ref="content" >
|
<div v-for="item,index in layoutList" :key="item" :class="moodbClassName[index]" class="modal_imgItem" v-layout="item" @mousedown="setpitch(item,index)" @touchstart="setpitch(item,index)" ref="content" :style="{'background-image':`url(${item.imgUrl})`,'transform':`scale(${item.zoom?item.zoom:1}) rotateZ(${item.angle?item.angle:0}deg)`}">
|
||||||
<img crossOrigin="anonymous" :src="item.imgUrl" :style="{'transform':`translate(-50%, -50%) scale(${item.zoom?item.zoom:1}) rotateZ(${item.angle?item.angle:0}deg)`}" draggable="false" :class="moodbClassName[index]" v-modelImg>
|
<!-- <img crossOrigin="anonymous" :src="item.imgUrl" :style="{'transform':`translate(-50%, -50%) scale(${item.zoom?item.zoom:1}) rotateZ(${item.angle?item.angle:0}deg)`}" draggable="false" :class="moodbClassName[index]" v-modelImg> -->
|
||||||
<ul v-show="item.setPitch" class="layout_btn" >
|
<ul v-show="item.setPitch" class="layout_btn" >
|
||||||
<li class="layout_btn_top" v-compile.stop="'top'"></li>
|
<li class="layout_btn_top" v-compile.stop="'top'"></li>
|
||||||
<li class="layout_btn_bottom" v-compile.stop="'bottom'"></li>
|
<li class="layout_btn_bottom" v-compile.stop="'bottom'"></li>
|
||||||
@@ -736,6 +736,7 @@ export default defineComponent({
|
|||||||
setmoodb(item:any){
|
setmoodb(item:any){
|
||||||
this.moodbClassName = item
|
this.moodbClassName = item
|
||||||
this.$emit('setmoodbClass',this.moodbClassName)
|
this.$emit('setmoodbClass',this.moodbClassName)
|
||||||
|
this.styleObj.class = this.moodbClassName
|
||||||
if(this.content){
|
if(this.content){
|
||||||
for (item of (this.content as any)) {
|
for (item of (this.content as any)) {
|
||||||
item.classList.remove('active')
|
item.classList.remove('active')
|
||||||
@@ -772,7 +773,7 @@ export default defineComponent({
|
|||||||
initDomStyle(){
|
initDomStyle(){
|
||||||
nextTick(()=>{
|
nextTick(()=>{
|
||||||
this.content.forEach((item:any,index:any) => {
|
this.content.forEach((item:any,index:any) => {
|
||||||
if(this.styleObj.domStyle[index]){
|
if(this.styleObj.domStyle[index]?.left){
|
||||||
item.classList.add('active')
|
item.classList.add('active')
|
||||||
this.initStyle(item,this.styleObj.domStyle[index])
|
this.initStyle(item,this.styleObj.domStyle[index])
|
||||||
}
|
}
|
||||||
@@ -794,7 +795,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
initStyle(dom:any,style:any){
|
initStyle(dom:any,style:any){
|
||||||
if(!style)return
|
if(!style || !dom)return
|
||||||
for (const [property, value] of Object.entries(style)) {
|
for (const [property, value] of Object.entries(style)) {
|
||||||
|
|
||||||
dom.style.setProperty(property, value);
|
dom.style.setProperty(property, value);
|
||||||
@@ -806,7 +807,7 @@ export default defineComponent({
|
|||||||
this.styleObj.domStyle.push(this.setStyle(item.style))
|
this.styleObj.domStyle.push(this.setStyle(item.style))
|
||||||
this.domObj.dom.forEach((domName:any,index:any) => {
|
this.domObj.dom.forEach((domName:any,index:any) => {
|
||||||
let style = this.domObj.domStyle[index]
|
let style = this.domObj.domStyle[index]
|
||||||
let dom = item.querySelector(domName)
|
let dom = item.querySelector(domName) || item
|
||||||
this.styleObj[style].push(this.setStyle(dom.style))
|
this.styleObj[style].push(this.setStyle(dom.style))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -840,7 +841,6 @@ export default defineComponent({
|
|||||||
let config:any = {headers:{'Content-Type':'multipart/form-data','Accept':'*/*' }}
|
let config:any = {headers:{'Content-Type':'multipart/form-data','Accept':'*/*' }}
|
||||||
Https.axiosPost(Https.httpUrls.elementUpload,param,config)
|
Https.axiosPost(Https.httpUrls.elementUpload,param,config)
|
||||||
.then((rv: any) => {
|
.then((rv: any) => {
|
||||||
// console.log(rv);
|
|
||||||
rv.imgUrl = rv.url
|
rv.imgUrl = rv.url
|
||||||
this.layout = false
|
this.layout = false
|
||||||
this.loadingShow = false
|
this.loadingShow = false
|
||||||
@@ -1062,27 +1062,12 @@ export default defineComponent({
|
|||||||
// height: 100%;
|
// height: 100%;
|
||||||
// }
|
// }
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
&.active{
|
&.active{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
img{
|
|
||||||
// object-fit: cover;
|
|
||||||
// width: 100%;
|
|
||||||
// height: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
float: left;
|
|
||||||
user-select:none;
|
|
||||||
-webkit-user-drag: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%,-50%) scale(1);
|
|
||||||
}
|
|
||||||
::selection {
|
|
||||||
// background: rgba(0,0,0,0);
|
|
||||||
// background: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.wh1{
|
.wh1{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
|||||||
<!-- <div class="icon" @click="toGmailLogin"> -->
|
<!-- <div class="icon" @click="toGmailLogin"> -->
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<img src="@/assets/images/loginPage/gmailIcon.svg" alt="">
|
<img src="@/assets/images/loginPage/gmailIcon.svg" alt="">
|
||||||
<span>{{ $props.text }}</span>
|
<span>{{ displayText }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
props: {
|
props: {
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'Sign in with Google'
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
@@ -108,6 +108,9 @@
|
|||||||
const toGmailLogin = ()=>{
|
const toGmailLogin = ()=>{
|
||||||
message.info(t('account.canNotUtilize'))
|
message.info(t('account.canNotUtilize'))
|
||||||
}
|
}
|
||||||
|
const displayText = computed(() => {
|
||||||
|
return props.text || t('Login.LoginWithGoogle')
|
||||||
|
})
|
||||||
onBeforeUnmount(()=>{
|
onBeforeUnmount(()=>{
|
||||||
var existingScript = document.querySelector(`script[src="${data.scriptSrc}"]`);
|
var existingScript = document.querySelector(`script[src="${data.scriptSrc}"]`);
|
||||||
if(existingScript){
|
if(existingScript){
|
||||||
@@ -120,6 +123,7 @@
|
|||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
toGmailLogin,
|
toGmailLogin,
|
||||||
|
displayText,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -553,13 +553,14 @@ export default defineComponent({
|
|||||||
loginType: "EMAIL",
|
loginType: "EMAIL",
|
||||||
userId: this.userId,
|
userId: this.userId,
|
||||||
};
|
};
|
||||||
this.$emit('update:isMask',true)
|
this.store.commit('set_loading', true)
|
||||||
Https.axiosPost(Https.httpUrls.accountLogin, data)
|
Https.axiosPost(Https.httpUrls.accountLogin, data)
|
||||||
.then((rv: any) => {
|
.then((rv: any) => {
|
||||||
this.setSuccessLogin(rv);
|
this.setSuccessLogin(rv);
|
||||||
|
this.store.commit('set_loading', false)
|
||||||
})
|
})
|
||||||
.catch((res) => {
|
.catch((res) => {
|
||||||
this.$emit('update:isMask',false)
|
this.store.commit('set_loading', false)
|
||||||
});
|
});
|
||||||
},1000)
|
},1000)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="Container">
|
<div class="Container">
|
||||||
<div class="icon" @click="openWeiXinModel">
|
<div class="icon" @click="openWeiXinModel">
|
||||||
<img src="@/assets/images/loginPage/weiXinIcon.svg" alt="" />
|
<img src="@/assets/images/loginPage/weiXinIcon.svg" alt="" />
|
||||||
<span>{{ $props.text }}</span>
|
<span>{{ displayText }}</span>
|
||||||
</div>
|
</div>
|
||||||
<weiXinModel ref="weiXinModel"></weiXinModel>
|
<weiXinModel ref="weiXinModel"></weiXinModel>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
toRefs,
|
toRefs,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import weiXinModel from "./weiXinModel.vue";
|
import weiXinModel from "./weiXinModel.vue";
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "login",
|
name: "login",
|
||||||
components: {
|
components: {
|
||||||
@@ -26,20 +27,25 @@ export default defineComponent({
|
|||||||
props: {
|
props: {
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "Sign in with Wechat",
|
default: ''
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup() {
|
setup(props) {
|
||||||
let weiXinDom = reactive({
|
let weiXinDom = reactive({
|
||||||
weiXinModel: null,
|
weiXinModel: null,
|
||||||
});
|
});
|
||||||
|
const { t } = useI18n()
|
||||||
const openWeiXinModel = () => {
|
const openWeiXinModel = () => {
|
||||||
weiXinDom.weiXinModel.init();
|
weiXinDom.weiXinModel.init();
|
||||||
};
|
};
|
||||||
|
const displayText = computed(() => {
|
||||||
|
return props.text || t('Login.LoginWithWechat')
|
||||||
|
})
|
||||||
onMounted(() => {});
|
onMounted(() => {});
|
||||||
return {
|
return {
|
||||||
...toRefs(weiXinDom),
|
...toRefs(weiXinDom),
|
||||||
openWeiXinModel,
|
openWeiXinModel,
|
||||||
|
displayText,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -458,6 +458,7 @@ export default defineComponent({
|
|||||||
message.info(t('newScaleImage.jsContent2'))
|
message.info(t('newScaleImage.jsContent2'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
store.state.Workspace.cachedRoutes = [];
|
||||||
let id = await getWorks(imgData.scaleImageData.id)
|
let id = await getWorks(imgData.scaleImageData.id)
|
||||||
await router.push(`/home/history/${id}`)
|
await router.push(`/home/history/${id}`)
|
||||||
store.commit('setChooseIsDesign',false)
|
store.commit('setChooseIsDesign',false)
|
||||||
|
|||||||
@@ -132,8 +132,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal_img_max">
|
<div class="modal_img_max">
|
||||||
<div v-if="!modalImg[0]?.id" class="modal_img" id="modal_img" :class="{active:flex_direction}">
|
<div v-if="!modalImg[0]?.id" class="modal_img" id="modal_img" :class="{active:flex_direction}">
|
||||||
<div v-for="item,index in layoutList" :class="[moodb_className[index]]" class="modal_imgItem">
|
<!-- <div class="modal_img" id="modal_img" :class="{active:flex_direction}"> -->
|
||||||
<img :src="item.imgUrl" v-modelImg>
|
<div v-for="item,index in layoutList" :class="[moodb_className[index]]" :style="{'background-image':`url(${item.imgUrl})`}" class="modal_imgItem">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="modal_img">
|
<div v-else class="modal_img">
|
||||||
@@ -244,30 +244,6 @@ export default defineComponent({
|
|||||||
this.token = getCookie("token") || "";
|
this.token = getCookie("token") || "";
|
||||||
this.uploadUrl = getUploadUrl();
|
this.uploadUrl = getUploadUrl();
|
||||||
},
|
},
|
||||||
directives:{
|
|
||||||
modelImg:{
|
|
||||||
mounted(el) {
|
|
||||||
let parentNode = el.parentNode
|
|
||||||
if(parentNode.offsetHeight >= parentNode.offsetWidth){
|
|
||||||
el.style.height = 100+'%'
|
|
||||||
el.style.width = 'auto'
|
|
||||||
}else{
|
|
||||||
el.style.width = 100+'%'
|
|
||||||
el.style.height = 'auto'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updated (el) {
|
|
||||||
let parentNode = el.parentNode
|
|
||||||
if(parentNode.offsetHeight >= parentNode.offsetWidth){
|
|
||||||
el.style.height = 100+'%'
|
|
||||||
el.style.width = 'auto'
|
|
||||||
}else{
|
|
||||||
el.style.width = 100+'%'
|
|
||||||
el.style.height = 'auto'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
open(num: Number) {
|
open(num: Number) {
|
||||||
this.openClick = num;
|
this.openClick = num;
|
||||||
@@ -445,7 +421,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.edieShow = true
|
this.edieShow = true
|
||||||
if(this.moodb_[arr.length-1].length == 2){
|
if(this.moodb_[arr.length-1].length == 1){
|
||||||
this.moodb_className = this.moodb_[arr.length-1][0]
|
this.moodb_className = this.moodb_[arr.length-1][0]
|
||||||
}else{
|
}else{
|
||||||
this.moodb_className = this.moodb_[arr.length-1][random]
|
this.moodb_className = this.moodb_[arr.length-1][random]
|
||||||
@@ -628,6 +604,7 @@ export default defineComponent({
|
|||||||
height: calc(5rem*1.2);
|
height: calc(5rem*1.2);
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
&.modal_img::-webkit-scrollbar {
|
&.modal_img::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -706,15 +683,9 @@ export default defineComponent({
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
img{
|
background-repeat: no-repeat;
|
||||||
position: absolute;
|
background-position: center;
|
||||||
top: 50%;
|
background-size: cover;
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%,-50%);
|
|
||||||
// float: left;
|
|
||||||
// user-select:none;
|
|
||||||
// -webkit-user-drag: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.wh1{
|
.wh1{
|
||||||
width: 23%;
|
width: 23%;
|
||||||
|
|||||||
@@ -242,7 +242,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="prompt-input-container" v-show="!showMotion">
|
<div class="prompt-input-container" v-show="!showMotion">
|
||||||
<div class="title">{{ $t('ProductImg.Prompt') }}</div>
|
<div class="title">
|
||||||
|
<span>{{ $t('ProductImg.Prompt') }}</span>
|
||||||
|
<SvgIcon
|
||||||
|
class="cursor-icon"
|
||||||
|
@click="handleNavigateHelp"
|
||||||
|
name="CHelpFlip"
|
||||||
|
size="18"
|
||||||
|
color="#000"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<promptInput :content="prompt" ref="promptInputRef" />
|
<promptInput :content="prompt" ref="promptInputRef" />
|
||||||
</div>
|
</div>
|
||||||
<div class="transferPose" v-show="showMotion">
|
<div class="transferPose" v-show="showMotion">
|
||||||
@@ -380,7 +389,7 @@
|
|||||||
<a-spin size="large" />
|
<a-spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
<template>
|
<template>
|
||||||
<Prompt v-if="scaleImageList[scaleImageIndex]?.resultType === 'ToProductImage'" v-model:showModal="showPromptAssist" isDesignPage />
|
<Prompt v-if="scaleImageList[scaleImageIndex]?.resultType === 'ToProductImage'" v-model:showModal="showPromptAssist" />
|
||||||
<PromptEditProduct v-if="scaleImageList[scaleImageIndex]?.resultType === 'Relight'" v-model:showModal="showPromptAssist" />
|
<PromptEditProduct v-if="scaleImageList[scaleImageIndex]?.resultType === 'Relight'" v-model:showModal="showPromptAssist" />
|
||||||
</template>
|
</template>
|
||||||
<Product
|
<Product
|
||||||
@@ -450,7 +459,7 @@ export default defineComponent({
|
|||||||
let userDetail: any = computed(() => {
|
let userDetail: any = computed(() => {
|
||||||
return store.state.UserHabit.userDetail
|
return store.state.UserHabit.userDetail
|
||||||
})
|
})
|
||||||
let { t } = useI18n()
|
let { t, locale } = useI18n()
|
||||||
const textareaRef = useTemplateRef<HTMLTextAreaElement>('textareaRef')
|
const textareaRef = useTemplateRef<HTMLTextAreaElement>('textareaRef')
|
||||||
const videoType = ref(2)
|
const videoType = ref(2)
|
||||||
const showMotion = computed(() => videoType.value === 1)
|
const showMotion = computed(() => videoType.value === 1)
|
||||||
@@ -599,13 +608,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
const showPromptAssist = ref(false)
|
const showPromptAssist = ref(false)
|
||||||
const handleClickAssistBtn = () => {
|
const handleClickAssistBtn = () => {
|
||||||
const { httpType } = store.state.Workspace.probjects
|
// const { httpType } = store.state.Workspace.probjects
|
||||||
const isSingleDesign = httpType === 'SINGLE_DESIGN'
|
// const isSingleDesign = httpType === 'SINGLE_DESIGN'
|
||||||
if (!isSingleDesign) {
|
// if (!isSingleDesign) {
|
||||||
const promptText = t('ProductImg.Series')
|
// const promptText = t('ProductImg.Series')
|
||||||
productimg.productimgSearchName = promptText
|
// productimg.productimgSearchName = promptText
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
showPromptAssist.value = true
|
showPromptAssist.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1310,7 +1319,13 @@ export default defineComponent({
|
|||||||
return videoType.value === 3 ? false : true
|
return videoType.value === 3 ? false : true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const handleNavigateHelp = () => {
|
||||||
|
const url =
|
||||||
|
locale === 'CHINESE_SIMPLIFIED'
|
||||||
|
? 'https://aida-user-manual-chinese.super.site/2b08f755cedd80a985cffdf2af80c538'
|
||||||
|
: 'https://aida-user-manual.super.site/advanced-tool/animated-product-image/to-product-video-prompt-assist '
|
||||||
|
window.open(url,'_blank')
|
||||||
|
}
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearInterval(prductimgTime)
|
clearInterval(prductimgTime)
|
||||||
clearInterval(remPrductimgTime)
|
clearInterval(remPrductimgTime)
|
||||||
@@ -1374,7 +1389,8 @@ export default defineComponent({
|
|||||||
handlePlayNewVideo,
|
handlePlayNewVideo,
|
||||||
isNewVideoPlaying,
|
isNewVideoPlaying,
|
||||||
showDropdown,
|
showDropdown,
|
||||||
inputPlaceholder
|
inputPlaceholder,
|
||||||
|
handleNavigateHelp
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -1786,12 +1802,16 @@ export default defineComponent({
|
|||||||
:deep(.promptInput) {
|
:deep(.promptInput) {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
font-weight: 500;
|
display: flex;
|
||||||
color: #000;
|
align-items: center;
|
||||||
font-size: 1.7rem;
|
column-gap: 1rem;
|
||||||
margin-bottom: 1.4rem;
|
.cursor-icon {
|
||||||
}
|
display: flex;
|
||||||
|
width: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.prompt-container {
|
.prompt-container {
|
||||||
margin-top: 4rem;
|
margin-top: 4rem;
|
||||||
|
|||||||
@@ -55,13 +55,16 @@ export default defineComponent({
|
|||||||
const data = reactive({
|
const data = reactive({
|
||||||
openType:'',
|
openType:'',
|
||||||
componentKey:null,
|
componentKey:null,
|
||||||
isShowMark:false,
|
isShowMark:true,
|
||||||
routeQuery:{} as any,
|
routeQuery:{} as any,
|
||||||
selectObject:computed(()=>store.state.Workspace.probjects) as any,//选择的项目
|
selectObject:computed(()=>store.state.Workspace.probjects) as any,//选择的项目
|
||||||
chatData:null as any,
|
chatData:null as any,
|
||||||
dataLoad:true as any,
|
dataLoad:true as any,
|
||||||
cachedRoutes:computed(()=>store.state.Workspace.cachedRoutes),//
|
cachedRoutes:computed(()=>store.state.Workspace.cachedRoutes),//
|
||||||
})
|
})
|
||||||
|
onMounted(()=>{
|
||||||
|
data.isShowMark = false
|
||||||
|
})
|
||||||
let settingGetHistory:any = inject('settingGetHistory')
|
let settingGetHistory:any = inject('settingGetHistory')
|
||||||
const setIsShowMark = (boolean:boolean)=>{
|
const setIsShowMark = (boolean:boolean)=>{
|
||||||
data.isShowMark = boolean
|
data.isShowMark = boolean
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ const promptList = computed(() => {
|
|||||||
return [t('ProductImg.UploadWithoutModel'), t('ProductImg.UploadWithModel')]
|
return [t('ProductImg.UploadWithoutModel'), t('ProductImg.UploadWithModel')]
|
||||||
} else {
|
} else {
|
||||||
// 如果是从design来的
|
// 如果是从design来的
|
||||||
if (isSingleDesign) {
|
if (props.isDesignPage) {
|
||||||
// SINGLE_DESIGN: 两个提示词
|
// SINGLE_DESIGN: 两个提示词
|
||||||
// 根据年龄和性别选择对应的提示词
|
// 根据年龄和性别选择对应的提示词
|
||||||
let firstPrompt: string // 不带模特的提示词
|
let firstPrompt: string // 不带模特的提示词
|
||||||
@@ -255,8 +255,8 @@ const exampleList = computed(() => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const { ageGroup, httpType, sex } = store.state.Workspace.probjects
|
const { ageGroup, httpType, sex } = store.state.Workspace.probjects
|
||||||
const isSingleDesign = httpType === 'SINGLE_DESIGN'
|
// const isSingleDesign = httpType === 'SINGLE_DESIGN'
|
||||||
if (!isSingleDesign) return {}
|
// if (!isSingleDesign) return {}
|
||||||
const isAdult = ageGroup === 'Adult'
|
const isAdult = ageGroup === 'Adult'
|
||||||
const isFemale = sex === 'Female'
|
const isFemale = sex === 'Female'
|
||||||
if (isAdult) {
|
if (isAdult) {
|
||||||
|
|||||||
@@ -1101,13 +1101,13 @@ export default defineComponent({
|
|||||||
|
|
||||||
const showPromptAssist = ref(false)
|
const showPromptAssist = ref(false)
|
||||||
const handleClickAssistBtn = () => {
|
const handleClickAssistBtn = () => {
|
||||||
const { httpType } = store.state.Workspace.probjects
|
// const { httpType } = store.state.Workspace.probjects
|
||||||
const isSingleDesign = httpType === 'SINGLE_DESIGN'
|
// const isSingleDesign = httpType === 'SINGLE_DESIGN'
|
||||||
if (props.isDesignPage && !isSingleDesign) {
|
// if (props.isDesignPage && !isSingleDesign) {
|
||||||
const promptText = t('ProductImg.Series')
|
// const promptText = t('ProductImg.Series')
|
||||||
productImgData.searchName[props.productimgMenu.value] = promptText
|
// productImgData.searchName[props.productimgMenu.value] = promptText
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
showPromptAssist.value = true
|
showPromptAssist.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
<ul
|
<ul
|
||||||
class="product_detail"
|
class="product_detail"
|
||||||
:class="[
|
:class="[
|
||||||
{ academic: item.type == 'academic' && !isSelectSuccessively },
|
{ academic: item.type == 'academic' },
|
||||||
{ chinese: isSelectSuccessively }
|
{ chinese: isSelectSuccessively }
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
@@ -106,12 +106,12 @@ export default defineComponent({
|
|||||||
Yearly: '年度',
|
Yearly: '年度',
|
||||||
monthly: [
|
monthly: [
|
||||||
{
|
{
|
||||||
title: '免费版',
|
title: '使用版',
|
||||||
img: CChargeIcon,
|
img: CChargeIcon,
|
||||||
type: 'personal',
|
type: 'personal',
|
||||||
info: '您的AI时尚设计助手',
|
info: '您的AI时尚设计助手',
|
||||||
price: 'HK$0',
|
price: 'HK$0',
|
||||||
detail: '5天·50积分',
|
detail: '自注册之日起 5 天内 · 50 个积分',
|
||||||
highlight: '',
|
highlight: '',
|
||||||
discounts: '9折优惠',
|
discounts: '9折优惠',
|
||||||
detailList: [
|
detailList: [
|
||||||
@@ -185,12 +185,12 @@ export default defineComponent({
|
|||||||
],
|
],
|
||||||
yearl: [
|
yearl: [
|
||||||
{
|
{
|
||||||
title: '免费版',
|
title: '试用版',
|
||||||
img: CChargeIcon,
|
img: CChargeIcon,
|
||||||
type: 'personal',
|
type: 'personal',
|
||||||
info: '您的AI时尚设计助手',
|
info: '您的AI时尚设计助手',
|
||||||
price: 'HK$0',
|
price: 'HK$0',
|
||||||
detail: '5天·50积分',
|
detail: '自注册之日起 5 天内 · 50 个积分',
|
||||||
highlight: '',
|
highlight: '',
|
||||||
discounts: '9折优惠',
|
discounts: '9折优惠',
|
||||||
detailList: [
|
detailList: [
|
||||||
@@ -250,12 +250,12 @@ export default defineComponent({
|
|||||||
Yearly: 'Yearly',
|
Yearly: 'Yearly',
|
||||||
monthly: [
|
monthly: [
|
||||||
{
|
{
|
||||||
title: 'Free',
|
title: 'Trial',
|
||||||
img: CChargeIcon,
|
img: CChargeIcon,
|
||||||
type: 'personal',
|
type: 'personal',
|
||||||
info: 'Your AI Fashion Design Assistant',
|
info: 'Your AI Fashion Design Assistant',
|
||||||
price: 'HK$0',
|
price: 'HK$0',
|
||||||
detail: '5 days · 50 credits',
|
detail: '5 days from sign-up · 50 credits',
|
||||||
highlight: '',
|
highlight: '',
|
||||||
discounts: '10% off',
|
discounts: '10% off',
|
||||||
detailList: [
|
detailList: [
|
||||||
@@ -329,12 +329,12 @@ export default defineComponent({
|
|||||||
],
|
],
|
||||||
yearl: [
|
yearl: [
|
||||||
{
|
{
|
||||||
title: 'Free',
|
title: 'Trial',
|
||||||
img: CChargeIcon,
|
img: CChargeIcon,
|
||||||
type: 'free',
|
type: 'personal',
|
||||||
info: 'Your AI Fashion Design Assistant',
|
info: 'Your AI Fashion Design Assistant',
|
||||||
price: 'HK$0',
|
price: 'HK$0',
|
||||||
detail: '5 days · 50 credits',
|
detail: '5 days from sign-up · 50 credits',
|
||||||
highlight: '',
|
highlight: '',
|
||||||
discounts: '10% off',
|
discounts: '10% off',
|
||||||
detailList: [
|
detailList: [
|
||||||
|
|||||||
@@ -1543,11 +1543,13 @@ export default {
|
|||||||
LoginMethod: '使用以下方式登录:',
|
LoginMethod: '使用以下方式登录:',
|
||||||
Individual: '个人账号',
|
Individual: '个人账号',
|
||||||
Academic: '学术账号',
|
Academic: '学术账号',
|
||||||
LogoOnToAiDA: '登录到AiDA 3.1',
|
LogonToAiDA: '登录到AiDA 3.1',
|
||||||
Infomation: '请填写以下信息',
|
Infomation: '请填写以下信息',
|
||||||
Device: '请使用iPad或电脑登录',
|
Device: '请使用iPad或电脑登录',
|
||||||
AgreePolicies: '请勾选条款、隐私政策和费用',
|
AgreePolicies: '请勾选条款、隐私政策和费用',
|
||||||
PasswordConditions: '您必须满足所有密码条件才能注册'
|
PasswordConditions: '您必须满足所有密码条件才能注册',
|
||||||
|
LoginWithGoogle: '使用谷歌账号登录',
|
||||||
|
LoginWithWechat: '使用微信登录',
|
||||||
},
|
},
|
||||||
LoginPersonal: {
|
LoginPersonal: {
|
||||||
Email: '邮箱',
|
Email: '邮箱',
|
||||||
|
|||||||
@@ -959,7 +959,7 @@ export default {
|
|||||||
MOSTPOPULAR: 'MOST POPULAR',
|
MOSTPOPULAR: 'MOST POPULAR',
|
||||||
Monthly: 'Monthly',
|
Monthly: 'Monthly',
|
||||||
Yearly: 'Yearly',
|
Yearly: 'Yearly',
|
||||||
promotionCode: 'Coupon',
|
promotionCode: 'Coupon Code',
|
||||||
use: 'Apply',
|
use: 'Apply',
|
||||||
PromoCodeError:
|
PromoCodeError:
|
||||||
'Please check if the promo code is correct or if the date has expired',
|
'Please check if the promo code is correct or if the date has expired',
|
||||||
@@ -1591,7 +1591,9 @@ export default {
|
|||||||
Infomation: 'Please fill your information below',
|
Infomation: 'Please fill your information below',
|
||||||
Device: 'If you need to design, please log in using an iPad or computer.',
|
Device: 'If you need to design, please log in using an iPad or computer.',
|
||||||
AgreePolicies: 'Please agree to all terms, privacy policy, and fees.',
|
AgreePolicies: 'Please agree to all terms, privacy policy, and fees.',
|
||||||
PasswordConditions: 'You must satisfy ALL password conditions to register.'
|
PasswordConditions: 'You must satisfy ALL password conditions to register.',
|
||||||
|
LoginWithGoogle: 'Sign in with Google',
|
||||||
|
LoginWithWechat: 'Sign in with Wechat',
|
||||||
},
|
},
|
||||||
LoginPersonal: {
|
LoginPersonal: {
|
||||||
Email: 'Email',
|
Email: 'Email',
|
||||||
|
|||||||
@@ -290,6 +290,13 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import("@/component/Administrator/organization/organization.vue"),
|
import("@/component/Administrator/organization/organization.vue"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "subscriptionPlan",
|
||||||
|
name: "subscriptionPlan",
|
||||||
|
meta: { enter: 3 },
|
||||||
|
component: () =>
|
||||||
|
import("@/component/Administrator/subscriptionPlan.vue"),
|
||||||
|
},
|
||||||
//企业版教育管理员页面
|
//企业版教育管理员页面
|
||||||
{
|
{
|
||||||
path: "allUserSE",
|
path: "allUserSE",
|
||||||
@@ -498,6 +505,7 @@ function isTimeRangePassed(timeRange) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
router.beforeEach((to: any, from, next) => {
|
router.beforeEach((to: any, from, next) => {
|
||||||
|
store.commit("set_view_loading", true);
|
||||||
//系统维护时间
|
//系统维护时间
|
||||||
const time = '2025-11-21T23:00:00 - 2025-11-22T00:00:00';
|
const time = '2025-11-21T23:00:00 - 2025-11-22T00:00:00';
|
||||||
if (isTimeRangePassed(time) == 'in_progress') {
|
if (isTimeRangePassed(time) == 'in_progress') {
|
||||||
@@ -540,5 +548,7 @@ router.beforeEach((to: any, from, next) => {
|
|||||||
|
|
||||||
// if(systemUser == 0){//游客用户只能进入这两个页面
|
// if(systemUser == 0){//游客用户只能进入这两个页面
|
||||||
});
|
});
|
||||||
|
router.afterEach((to, from) => {
|
||||||
|
store.commit("set_view_loading", false);
|
||||||
|
});
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -13,10 +13,18 @@ export interface RootState{
|
|||||||
|
|
||||||
export default createStore<RootState>({
|
export default createStore<RootState>({
|
||||||
state: {
|
state: {
|
||||||
|
loading: false,
|
||||||
|
view_loading: false,
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
set_loading(state, v){
|
||||||
|
state.loading = v;
|
||||||
|
},
|
||||||
|
set_view_loading(state, v){
|
||||||
|
state.view_loading = v;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -177,6 +177,12 @@ const all = (t)=>{
|
|||||||
route:'/administrator/organization',
|
route:'/administrator/organization',
|
||||||
key:'sub13',
|
key:'sub13',
|
||||||
isShow:true,
|
isShow:true,
|
||||||
|
},{
|
||||||
|
name:'Subscription Plan',
|
||||||
|
icon:'usetime',
|
||||||
|
route:'/administrator/subscriptionPlan',
|
||||||
|
key:'sub14',
|
||||||
|
isShow:true,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
const schoolOrEnterprise = (t) =>{
|
const schoolOrEnterprise = (t) =>{
|
||||||
|
|||||||
@@ -325,6 +325,11 @@ export const Https = {
|
|||||||
deletePromCode:`/api/stripe/deletePromCode`,//删除优惠券
|
deletePromCode:`/api/stripe/deletePromCode`,//删除优惠券
|
||||||
addOrganization:`/api/inquiry/addOrganization`,//添加企业版或者教育版
|
addOrganization:`/api/inquiry/addOrganization`,//添加企业版或者教育版
|
||||||
queryOrganization:`/api/inquiry/queryOrganization`,//查询企业版或者教育版
|
queryOrganization:`/api/inquiry/queryOrganization`,//查询企业版或者教育版
|
||||||
|
createSubscribePlan:'/api/subscription_plan/createPlan', // 创建订阅计划
|
||||||
|
deleteSubscribePlan: '/api/subscription_plan/deletePlan', // 删除订阅计划
|
||||||
|
updateSubscribePlan: '/api/subscription_plan/updatePlan', // 修改订阅计划
|
||||||
|
searchAllSubscribePlan: '/api/subscription_plan/searchByPage', // 查询所有订阅计划
|
||||||
|
switchSubscribePlan:'/api/subscription_plan/switchSubscriptionPlan', // 切换订阅计划
|
||||||
|
|
||||||
|
|
||||||
//云生成
|
//云生成
|
||||||
|
|||||||
@@ -427,11 +427,12 @@
|
|||||||
<div class="userSystem" v-show="pastDuePage">
|
<div class="userSystem" v-show="pastDuePage">
|
||||||
{{ $t('Header.pastDue') }}
|
{{ $t('Header.pastDue') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="router" v-if="!getLangIsShowMark">
|
<div class="router" v-if="!loading">
|
||||||
<home
|
<home
|
||||||
ref="home"
|
ref="home"
|
||||||
@setNewProject="() => (leftShow = true)"
|
@setNewProject="() => (leftShow = true)"
|
||||||
@setTask="setTask"
|
@setTask="setTask"
|
||||||
|
:key="userDetail.language"
|
||||||
></home>
|
></home>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -442,10 +443,10 @@
|
|||||||
<UpgradePlan ref="UpgradePlan"></UpgradePlan>
|
<UpgradePlan ref="UpgradePlan"></UpgradePlan>
|
||||||
<TaskPage ref="TaskPage"></TaskPage>
|
<TaskPage ref="TaskPage"></TaskPage>
|
||||||
|
|
||||||
<div class="mark_loading" v-show="getLangIsShowMark">
|
<!-- <div class="mark_loading" v-show="loading">
|
||||||
<a-spin size="large" />
|
<a-spin size="large" />
|
||||||
</div>
|
</div> -->
|
||||||
<!-- <RobotAssist v-if="!getLangIsShowMark"></RobotAssist> -->
|
<!-- <RobotAssist v-if="!loading"></RobotAssist> -->
|
||||||
<scaleVideo ref="scaleVideo"></scaleVideo>
|
<scaleVideo ref="scaleVideo"></scaleVideo>
|
||||||
<!-- 进行续订 -->
|
<!-- 进行续订 -->
|
||||||
<renew ref="renew"></renew>
|
<renew ref="renew"></renew>
|
||||||
@@ -656,7 +657,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
let activeCredits = ref(false)
|
let activeCredits = ref(false)
|
||||||
let getLangIsShowMark = ref(true)
|
let loading = computed(() => (store.state.loading))
|
||||||
let messageNum = computed(() => {
|
let messageNum = computed(() => {
|
||||||
return store.state.UserHabit.messageSystem.messageNum
|
return store.state.UserHabit.messageSystem.messageNum
|
||||||
})
|
})
|
||||||
@@ -1070,7 +1071,7 @@ export default defineComponent({
|
|||||||
isMurmur,
|
isMurmur,
|
||||||
credits,
|
credits,
|
||||||
activeCredits,
|
activeCredits,
|
||||||
getLangIsShowMark,
|
loading,
|
||||||
messageNum,
|
messageNum,
|
||||||
messageType,
|
messageType,
|
||||||
...toRefs(stateList),
|
...toRefs(stateList),
|
||||||
@@ -1128,11 +1129,11 @@ export default defineComponent({
|
|||||||
this.store
|
this.store
|
||||||
.dispatch('getLangType')
|
.dispatch('getLangType')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.getLangIsShowMark = false
|
this.store.commit('set_loading', false)
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.getLangIsShowMark = false
|
this.store.commit('set_loading', false)
|
||||||
reject()
|
reject()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1334,7 +1335,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
setLang(v) {
|
setLang(v) {
|
||||||
this.getLangIsShowMark = true
|
this.store.commit('set_loading', true)
|
||||||
Https.axiosGet(Https.httpUrls.changeUserLanguage, { params: { language: v } })
|
Https.axiosGet(Https.httpUrls.changeUserLanguage, { params: { language: v } })
|
||||||
.then(rv => {
|
.then(rv => {
|
||||||
if (rv) {
|
if (rv) {
|
||||||
@@ -1351,11 +1352,11 @@ export default defineComponent({
|
|||||||
window.location.reload()
|
window.location.reload()
|
||||||
// window.location.href = '/home';
|
// window.location.href = '/home';
|
||||||
}
|
}
|
||||||
this.getLangIsShowMark = false
|
this.store.commit('set_loading', false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.getLangIsShowMark = false
|
this.store.commit('set_loading', false)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -43,15 +43,12 @@
|
|||||||
<span>{{ t('Login.LogonToAiDA') }}</span>
|
<span>{{ t('Login.LogonToAiDA') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info" v-show="!loginType">{{ t('Login.Infomation') }}</div>
|
<div class="info" v-show="!loginType">{{ t('Login.Infomation') }}</div>
|
||||||
<personal ref="personal" v-if="loginType == 'personal'" v-model:isMask="isMask"></personal>
|
<personal ref="personal" v-if="loginType == 'personal'"></personal>
|
||||||
<school ref="school" v-if="loginType == 'school'"></school>
|
<school ref="school" v-if="loginType == 'school'"></school>
|
||||||
<enterprise ref="enterprise" v-if="loginType == 'enterprise'"></enterprise>
|
<enterprise ref="enterprise" v-if="loginType == 'enterprise'"></enterprise>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mark_loading" v-show="isMask">
|
|
||||||
<a-spin size="large" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -103,7 +100,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
const loginData = reactive({
|
const loginData = reactive({
|
||||||
loginType: "",
|
loginType: "",
|
||||||
isMask: false,
|
|
||||||
});
|
});
|
||||||
const dataDom = reactive({
|
const dataDom = reactive({
|
||||||
personal: null as any,
|
personal: null as any,
|
||||||
|
|||||||
Reference in New Issue
Block a user