41 Commits

Author SHA1 Message Date
wxd
967996429b 更新 .gitea/workflows/prod_build_schedule.yaml 2026-03-23 10:45:31 +08:00
wxd
f88129f8a9 更新 .gitea/workflows/prod_build_schedule.yaml 2026-03-13 14:02:26 +08:00
wxd
02ccd546bc 更新 .gitea/workflows/prod_build_schedule.yaml 2026-03-06 18:45:13 +08:00
wxd
4006e7f1c1 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m6s
2026-03-05 10:59:08 +08:00
wxd
352f2b7bae 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m22s
2026-03-02 09:52:19 +08:00
wxd
1839bae545 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m25s
2026-02-24 11:38:54 +08:00
wxd
93cb27238b 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 1m54s
2026-02-06 14:18:58 +08:00
wxd
a0de3ce96d 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 1m51s
2026-02-06 10:37:16 +08:00
wxd
989c8468f0 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 2m9s
2026-02-05 16:44:03 +08:00
wxd
f8a864d740 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 1m55s
2026-02-04 15:59:53 +08:00
wxd
afde6d2024 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 2m49s
2026-02-03 09:40:44 +08:00
fe4b39ac97 更新 .gitea/workflows/research_build_manual.yaml 2026-02-02 10:34:19 +08:00
c25e5042dd 上传文件至「.gitea/workflows」 2026-02-02 10:33:11 +08:00
wxd
27849503b3 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 1m56s
2026-01-29 16:56:04 +08:00
wxd
5fdf071510 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 1m48s
2026-01-29 16:32:36 +08:00
wxd
ff3e62506c 更新 .gitea/workflows/prod_build_schedule.yaml 2026-01-29 16:32:27 +08:00
wxd
24accf803d 更新 .gitea/workflows/prod_build_schedule.yaml 2026-01-29 09:55:57 +08:00
wxd
b5dcc80759 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 1m55s
2026-01-28 16:36:56 +08:00
wxd
a294116696 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m8s
2026-01-28 10:06:12 +08:00
wxd
d45f0b0ecd 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 2m1s
2026-01-27 14:18:56 +08:00
wxd
2a522e06a0 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m46s
2026-01-27 10:16:57 +08:00
wxd
af3bff6d80 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 5m20s
2026-01-26 16:45:17 +08:00
wxd
d9d57066fc 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m59s
2026-01-24 11:56:59 +08:00
wxd
e34986d09d 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 2m5s
2026-01-23 22:32:58 +08:00
wxd
57adf91646 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 2m5s
2026-01-23 21:44:02 +08:00
wxd
016c1de922 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m53s
2026-01-23 15:46:05 +08:00
wxd
a167d3f2ba 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m55s
2026-01-21 16:59:55 +08:00
wxd
fb7bf53680 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m47s
2026-01-20 16:49:28 +08:00
wxd
cb453297be 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m55s
2026-01-15 10:52:10 +08:00
wxd
a2f4f946ac 更新 .gitea/workflows/prod_build_schedule.yaml 2026-01-14 17:10:29 +08:00
67d5bb6874 2025.12.19 生产部署
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m35s
2025-12-19 17:47:22 +08:00
db20117500 2025.12.17 生产部署
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 3m32s
2025-12-17 14:03:27 +08:00
f3e4408dc0 更新 .gitea/workflows/prod_build_schedule.yaml
All checks were successful
AiDA WEB-Node.js 生产分支构建部署 / build (18.18.0) (push) Successful in 2m14s
2025-12-16 21:50:38 +08:00
c2d13187f0 2025.12.16 生产部署 2025-12-16 17:24:46 +08:00
7155dedc8d 更新 .gitea/workflows/prod_build_schedule.yaml 2025-12-16 17:23:32 +08:00
de8a6b9dc7 上传文件至「.gitea/workflows」 2025-12-01 17:13:08 +08:00
9c562143da 更新 .gitea/workflows/prod_build_manual.yaml 2025-11-29 00:07:07 +08:00
21fb901580 更新 .gitea/workflows/prod_build_schedule.yaml 2025-11-29 00:06:19 +08:00
29eb464772 更新 .gitea/workflows/prod_build_schedule.yaml
Some checks failed
AiDA WEB-Node.js StableVersion 分支构建部署 / build (18.18.0) (push) Failing after 2m34s
2025-11-28 17:36:57 +08:00
6f5d696c2d 更新 .gitea/workflows/prod_build_schedule.yaml 2025-11-28 17:35:50 +08:00
14a3e467f8 更新 .gitea/workflows/develop_build_commit.yaml 2025-11-28 16:07:40 +08:00
29 changed files with 1503 additions and 2022 deletions

View File

@@ -0,0 +1,90 @@
name: git commit 控制 AiDA WEB-Node.js 开发分支构建部署
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: 0.记录开始时间
id: build_start_time
run: echo "current_time=$(TZ='Asia/Hong_Kong' date '+%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_OUTPUT
- name: 1.检出代码
uses: actions/checkout@v4
with:
ref: dev_vite
- 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 重载命令已发送。"
- name: 5.发送构建结果邮件
if: always() # 无论上一步是否失败,都执行此步骤
uses: dawidd6/action-send-mail@v3
with:
from: ${{ secrets.MAIL_USERNAME }}
# --- 邮件配置 ---
server_address: smtp.gmail.com # 替换为你的SMTP服务器地址
server_port: 465 # 替换为你的SMTP端口 (通常是465或587)
username: ${{ secrets.MAIL_USERNAME }} # 存储在Secrets中的邮箱用户名
password: ${{ secrets.MAIL_PASSWORD }} # 存储在Secrets中的邮箱密码
subject: 'Gitea Actions 构建通知: ${{ job.status }} - AiDA back-java Develop'
# 收件人列表,可以根据需要更改
to: 'xupei3360@163.com,txli@aidlab.hk,cgzhou@aidlab.hk,zchengrong@yeah.net' # 替换为实际收件人邮箱
# --- 邮件正文内容 ---
body: |
项目: AiDA back-java Develop
分支: dev/3.1_release_merge
🎉 构建结果: ${{ job.status }}
📅 构建时间: ${{ steps.build_start_time.outputs.current_time }}
🔗 构建链接: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
# 确保邮件内容为纯文本,或者你可以设置为 html: true 并调整 body
content_type: text/plain

View File

@@ -0,0 +1,85 @@
name: 手动触发 AiDA WEB-Node.js 开发分支构建部署
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: 0.记录开始时间
id: build_start_time
run: echo "current_time=$(TZ='Asia/Hong_Kong' date '+%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_OUTPUT
- name: 1.检出代码
uses: actions/checkout@v4
with:
ref: dev_vite
- 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 重载命令已发送。"
- name: 5.发送构建结果邮件
if: always() # 无论上一步是否失败,都执行此步骤
uses: dawidd6/action-send-mail@v3
with:
from: ${{ secrets.MAIL_USERNAME }}
# --- 邮件配置 ---
server_address: smtp.gmail.com # 替换为你的SMTP服务器地址
server_port: 465 # 替换为你的SMTP端口 (通常是465或587)
username: ${{ secrets.MAIL_USERNAME }} # 存储在Secrets中的邮箱用户名
password: ${{ secrets.MAIL_PASSWORD }} # 存储在Secrets中的邮箱密码
subject: 'Gitea Actions 构建通知: ${{ job.status }} - AiDA back-java Develop'
# 收件人列表,可以根据需要更改
to: 'xupei3360@163.com,txli@aidlab.hk,cgzhou@aidlab.hk,zchengrong@yeah.net' # 替换为实际收件人邮箱
# --- 邮件正文内容 ---
body: |
项目: AiDA back-java Develop
分支: dev/3.1_release_merge
🎉 构建结果: ${{ job.status }}
📅 构建时间: ${{ steps.build_start_time.outputs.current_time }}
🔗 构建链接: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
# 确保邮件内容为纯文本,或者你可以设置为 html: true 并调整 body
content_type: text/plain

