Merge branch 'develop' into 'sit'
Develop See merge request K17_AITS/tra-app!126
This commit is contained in:
+1
-1
@@ -29,7 +29,7 @@ VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
|
||||
|
||||
# 任东
|
||||
# VITE_APP_BASE_H5_API_Url_TRAAPP = 'http://25.64.16.130:9601'
|
||||
# VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.16.130:9602'
|
||||
VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.16.130:9602'
|
||||
|
||||
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.16.140:9604'
|
||||
# 张建成
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgZfELkUrcpqqAVlQg
|
||||
UfKNlPbySAnafYL+vEndTe9/xoqgCgYIKoZIzj0DAQehRANCAATBz1/RZd+1AEgU
|
||||
VfnoB1PNUMynS8FQ/zJdTcZ25qEuu6WVkWb82Ltmb1lIRSZpRWEbpQzL+7X6zQ95
|
||||
yjRmUksW
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
# 脚本名称:sync_remote_branch.sh
|
||||
# 功能描述:修复origin/sit引用问题,强制同步远程分支到本地develop
|
||||
# 使用方法:./sync_remote_branch.sh <远程分支名>
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 检查参数
|
||||
if [ $# -eq 0 ]; then
|
||||
echo -e "${RED}错误:未传入分支名称参数!${NC}"
|
||||
echo -e "使用方法:${YELLOW}$0 <远程分支名>${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "▶ 先设置内网网络\n"
|
||||
sudo route add -host 25.13.9.101 25.64.16.254
|
||||
# 配置参数
|
||||
TARGET_BRANCH="$1"
|
||||
WORK_DIR="Desktop/lx_uniapp/build/git_work_code"
|
||||
REMOTE_NAME="origin"
|
||||
LOCAL_BRANCH="develop"
|
||||
|
||||
echo -e "\n${YELLOW}===== 开始执行分支同步脚本 =====${NC}"
|
||||
echo -e "▶ 远程目标分支:${YELLOW}${TARGET_BRANCH}${NC}"
|
||||
echo -e "▶ 本地工作目录:${YELLOW}${WORK_DIR}${NC}\n"
|
||||
|
||||
# 1. 进入工作目录
|
||||
echo -e "${YELLOW}1. 检查并进入工作目录...${NC}"
|
||||
if [ ! -d "$WORK_DIR" ]; then
|
||||
echo -e "${RED}错误:工作目录不存在!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
cd "$WORK_DIR" || {
|
||||
echo -e "${RED}错误:无法进入工作目录!${NC}"
|
||||
exit 1
|
||||
}
|
||||
echo -e "✅ 成功进入工作目录:$(pwd)\n"
|
||||
|
||||
# 2. 检查Git仓库
|
||||
echo -e "${YELLOW}2. 检查Git仓库...${NC}"
|
||||
if [ ! -d ".git" ]; then
|
||||
echo -e "${RED}错误:当前目录不是Git仓库!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "✅ 确认是Git仓库\n"
|
||||
|
||||
# 3. 关键修复:获取远程分支完整信息(不限制深度)
|
||||
echo -e "${YELLOW}3. 完整获取远程所有分支完整信息...${NC}"
|
||||
# 先移除可能存在的浅层克隆限制
|
||||
git config --unset core.depth
|
||||
# 完整拉取所有分支信息
|
||||
if ! git fetch "$REMOTE_NAME" "+refs/heads/*:refs/remotes/$REMOTE_NAME/*"; then
|
||||
echo -e "${RED}错误:拉取远程分支信息失败!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "✅ 远程分支信息拉取完成\n"
|
||||
|
||||
# 4. 再次检查远程分支是否存在
|
||||
echo -e "${YELLOW}4. 验证远程 ${TARGET_BRANCH} 分支...${NC}"
|
||||
if ! git show-ref --verify --quiet "refs/remotes/$REMOTE_NAME/$TARGET_BRANCH"; then
|
||||
echo -e "${RED}错误:远程确实不存在 ${TARGET_BRANCH} 分支!${NC}"
|
||||
echo -e "远程所有分支列表:"
|
||||
git branch -r
|
||||
exit 1
|
||||
fi
|
||||
echo -e "✅ 确认远程存在 ${YELLOW}${TARGET_BRANCH}${NC} 分支\n"
|
||||
|
||||
# 5. 同步到本地develop分支
|
||||
echo -e "${YELLOW}5. 同步远程 ${TARGET_BRANCH} 到本地 ${LOCAL_BRANCH}...${NC}"
|
||||
echo -e "${YELLOW}⚠️ 警告:将覆盖本地develop分支所有内容!${NC}"
|
||||
|
||||
# 确保本地有develop分支
|
||||
git checkout -B "$LOCAL_BRANCH" >/dev/null 2>&1
|
||||
|
||||
# 关键命令:使用完整引用路径
|
||||
if ! git reset --hard "refs/remotes/$REMOTE_NAME/$TARGET_BRANCH"; then
|
||||
echo -e "${RED}错误:同步远程分支到本地失败!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 6. 完成提示
|
||||
echo -e "\n${GREEN}===== 操作完成 =====${NC}"
|
||||
echo -e "✅ 本地 ${YELLOW}${LOCAL_BRANCH}${NC} 已同步为远程 ${YELLOW}${TARGET_BRANCH}${NC} 最新状态"
|
||||
echo -e "当前版本:"
|
||||
git log -1 --pretty=format:"%h - %an, %ar : %s"
|
||||
@@ -0,0 +1,382 @@
|
||||
import jwt
|
||||
import time
|
||||
import requests
|
||||
import os
|
||||
import sys
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_chinese_date_time():
|
||||
"""
|
||||
获取当前时间,以中文格式返回(如:8月5日 上午8点24分)
|
||||
包含月、日、上午/下午、时、分,不包含秒数
|
||||
"""
|
||||
# 获取当前时间
|
||||
now = datetime.datetime.now()
|
||||
|
||||
# 获取月份和日期
|
||||
month = now.month
|
||||
day = now.day
|
||||
|
||||
# 获取24小时制小时数和分钟数
|
||||
hour_24 = now.hour
|
||||
minute = now.minute
|
||||
|
||||
# 确定上午/下午
|
||||
if hour_24 < 12:
|
||||
period = "上午"
|
||||
# 转换为12小时制(0点特殊处理为12点)
|
||||
hour_12 = hour_24 if hour_24 != 0 else 12
|
||||
else:
|
||||
period = "下午"
|
||||
# 转换为12小时制(12点保持12点,13点及以后减12)
|
||||
hour_12 = hour_24 if hour_24 == 12 else hour_24 - 12
|
||||
|
||||
# 格式化返回字符串
|
||||
return f"{month}月{day}日 {period}{hour_12}点{minute}分"
|
||||
|
||||
|
||||
# 获取参数
|
||||
# 处理第一个参数(环境),默认值为'develop'
|
||||
if len(sys.argv) >= 2 and sys.argv[1]:
|
||||
env_name = sys.argv[1]
|
||||
else:
|
||||
env_name = 'develop'
|
||||
|
||||
# 处理第二个参数(构建号),默认值为'2'
|
||||
if len(sys.argv) >= 3 and sys.argv[2]:
|
||||
build_number_str = sys.argv[2]
|
||||
else:
|
||||
build_number_str = '68'
|
||||
|
||||
# 验证第一个参数(枚举值)
|
||||
valid_envs = ['develop', 'sit', 'uat']
|
||||
if env_name not in ['develop', 'sit', 'uat']:
|
||||
print(f"错误:环境名称 '{env_name}' 无效有效")
|
||||
print(f"有效环境名称:{', '.join(valid_envs)}")
|
||||
sys.exit(1)
|
||||
|
||||
# 验证第二个参数(数字字符串)
|
||||
if not build_number_str.isdigit():
|
||||
print(f"错误:构建号 '{build_number_str}' 必须是纯数字字符串")
|
||||
sys.exit(1)
|
||||
|
||||
# 参数验证通过,继续处理
|
||||
TARGET_BUILD_NUMBER = build_number_str
|
||||
|
||||
if env_name == 'develop':
|
||||
BETA_GROUP_NAME = "外部自动化测试" # 测试群组
|
||||
BETA_GROUP_NAME = "UAT" # 测试群组
|
||||
TARGET_VERSION = "0.3.1"
|
||||
elif env_name == 'sit':
|
||||
BETA_GROUP_NAME = "SIT"
|
||||
TARGET_VERSION = "0.2.1"
|
||||
elif env_name == 'uat':
|
||||
BETA_GROUP_NAME = "UAT"
|
||||
TARGET_VERSION = "0.3.1"
|
||||
|
||||
# 接收参数结束
|
||||
|
||||
# 配置参数
|
||||
API_KEY = "ACRB9XFL9G"
|
||||
API_ISSUER = "69a6de7f-3378-47e3-e053-5b8c7c11a4d1"
|
||||
APP_NAME = "吉AI学" # 应用名称
|
||||
API_ENDPOINT = "https://api.appstoreconnect.apple.com/v1"
|
||||
|
||||
|
||||
|
||||
MAX_RETRIES = 10 # 最大重试次数
|
||||
RETRY_DELAY = 30 # 重试间隔(秒)
|
||||
if 'PYCHARM_HOSTED' in os.environ: # 说明是pycharm
|
||||
Auth_Key_Path = './'
|
||||
else:
|
||||
Auth_Key_Path = '/Users/jlbank/Desktop/lx_uniapp/auto_build' # p8私钥文件位置
|
||||
|
||||
private_key_path = Path(f'{Auth_Key_Path}/AuthKey_{API_KEY}.p8')
|
||||
|
||||
def generate_jwt_token(api_key, api_issuer, private_key_path):
|
||||
"""生成 JWT 令牌"""
|
||||
with open(private_key_path, "r") as f:
|
||||
private_key = f.read()
|
||||
|
||||
# 令牌有效期 20 分钟
|
||||
current_time = int(time.time())
|
||||
payload = {
|
||||
"iss": api_issuer,
|
||||
"exp": current_time + 20 * 60,
|
||||
"iat": current_time,
|
||||
"aud": "appstoreconnect-v1"
|
||||
}
|
||||
|
||||
# 生成 JWT 令牌
|
||||
token = jwt.encode(
|
||||
payload,
|
||||
private_key,
|
||||
algorithm="ES256",
|
||||
headers={"kid": api_key}
|
||||
)
|
||||
return token
|
||||
|
||||
|
||||
def find_specific_build(app_id, headers):
|
||||
"""通过预发布版本ID查找指定构建版本,带重试机制"""
|
||||
retries = 0
|
||||
|
||||
while retries < MAX_RETRIES:
|
||||
try:
|
||||
print(f"\n🔍 查找版本 {TARGET_VERSION} 构建号 {TARGET_BUILD_NUMBER} (尝试 {retries + 1}/{MAX_RETRIES})")
|
||||
|
||||
# 1. 先获取目标版本对应的预发布版本ID
|
||||
print(" 正在获取预发布版本ID...")
|
||||
pre_release_url = f"{API_ENDPOINT}/preReleaseVersions"
|
||||
pre_release_params = {
|
||||
"filter[version]": TARGET_VERSION,
|
||||
"filter[app]": app_id
|
||||
}
|
||||
|
||||
pre_release_response = requests.get(pre_release_url, headers=headers, params=pre_release_params)
|
||||
pre_release_response.raise_for_status()
|
||||
pre_release_data = pre_release_response.json()
|
||||
|
||||
if not pre_release_data["data"]:
|
||||
print(f"❓ 未找到版本为 {TARGET_VERSION} 的预发布版本")
|
||||
retries += 1
|
||||
if retries < MAX_RETRIES:
|
||||
time.sleep(RETRY_DELAY)
|
||||
continue
|
||||
|
||||
pre_release_id = pre_release_data["data"][0]["id"]
|
||||
print(f" 找到预发布版本ID: {pre_release_id}")
|
||||
|
||||
# 2. 使用预发布版本ID筛选构建版本(修正参数错误)
|
||||
builds_url = f"{API_ENDPOINT}/builds"
|
||||
params = {
|
||||
"filter[preReleaseVersion]": pre_release_id,
|
||||
"sort": "-version",
|
||||
"limit": "10",
|
||||
# 移除了无效的ciBuildGroup关联
|
||||
"include": "buildBetaDetail,betaBuildLocalizations,preReleaseVersion,betaAppReviewSubmission,icons,buildBundles,betaGroups",
|
||||
# 移除了无效的ciBuildGroups字段
|
||||
"limit[betaBuildLocalizations]": "40",
|
||||
# 将betaGroups的limit调整为最大值50
|
||||
"limit[betaGroups]": "50"
|
||||
}
|
||||
|
||||
response = requests.get(builds_url, headers=headers, params=params)
|
||||
|
||||
if response.status_code == 400:
|
||||
print(f" 错误响应内容: {response.text}")
|
||||
response.raise_for_status()
|
||||
|
||||
builds_data = response.json()
|
||||
|
||||
# 在返回结果中筛选出符合构建号的构建
|
||||
matching_builds = [
|
||||
b for b in builds_data["data"]
|
||||
if b["attributes"].get("version") == TARGET_BUILD_NUMBER
|
||||
]
|
||||
if not matching_builds:
|
||||
print(f"❓ 未找到版本 {TARGET_VERSION} 构建号 {TARGET_BUILD_NUMBER} 的构建")
|
||||
else:
|
||||
# 检查是否有状态为VALID的构建
|
||||
valid_build = next((
|
||||
b for b in matching_builds
|
||||
if b["attributes"]["processingState"] == "VALID"
|
||||
), None)
|
||||
|
||||
if valid_build:
|
||||
return valid_build
|
||||
else:
|
||||
# 显示找到的构建但状态不是VALID
|
||||
states = [b["attributes"]["processingState"] for b in matching_builds]
|
||||
print(f"⏳ 找到匹配的构建但状态为: {', '.join(states)},等待处理完成...")
|
||||
|
||||
# 重试前等待
|
||||
if retries < MAX_RETRIES - 1:
|
||||
print(f"将在 {RETRY_DELAY} 秒后重试...")
|
||||
time.sleep(RETRY_DELAY)
|
||||
|
||||
retries += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"查询过程中出错: {str(e)}")
|
||||
retries += 1
|
||||
if retries < MAX_RETRIES:
|
||||
time.sleep(RETRY_DELAY)
|
||||
|
||||
return None
|
||||
def main():
|
||||
try:
|
||||
# 1. 生成 JWT 令牌
|
||||
|
||||
if not private_key_path.exists():
|
||||
raise FileNotFoundError(f"私钥文件不存在: {private_key_path}")
|
||||
|
||||
token = generate_jwt_token(API_KEY, API_ISSUER, private_key_path)
|
||||
print("✅ JWT 令牌生成成功")
|
||||
|
||||
# 2. 配置请求头
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# 3. 获取应用 ID
|
||||
print(f"\n🔍 查找应用: {APP_NAME}")
|
||||
apps_url = f"{API_ENDPOINT}/apps"
|
||||
response = requests.get(apps_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
apps_data = response.json()
|
||||
app = next((a for a in apps_data["data"] if a["attributes"]["name"] == APP_NAME), None)
|
||||
if not app:
|
||||
raise ValueError(f"未找到应用: {APP_NAME}")
|
||||
|
||||
app_id = app["id"]
|
||||
print(f"✅ 找到应用 ID: {app_id}")
|
||||
|
||||
# 4. 获取指定版本和构建号的构建版本(带重试)
|
||||
valid_build = find_specific_build(app_id, headers)
|
||||
if not valid_build:
|
||||
raise ValueError(
|
||||
f"经过 {MAX_RETRIES} 次尝试后,仍未找到可用的构建版本: {TARGET_VERSION} ({TARGET_BUILD_NUMBER})")
|
||||
|
||||
build_id = valid_build["id"]
|
||||
build_version = TARGET_VERSION
|
||||
build_number = valid_build["attributes"]["version"]
|
||||
print(f"✅ 找到构建版本 ID: {build_id} (版本号: {build_version}, 构建号: {build_number})")
|
||||
|
||||
# 5. 获取外部测试群组 ID
|
||||
print(f"\n🔍 查找测试群组: {BETA_GROUP_NAME}")
|
||||
groups_url = f"{API_ENDPOINT}/betaGroups"
|
||||
response = requests.get(groups_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
groups_data = response.json()
|
||||
beta_group = next((g for g in groups_data["data"] if
|
||||
g["attributes"]["name"] == BETA_GROUP_NAME and
|
||||
g["attributes"]["isInternalGroup"] is False), None) # 确保是外部群组
|
||||
if not beta_group:
|
||||
raise ValueError(f"未找到外部测试群组: {BETA_GROUP_NAME}")
|
||||
|
||||
beta_group_id = beta_group["id"]
|
||||
print(f"✅ 找到外部测试群组 ID: {beta_group_id}")
|
||||
|
||||
# 6. 添加构建到测试群组后,更新测试内容(betaBuildLocalizations)
|
||||
print("\n📝 更新测试版本的本地化信息(whatsNew)...")
|
||||
|
||||
# 7. 将构建版本添加到测试群组
|
||||
print(f"\n🔗 将构建 {build_id} 添加到测试群组 {beta_group_id}...")
|
||||
add_build_url = f"{API_ENDPOINT}/betaGroups/{beta_group_id}/relationships/builds"
|
||||
|
||||
# 准备添加构建的请求数据
|
||||
add_build_payload = {
|
||||
"data": [{"id": build_id, "type": "builds"}]
|
||||
}
|
||||
|
||||
# 发送添加请求
|
||||
add_response = requests.post(add_build_url, headers=headers, json=add_build_payload)
|
||||
try:
|
||||
add_response.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
# 处理已存在的情况(409 Conflict 是正常现象,说明已添加)
|
||||
if add_response.status_code == 409:
|
||||
print("⚠️ 构建已在测试群组中,跳过添加步骤")
|
||||
else:
|
||||
print(f"❌ 添加构建到测试群组失败: {e}")
|
||||
print("错误详情:", add_response.json())
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("✅ 构建已成功添加到测试群组")
|
||||
# 8. 更新测试内容(whatsNew)
|
||||
# 先获取该构建的本地化信息 ID(如果已有)
|
||||
# 构建 ID(build_id 是你要操作的构建版本 ID)
|
||||
get_localizations_url = f"{API_ENDPOINT}/builds/{build_id}/betaBuildLocalizations"
|
||||
response = requests.get(get_localizations_url, headers=headers)
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f"❌ 获取本地化信息失败: {e}")
|
||||
print("错误详情:", response.json())
|
||||
sys.exit(1)
|
||||
localizations = response.json().get("data", [])
|
||||
|
||||
print("\n📝 更新测试版本说明...")
|
||||
if localizations:
|
||||
# 使用现有本地化信息
|
||||
localization_id = localizations[0]["id"]
|
||||
update_url = f"{API_ENDPOINT}/betaBuildLocalizations/{localization_id}"
|
||||
print(f"使用现有本地化信息 ID: {localization_id}")
|
||||
else:
|
||||
# 创建新的本地化信息(默认简体中文)
|
||||
print("创建新的本地化信息...")
|
||||
create_url = f"{API_ENDPOINT}/betaBuildLocalizations"
|
||||
create_payload = {
|
||||
"data": {
|
||||
"type": "betaBuildLocalizations",
|
||||
"attributes": {"locale": "zh-Hans", "whatsNew": ""},
|
||||
"relationships": {
|
||||
"build": {"data": {"id": build_id, "type": "builds"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
create_response = requests.post(create_url, headers=headers, json=create_payload)
|
||||
create_response.raise_for_status()
|
||||
localization_id = create_response.json()["data"]["id"]
|
||||
update_url = f"{API_ENDPOINT}/betaBuildLocalizations/{localization_id}"
|
||||
now_time_text = get_chinese_date_time()
|
||||
# 更新测试内容
|
||||
update_payload = {
|
||||
"data": {
|
||||
"id": localization_id,
|
||||
"type": "betaBuildLocalizations",
|
||||
"attributes": {
|
||||
"whatsNew": f"当前版本: {TARGET_BUILD_NUMBER}\n提交时间:{now_time_text}\n自动化提交"
|
||||
}
|
||||
}
|
||||
}
|
||||
update_response = requests.patch(update_url, headers=headers, json=update_payload)
|
||||
update_response.raise_for_status()
|
||||
print("✅ 测试版本说明已更新")
|
||||
|
||||
# 9. 提交审核(触发测试状态)- 修复API端点
|
||||
print("\n📋 提交构建版本审核...")
|
||||
|
||||
# 正确的提交审核API端点
|
||||
submit_url = f"{API_ENDPOINT}/betaAppReviewSubmissions"
|
||||
submit_payload = {
|
||||
"data": {
|
||||
"type": "betaAppReviewSubmissions",
|
||||
"relationships": {
|
||||
"build": {
|
||||
"data": {"id": build_id, "type": "builds"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
submit_response = requests.post(submit_url, headers=headers, json=submit_payload)
|
||||
try:
|
||||
submit_response.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
# 处理已提交的情况(409冲突)
|
||||
if submit_response.status_code == 409:
|
||||
print("⚠️ 构建已提交审核,无需重复操作")
|
||||
else:
|
||||
print(f"❌ 提交审核失败: {e}")
|
||||
print("错误详情:", submit_response.json())
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("✅ 构建版本已成功提交审核")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==============================================
|
||||
# 配置参数 - 请根据实际情况修改以下参数
|
||||
# ==============================================
|
||||
# 钥匙串配置
|
||||
KEYCHAIN_PASSWORD="1234567" # 钥匙串密码
|
||||
KEYCHAIN_PATH="$HOME/Library/Keychains/login.keychain-db" # 钥匙串路径
|
||||
|
||||
# 项目路径配置
|
||||
PROJECT_DIR="$HOME/Desktop/lx_uniapp/build/git_work_code/Ios/HBuilder-Hello" # 项目目录
|
||||
PROJECT_NAME="HBuilder-Hello.xcodeproj" # Xcode项目文件名
|
||||
SCHEME_NAME="HBuilder" # 构建方案名称
|
||||
CONFIGURATION="Release" # 构建配置
|
||||
|
||||
# 构建输出配置
|
||||
BUILD_DIR="$HOME/Desktop/lx_uniapp/build" # 构建输出目录
|
||||
ARCHIVE_NAME="HBuilder.xcarchive" # 归档文件名
|
||||
IPA_DIR_NAME="ipa_production" # IPA输出目录名
|
||||
DERIVED_DATA_DIR="$HOME/Library/Developer/Xcode/DerivedData/*" # 衍生数据目录
|
||||
|
||||
# 版本配置
|
||||
MARKETING_VERSION="$1" # 市场版本号
|
||||
CURRENT_PROJECT_VERSION="$2" # 项目内部版本号
|
||||
|
||||
# 签名配置
|
||||
CODE_SIGN_IDENTITY="Apple Distribution: Bank of Jilin Co., Ltd. (2EP6SRRN43)" # 签名标识
|
||||
PROVISIONING_PROFILE="e3487fe1-19dc-4fc7-90df-11caf27da0c6" # 配置文件UUID
|
||||
DEVELOPMENT_TEAM="2EP6SRRN43" # 开发团队ID
|
||||
CODE_SIGNING_REQUIRED="YES" # 是否需要签名
|
||||
|
||||
# 导出配置
|
||||
EXPORT_OPTIONS_PLIST="$BUILD_DIR/ExportOptions_Production.plist" # 导出配置plist路径
|
||||
# ==============================================
|
||||
|
||||
# 错误处理函数
|
||||
error_exit() {
|
||||
echo "❌ $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 0. 解锁钥匙串并授予权限
|
||||
echo "🔑 解锁钥匙串..."
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" || {
|
||||
error_exit "钥匙串解锁失败"
|
||||
}
|
||||
|
||||
# 1. 进入项目目录
|
||||
echo "📂 进入项目目录: $PROJECT_DIR"
|
||||
cd "$PROJECT_DIR" || {
|
||||
error_exit "无法进入项目目录: $PROJECT_DIR"
|
||||
}
|
||||
|
||||
# 2. 清理旧构建
|
||||
echo "🧹 清理旧构建..."
|
||||
xcodebuild -project "$PROJECT_NAME" -scheme "$SCHEME_NAME" -configuration "$CONFIGURATION" clean || {
|
||||
error_exit "清理构建失败"
|
||||
}
|
||||
|
||||
# 3. 删除上次构建内容
|
||||
echo "🗑️ 删除上次构建内容..."
|
||||
setopt rm_star_silent nullglob
|
||||
rm -rf "$BUILD_DIR/$ARCHIVE_NAME" "$BUILD_DIR/$IPA_DIR_NAME" "$DERIVED_DATA_DIR"
|
||||
unsetopt rm_star_silent nullglob # 恢复默认设置
|
||||
|
||||
# 4. 归档项目
|
||||
echo "📦 开始归档项目..."
|
||||
xcodebuild -project "$PROJECT_NAME" \
|
||||
-scheme "$SCHEME_NAME" \
|
||||
-configuration "$CONFIGURATION" \
|
||||
-archivePath "$BUILD_DIR/$ARCHIVE_NAME" \
|
||||
MARKETING_VERSION="$MARKETING_VERSION" \
|
||||
CURRENT_PROJECT_VERSION="$CURRENT_PROJECT_VERSION" \
|
||||
-destination 'generic/platform=iOS' \
|
||||
CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY" \
|
||||
PROVISIONING_PROFILE="$PROVISIONING_PROFILE" \
|
||||
DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \
|
||||
CODE_SIGNING_REQUIRED="$CODE_SIGNING_REQUIRED" \
|
||||
OTHER_CFLAGS="" \
|
||||
OTHER_CPLUSPLUS_FLAGS="" \
|
||||
OTHER_SWIFT_FLAGS="" \
|
||||
archive || {
|
||||
error_exit "项目归档失败"
|
||||
}
|
||||
|
||||
# 5. 导出IPA文件
|
||||
echo "📤 导出IPA文件..."
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath "$BUILD_DIR/$ARCHIVE_NAME" \
|
||||
-exportPath "$BUILD_DIR/$IPA_DIR_NAME" \
|
||||
-exportOptionsPlist "$EXPORT_OPTIONS_PLIST" || {
|
||||
error_exit "IPA导出失败"
|
||||
}
|
||||
|
||||
echo "🎉 构建完成!IPA文件已导出至: $BUILD_DIR/$IPA_DIR_NAME"
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
set -e # 脚本执行过程中若有命令失败则立即退出
|
||||
|
||||
# ==============================================
|
||||
# 颜色定义
|
||||
# ==============================================
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # 无颜色(重置)
|
||||
|
||||
# ==============================================
|
||||
# 配置变量 - 根据实际情况修改以下参数
|
||||
# ==============================================
|
||||
API_KEY="ACRB9XFL9G" # App Store Connect API 密钥ID
|
||||
API_ISSUER="69a6de7f-3378-47e3-e053-5b8c7c11a4d1" # API 密钥对应的Issuer ID
|
||||
IPA_PATH="$HOME/Desktop/lx_uniapp/build/ipa_production/HBuilder.ipa" # IPA文件路径
|
||||
APP_TYPE="ios" # 应用类型(固定为ios)
|
||||
|
||||
# ==============================================
|
||||
# 验证IPA文件是否存在
|
||||
# ==============================================
|
||||
if [ ! -f "$IPA_PATH" ]; then
|
||||
echo -e "${RED}❌ 错误:未找到IPA文件,请检查路径是否正确${NC}"
|
||||
echo -e " 查找路径:$IPA_PATH"
|
||||
exit 1
|
||||
fi
|
||||
# ==============================================
|
||||
# 1. 验证IPA文件合法性
|
||||
# ==============================================
|
||||
echo -e "\n${YELLOW}📋 开始验证IPA文件...${NC}"
|
||||
echo -e " 文件路径:$IPA_PATH"
|
||||
|
||||
# 创建临时文件保存输出
|
||||
TMP_FILE=$(mktemp)
|
||||
|
||||
# 执行验证命令,直接输出到终端,同时重定向到临时文件
|
||||
xcrun altool --validate-app \
|
||||
--type "$APP_TYPE" \
|
||||
--file "$IPA_PATH" \
|
||||
--apiKey "$API_KEY" \
|
||||
--apiIssuer "$API_ISSUER" > "$TMP_FILE" 2>&1
|
||||
# 直接打印临时文件内容(确保错误信息显示)
|
||||
cat "$TMP_FILE"
|
||||
|
||||
# 获取退出码
|
||||
VALIDATE_EXIT_CODE=$?
|
||||
|
||||
# 从临时文件读取内容用于分析
|
||||
VALIDATE_OUTPUT=$(cat "$TMP_FILE")
|
||||
rm "$TMP_FILE" # 清理临时文件
|
||||
|
||||
# 验证结果处理
|
||||
if [ $VALIDATE_EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ IPA文件验证成功,符合上传要求${NC}"
|
||||
else
|
||||
# 检查是否是版本号重复错误
|
||||
if echo "$VALIDATE_OUTPUT" | grep -q "The bundle version must be higher than the previously uploaded version"; then
|
||||
# 提取之前的版本号
|
||||
PREVIOUS_VERSION=$(echo "$VALIDATE_OUTPUT" | grep -oE 'previousBundleVersion = [0-9]+' | awk '{print $3}')
|
||||
if [ -z "$PREVIOUS_VERSION" ]; then
|
||||
PREVIOUS_VERSION=$(echo "$VALIDATE_OUTPUT" | grep -oE 'version: ‘[0-9]+’' | sed "s/version: ‘//;s/’//")
|
||||
fi
|
||||
|
||||
echo -e "${RED}❌ 错误:版本号重复${NC}"
|
||||
echo -e "${RED} 已上传的最新版本号为: $PREVIOUS_VERSION${NC}"
|
||||
echo -e "${RED} 请将版本号修改为 $((PREVIOUS_VERSION + 1)) 或更高后重试${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ IPA文件验证失败,请查看上方错误信息${NC}"
|
||||
fi
|
||||
exit $VALIDATE_EXIT_CODE
|
||||
fi
|
||||
# ==============================================
|
||||
# 2. 上传IPA文件到App Store Connect
|
||||
# ==============================================
|
||||
echo -e "\n${YELLOW}🚀 开始上传IPA文件...${NC}"
|
||||
echo -e " 文件路径:$IPA_PATH"
|
||||
|
||||
xcrun altool --upload-app \
|
||||
--type "$APP_TYPE" \
|
||||
--file "$IPA_PATH" \
|
||||
--apiKey "$API_KEY" \
|
||||
--apiIssuer "$API_ISSUER"
|
||||
|
||||
# 上传成功提示
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "\n${GREEN}🎉 上传成功!可在App Store Connect中查看构建版本${NC}"
|
||||
echo -e " 提示:构建版本处理需要几分钟,请耐心等待"
|
||||
else
|
||||
echo -e "\n${RED}❌ 上传失败,请根据错误信息修复后重试${NC}"
|
||||
exit 1
|
||||
fi
|
||||
+17
-17
@@ -163,8 +163,10 @@ const { statusBarHeight } = uni.getWindowInfo()
|
||||
|
||||
const pageTopPadding = computed(() => statusBarHeight + 'px')
|
||||
|
||||
const isContinue = ref('N')
|
||||
// 初始化数据
|
||||
const getData = async flag => {
|
||||
isContinue.value = "N"
|
||||
try {
|
||||
const { body } = await getCourseStudyInfoApi({
|
||||
crsId: crsId.value
|
||||
@@ -187,7 +189,8 @@ const getData = async flag => {
|
||||
body: {
|
||||
crsId: crsId.value,
|
||||
teacherNo: choiceTeacher.value,
|
||||
studyType: isPreview.value ? 'P' : 'S'
|
||||
studyType: isPreview.value ? 'P' : 'S',
|
||||
isContinue: isContinue.value
|
||||
}
|
||||
})
|
||||
return
|
||||
@@ -199,12 +202,14 @@ const getData = async flag => {
|
||||
// 继续学习 , 获取学习历史接口
|
||||
// 滚动到最下面
|
||||
scrollToBottomNow()
|
||||
isContinue.value = "Y"
|
||||
sendMessage({
|
||||
commond: 'STDY',
|
||||
body: {
|
||||
crsId: crsId.value,
|
||||
teacherNo: choiceTeacher.value,
|
||||
studyType: isPreview.value ? 'P' : 'S'
|
||||
studyType: isPreview.value ? 'P' : 'S',
|
||||
isContinue: isContinue.value
|
||||
}
|
||||
})
|
||||
step.value = 2
|
||||
@@ -331,7 +336,8 @@ const nextStep = () => {
|
||||
body: {
|
||||
crsId: crsId.value,
|
||||
teacherNo: choiceTeacher.value,
|
||||
studyType: isPreview.value ? 'P' : 'S'
|
||||
studyType: isPreview.value ? 'P' : 'S',
|
||||
isContinue: isContinue.value
|
||||
}
|
||||
})
|
||||
step.value = 2
|
||||
@@ -361,13 +367,16 @@ const canAnswerText = computed(() => {
|
||||
return '每个问题最多连续发送五次答案!'
|
||||
})
|
||||
|
||||
const oldStdyId = ref('')
|
||||
// 获取段落
|
||||
const getGraphInfoUnStudy = (flag = 0) => {
|
||||
let _id = new Date().getTime() + 'id'
|
||||
let _data = {
|
||||
crsId: crsId.value,
|
||||
stdyId: unref(stdyId),
|
||||
stdyBatch: unref(stdyBatch)
|
||||
stdyBatch: unref(stdyBatch),
|
||||
oldStdyId: unref(oldStdyId),
|
||||
isContinue: isContinue.value
|
||||
}
|
||||
if (flag === 1 && !!startPgrphId.value) {
|
||||
_data.pgrphId = startPgrphId.value
|
||||
@@ -510,7 +519,9 @@ const nextQuestionSuccess = (info, type = 'next') => {
|
||||
requestBody: JSON.stringify({
|
||||
stdyId: unref(stdyId),
|
||||
pgrphId: info?.pgrphId,
|
||||
logId: info?.logId
|
||||
logId: info?.logId,
|
||||
oldStdyId: unref(oldStdyId),
|
||||
isContinue: isContinue.value
|
||||
})
|
||||
}).then(res => {
|
||||
talkingList.value.push({
|
||||
@@ -641,24 +652,13 @@ const logId = ref('')
|
||||
// ws 初始化逻辑
|
||||
const initsocketTask = () => {
|
||||
socketStore.creatSocket()
|
||||
// unref(SocketObj).on('open', (res)=>{
|
||||
// if(startPgrphId.value){
|
||||
// sendMessage({
|
||||
// "commond" : 'STDY',
|
||||
// "body" : {
|
||||
// crsId: crsId.value,
|
||||
// teacherNo: choiceTeacher.value
|
||||
// }
|
||||
// })
|
||||
// step.value = 2
|
||||
// }
|
||||
// })
|
||||
unref(SocketObj).on('message', res => {
|
||||
const { rtnCode, body, commond } = JSON.parse(res.data)
|
||||
if (rtnCode === '0000') {
|
||||
if (commond === 'STDYBTH' && !stdyId.value) {
|
||||
stdyId.value = body.stdyId || ''
|
||||
stdyBatch.value = body.stdyBatch || ''
|
||||
oldStdyId.value = body.oldStdyId || ''
|
||||
logId.value = body.logId || ''
|
||||
getGraphInfoUnStudy(1)
|
||||
step.value = 2
|
||||
|
||||
@@ -1,299 +1,307 @@
|
||||
<template>
|
||||
<view class="answer-content" v-if="props.isLoading">
|
||||
<view class="talking-cont">
|
||||
<CommonLoading color="#06f" />
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="answer-content" v-else>
|
||||
<view class="talking-cont">
|
||||
<MarkdownPreview :mdString="mdText" />
|
||||
</view>
|
||||
<view class="talking-link">
|
||||
<view :class="['link-cont', activeNames.indexOf(1) >= 0 ? 'is-active' : '']" v-if="fileList.length > 0">
|
||||
<view class="link-info" @click="changeActive(1)">
|
||||
<view class="info-text">
|
||||
引用知识库{{fileList.length}}篇相关资料
|
||||
</view>
|
||||
</view>
|
||||
<view class="link-items">
|
||||
<view class="link-item" v-for="(item, index) in fileList" @click.stop="previewFile(item)">{{index+1}}. {{item.docName}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="!props.isAnswering" class="link-cont-desc">本次回答参考资料来源于网络</view>
|
||||
<view :class="['link-cont', activeNames.indexOf(2) >= 0 ? 'is-active' : '']" v-if="crsList.length > 0">
|
||||
<view class="link-info" @click="changeActive(2)">
|
||||
<view class="info-text">
|
||||
发现{{crsList.length}}个关联课程
|
||||
</view>
|
||||
</view>
|
||||
<view class="link-items">
|
||||
<view class="link-item" v-for="(item, index) in crsList" @click.stop="previewCrs(item)">{{index+1}}. {{item.crsName}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btns-cont" v-if="!props.isLoading">
|
||||
<view class="replay-btn" @click="reAnswer">
|
||||
<image class="icon" src="@/static/images/dialog/answer-restart1.png" style="width: 100%;height: 100%;"></image>
|
||||
</view>
|
||||
<view class="replay-btn" @click="stopAnswer" v-if="props.isAnswering && props.isLast">
|
||||
<image class="icon" src="@/static/images/dialog/answer-stop1.png" style="width: 100%;height: 100%;"></image>
|
||||
</view>
|
||||
<view class="replay-btn" @click="zanAnswer">
|
||||
<image class="icon" v-if="props.dataInfo.likeStat !== 'Y'" src="@/static/images/dialog/answer-zan1.png" style="width: 100%;height: 100%;"></image>
|
||||
<image class="icon" v-else src="@/static/images/dialog/answer-yizan1.png" style="width: 100%;height: 100%;"></image>
|
||||
</view>
|
||||
<view class="play-btn" @click="feedbackAnswer">
|
||||
<image class="icon" v-if="props.dataInfo.downvoteStat !== 'Y'" src="@/static/images/dialog/answer-fankui1.png" style="width: 100%;height: 100%;"></image>
|
||||
<image class="icon" v-else src="@/static/images/dialog/answer-yifankui1.png" style="width: 100%;height: 100%;"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="answer-content" v-if="props.isLoading">
|
||||
<view class="talking-cont">
|
||||
<CommonLoading color="#06f" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="answer-content" v-else>
|
||||
<view class="talking-cont">
|
||||
<MarkdownPreview :mdString="mdText" />
|
||||
</view>
|
||||
<view class="talking-link">
|
||||
<view :class="['link-cont', activeNames.indexOf(1) >= 0 ? 'is-active' : '']" v-if="fileList.length > 0">
|
||||
<view class="link-info" @click="changeActive(1)">
|
||||
<view class="info-text"> 引用知识库{{ fileList.length }}篇相关资料 </view>
|
||||
</view>
|
||||
<view class="link-items">
|
||||
<view class="link-item" v-for="(item, index) in fileList" @click.stop="previewFile(item)">{{ index + 1 }}. {{ item.docName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="!props.isAnswering" class="link-cont-desc">本次回答参考资料来源于网络</view>
|
||||
<view :class="['link-cont', activeNames.indexOf(2) >= 0 ? 'is-active' : '']" v-if="crsList.length > 0">
|
||||
<view class="link-info" @click="changeActive(2)">
|
||||
<view class="info-text"> 发现{{ crsList.length }}个关联课程 </view>
|
||||
</view>
|
||||
<view class="link-items">
|
||||
<view class="link-item" v-for="(item, index) in crsList" @click.stop="previewCrs(item)">{{ index + 1 }}. {{ item.crsName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btns-cont" v-if="!props.isLoading">
|
||||
<view class="replay-btn" @click="reAnswer" v-if="!props.dataInfo.isHistory">
|
||||
<image class="icon" src="@/static/images/dialog/answer-restart1.png" style="width: 100%; height: 100%"></image>
|
||||
</view>
|
||||
<view class="replay-btn" @click="stopAnswer" v-if="props.isAnswering && props.isLast">
|
||||
<image class="icon" src="@/static/images/dialog/answer-stop1.png" style="width: 100%; height: 100%"></image>
|
||||
</view>
|
||||
<view class="replay-btn" @click="zanAnswer">
|
||||
<image class="icon" v-if="props.dataInfo.likeStat !== 'Y'" src="@/static/images/dialog/answer-zan1.png" style="width: 100%; height: 100%"></image>
|
||||
<image class="icon" v-else src="@/static/images/dialog/answer-yizan1.png" style="width: 100%; height: 100%"></image>
|
||||
</view>
|
||||
<view class="play-btn" @click="feedbackAnswer">
|
||||
<image class="icon" v-if="props.dataInfo.downvoteStat !== 'Y'" src="@/static/images/dialog/answer-fankui1.png" style="width: 100%; height: 100%"></image>
|
||||
<image class="icon" v-else src="@/static/images/dialog/answer-yifankui1.png" style="width: 100%; height: 100%"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
onMounted,
|
||||
computed,
|
||||
ref,
|
||||
watch
|
||||
} from 'vue'
|
||||
import MarkdownPreview from '@/components/markdown-preview/markdown-preview.vue';
|
||||
import common from '@/common/common';
|
||||
import { onMounted, computed, ref, watch } from 'vue'
|
||||
import MarkdownPreview from '@/components/markdown-preview/markdown-preview.vue'
|
||||
import common from '@/common/common'
|
||||
|
||||
const emit = defineEmits(['handleEvents'])
|
||||
const props = defineProps({
|
||||
dataInfo: {
|
||||
default: () => ({}),
|
||||
type: Object
|
||||
},
|
||||
isLoading: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
isAnswering: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
isLast: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['handleEvents'])
|
||||
const props = defineProps({
|
||||
dataInfo: {
|
||||
default: () => ({}),
|
||||
type: Object
|
||||
},
|
||||
isLoading: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
isAnswering: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
isLast: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.dataInfo, () => {}, {
|
||||
deep: true,
|
||||
immediate: true
|
||||
})
|
||||
watch(() => props.isLoading, () => {}, {
|
||||
deep: true,
|
||||
immediate: true
|
||||
})
|
||||
const mdText = computed(() => props.dataInfo?.talkingText)
|
||||
const fileList = computed(() => props.dataInfo?.talkingFiles || [])
|
||||
const crsList = computed(()=> props.dataInfo?.talkingCrsList || [])
|
||||
const activeNames = ref([1, 2])
|
||||
watch(
|
||||
() => props.dataInfo,
|
||||
() => {},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.isLoading,
|
||||
() => {},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
const mdText = computed(() => {
|
||||
//#ifndef APP-PLUS
|
||||
// h5 假数据
|
||||
let _text = (props.dataInfo?.talkingText|| '').replace(/\\n/g, `\n`);
|
||||
return _text
|
||||
// #endif
|
||||
//#ifdef APP-PLUS
|
||||
return (props.dataInfo?.talkingText|| '')
|
||||
// #endif
|
||||
|
||||
})
|
||||
const fileList = computed(() => props.dataInfo?.talkingFiles || [])
|
||||
const crsList = computed(() => props.dataInfo?.talkingCrsList || [])
|
||||
const activeNames = ref([1, 2])
|
||||
|
||||
const changeActive = (val) => {
|
||||
if (activeNames.value.indexOf(val) >= 0) {
|
||||
activeNames.value = activeNames.value.filter(t => t !== val)
|
||||
} else {
|
||||
activeNames.value.push(val)
|
||||
}
|
||||
}
|
||||
const reAnswer = () => {
|
||||
emit('handleEvents', 'reAnswer')
|
||||
}
|
||||
const stopAnswer = () => {
|
||||
emit('handleEvents', 'stop')
|
||||
}
|
||||
const zanAnswer = () => {
|
||||
emit('handleEvents', 'great')
|
||||
}
|
||||
const feedbackAnswer = ()=>{
|
||||
emit('handleEvents', 'feedback')
|
||||
}
|
||||
const previewFile = (item) => {
|
||||
emit('handleEvents', 'preview', item)
|
||||
}
|
||||
const previewCrs = (item) => {
|
||||
common.navigateTo('/pages/courseDetail/index?crsId=' + item.crsId)
|
||||
}
|
||||
const changeActive = val => {
|
||||
if (activeNames.value.indexOf(val) >= 0) {
|
||||
activeNames.value = activeNames.value.filter(t => t !== val)
|
||||
} else {
|
||||
activeNames.value.push(val)
|
||||
}
|
||||
}
|
||||
const reAnswer = () => {
|
||||
emit('handleEvents', 'reAnswer')
|
||||
}
|
||||
const stopAnswer = () => {
|
||||
emit('handleEvents', 'stop')
|
||||
}
|
||||
const zanAnswer = () => {
|
||||
emit('handleEvents', 'great')
|
||||
}
|
||||
const feedbackAnswer = () => {
|
||||
emit('handleEvents', 'feedback')
|
||||
}
|
||||
const previewFile = item => {
|
||||
emit('handleEvents', 'preview', item)
|
||||
}
|
||||
const previewCrs = item => {
|
||||
common.navigateTo('/pages/courseDetail/index?crsId=' + item.crsId)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.answer-content {
|
||||
background-color: #f5f5f5;
|
||||
padding: 20rpx;
|
||||
border-radius: 15rpx;
|
||||
padding-bottom: 10rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
width: 100%;
|
||||
min-height: 80rpx;
|
||||
.answer-content {
|
||||
background-color: #f5f5f5;
|
||||
padding: 20rpx;
|
||||
border-radius: 15rpx;
|
||||
padding-bottom: 10rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
width: 100%;
|
||||
min-height: 80rpx;
|
||||
|
||||
.talking-cont {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
.talking-cont {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.btns-cont {
|
||||
overflow: hidden;
|
||||
border-top: 3rpx solid #eee;
|
||||
padding-bottom: 10rpx;
|
||||
.btns-cont {
|
||||
overflow: hidden;
|
||||
border-top: 3rpx solid #eee;
|
||||
padding-bottom: 10rpx;
|
||||
|
||||
.replay-btn {
|
||||
float: left;
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
overflow: hidden;
|
||||
margin-right: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.replay-btn {
|
||||
float: left;
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
overflow: hidden;
|
||||
margin-right: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.play-btn {
|
||||
float: left;
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
overflow: hidden;
|
||||
margin-right: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.play-btn {
|
||||
float: left;
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
overflow: hidden;
|
||||
margin-right: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.next-learn-btn {
|
||||
float: left;
|
||||
padding: 0 60rpx 0 20rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
font-size: 30rpx;
|
||||
background-color: #06f;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
margin-top: 16rpx;
|
||||
margin-right: 16rpx;
|
||||
position: relative;
|
||||
.next-learn-btn {
|
||||
float: left;
|
||||
padding: 0 60rpx 0 20rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
font-size: 30rpx;
|
||||
background-color: #06f;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
margin-top: 16rpx;
|
||||
margin-right: 16rpx;
|
||||
position: relative;
|
||||
|
||||
&.replay-study-btn {
|
||||
float: right;
|
||||
}
|
||||
&.replay-study-btn {
|
||||
float: right;
|
||||
}
|
||||
|
||||
&.restart {
|
||||
background-color: #eaf2ff;
|
||||
color: #06f;
|
||||
}
|
||||
&.restart {
|
||||
background-color: #eaf2ff;
|
||||
color: #06f;
|
||||
}
|
||||
|
||||
.icon-iamge {
|
||||
width: 25rpx;
|
||||
height: 25rpx;
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
margin-top: -12rpx;
|
||||
}
|
||||
}
|
||||
.icon-iamge {
|
||||
width: 25rpx;
|
||||
height: 25rpx;
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
margin-top: -12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.replay-learn-btn {
|
||||
float: left;
|
||||
padding: 0 60rpx 0 20rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
font-size: 30rpx;
|
||||
color: #06f;
|
||||
background-color: #eaf2ff;
|
||||
border-radius: 8rpx;
|
||||
margin-top: 16rpx;
|
||||
margin-right: 16rpx;
|
||||
position: relative;
|
||||
.replay-learn-btn {
|
||||
float: left;
|
||||
padding: 0 60rpx 0 20rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
font-size: 30rpx;
|
||||
color: #06f;
|
||||
background-color: #eaf2ff;
|
||||
border-radius: 8rpx;
|
||||
margin-top: 16rpx;
|
||||
margin-right: 16rpx;
|
||||
position: relative;
|
||||
|
||||
.icon-iamge {
|
||||
width: 25rpx;
|
||||
height: 25rpx;
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
margin-top: -12rpx;
|
||||
}
|
||||
}
|
||||
.icon-iamge {
|
||||
width: 25rpx;
|
||||
height: 25rpx;
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
margin-top: -12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.next-btn {
|
||||
float: right;
|
||||
padding: 0 60rpx 0 20rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
font-size: 30rpx;
|
||||
background-color: #06f;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
margin-top: 16rpx;
|
||||
position: relative;
|
||||
.next-btn {
|
||||
float: right;
|
||||
padding: 0 60rpx 0 20rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
font-size: 30rpx;
|
||||
background-color: #06f;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
margin-top: 16rpx;
|
||||
position: relative;
|
||||
|
||||
.icon-iamge {
|
||||
width: 25rpx;
|
||||
height: 25rpx;
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
margin-top: -12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.link-cont-desc{
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
.link-cont {
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 28rpx;
|
||||
.link-info {
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
overflow: hidden;
|
||||
margin-bottom: 12rpx;
|
||||
.icon-iamge {
|
||||
width: 25rpx;
|
||||
height: 25rpx;
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
margin-top: -12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.link-cont-desc {
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
.link-cont {
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 28rpx;
|
||||
.link-info {
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
overflow: hidden;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.info-text {
|
||||
float: left;
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
.info-text {
|
||||
float: left;
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
&::after {
|
||||
transition: all 0.3s ease-in-out;
|
||||
content: '';
|
||||
display: block;
|
||||
float: left;
|
||||
position: relative;
|
||||
top: 12rpx;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-right: 4rpx solid #333;
|
||||
border-bottom: 4rpx solid #333;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
}
|
||||
&::after {
|
||||
transition: all 0.3s ease-in-out;
|
||||
content: '';
|
||||
display: block;
|
||||
float: left;
|
||||
position: relative;
|
||||
top: 12rpx;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-right: 4rpx solid #333;
|
||||
border-bottom: 4rpx solid #333;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
.link-info {
|
||||
&::after {
|
||||
top: 18rpx;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
&.is-active {
|
||||
.link-info {
|
||||
&::after {
|
||||
top: 18rpx;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.link-items {
|
||||
max-height: 600rpx;
|
||||
}
|
||||
}
|
||||
.link-items {
|
||||
max-height: 600rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.link-items {
|
||||
max-height: 0rpx;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-in-out;
|
||||
.link-items {
|
||||
max-height: 0rpx;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-in-out;
|
||||
|
||||
.link-item {
|
||||
line-height: 48rpx;
|
||||
color: #06f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.link-item {
|
||||
line-height: 48rpx;
|
||||
color: #06f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+385
-366
File diff suppressed because it is too large
Load Diff
@@ -49,16 +49,18 @@ export default {
|
||||
wv.setStyle({
|
||||
top: 80
|
||||
})
|
||||
// uni.onKeyboardHeightChange(setHeight)
|
||||
uni.onKeyboardHeightChange(setHeight)
|
||||
}, 1000)
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
// setHeight(res){
|
||||
// wv.setStyle({
|
||||
// top: res.height + 20
|
||||
// })
|
||||
// },
|
||||
setHeight(res){
|
||||
//#ifdef APP-PLUS
|
||||
wv.setStyle({
|
||||
top: res.height + 20
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
backRouter() {
|
||||
common.navigateBack()
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user