修改包名

master
hs 2026-03-06 16:15:20 +08:00
parent 0b5f169a91
commit 96c52d91bb
18 changed files with 474 additions and 327 deletions

View File

@ -14,7 +14,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
namespace = "com.company.myapp2"
namespace = "com.qysz.qgxgf"
compileSdk = flutter.compileSdkVersion
ndkVersion = "28.1.13356709"
@ -30,7 +30,7 @@ android {
}
defaultConfig {
applicationId = "com.company.myapp2"
applicationId = "com.qysz.qgxgf"
minSdk = 24
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode

View File

@ -104,7 +104,7 @@
<!-- FileProvider 配置 -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.company.myapp2.fileprovider"
android:authorities="com.qysz.qgxgf.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@ -1,123 +0,0 @@
package com.company.myapp2
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.core.content.FileProvider
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File
class MainActivity: FlutterActivity() {
private val CHANNEL = "app.install"
private val REQ_INSTALL_UNKNOWN = 9999
// 暂存安装请求(仅在跳转设置并等待返回时使用)
private var pendingApkPath: String? = null
private var pendingResult: MethodChannel.Result? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"installApk" -> {
val path = call.argument<String>("path")
if (path == null) {
result.error("NO_PATH", "no path provided", null)
return@setMethodCallHandler
}
handleInstallRequest(path, result)
}
else -> result.notImplemented()
}
}
}
private fun handleInstallRequest(path: String, result: MethodChannel.Result) {
val file = File(path)
if (!file.exists()) {
result.error("NO_FILE", "file not exist", null)
return
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 8.0+ 需要 app 级别未知来源授权
if (!packageManager.canRequestPackageInstalls()) {
// 存储请求信息以便用户返回后继续
pendingApkPath = path
pendingResult = result
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
Uri.parse("package:$packageName"))
// 使用 startActivityForResult 以便用户返回后可以继续安装
startActivityForResult(intent, REQ_INSTALL_UNKNOWN)
return
}
}
// 已有授权 或 非 8.0+:直接安装
installApkInternal(path, result)
}
// 真正执行安装的函数(假定有权限)
private fun installApkInternal(path: String, result: MethodChannel.Result) {
val file = File(path)
if (!file.exists()) {
result.error("NO_FILE", "file not exist", null)
return
}
try {
val apkUri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", file)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
result.success(true)
} catch (e: Exception) {
result.error("INSTALL_FAILED", e.message, null)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQ_INSTALL_UNKNOWN) {
// 用户从系统设置页返回后,检查是否已授权
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (packageManager.canRequestPackageInstalls()) {
// 授权已开:继续安装
val path = pendingApkPath
val res = pendingResult
// 清理 pending 状态
pendingApkPath = null
pendingResult = null
if (path != null && res != null) {
installApkInternal(path, res)
} else {
// 安全兜底:若没有 pending 数据,通知 caller 重新触发
res?.error("NO_PENDING", "no pending install info", null)
}
} else {
// 用户仍未授权
pendingApkPath = null
pendingResult?.error("NEED_INSTALL_PERMISSION", "user did not allow install unknown apps", null)
pendingResult = null
}
} else {
// API < 26尝试直接安装一次作为尝试某些 ROM 无法精准判断)
val path = pendingApkPath
val res = pendingResult
pendingApkPath = null
pendingResult = null
if (path != null && res != null) {
installApkInternal(path, res)
} else {
res?.error("NO_PENDING", "no pending install info", null)
}
}
}
}
}

View File

@ -1,4 +1,4 @@
package com.company.myapp2
package com.qysz.qgxgf
import android.app.Application
import android.content.Context

View File

@ -80,7 +80,7 @@ PODS:
- Flutter
- permission_handler_apple (9.3.0):
- Flutter
- photo_manager (3.8.0):
- photo_manager (3.9.0):
- Flutter
- FlutterMacOS
- SDWebImage (5.21.1):
@ -216,7 +216,7 @@ SPEC CHECKSUMS:
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
pdfx: 77f4dddc48361fbb01486fa2bdee4532cbb97ef3
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
photo_manager: 343d78032bf7ebe944d2ab9702204dc2eda07338
photo_manager: 25fd77df14f4f0ba5ef99e2c61814dde77e2bceb
SDWebImage: f29024626962457f3470184232766516dee8dfea
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4

View File