View File

@@ -0,0 +1,78 @@
name: AiDA WEB-Node.js 生产分支构建部署
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18.18.0 ]
steps:
- name: 0.记录开始时间
id: build_start_time
run: echo "current_time=$(TZ='Asia/Hong_Kong' date '+%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_OUTPUT
- name: 1.检出代码
uses: actions/checkout@v4
with:
ref: StableVersion
- name: 2.设置 Node.js 环境
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.发送构建结果邮件
if: always() # 无论上一步是否失败,都执行此步骤
uses: dawidd6/action-send-mail@v3
with:
from: ${{ secrets.MAIL_USERNAME }}
# --- 邮件配置 ---
server_address: smtp.gmail.com # 替换为你的SMTP服务器地址
server_port: 465 # 替换为你的SMTP端口 (通常是465或587)
username: ${{ secrets.MAIL_USERNAME }} # 存储在Secrets中的邮箱用户名
password: ${{ secrets.MAIL_PASSWORD }} # 存储在Secrets中的邮箱密码
subject: 'Gitea Actions 构建通知: ${{ job.status }} - AiDA back-java Develop'
# 收件人列表,可以根据需要更改
to: 'xupei3360@163.com,txli@aidlab.hk,cgzhou@aidlab.hk,zchengrong@yeah.net' # 替换为实际收件人邮箱
# --- 邮件正文内容 ---
body: |
项目: AiDA back-java Develop
分支: dev/3.1_release_merge
🎉 构建结果: ${{ job.status }}
📅 构建时间: ${{ steps.build_start_time.outputs.current_time }}
🔗 构建链接: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
# 确保邮件内容为纯文本,或者你可以设置为 html: true 并调整 body
content_type: text/plain

View File

@@ -0,0 +1,81 @@
name: AiDA WEB-Node.js 生产分支构建部署
on:
schedule:
# cron为UTC时区构建时间=部署时间-8小时 {*分 (-8)时 *日 *月 *周} ---
# 示例: 1月1日22点22分触发构建 cron写作 - '22 14 1 1 *'
- cron: '00 14 23 3 *'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18.18.0 ]
steps:
- name: 0.记录开始时间
id: build_start_time
run: echo "current_time=$(TZ='Asia/Hong_Kong' date '+%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_OUTPUT
- name: 1.检出代码
uses: actions/checkout@v4
with:
ref: StableVersion
- name: 2.设置 Node.js 环境
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.发送构建结果邮件
if: always() # 无论上一步是否失败,都执行此步骤
uses: dawidd6/action-send-mail@v3
with:
from: ${{ secrets.MAIL_USERNAME }}
# --- 邮件配置 ---
server_address: smtp.gmail.com # 替换为你的SMTP服务器地址
server_port: 465 # 替换为你的SMTP端口 (通常是465或587)
username: ${{ secrets.MAIL_USERNAME }} # 存储在Secrets中的邮箱用户名
password: ${{ secrets.MAIL_PASSWORD }} # 存储在Secrets中的邮箱密码
subject: 'Gitea Actions 构建通知: ${{ job.status }} - AiDA back-java Develop'
# 收件人列表,可以根据需要更改
to: 'cgzhou@aidlab.hk,zchengrong@yeah.net' # 替换为实际收件人邮箱
# --- 邮件正文内容 ---
body: |
项目: AiDA WEB-Node.js 生产分支构建部署
分支: StableVersion
🎉 构建结果: ${{ job.status }}
📅 构建时间: ${{ steps.build_start_time.outputs.current_time }}
🔗 构建链接: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
# 确保邮件内容为纯文本,或者你可以设置为 html: true 并调整 body
content_type: text/plain

View File

@@ -0,0 +1,85 @@
name: 手动触发 AiDA WEB-Node.js 开发分支构建部署
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18.18.0 ]
env:
REMOTE_DEPLOY_PATH: /workspace/workspace_aida/Research/research-aida-web-front
steps:
- name: 0.记录开始时间
id: build_start_time
run: echo "current_time=$(TZ='Asia/Hong_Kong' date '+%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_OUTPUT
- name: 1.检出代码
uses: actions/checkout@v4
with:
ref: research
- 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 重载命令已发送。"
- name: 5.发送构建结果邮件
if: always() # 无论上一步是否失败,都执行此步骤
uses: dawidd6/action-send-mail@v3
with:
from: ${{ secrets.MAIL_USERNAME }}
# --- 邮件配置 ---
server_address: smtp.gmail.com # 替换为你的SMTP服务器地址
server_port: 465 # 替换为你的SMTP端口 (通常是465或587)
username: ${{ secrets.MAIL_USERNAME }} # 存储在Secrets中的邮箱用户名
password: ${{ secrets.MAIL_PASSWORD }} # 存储在Secrets中的邮箱密码
subject: 'Gitea Actions 构建通知: ${{ job.status }} - AiDA back-java Develop'
# 收件人列表,可以根据需要更改
to: 'xupei3360@163.com,txli@aidlab.hk,cgzhou@aidlab.hk,zchengrong@yeah.net' # 替换为实际收件人邮箱
# --- 邮件正文内容 ---
body: |
项目: AiDA back-java Develop
分支: dev/3.1_release_merge
🎉 构建结果: ${{ job.status }}
📅 构建时间: ${{ steps.build_start_time.outputs.current_time }}
🔗 构建链接: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
# 确保邮件内容为纯文本,或者你可以设置为 html: true 并调整 body
content_type: text/plain

View File

@@ -1,16 +1,7 @@
<template>
<router-view/>
<div class="loading" v-show="loading"><a-spin :delay="0.5" /></div>
</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">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
@@ -18,19 +9,7 @@
-moz-osx-font-smoothing: grayscale;
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{
*{
-webkit-touch-callout:none;

View File

@@ -1250,14 +1250,10 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
background: #000 !important;
border-color: #000 !important;
}
.ant-spin .ant-spin-dot {
width: 1.5em;
height: 1.5em;
}
.ant-spin-dot-item {
background-color: #000000 !important;
width: 0.9em !important;
height: 0.9em !important;
width: 9px !important;
height: 9px !important;
}
.ant-spin {
color: #000;

View File

@@ -1378,14 +1378,10 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
}
}
//loding样式
.ant-spin .ant-spin-dot{
width: 1.5em;
height: 1.5em;
}
.ant-spin-dot-item{
background-color: #000000 !important;
width: .9em !important;
height: .9em !important;
width: 9px !important;
height: 9px !important;
}
.ant-spin{
color: #000;

View File

@@ -1,848 +0,0 @@
<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>

View File

@@ -34,8 +34,8 @@
</div>
</div>
<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" :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> -->
<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" >
<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" >
<li class="layout_btn_top" v-compile.stop="'top'"></li>
<li class="layout_btn_bottom" v-compile.stop="'bottom'"></li>
@@ -736,7 +736,6 @@ export default defineComponent({
setmoodb(item:any){
this.moodbClassName = item
this.$emit('setmoodbClass',this.moodbClassName)
this.styleObj.class = this.moodbClassName
if(this.content){
for (item of (this.content as any)) {
item.classList.remove('active')
@@ -773,7 +772,7 @@ export default defineComponent({
initDomStyle(){
nextTick(()=>{
this.content.forEach((item:any,index:any) => {
if(this.styleObj.domStyle[index]?.left){
if(this.styleObj.domStyle[index]){
item.classList.add('active')
this.initStyle(item,this.styleObj.domStyle[index])
}
@@ -795,7 +794,7 @@ export default defineComponent({
})
},
initStyle(dom:any,style:any){
if(!style || !dom)return
if(!style)return
for (const [property, value] of Object.entries(style)) {
dom.style.setProperty(property, value);
@@ -807,7 +806,7 @@ export default defineComponent({
this.styleObj.domStyle.push(this.setStyle(item.style))
this.domObj.dom.forEach((domName:any,index:any) => {
let style = this.domObj.domStyle[index]
let dom = item.querySelector(domName) || item
let dom = item.querySelector(domName)
this.styleObj[style].push(this.setStyle(dom.style))
})
});
@@ -841,6 +840,7 @@ export default defineComponent({
let config:any = {headers:{'Content-Type':'multipart/form-data','Accept':'*/*' }}
Https.axiosPost(Https.httpUrls.elementUpload,param,config)
.then((rv: any) => {
// console.log(rv);
rv.imgUrl = rv.url
this.layout = false
this.loadingShow = false
@@ -1062,12 +1062,27 @@ export default defineComponent({
// height: 100%;
// }
overflow: hidden;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
&.active{
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{

View File

@@ -19,8 +19,7 @@
<div class="generalModel_closeIcon" @click.stop="cancelDsign()">
<!-- <i class="fi fi-rr-cross-small"></i> -->
<svg
width="100%"
height="100%"
width="100%" height="100%"
viewBox="0 0 46 46"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@@ -50,32 +49,20 @@
<div class="renewContent">
<div class="left">
<div class="video">
<video
autoplay
loop
muted
playsinline
src="@/assets/images/icon/paymentVideo.mp4"
></video>
<video autoplay loop muted playsinline src="@/assets/images/icon/paymentVideo.mp4"></video>
<div class="mask"></div>
</div>
<div class="title">{{ $t('Renew.Strengths') }}</div>
<div class="title">{{ $t("Renew.Strengths") }}</div>
<div class="content">
<div class="benefitsItem">
<div class="describe">
<div class="icon">
<img src="@/assets/images/icon/paymentIntroduce1.png" alt="" />
<img src="@/assets/images/icon/paymentIntroduce1.png" alt="">
</div>
<div class="text">
<p class="title">
{{ $t('Renew.StrengthsTitle1') }}
<br />
{{ $t('Renew.StrengthsTitle1_1') }}
</p>
<p class="title">{{ $t("Renew.StrengthsTitle1") }}<br>{{ $t("Renew.StrengthsTitle1_1") }}</p>
<p class="info">
{{ $t('Renew.StrengthsInfo1') }}
<br />
{{ $t('Renew.StrengthsInfo1_1') }}
{{ $t("Renew.StrengthsInfo1") }}<br>{{ $t("Renew.StrengthsInfo1_1") }}
</p>
</div>
</div>
@@ -83,18 +70,12 @@
<div class="benefitsItem">
<div class="describe">
<div class="icon">
<img src="@/assets/images/icon/paymentIntroduce2.png" alt="" />
<img src="@/assets/images/icon/paymentIntroduce2.png" alt="">
</div>
<div class="text">
<p class="title">
{{ $t('Renew.StrengthsTitle2') }}
<br />
{{ $t('Renew.StrengthsTitle2_1') }}
</p>
<p class="title">{{ $t("Renew.StrengthsTitle2") }}<br>{{ $t("Renew.StrengthsTitle2_1") }}</p>
<p class="info">
{{ $t('Renew.StrengthsInfo2') }}
<br />
{{ $t('Renew.StrengthsInfo2_1') }}
{{ $t("Renew.StrengthsInfo2") }}<br>{{ $t("Renew.StrengthsInfo2_1") }}
</p>
</div>
</div>
@@ -102,21 +83,13 @@
<div class="benefitsItem">
<div class="describe">
<div class="icon">
<img src="@/assets/images/icon/paymentIntroduce3.png" alt="" />
<img src="@/assets/images/icon/paymentIntroduce3.png" alt="">
</div>
<div class="text">
<p class="title"></p>
<p class="title">
{{ $t('Renew.StrengthsTitle3') }}
<br />
{{ $t('Renew.StrengthsTitle3_1') }}
</p>
<p class="title">{{ $t("Renew.StrengthsTitle3") }}<br>{{ $t("Renew.StrengthsTitle3_1") }}</p>
<p class="info">
{{ $t('Renew.StrengthsInfo3') }}
<br />
{{ $t('Renew.StrengthsInfo3_1') }}
<br />
{{ $t('Renew.StrengthsInfo3_2') }}
{{ $t("Renew.StrengthsInfo3") }}<br>{{ $t("Renew.StrengthsInfo3_1") }}<br>{{ $t("Renew.StrengthsInfo3_2") }}
</p>
</div>
</div>
@@ -124,18 +97,12 @@
<div class="benefitsItem">
<div class="describe">
<div class="icon">
<img src="@/assets/images/icon/paymentIntroduce4.png" alt="" />
<img src="@/assets/images/icon/paymentIntroduce4.png" alt="">
</div>
<div class="text">
<p class="title">
{{ $t('Renew.StrengthsTitle4') }}
<br />
{{ $t('Renew.StrengthsTitle4_1') }}
</p>
<p class="title">{{ $t("Renew.StrengthsTitle4") }}<br>{{ $t("Renew.StrengthsTitle4_1") }}</p>
<p class="info">
{{ $t('Renew.StrengthsInfo4') }}
<br />
{{ $t('Renew.StrengthsInfo4_1') }}
{{ $t("Renew.StrengthsInfo4") }}<br>{{ $t("Renew.StrengthsInfo4_1") }}
</p>
</div>
</div>
@@ -144,7 +111,7 @@
</div>
<div class="right">
<div class="title">
{{ $t('Renew.title') }}
{{ $t("Renew.title") }}
</div>
<!-- <div class="info">{{ $t("Renew.unlimited") }}</div> -->
<div class="content">
@@ -152,31 +119,36 @@
class="productItem"
:class="{
active: item.price == current.price,
sellWell: item.sellWell
sellWell: item.sellWell,
}"
v-for="item in personage"
:key="item.price"
@click="setPromotionData(item)"
>
<div class="popular" v-show="item.sellWell">
{{ $t('Renew.MOSTPOPULAR') }}
</div>
<div class="popular" v-show="item.sellWell">{{ $t("Renew.MOSTPOPULAR") }}</div>
<div class="priceBox">
<div class="left">
<p class="productType">1 {{ item.type.label }}</p>
<p class="price">HK $ {{ item.price }}</p>
<p class="price">${{ item.price }}</p>
</div>
<div class="right">
<div class="promotion">
<div class="succeed" v-show="item.promotionData.error == 'true'">
<div
class="succeed"
v-show="item.promotionData.error == 'true'"
>
{{ item.promotionData.code }}
<i class="fi fi-sr-times-hexagon" @click="clearPromotionCode"></i>
<i
class="fi fi-sr-times-hexagon"
@click="clearPromotionCode"
></i>
</div>
<div
class="input"
@click.stop
v-show="
!item.promotionData.error || item.promotionData.error == 'false'
!item.promotionData.error ||
item.promotionData.error == 'false'
"
>
<input
@@ -184,11 +156,14 @@
:placeholder="$t('Renew.promotionCode')"
v-model="item.promotionData.code"
/>
<div class="apply-btn" @click="examine(item)">
{{ $t('Renew.use') }}
<div style="cursor: pointer; background-color: #000; font-size: 1.2rem; color: #fff; padding: 0 .5rem;" @click="examine(item)">
{{ $t("Renew.use") }}
</div>
</div>
<div class="error" v-show="item.promotionData.error == 'false'">
<div
class="error"
v-show="item.promotionData.error == 'false'"
>
{{ item.promotionData.str }}
</div>
</div>
@@ -206,7 +181,7 @@
<div class="payment">
<div class="allocation">
<div class="selectType">
<div class="text">{{ $t('Renew.Payment') }}:</div>
<div class="text">{{ $t("Renew.Payment") }}:</div>
<label>
<input
name="payment"
@@ -215,7 +190,7 @@
v-model="PaymentType"
@change="setPaymentType('CreditCard')"
/>
{{ $t('Renew.CreditCard') }}
{{ $t("Renew.CreditCard") }}
</label>
<label>
<input
@@ -225,47 +200,43 @@
v-model="PaymentType"
@change="setPaymentType('Alipay')"
/>
{{ $t('Renew.Alipay') }}
{{ $t("Renew.Alipay") }}
</label>
</div>
</div>
<div class="gallery_btn gallery_btn_radius" @click="payment">
{{ $t('upgradePlan.Continue') }}
{{ $t("upgradePlan.Continue") }}
</div>
<div class="payMethod_payAffirm_clause" ref="labelDisclaimer">
<label>
<!-- <input type="checkbox" v-model="clause" /> -->
<span class="generalModelDescription">
{{ $t('upgradePlan.policy') }}
<span class="generalModelDescription"
>{{ $t("upgradePlan.policy")}}
<a
href="https://code-create.com.hk/aida-terms-and-conditions/"
target="_blank"
>
{{ $t('upgradePlan.policy1') }}
target="_blank">
{{ $t("upgradePlan.policy1") }}
</a>
&
<a
href="https://code-create.com.hk/aida-subscription-agreement/"
target="_blank"
>
{{ $t('upgradePlan.policy2') }}
target="_blank">
{{ $t("upgradePlan.policy2") }}
</a>
<!-- . * -->
</span>
</span
>
</label>
<label class="secure">
<span>
<i class="fi fi-rr-shield-check"></i>
Pay safe & secure
</span>
<span><i class="fi fi-rr-shield-check"></i>Pay safe & secure</span>
</label>
<label class="payIcon">
<img class="stripe" src="@/assets/images/icon/stripe.svg" alt="" />
<img class="alipay" src="@/assets/images/icon/alipay.svg" alt="" />
<img class="alipay" src="@/assets/images/icon/jcbPay.svg" alt="" />
<img class="alipay" src="@/assets/images/icon/mastercardPay.svg" alt="" />
<img class="alipay" src="@/assets/images/icon/yinlianPay.svg" alt="" />
<img class="alipay" src="@/assets/images/icon/visaPay.svg" alt="" />
<img class="stripe" src="@/assets/images/icon/stripe.svg" alt="">
<img class="alipay" src="@/assets/images/icon/alipay.svg" alt="">
<img class="alipay" src="@/assets/images/icon/jcbPay.svg" alt="">
<img class="alipay" src="@/assets/images/icon/mastercardPay.svg" alt="">
<img class="alipay" src="@/assets/images/icon/yinlianPay.svg" alt="">
<img class="alipay" src="@/assets/images/icon/visaPay.svg" alt="">
</label>
</div>
<!-- <div class="paySecure">
@@ -279,104 +250,101 @@
<a-spin size="large" />
</div>
<div class="mark_loading" v-show="isShowMark" state="true">
<div class="mark_loading_title">{{ $t('upgradePlan.completed') }}</div>
<div class="mark_loading_intro">{{ $t('upgradePlan.hint') }}</div>
<div class="mark_loading_title">{{ $t("upgradePlan.completed") }}</div>
<div class="mark_loading_intro">{{ $t("upgradePlan.hint") }}</div>
<div class="mark_loading_title_box">
<div class="mark_loading_btn mark_loading_btn2" @click="setPaidBack">
{{ $t('upgradePlan.Back') }}
{{ $t("upgradePlan.Back") }}
</div>
<div class="mark_loading_btn" @click="completePayment">OK</div>
</div>
</div>
</a-modal>
<payMethod ref="payMethod" @completePayment="cancelDsign" type="renew"></payMethod>
<payMethod
ref="payMethod"
@completePayment="cancelDsign"
type="renew"
></payMethod>
</template>
<script lang="ts">
import {
defineComponent,
computed,
reactive,
toRefs,
onMounted,
onBeforeUnmount
} from 'vue'
import { message } from 'ant-design-vue'
import payMethod from '@/component/Pay/payMethod.vue'
import { useStore } from 'vuex'
import { useI18n } from 'vue-i18n'
import { Https } from '@/tool/https'
import md5 from 'md5'
import { defineComponent, computed, reactive, toRefs, onMounted, onBeforeUnmount } from "vue";
import { message } from "ant-design-vue";
import payMethod from "@/component/Pay/payMethod.vue";
import { useStore } from "vuex";
import { useI18n } from "vue-i18n";
import { Https } from "@/tool/https";
import md5 from "md5";
export default defineComponent({
components: {
payMethod
payMethod,
},
setup() {
const store = useStore()
const { t } = useI18n()
const store = useStore();
const { t } = useI18n();
let renew = reactive({
renewModel: false,
renewModelMask: true,
pageWidth: '50%'
})
pageWidth: "50%",
});
let payMethodData = reactive({
clause: false,
labelDisclaimer: null as any,
newWindow: null as any,
isShowMark: false,
isShowMark_: false
})
isShowMark_: false,
});
let renewData = reactive({
personage: [
{
price: '100',
price: "100",
sellWell: true,
activity: false, //活动打折
type: {
value: 'EcoMonth',
label: computed(() => t('Renew.Monthly'))
value: "EcoMonth",
label: computed(()=>t("Renew.Monthly")),
},
// PaymentType: "CreditCard",
promotionData: {
code: '',
error: '',
str: '',
price: ''
}
code: "",
error: "",
str: "",
price: "",
},
},
{
price: '500',
price: "500",
sellWell: false,
activity: false, //活动打折
type: {
value: 'Month',
label: computed(() => t('Renew.Monthly'))
value: "Month",
label: computed(()=>t("Renew.Monthly")),
},
// PaymentType: "CreditCard",
promotionData: {
code: '',
error: '',
str: '',
price: ''
}
code: "",
error: "",
str: "",
price: "",
},
},
{
price: '5,000',
price: "5,000",
sellWell: false,
activity: false, //活动打折
typeValue: 'year',
typeValue: "year",
type: {
value: 'Year',
label: computed(() => t('Renew.Yearly'))
value: "Year",
label: computed(()=>t("Renew.Yearly")),
},
// PaymentType: "CreditCard",
promotionData: {
code: '',
error: '',
str: '',
price: ''
}
}
code: "",
error: "",
str: "",
price: "",
},
},
],
// personage:computed(()=>{
// return {
@@ -397,55 +365,55 @@ export default defineComponent({
// }),
firm: computed(() => {
return {
title: 'Education Edition',
title: "Education Edition",
price: {
year: '500'
year: "500",
},
unit: {
year: 'HKD / Year'
year: "HKD / Year",
},
type: 'year',
type: "year",
autoRenewal: {
text: t('Renew.automatically'),
value: true
text: t("Renew.automatically"),
value: true,
},
typeList: ['year'],
info: 'Customised plan'
}
typeList: ["year"],
info: "Customised plan",
};
}),
education: computed(() => {
return {
title: 'Enterprise Edition',
title: "Enterprise Edition",
price: {
year: '500'
year: "500",
},
unit: {
year: 'HKD / Year'
year: "HKD / Year",
},
type: 'year',
type: "year",
autoRenewal: {
text: t('Renew.automatically'),
value: true
text: t("Renew.automatically"),
value: true,
},
typeList: ['year'],
info: 'Customised plan'
}
typeList: ["year"],
info: "Customised plan",
};
}),
current: {} as any,
payMethod: null as any,
PaymentType: 'CreditCard'
})
PaymentType: "CreditCard",
});
const init = () => {
renew.renewModel = true
renewData.current = renewData.personage[1]
}
renew.renewModel = true;
renewData.current = renewData.personage[1];
};
const cancelDsign = () => {
renew.renewModel = false
renew.renewModel = false;
store.dispatch('getUserDetail')
}
};
const setPaymentType = (str: any) => {
renewData.PaymentType = str
}
renewData.PaymentType = str;
};
const payment = () => {
// if (!payMethodData.clause) {
// let labelDisclaimer: any = payMethodData.labelDisclaimer;
@@ -457,92 +425,104 @@ export default defineComponent({
// }
// return;
// }
if (!renewData.PaymentType) return message.info(t('Renew.PleaseSelectPayment'))
let url = window.location.origin + '/paySucceed'
if (!renewData.PaymentType)
return message.info(t("Renew.PleaseSelectPayment"));
let url = window.location.origin + "/paySucceed";
let data = {
autoRenewal: renewData.PaymentType != 'Alipay', //false为不自动续费
productName: 'Subscription',
autoRenewal: renewData.PaymentType != "Alipay", //false为不自动续费
productName: "Subscription",
quantity: 1,
returnUrl: url,
subscribeType: renewData.current.type.value, //yearly为年费monthly为月费
wallet: 'ALIPAYHK',
promotionCode: renewData.current.promotionData.code
}
let httpsUrl = Https.httpUrls.payStripe
payMethodData.isShowMark_ = true
wallet: "ALIPAYHK",
promotionCode: renewData.current.promotionData.code,
};
let httpsUrl = Https.httpUrls.payStripe;
payMethodData.isShowMark_ = true;
Https.axiosPost(httpsUrl, data)
.then((rv: any) => {
var width = 800
var height = 600
var left = (screen.width - width) / 2
var top = (screen.height - height) / 2
var width = 800;
var height = 600;
var left = (screen.width - width) / 2;
var top = (screen.height - height) / 2;
payMethodData.newWindow = window.open(
'',
'_blank',
'width=' + width + ', height=' + height + ', left=' + left + ', top=' + top
)
let herf = rv
"",
"_blank",
"width=" +
width +
", height=" +
height +
", left=" +
left +
", top=" +
top
);
let herf = rv;
if (payMethodData.newWindow) {
payMethodData.newWindow.location.href = herf
payMethodData.newWindow.location.href = herf;
} else {
// window.open(herf, '_blank');
window.location.href = herf
window.location.href = herf;
}
payMethodData.newWindow = null
payMethodData.isShowMark = true
payMethodData.isShowMark_ = false
})
.catch(res => {
payMethodData.isShowMark_ = false
payMethodData.newWindow = null;
payMethodData.isShowMark = true;
payMethodData.isShowMark_ = false;
})
.catch((res) => {
payMethodData.isShowMark_ = false;
});
// renewData.payMethod.init(data)
}
};
const setPaidBack = () => {
payMethod.payMethodModel = false
payMethodData.isShowMark = false
payMethodData.clause = false
}
payMethod.payMethodModel = false;
payMethodData.isShowMark = false;
payMethodData.clause = false;
};
const completePayment = () => {
renew.renewModel = false
setPaidBack()
}
renew.renewModel = false;
setPaidBack();
};
const examine = (item: any) => {
// renewData.promotionData.error
let price = item?.price
const normalNumber = Number(price.replace(/,/g, ''))
let price = item?.price;
const normalNumber = Number(price.replace(/,/g, ""));
if (!item.promotionData.code) {
return
return;
}
let data = {
promotionCode: item.promotionData.code,
price: normalNumber
}
Https.axiosGet(Https.httpUrls.checkCoupon, { params: data }).then((rv: any) => {
price: normalNumber,
};
Https.axiosGet(Https.httpUrls.checkCoupon, { params: data }).then(
(rv: any) => {
if (rv) {
renewData.personage.forEach((personageItem: any) => {
if (personageItem.price == item.price) {
if (rv.status == 'valid') {
item.promotionData.error = 'true'
item.promotionData.price = Number(rv.discountedPrice).toLocaleString()
if (rv.status == "valid") {
item.promotionData.error = "true";
item.promotionData.price = Number(
rv.discountedPrice
).toLocaleString();
} else {
item.promotionData.error = 'false'
item.promotionData.str = rv.message
item.promotionData.error = "false";
item.promotionData.str = rv.message;
}
}
})
});
} else {
}
})
}
);
};
const clearPromotionCode = () => {
renewData.current.promotionData.error = ''
renewData.current.promotionData.code = ''
renewData.current.promotionData.price = ''
}
renewData.current.promotionData.error = "";
renewData.current.promotionData.code = "";
renewData.current.promotionData.price = "";
};
const setPromotionData = (item: any) => {
renewData.current = item
}
renewData.current = item;
};
return {
store,
...toRefs(renew),
@@ -556,18 +536,18 @@ export default defineComponent({
completePayment,
examine,
clearPromotionCode,
setPromotionData
}
setPromotionData,
};
},
data() {
return {}
return {};
},
methods: {
turnToWindow(url: any) {
window.open(url)
}
}
})
window.open(url);
},
},
});
</script>
<style lang="less" scoped>
.renew {
@@ -627,7 +607,7 @@ export default defineComponent({
height: 100%;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.5);
background: rgba(0,0,0,.5);
}
}
@@ -699,11 +679,11 @@ export default defineComponent({
margin-bottom: 1rem;
border-radius: 1rem;
overflow: hidden;
border: 1.5px solid #e7ebff;
border: 1.5px solid #E7EBFF;
cursor: pointer;
> .popular {
background: #1b223c;
background: #1B223C;
font-weight: 600;
display: flex;
justify-content: center;
@@ -765,31 +745,15 @@ export default defineComponent({
display: flex;
align-items: center;
border-radius: 2rem;
border: 1.5px solid rgb(216, 218, 220);
border: 2px solid #000;
overflow: hidden;
padding: 0 1rem;
background-color: #fff;
input {
border: none;
height: 3rem;
width: 10rem;
font-size: 1.2rem;
background: rgba(0, 0, 0, 0);
&::placeholder {
color: #c4c4c4;
}
}
.apply-btn {
cursor: pointer;
background-color: rgb(27, 34, 60);
font-size: 0.8rem;
color: #fff;
width: 3.3rem;
height: 1.4rem;
line-height: 1.4rem;
display: flex;
align-items: center;
justify-content: center;
}
}
> .error {
@@ -804,11 +768,11 @@ export default defineComponent({
}
}
}
&.active,
&:hover {
&.active,&:hover {
// 40% opacity
background: rgba(231, 235, 255, 0.4);
// border: 2px solid #0ea982;
}
&.sellWell {
// border: 2px solid #0ea982;
@@ -846,15 +810,10 @@ export default defineComponent({
> .gallery_btn {
width: 100%;
margin: 2rem 0;
margin-bottom: 0.5rem;
margin-bottom: .5rem;
margin-top: 1rem;
background: #1b223c;
background: #1B223C;
color: #fff;
// width: 41.8rem;
height: 4rem;
box-sizing: border-box;
line-height: 4rem;
border-radius: 0.6rem;
}
> .payMethod_payAffirm_clause {
text-align: center;
@@ -880,8 +839,8 @@ export default defineComponent({
border: 1px solid #ededed;
background: #f1fbf9;
color: #1cb36c;
padding: 0.4rem 0.8rem;
border-radius: 0.5rem;
padding: .4rem .8rem;
border-radius: .5rem;
margin-top: 4rem;
> span{
display: flex;
@@ -889,14 +848,14 @@ export default defineComponent({
}
i{
display: flex;
margin-right: 0.5rem;
margin-right: .5rem;
}
}
> .payIcon{
height: 2rem;
margin-top: 1rem;
img{
margin: 0 0.3rem;
margin: 0 .3rem;
height: 100%;
}
}
@@ -994,3 +953,4 @@ export default defineComponent({
}
}
</style>

View File

@@ -6,7 +6,7 @@
<!-- <div class="icon" @click="toGmailLogin"> -->
<div class="icon">
<img src="@/assets/images/loginPage/gmailIcon.svg" alt="">
<span>{{ displayText }}</span>
<span>{{ $props.text }}</span>
</div>
</div>
</template>
@@ -21,7 +21,7 @@
props: {
text: {
type: String,
default: ''
default: 'Sign in with Google'
}
},
setup(props, { emit }) {
@@ -108,9 +108,6 @@
const toGmailLogin = ()=>{
message.info(t('account.canNotUtilize'))
}
const displayText = computed(() => {
return props.text || t('Login.LoginWithGoogle')
})
onBeforeUnmount(()=>{
var existingScript = document.querySelector(`script[src="${data.scriptSrc}"]`);
if(existingScript){
@@ -123,7 +120,6 @@
})
return {
toGmailLogin,
displayText,
}
},
})

View File

@@ -553,14 +553,13 @@ export default defineComponent({
loginType: "EMAIL",
userId: this.userId,
};
this.store.commit('set_loading', true)
this.$emit('update:isMask',true)
Https.axiosPost(Https.httpUrls.accountLogin, data)
.then((rv: any) => {
this.setSuccessLogin(rv);
this.store.commit('set_loading', false)
})
.catch((res) => {
this.store.commit('set_loading', false)
this.$emit('update:isMask',false)
});
},1000)
},

View File

@@ -2,7 +2,7 @@
<div class="Container">
<div class="icon" @click="openWeiXinModel">
<img src="@/assets/images/loginPage/weiXinIcon.svg" alt="" />
<span>{{ displayText }}</span>
<span>{{ $props.text }}</span>
</div>
<weiXinModel ref="weiXinModel"></weiXinModel>
</div>
@@ -18,7 +18,6 @@ import {
toRefs,
} from "vue";
import weiXinModel from "./weiXinModel.vue";
import { useI18n } from 'vue-i18n'
export default defineComponent({
name: "login",
components: {
@@ -27,25 +26,20 @@ export default defineComponent({
props: {
text: {
type: String,
default: ''
default: "Sign in with Wechat",
},
},
setup(props) {
setup() {
let weiXinDom = reactive({
weiXinModel: null,
});
const { t } = useI18n()
const openWeiXinModel = () => {
weiXinDom.weiXinModel.init();
};
const displayText = computed(() => {
return props.text || t('Login.LoginWithWechat')
})
onMounted(() => {});
return {
...toRefs(weiXinDom),
openWeiXinModel,
displayText,
};
},
});

View File

@@ -458,7 +458,6 @@ export default defineComponent({
message.info(t('newScaleImage.jsContent2'))
return
}
store.state.Workspace.cachedRoutes = [];
let id = await getWorks(imgData.scaleImageData.id)
await router.push(`/home/history/${id}`)
store.commit('setChooseIsDesign',false)

View File

@@ -132,8 +132,8 @@
</div>
<div class="modal_img_max">
<div v-if="!modalImg[0]?.id" class="modal_img" id="modal_img" :class="{active:flex_direction}">
<!-- <div class="modal_img" id="modal_img" :class="{active:flex_direction}"> -->
<div v-for="item,index in layoutList" :class="[moodb_className[index]]" :style="{'background-image':`url(${item.imgUrl})`}" class="modal_imgItem">
<div v-for="item,index in layoutList" :class="[moodb_className[index]]" class="modal_imgItem">
<img :src="item.imgUrl" v-modelImg>
</div>
</div>
<div v-else class="modal_img">
@@ -244,6 +244,30 @@ export default defineComponent({
this.token = getCookie("token") || "";
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: {
open(num: Number) {
this.openClick = num;
@@ -421,7 +445,7 @@ export default defineComponent({
}
}
this.edieShow = true
if(this.moodb_[arr.length-1].length == 1){
if(this.moodb_[arr.length-1].length == 2){
this.moodb_className = this.moodb_[arr.length-1][0]
}else{
this.moodb_className = this.moodb_[arr.length-1][random]
@@ -604,7 +628,6 @@ export default defineComponent({
height: calc(5rem*1.2);
overflow-x: hidden;
display: flex;
&.modal_img::-webkit-scrollbar {
display: none;
}
@@ -683,9 +706,15 @@ export default defineComponent({
position: relative;
overflow: hidden;
text-align: center;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
img{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
// float: left;
// user-select:none;
// -webkit-user-drag: none;
}
}
.wh1{
width: 23%;

View File

@@ -242,16 +242,7 @@
</div>
</div>
<div class="prompt-input-container" v-show="!showMotion">
<div class="title">
<span>{{ $t('ProductImg.Prompt') }}</span>
<SvgIcon
class="cursor-icon"
@click="handleNavigateHelp"
name="CHelpFlip"
size="18"
color="#000"
/>
</div>
<div class="title">{{ $t('ProductImg.Prompt') }}</div>
<promptInput :content="prompt" ref="promptInputRef" />
</div>
<div class="transferPose" v-show="showMotion">
@@ -389,7 +380,7 @@
<a-spin size="large" />
</div>
<template>
<Prompt v-if="scaleImageList[scaleImageIndex]?.resultType === 'ToProductImage'" v-model:showModal="showPromptAssist" />
<Prompt v-if="scaleImageList[scaleImageIndex]?.resultType === 'ToProductImage'" v-model:showModal="showPromptAssist" isDesignPage />
<PromptEditProduct v-if="scaleImageList[scaleImageIndex]?.resultType === 'Relight'" v-model:showModal="showPromptAssist" />
</template>
<Product
@@ -459,7 +450,7 @@ export default defineComponent({
let userDetail: any = computed(() => {
return store.state.UserHabit.userDetail
})
let { t, locale } = useI18n()
let { t } = useI18n()
const textareaRef = useTemplateRef<HTMLTextAreaElement>('textareaRef')
const videoType = ref(2)
const showMotion = computed(() => videoType.value === 1)
@@ -608,13 +599,13 @@ export default defineComponent({
})
const showPromptAssist = ref(false)
const handleClickAssistBtn = () => {
// const { httpType } = store.state.Workspace.probjects
// const isSingleDesign = httpType === 'SINGLE_DESIGN'
// if (!isSingleDesign) {
// const promptText = t('ProductImg.Series')
// productimg.productimgSearchName = promptText
// return
// }
const { httpType } = store.state.Workspace.probjects
const isSingleDesign = httpType === 'SINGLE_DESIGN'
if (!isSingleDesign) {
const promptText = t('ProductImg.Series')
productimg.productimgSearchName = promptText
return
}
showPromptAssist.value = true
}
@@ -1319,13 +1310,7 @@ export default defineComponent({
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(() => {
clearInterval(prductimgTime)
clearInterval(remPrductimgTime)
@@ -1389,8 +1374,7 @@ export default defineComponent({
handlePlayNewVideo,
isNewVideoPlaying,
showDropdown,
inputPlaceholder,
handleNavigateHelp
inputPlaceholder
}
},
data() {
@@ -1803,14 +1787,10 @@ export default defineComponent({
box-sizing: border-box;
}
.title {
display: flex;
align-items: center;
column-gap: 1rem;
.cursor-icon {
display: flex;
width: auto;
cursor: pointer;
}
font-weight: 500;
color: #000;
font-size: 1.7rem;
margin-bottom: 1.4rem;
}
}
.prompt-container {

View File

@@ -55,16 +55,13 @@ export default defineComponent({
const data = reactive({
openType:'',
componentKey:null,
isShowMark:true,
isShowMark:false,
routeQuery:{} as any,
selectObject:computed(()=>store.state.Workspace.probjects) as any,//选择的项目
chatData:null as any,
dataLoad:true as any,
cachedRoutes:computed(()=>store.state.Workspace.cachedRoutes),//
})
onMounted(()=>{
data.isShowMark = false
})
let settingGetHistory:any = inject('settingGetHistory')
const setIsShowMark = (boolean:boolean)=>{
data.isShowMark = boolean

View File

@@ -121,7 +121,7 @@ const promptList = computed(() => {
return [t('ProductImg.UploadWithoutModel'), t('ProductImg.UploadWithModel')]
} else {
// 如果是从design来的
if (props.isDesignPage) {
if (isSingleDesign) {
// SINGLE_DESIGN: 两个提示词
// 根据年龄和性别选择对应的提示词
let firstPrompt: string // 不带模特的提示词
@@ -255,8 +255,8 @@ const exampleList = computed(() => {
}
} else {
const { ageGroup, httpType, sex } = store.state.Workspace.probjects
// const isSingleDesign = httpType === 'SINGLE_DESIGN'
// if (!isSingleDesign) return {}
const isSingleDesign = httpType === 'SINGLE_DESIGN'
if (!isSingleDesign) return {}
const isAdult = ageGroup === 'Adult'
const isFemale = sex === 'Female'
if (isAdult) {

View File

@@ -1101,13 +1101,13 @@ export default defineComponent({
const showPromptAssist = ref(false)
const handleClickAssistBtn = () => {
// const { httpType } = store.state.Workspace.probjects
// const isSingleDesign = httpType === 'SINGLE_DESIGN'
// if (props.isDesignPage && !isSingleDesign) {
// const promptText = t('ProductImg.Series')
// productImgData.searchName[props.productimgMenu.value] = promptText
// return
// }
const { httpType } = store.state.Workspace.probjects
const isSingleDesign = httpType === 'SINGLE_DESIGN'
if (props.isDesignPage && !isSingleDesign) {
const promptText = t('ProductImg.Series')
productImgData.searchName[props.productimgMenu.value] = promptText
return
}
showPromptAssist.value = true
}

View File

@@ -51,7 +51,7 @@
<ul
class="product_detail"
:class="[
{ academic: item.type == 'academic' },
{ academic: item.type == 'academic' && !isSelectSuccessively },
{ chinese: isSelectSuccessively }
]"
>
@@ -106,12 +106,12 @@ export default defineComponent({
Yearly: '年度',
monthly: [
{
title: '使用版',
title: '免费版',
img: CChargeIcon,
type: 'personal',
info: '您的AI时尚设计助手',
price: 'HK$0',
detail: '自注册之日起 5 天内 · 50 个积分',
detail: '5天·50积分',
highlight: '',
discounts: '9折优惠',
detailList: [
@@ -185,12 +185,12 @@ export default defineComponent({
],
yearl: [
{
title: '试用版',
title: '免费版',
img: CChargeIcon,
type: 'personal',
info: '您的AI时尚设计助手',
price: 'HK$0',
detail: '自注册之日起 5 天内 · 50 个积分',
detail: '5天·50积分',
highlight: '',
discounts: '9折优惠',
detailList: [
@@ -250,12 +250,12 @@ export default defineComponent({
Yearly: 'Yearly',
monthly: [
{
title: 'Trial',
title: 'Free',
img: CChargeIcon,
type: 'personal',
info: 'Your AI Fashion Design Assistant',
price: 'HK$0',
detail: '5 days from sign-up · 50 credits',
detail: '5 days · 50 credits',
highlight: '',
discounts: '10% off',
detailList: [
@@ -329,12 +329,12 @@ export default defineComponent({
],
yearl: [
{
title: 'Trial',
title: 'Free',
img: CChargeIcon,
type: 'personal',
type: 'free',
info: 'Your AI Fashion Design Assistant',
price: 'HK$0',
detail: '5 days from sign-up · 50 credits',
detail: '5 days · 50 credits',
highlight: '',
discounts: '10% off',
detailList: [

View File

@@ -1543,13 +1543,11 @@ export default {
LoginMethod: '使用以下方式登录:',
Individual: '个人账号',
Academic: '学术账号',
LogonToAiDA: '登录到AiDA 3.1',
LogoOnToAiDA: '登录到AiDA 3.1',
Infomation: '请填写以下信息',
Device: '请使用iPad或电脑登录',
AgreePolicies: '请勾选条款、隐私政策和费用',
PasswordConditions: '您必须满足所有密码条件才能注册',
LoginWithGoogle: '使用谷歌账号登录',
LoginWithWechat: '使用微信登录',
PasswordConditions: '您必须满足所有密码条件才能注册'
},
LoginPersonal: {
Email: '邮箱',

View File

@@ -959,7 +959,7 @@ export default {
MOSTPOPULAR: 'MOST POPULAR',
Monthly: 'Monthly',
Yearly: 'Yearly',
promotionCode: 'Coupon Code',
promotionCode: 'Coupon',
use: 'Apply',
PromoCodeError:
'Please check if the promo code is correct or if the date has expired',
@@ -1591,9 +1591,7 @@ export default {
Infomation: 'Please fill your information below',
Device: 'If you need to design, please log in using an iPad or computer.',
AgreePolicies: 'Please agree to all terms, privacy policy, and fees.',
PasswordConditions: 'You must satisfy ALL password conditions to register.',
LoginWithGoogle: 'Sign in with Google',
LoginWithWechat: 'Sign in with Wechat',
PasswordConditions: 'You must satisfy ALL password conditions to register.'
},
LoginPersonal: {
Email: 'Email',

View File

@@ -290,13 +290,6 @@ const routes: Array<RouteRecordRaw> = [
component: () =>
import("@/component/Administrator/organization/organization.vue"),
},
{
path: "subscriptionPlan",
name: "subscriptionPlan",
meta: { enter: 3 },
component: () =>
import("@/component/Administrator/subscriptionPlan.vue"),
},
//企业版教育管理员页面
{
path: "allUserSE",
@@ -505,7 +498,6 @@ function isTimeRangePassed(timeRange) {
}
router.beforeEach((to: any, from, next) => {
store.commit("set_view_loading", true);
//系统维护时间
const time = '2025-11-21T23:00:00 - 2025-11-22T00:00:00';
if (isTimeRangePassed(time) == 'in_progress') {
@@ -548,7 +540,5 @@ router.beforeEach((to: any, from, next) => {
// if(systemUser == 0){//游客用户只能进入这两个页面
});
router.afterEach((to, from) => {
store.commit("set_view_loading", false);
});
export default router;

View File

@@ -13,18 +13,10 @@ export interface RootState{
export default createStore<RootState>({
state: {
loading: false,
view_loading: false,
},
getters: {
},
mutations: {
set_loading(state, v){
state.loading = v;
},
set_view_loading(state, v){
state.view_loading = v;
},
},
actions: {
},

View File

@@ -177,12 +177,6 @@ const all = (t)=>{
route:'/administrator/organization',
key:'sub13',
isShow:true,
},{
name:'Subscription Plan',
icon:'usetime',
route:'/administrator/subscriptionPlan',
key:'sub14',
isShow:true,
}]
}
const schoolOrEnterprise = (t) =>{

View File

@@ -325,11 +325,6 @@ export const Https = {
deletePromCode:`/api/stripe/deletePromCode`,//删除优惠券
addOrganization:`/api/inquiry/addOrganization`,//添加企业版或者教育版
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', // 切换订阅计划
//云生成

View File

@@ -427,12 +427,11 @@
<div class="userSystem" v-show="pastDuePage">
{{ $t('Header.pastDue') }}
</div>
<div class="router" v-if="!loading">
<div class="router" v-if="!getLangIsShowMark">
<home
ref="home"
@setNewProject="() => (leftShow = true)"
@setTask="setTask"
:key="userDetail.language"
></home>
</div>
</div>
@@ -443,10 +442,10 @@
<UpgradePlan ref="UpgradePlan"></UpgradePlan>
<TaskPage ref="TaskPage"></TaskPage>
<!-- <div class="mark_loading" v-show="loading">
<div class="mark_loading" v-show="getLangIsShowMark">
<a-spin size="large" />
</div> -->
<!-- <RobotAssist v-if="!loading"></RobotAssist> -->
</div>
<!-- <RobotAssist v-if="!getLangIsShowMark"></RobotAssist> -->
<scaleVideo ref="scaleVideo"></scaleVideo>
<!-- 进行续订 -->
<renew ref="renew"></renew>
@@ -657,7 +656,7 @@ export default defineComponent({
})
let activeCredits = ref(false)
let loading = computed(() => (store.state.loading))
let getLangIsShowMark = ref(true)
let messageNum = computed(() => {
return store.state.UserHabit.messageSystem.messageNum
})
@@ -1071,7 +1070,7 @@ export default defineComponent({
isMurmur,
credits,
activeCredits,
loading,
getLangIsShowMark,
messageNum,
messageType,
...toRefs(stateList),
@@ -1129,11 +1128,11 @@ export default defineComponent({
this.store
.dispatch('getLangType')
.then(() => {
this.store.commit('set_loading', false)
this.getLangIsShowMark = false
resolve()
})
.catch(() => {
this.store.commit('set_loading', false)
this.getLangIsShowMark = false
reject()
})
})
@@ -1335,7 +1334,7 @@ export default defineComponent({
})
},
setLang(v) {
this.store.commit('set_loading', true)
this.getLangIsShowMark = true
Https.axiosGet(Https.httpUrls.changeUserLanguage, { params: { language: v } })
.then(rv => {
if (rv) {
@@ -1352,11 +1351,11 @@ export default defineComponent({
window.location.reload()
// window.location.href = '/home';
}
this.store.commit('set_loading', false)
this.getLangIsShowMark = false
}
})
.catch(() => {
this.store.commit('set_loading', false)
this.getLangIsShowMark = false
})
},

View File

@@ -43,12 +43,15 @@
<span>{{ t('Login.LogonToAiDA') }}</span>
</div>
<div class="info" v-show="!loginType">{{ t('Login.Infomation') }}</div>
<personal ref="personal" v-if="loginType == 'personal'"></personal>
<personal ref="personal" v-if="loginType == 'personal'" v-model:isMask="isMask"></personal>
<school ref="school" v-if="loginType == 'school'"></school>
<enterprise ref="enterprise" v-if="loginType == 'enterprise'"></enterprise>
</div>
</div>
<div class="mark_loading" v-show="isMask">
<a-spin size="large" />
</div>
</div>
</div>
</template>
@@ -100,6 +103,7 @@ export default defineComponent({
const loginData = reactive({
loginType: "",
isMask: false,
});
const dataDom = reactive({
personal: null as any,