@ -498,17 +498,17 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8AKCJ9LW7D;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "秦港安全";
INFOPLIST_KEY_CFBundleDisplayName = "秦港相关方";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.2.3;
PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qgxgf-dev";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@ -525,7 +525,7 @@
DEVELOPMENT_TEAM = 8AKCJ9LW7D;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -544,7 +544,7 @@
DEVELOPMENT_TEAM = 8AKCJ9LW7D;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@ -561,7 +561,7 @@
DEVELOPMENT_TEAM = 8AKCJ9LW7D;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@ -694,17 +694,17 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8AKCJ9LW7D;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "秦港安全";
INFOPLIST_KEY_CFBundleDisplayName = "秦港相关方";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.2.3;
PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qgxgf-dev";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -720,24 +720,24 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Qinhuangdao Zhuoyun Technology Co., Ltd (8AKCJ9LW7D)";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 62;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8AKCJ9LW7D;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "秦港安全";
INFOPLIST_KEY_CFBundleDisplayName = "秦港相关方";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.2.3;
PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qgxgf-des";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";

View File

@ -5,7 +5,7 @@
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<string>秦港相关方</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>秦港双控</string>
<string>秦港相关方</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@ -28,8 +28,6 @@
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NFCReaderUsageDescription</key>
<string>需要NFC权限来读取和写入标签</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

View File

@ -1,10 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
</dict>
<dict/>
</plist>

View File

@ -2,61 +2,107 @@
import 'dart:convert';
class RouteModel {
final String target;
final List<RouteModel> children;
final bool hasMenu;
//
final String id;
final String menuName; // title
final String menuUrl; // path
final String parentId;
final String routeId;
final String component;
final String path;
final String title;
final String parentIds;
final String meta;
final String routeOrder;
final String menuPerms;
final int menuType; // 1/2 ...
final String menuAttribution;
final int sort;
final int showFlag; // 1 0
final Map<String, dynamic> extValues;
final List<RouteModel> children;
RouteModel({
required this.target,
required this.children,
required this.hasMenu,
required this.id,
required this.menuName,
required this.menuUrl,
required this.parentId,
required this.routeId,
required this.component,
required this.title,
required this.path,
required this.parentIds,
required this.meta,
required this.routeOrder,
required this.menuPerms,
required this.menuType,
required this.menuAttribution,
required this.sort,
required this.showFlag,
required this.extValues,
required this.children,
});
factory RouteModel.fromJson(Map<String, dynamic> json) {
//
String _s(dynamic v) => v == null ? '' : v.toString();
int _i(dynamic v) {
if (v == null) return 0;
if (v is int) return v;
return int.tryParse(v.toString()) ?? 0;
}
final rawChildren = json['children'];
final children = <RouteModel>[];
if (rawChildren is List) {
for (final c in rawChildren) {
if (c is Map<String, dynamic>) {
children.add(RouteModel.fromJson(c));
} else if (c is Map) {
children.add(RouteModel.fromJson(Map<String, dynamic>.from(c)));
}
}
}
// extValues
Map<String, dynamic> ext = {};
if (json['extValues'] is Map) {
ext = Map<String, dynamic>.from(json['extValues']);
}
return RouteModel(
target: json['target'] ?? '',
children: (json['children'] as List<dynamic>? ?? [])
.map((child) => RouteModel.fromJson(child))
.toList(),
hasMenu: json['hasMenu'] ?? false,
parentId: json['parent_ID'] ?? '',
routeId: json['route_ID'] ?? '',
component: json['component'] ?? '',
parentIds: json['parent_IDS'] ?? '',
meta: json['meta'] ?? '',
path: json['path'] ?? '',
title: json['path'] ?? '',
routeOrder: json['route_ORDER'] ?? '0',
id: _s(json['id']),
menuName: _s(json['menuName']),
menuUrl: _s(json['menuUrl']),
parentId: _s(json['parentId']),
parentIds: _s(json['parentIds']),
menuPerms: _s(json['menuPerms']),
menuType: _i(json['menuType']),
menuAttribution: _s(json['menuAttribution']),
sort: _i(json['sort']),
showFlag: _i(json['showFlag']),
extValues: ext,
children: children,
);
}
// // metatitle
// String get title {
// if (meta.isEmpty) return '';
// try {
// final metaMap = jsonDecode(meta) as Map<String, dynamic>;
// return metaMap['title'] ?? '';
// } catch (e) {
// return '';
// }
// }
Map<String, dynamic> toJson() => {
'id': id,
'menuName': menuName,
'menuUrl': menuUrl,
'parentId': parentId,
'parentIds': parentIds,
'menuPerms': menuPerms,
'menuType': menuType,
'menuAttribution': menuAttribution,
'sort': sort,
'showFlag': showFlag,
'extValues': extValues,
'children': children.map((c) => c.toJson()).toList(),
};
//
///
bool get visible => showFlag == 1;
/// menuType /
bool get isMenu => menuType == 2;
///
bool get isLeaf => children.isEmpty;
/// menuName extValues title 使
String get title {
if (menuName.isNotEmpty) return menuName;
if (extValues.containsKey('title')) return extValues['title']?.toString() ?? '';
return '';
}
}

View File

@ -1,34 +1,87 @@
// route_service.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:qhd_prevention/common/route_model.dart';
///
class RouteService {
import 'package:qhd_prevention/tools/tools.dart';
class RouteService extends ChangeNotifier {
static final RouteService _instance = RouteService._internal();
factory RouteService() => _instance;
RouteService._internal();
//
//
List<RouteModel> _allRoutes = [];
// Tabchildren
List<RouteModel> get mainTabs => _allRoutes.isNotEmpty
? _allRoutes.first.children
: [];
/// routes
List<RouteModel> get allRoutes => _allRoutes;
//
void initializeRoutes(List<dynamic> routeList) {
_allRoutes = routeList.map((route) => RouteModel.fromJson(route)).toList();
/// null
void initializeRoutes(List<dynamic>? routeList) {
_allRoutes = [];
if (routeList == null) return;
for (final item in routeList) {
try {
if (item is Map<String, dynamic>) {
_allRoutes.add(RouteModel.fromJson(item));
} else if (item is Map) {
_allRoutes.add(RouteModel.fromJson(Map<String, dynamic>.from(item)));
}
} catch (e) {
debugPrint('RouteService: parse route item failed: $e');
}
}
//
// sort
try {
_allRoutes.sort((a, b) => a.sort.compareTo(b.sort));
for (final r in _allRoutes) {
_sortRecursive(r);
}
} catch (_) {}
notifyListeners();
}
void _sortRecursive(RouteModel node) {
try {
node.children.sort((a, b) => a.sort.compareTo(b.sort));
for (final c in node.children) {
_sortRecursive(c);
}
} catch (_) {}
}
/// parentId == '0' parentId Tab visible
List<RouteModel> get mainTabs {
final tabs = _allRoutes.where((m) {
final isTop = m.parentId == '0' || m.parentId.isEmpty;
return isTop && m.visible; //
}).toList();
try {
tabs.sort((a, b) => a.sort.compareTo(b.sort));
} catch (_) {}
return tabs;
}
// menuUrl
RouteModel? findRouteByPath(String path) {
if (path.isEmpty) return null;
final needle = path.trim();
for (final route in _allRoutes) {
final found = _findRouteRecursive(route, path);
final found = _findRouteRecursive(route, needle);
if (found != null) return found;
}
return null;
}
RouteModel? _findRouteRecursive(RouteModel route, String path) {
if (route.path == path) return route;
// null
if (!route.visible) return null;
final routeUrl = route.menuUrl.trim();
if (routeUrl == path) return route;
for (final child in route.children) {
final found = _findRouteRecursive(child, path);
if (found != null) return found;
@ -36,23 +89,178 @@ class RouteService {
return null;
}
// TabhasMenutrue
// Tabvisible == true
List<RouteModel> getRoutesForTab(RouteModel tab) {
final routes = <RouteModel>[];
_collectLeafRoutes(tab, routes);
_collectVisibleLeafRoutes(tab, routes);
return routes;
}
void _collectLeafRoutes(RouteModel route, List<RouteModel> collector) {
if (route.hasMenu) {
/// children
void _collectVisibleLeafRoutes(RouteModel route, List<RouteModel> collector) {
if (!route.visible) return; //
if (route.isLeaf) {
collector.add(route);
if (!route.isLeaf) {
return;
}
for (final child in route.children) {
_collectLeafRoutes(child, collector);
}
}
_collectVisibleLeafRoutes(child, collector);
}
}
// --------------------- ---------------------
/// menuPerms == perm
///
bool hasPerm(String perm) {
if (perm.isEmpty) return false;
final needle = perm.trim();
bool found = false;
void visit(RouteModel m) {
if (found) return;
//
if (!m.visible) return;
final mp = (m.menuPerms ?? '').trim();
if (mp.isNotEmpty && mp == needle) {
found = true;
return;
}
for (final c in m.children) {
visit(c);
if (found) return;
}
}
for (final top in _allRoutes) {
visit(top);
if (found) break;
}
return found;
}
bool hasAnyPerms(List<String> perms) {
for (final p in perms) {
if (hasPerm(p)) return true;
}
return false;
}
Map<String, bool> permsMap(List<String> perms) {
final Map<String, bool> map = {};
for (final p in perms) {
map[p] = hasPerm(p);
}
return map;
}
/// menuPerms RouteModel
///
RouteModel? findRouteByPerm(String perm) {
if (perm.isEmpty) return null;
final needle = perm.trim();
RouteModel? result;
void visit(RouteModel m) {
// printLongString(json.encode(m.toJson()));
if (result != null) return;
if (!m.visible) return; //
final mp = (m.menuPerms ?? '').trim();
if (mp.isNotEmpty && mp == needle) {
result = m;
return;
}
for (final c in m.children) {
visit(c);
if (result != null) return;
}
}
for (final top in _allRoutes) {
visit(top);
if (result != null) break;
}
return result;
}
/// menuPerms
List<String> collectAllPerms() {
final List<String> perms = [];
void visit(RouteModel m) {
if (!m.visible) return; //
final mp = (m.menuPerms ?? '').trim();
if (mp.isNotEmpty) perms.add(mp);
for (final c in m.children) visit(c);
}
for (final top in _allRoutes) visit(top);
return perms;
}
///
static Future<String> getMenuPath(parentPerm,targetPerm) async {
try {
final routeService = RouteService();
// ''children
if (targetPerm.isEmpty) {
final route = routeService.findRouteByPerm(parentPerm);
if (route != null) {
// menuUrl
final childUrl = findFirstVisibleChildUrl(route);
if (childUrl.isNotEmpty) return childUrl;
return '';
}
}
//branchCompany-plan-execute-inspection-records
RouteModel? parent = routeService.findRouteByPerm(parentPerm);
if (parent != null) {
// parent targetPerm
final RouteModel? foundInParent = _findRouteInSubtreeByPerm(parent, targetPerm);
if (foundInParent != null && foundInParent.menuUrl.trim().isNotEmpty) {
return foundInParent.menuUrl.trim();
}
}
// ->
return '';
} catch (e, st) {
debugPrint('_getMenuPath error: $e\n$st');
return '';
}
}
/// menuPerm
static RouteModel? _findRouteInSubtreeByPerm(RouteModel node, String perm) {
if (node.menuPerms.trim() == perm && node.visible) return node;
for (final c in node.children) {
final res = _findRouteInSubtreeByPerm(c, perm);
if (res != null) return res;
}
return null;
}
/// menuPerm
RouteModel? _findRouteInAllByPerm(List<RouteModel> roots, String perm) {
for (final r in roots) {
final res = _findRouteInSubtreeByPerm(r, perm);
if (res != null) return res;
}
return null;
}
/// node visible menuUrl
///
static String findFirstVisibleChildUrl(RouteModel node) {
final children = node.children;
if (children == null || children.isEmpty) return '';
for (final c in children) {
// menuUrl
if ((c.showFlag == 1) && (c.menuUrl ?? '').isNotEmpty) {
return c.menuUrl;
}
// return '';
}
return '';
}
}

View File

@ -104,6 +104,9 @@ class MediaPickerRow extends StatefulWidget {
/// 4/ 1
final int crossAxisCount;
/// 1 2 3
final int selectPictureType;
const MediaPickerRow({
Key? key,
this.maxCount = 4,
@ -118,6 +121,7 @@ class MediaPickerRow extends StatefulWidget {
this.isCamera = false,
this.followInitialUpdates = false, // false
this.crossAxisCount = 4, // 4
this.selectPictureType = 3,
}) : super(key: key);
@override
@ -263,6 +267,7 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
builder: (_) => SafeArea(
child: Wrap(
children: [
if(widget.selectPictureType==3||widget.selectPictureType==1)
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam),
@ -272,6 +277,7 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
_pickCamera();
},
),
if(widget.selectPictureType==3||widget.selectPictureType==2)
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library),
@ -644,6 +650,8 @@ class RepairedPhotoSection extends StatefulWidget {
final bool inlineSingle;
/// inlineSingle true px
final double inlineImageWidth;
/// 1 2 3
final int selectPictureType;
const RepairedPhotoSection({
Key? key,
@ -668,6 +676,7 @@ class RepairedPhotoSection extends StatefulWidget {
this.sectionKey = kAcceptVideoSectionKey,
this.inlineSingle = false,
this.inlineImageWidth = 88.0,
this.selectPictureType = 3,
}) : super(key: key);
@override
@ -802,6 +811,7 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
builder: (ctx) => SafeArea(
child: Wrap(
children: [
if(widget.selectPictureType==3||widget.selectPictureType==1)
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam),
@ -837,6 +847,7 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
}
},
),
if(widget.selectPictureType==3||widget.selectPictureType==2)
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library),
@ -924,7 +935,8 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding),
child: ListItemFactory.createRowSpaceBetweenItem(
leftText: widget.title,
rightText: widget.isShowNum ? '${_mediaPaths.length}/${widget.maxCount}' : '',
// rightText: widget.isShowNum ? '${_mediaPaths.length}/${widget.maxCount}' : '',
rightText: widget.isShowNum ? '${_getCurrentCount()}/${widget.maxCount}' : '',
isRequired: widget.isRequired,
),
),
@ -937,6 +949,7 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
initialMediaPaths: _mediaPaths,
onMediaRemovedForIndex: widget.onMediaRemovedForIndex,
isCamera: widget.isCamera,
selectPictureType:widget.selectPictureType,
onChanged: (files) {
final newPaths = files.map((f) => f.path).toList();
setState(() {
@ -988,5 +1001,17 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
),
);
}
int _getCurrentCount() {
// followInitialUpdates true使
// false使
if (widget.followInitialUpdates) {
return _mediaPaths.length;
} else {
return widget.initialMediaPaths?.length ?? _mediaPaths.length;
}
}
}

View File

@ -0,0 +1,17 @@
import 'package:dio/dio.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/http/HttpManager.dart';
import 'package:qhd_prevention/services/SessionService.dart';
class AppMenuApi {
static Future<Map<String, dynamic>> getAppMenu() async {
return HttpManager().request(
ApiService.basePath,
'/appmenu/appMenu/appListTree',
method: Method.get,
data: {
'menuAttribution': 'QINGANG_RELATED_PARTIES',
},
);
}
}

View File

@ -221,17 +221,3 @@ class CertificateApi {
}
}
//
class TodoApi {
static Future<Map<String, dynamic>> getTodoList(Map data) {
return HttpManager().request(
ApiService.basePath + '/appmenu',
'/todoList/list',
method: Method.post,
data: {
'eqFlag' : 1,
...data
},
);
}
}

View File

@ -242,7 +242,9 @@ class _StudyTakeExamPageState extends State<StudyTakeExamPage> {
if (res['success']) {
final data = res['data'] as Map<String, dynamic>? ?? {};
final score = data['examScore'] ?? 0;
final passed = data['result'] == 1;
var passed = data['result'] == 1;
//
final remain = data['surplusExamNum'] ?? 0 <= 0;
// /
final result = await CustomAlertDialog.showConfirm(
@ -251,7 +253,7 @@ class _StudyTakeExamPageState extends State<StudyTakeExamPage> {
content: passed
? '您的成绩为 $score 分,恭喜您通过本次考试,请继续保持!'
: '您的成绩为 $score 分,很遗憾您没有通过本次考试,请再接再厉!',
cancelText: passed ? '' : '继续考试',
cancelText: remain ? '' : '继续考试',
confirmText: '确定',
);

View File

@ -174,7 +174,6 @@ class HomePageState extends RouteAwareState<HomePage>
// PageController
_notifPageController = PageController(initialPage: 0);
_getToDoWorkList();
// 3
_notifTimer = Timer.periodic(const Duration(seconds: 3), (timer) {
@ -203,13 +202,7 @@ class HomePageState extends RouteAwareState<HomePage>
});
}
//
void _getToDoWorkList() async {
final result = await TodoApi.getTodoList({});
setState(() {
totalList = result['data'];
});
}
///
Future<void> _getNeedSafetyCommitment() async {
if (_isShowCheckLogin) {

View File

@ -443,7 +443,7 @@ class _CertificateDetailPageState extends State<CertificateDetailPage> {
],
ItemListWidget.selectableLineTitleTextRightButton(
label: '证书作业类型:',
label: '证书类型:',
isEditable: widget.model == CertifitcateEditMode.add,
text: pd['typeName'] ?? '请选择',
isRequired: widget.model == CertifitcateEditMode.add,

View File

@ -953,10 +953,10 @@ packages:
dependency: "direct main"
description:
name: photo_manager
sha256: "99355f3b3591a00416cc787bbf7f04510f672d602814e0063bf4dc40603041f0"
sha256: fb3bc8ea653370f88742b3baa304700107c83d12748aa58b2b9f2ed3ef15e6c2
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "3.8.0"
version: "3.9.0"
photo_manager_image_provider:
dependency: transitive
description:

View File

@ -69,7 +69,7 @@ dependencies:
# 相册
image_picker: ^1.1.2
wechat_assets_picker: ^9.5.1
photo_manager: ^3.7.1
photo_manager: ^3.9.0
file_picker: ^10.3.2
# 日历
table_calendar: ^3.2.0