docs: 添加涉密文件自检工具实施计划

This commit is contained in:
2026-06-08 13:53:24 +08:00
commit 31161d9a5f
1838 changed files with 455407 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
// =========================================
// =============== 全屏遮罩层 ===============
// =========================================
import QtQuick 2.15
import QtQuick.Controls 2.15
import "../Widgets"
Popup {
// 展示遮罩层。同一个id重复调用时可刷新显示文本。
function showMask(msg="", id="") {
if(msgDict === undefined)
msgDict = {}
if(!(id in msgDict)) // 新加入的id
idList.push(id)
msgDict[id] = msg // 刷新消息字典
message = msg // 刷新消息显示
visible = true // 开启显示状态
}
// 隐藏指定id的遮罩层
function hideMask(id="") {
if(id in msgDict) { // id已记录则移除对应项
delete msgDict[id]
const index = idList.indexOf(id)
if(index !== -1)
idList.splice(index, 1)
}
// 不存在激活的id时解除显示状态
if(idList.length === 0) {
visible = false
}
// 否则寻找上一个激活的id刷新显示文本
else {
const lastID = idList[idList.length - 1]
const msg = msgDict[lastID] || ""
message = msg
}
}
property string message: ""
property var idList: [] // 存放所有已显示的id有序
property var msgDict: {} // key为idvalue为消息文本
visible: false
modal: true // 阻止按键穿透
closePolicy: Popup.NoAutoClose
parent: Overlay.overlay
anchors.centerIn: parent
property int maxWidth: parent.width - size_.line*10
width: text.width + size_.spacing*2
height: text.height + size_.spacing*2
// 背景
background: Rectangle {
visible: message!==""
color: theme.bgColor
radius: size_.panelRadius
Text_ {
id: text
anchors.centerIn: parent
text: message
width: textMetrics.width > maxWidth ? maxWidth : implicitWidth // 宽度根据内容自适应,不超过 maxWidth
wrapMode: textMetrics.width > maxWidth ? Text.Wrap : Text.NoWrap
}
}
// 文本宽度测量器,不可见
Text_ { // TextMetrics不准确只能用普通Text
id: textMetrics
visible: false
text: message
}
}

View File

@@ -0,0 +1,204 @@
// ==============================================
// =============== 带确认的消息盒子 ===============
// ==============================================
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15 // 阴影
import "../Widgets"
Rectangle {
id: msgRoot
// ========================= 【参数】 =========================
// 常规参数
property string title: "" // 标题
property string msg: "" // 内容
property var onClosed: undefined // 关闭函数,外部传入
property string type: "" // 可选:""默认, "warning"警告, "error"错误
// 定制参数
property int shadowWidth: qmlapp.enabledEffect ? size_.spacing*3 : 0 // 边缘阴影宽度
property string icon: "bell" // 图标
property string iconColorKey: "specialTextColor" // 图标前景颜色
property string iconBgColorKey: "coverColor1" // 图标背景颜色
property var btnsList: [ // 按钮列表
// text 显示文本, value 点击返回的值, textColor 文本颜色, bgColor 背景颜色
{"text":qsTr("取消"), "value": false, "textColor": theme.subTextColor, "bgColor": theme.bgColor},
{"text":qsTr("确定"), "value": true, "textColor": theme.specialTextColor, "bgColor": theme.specialBgColor},
]
// 自动类型参数
Component.onCompleted: {
switch(type) {
case "warning":
icon = "warning"
iconColorKey = "noColor"
if(title==="")
title=qsTr("警告")
break
case "error":
icon = "no"
iconColorKey = "noColor"
if(title==="")
title=qsTr("发生了一点小问题")
break
}
}
// ===========================================================
width: size_.line * 25
height: childrenRect.height
color: theme.bgColor
radius: qmlapp.enabledEffect ? size_.panelRadius : 0
// 列布局
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: size_.spacing
// 顶部占位
Item {
width: 1
height: 1
}
// 图标
Rectangle {
width: size_.line*3
height: size_.line*3
anchors.horizontalCenter: parent.horizontalCenter
color: theme[iconBgColorKey]
radius: 99999
Icon_ {
width: parent.height*0.6
height: parent.height*0.6
anchors.centerIn: parent
color: theme[iconColorKey]
icon: msgRoot.icon
}
}
// 标题
Text_ {
id: textTitle
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: size_.largeText
visible: title!==""
text: title
}
// 内容
Text_ {
id: textMsg
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: size_.line
wrapMode: TextEdit.Wrap // 尽量在单词边界处换行
horizontalAlignment: Text.AlignHCenter // 水平居中
visible: msg!==""
text: msg
}
// 下层小按钮
Item {
anchors.left: parent.left
anchors.right: parent.right
height: type==="error"?size_.line:1
// 错误
Row {
visible: type==="error"
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: size_.spacing
Button_ {
anchors.top: parent.top
anchors.bottom: parent.bottom
text_: qsTr("复制")
textSize: size_.smallText
textColor_: theme.subTextColor
onClicked: {
qmlapp.utilsConnector.copyText(msg)
qmlapp.popup.simple(qsTr("已复制报错信息 %1").arg(msg.length), qsTr("请前往 Issues 页面寻找解答或反馈"))
}
}
Button_ {
anchors.top: parent.top
anchors.bottom: parent.bottom
property string issueUrl: UmiAbout.url.newIssue
text_: qsTr("问题反馈")
toolTip: issueUrl
textSize: size_.smallText
textColor_: theme.subTextColor
onClicked: {
Qt.openUrlExternally(issueUrl)
}
}
}
}
// 底部按钮
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: size_.line*2+size_.spacing*2
color: theme.coverColor1
Row {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.margins: size_.spacing
property real lrMargin: size_.spacing*4
spacing: size_.spacing
property int childrenWidth: 10
Component.onCompleted: { // 计算子级宽度
const n = btnsList.length
if(n==1){ // 单个按钮,宽度为一半
width = parent.width / 2
childrenWidth = width
}
else { // 多个按钮,宽度平均分配
width = parent.width - lrMargin
childrenWidth = (width - (n - 1) * spacing) / n
}
}
Repeater {
model: btnsList
Button_ {
property var info: btnsList[index]
anchors.top: parent.top
anchors.bottom: parent.bottom
width: parent.childrenWidth
text_: info.text
bgColor_: info.bgColor?info.bgColor:theme.bgColor
textColor_: info.textColor?info.textColor:theme.subTextColor
onClicked: {
if(typeof onClosed === "function")
onClosed(info.value) // 调用关闭函数
}
}
}
}
}
// 内圆角裁切
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: msgRoot.width
height: msgRoot.height
radius: msgRoot.radius
}
}
}
// 边缘阴影
layer.enabled: shadowWidth>0
layer.effect: DropShadow {
transparentBorder: true
color: theme.coverColor4
samples: shadowWidth
}
}

View File

@@ -0,0 +1,271 @@
// ==================================================
// =============== 外部通知弹窗 ===============
// ==================================================
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import "../Widgets"
Item {
id: messageRoot
// ========================= 【对外接口】 =========================
// 显示一个消息
function showMessage(title, msg, type) {
const argd = {
title:title, msg:msg, type:type
}
createWin(winMsg, argd)
}
// 带“不再提示”的消息
// mid: 唯一标识一个提示
function showMessageMemory(mid, title, msg, type) {
// 检查全局设置记忆列表中是否已记录mid
const midList = qmlapp.globalConfigs.getValue("window.messageMemory")
if(midList.includes(mid)) // 已记录则不再弹出
return
const argd = {
mid: mid, title:title, msg:msg, type:type
}
createWin(winMsgMemory, argd)
}
// 显示一个双选项对话窗
function showDialog(title, msg, callback, yesText, noText, type) {
const argd = {
title:title, msg:msg, callback:callback, yesText:yesText, noText:noText, type:type
}
createWin(winDialog, argd)
}
// 显示一个双选项带倒计时对话窗
function showDialogCountdown(title, msg, callback, yesText, noText, type, time) {
const argd = {
title:title, msg:msg, callback:callback, yesText:yesText, noText:noText, type:type, time:time
}
createWin(winDialogCountdown, argd)
}
// ========================= 【弹窗】 =========================
// 主按钮颜色
function getYesColor(type) {
switch(type) {
case "warning":
case "error":
return theme.noColor
default:
return theme.specialTextColor
}
}
property var winDict: {}
// 生成一个弹窗返回生成ID
function createWin(winComponent, argd) {
// 初始化字典
if(winDict===undefined) winDict={}
// 生成一个id
const winId = (Date.now()+Math.random()).toString()
argd.winId = winId // 添加id
// 生成组件,计入字典
const obj = winComponent.createObject(this, argd)
winDict[winId] = obj
// 显示遮罩层
qmlapp.popup.showMask("", winId)
}
// 关闭一个弹窗传入生成ID
function close(winId) {
if(winDict.hasOwnProperty(winId)) {
winDict[winId].destroy()
}
// 隐藏遮罩层
qmlapp.popup.hideMask(winId)
}
// 只有单个确认键的普通消息
Component {
id: winMsg
FramelessWindow {
id: win
property string title: ""
property string msg: ""
property string type: ""
property string winId: ""
visible: true
width: msgComp.width+msgComp.shadowWidth
height: msgComp.height+msgComp.shadowWidth
color: "#00000000"
// 消息盒组件
MessageBox {
id: msgComp
anchors.centerIn: parent
title: win.title // 标题
msg: win.msg // 内容
type: win.type // 类型
btnsList: [ // 按钮列表
{"text": qsTr("确定"), "textColor": theme.specialTextColor, "bgColor": theme.specialBgColor},
]
onClosed: win.close // 关闭函数
}
function close() {
messageRoot.close(winId)
}
}
}
// 带“不再提示”的记忆消息
Component {
id: winMsgMemory
FramelessWindow {
id: win
property string title: ""
property string msg: ""
property string type: ""
property string winId: ""
property string mid: ""
visible: true
width: msgComp.width+msgComp.shadowWidth
height: msgComp.height+msgComp.shadowWidth
color: "#00000000"
// 消息盒组件
MessageBox {
id: msgComp
anchors.centerIn: parent
title: win.title // 标题
msg: win.msg // 内容
type: win.type // 类型
btnsList: [ // 按钮列表
{"text": qsTr("不再提示"), "value": true, "textColor": theme.specialTextColor, "bgColor": theme.specialBgColor},
{"text": qsTr("知道了"), "value": false, "textColor": theme.subTextColor, "bgColor": theme.bgColor},
]
onClosed: (value)=>{
if(value) {
// 将mid添加到全局设置记忆列表中
let midList = qmlapp.globalConfigs.getValue("window.messageMemory")
midList.push(mid)
qmlapp.globalConfigs.setValue("window.messageMemory", midList)
}
messageRoot.close(winId) // 关闭窗口
}
}
}
}
// 有两个键(确认/取消)的对话框
Component {
id: winDialog
FramelessWindow {
id: win
property string title: ""
property string msg: ""
property string type: ""
property string winId: ""
property string yesText: ""
property string noText: ""
property var callback // 回调函数
visible: true
width: msgComp.width+msgComp.shadowWidth
height: msgComp.height+msgComp.shadowWidth
color: "#00000000"
MessageBox {
id: msgComp
anchors.centerIn: parent
title: win.title // 标题
msg: win.msg // 内容
type: win.type // 类型
btnsList: [ // 按钮列表
// 确认
{"text": yesText, "value": true, "textColor": messageRoot.getYesColor(win.type), "bgColor": theme.specialBgColor},
// 取消
{"text": noText, "value": false, "textColor": theme.subTextColor, "bgColor": theme.bgColor},
]
onClosed: (value)=>{
callback(value)
messageRoot.close(winId) // 关闭窗口
}
}
}
}
// 带倒计时的双键对话框
Component {
id: winDialogCountdown
FramelessWindow {
id: win
property string title: ""
property string msg: ""
property string type: ""
property string winId: ""
property string yesText: ""
property string yesTextTime: "" // 带倒计时的确定文本
property string noText: ""
property int time: 10000
property int nowTime: 0
property int interval: 1000
property var callback // 回调函数
visible: true
width: msgComp.width+msgComp.shadowWidth
height: msgComp.height+msgComp.shadowWidth
color: "#00000000"
Component.onCompleted: {
win.yesTextTime = win.yesText+` (${win.time*0.001})`
win.nowTime = win.time
timer.running = true
}
Timer {
id: timer
interval: win.interval // 间隔
running: false
repeat: true // 重复执行
onTriggered: {
win.nowTime -= win.interval
win.yesTextTime = win.yesText+` (${win.nowTime*0.001})`
if(win.nowTime<=0) {
timer.stop() // 停止计时器
callback(true) // 回调
messageRoot.close(winId) // 关闭窗口
return
}
}
}
MessageBox {
id: msgComp
anchors.centerIn: parent
title: win.title // 标题
msg: win.msg // 内容
type: win.type // 类型
btnsList: [ // 按钮列表
// 确认
{"text": yesTextTime, "value": true, "textColor": messageRoot.getYesColor(win.type), "bgColor": theme.specialBgColor},
// 取消
{"text": noText, "value": false, "textColor": theme.subTextColor, "bgColor": theme.bgColor},
]
onClosed: (value)=>{
timer.running = false // 停止计时器
callback(value)
messageRoot.close(winId) // 关闭窗口
}
}
}
}
}

View File

@@ -0,0 +1,186 @@
// ==================================================
// =============== 简单通知弹窗 ===============
// =============== 无确认,计时自动消失 ===============
// ==================================================
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15 // 阴影
import QtQuick.Window 2.15
import "../Widgets"
Item {
// ========================= 【对外接口】 =========================
function show(title, msg, showType) {
if(showType=="default") {
showType = qmlapp.globalConfigs.getValue("window.simpleNotificationType")
}
if(showType=="none") {
return // 不发送
}
const time = 3000
// 强制发送外部
if(showType=="onlyOutside") {
notificationWindow.show(title, msg, time)
}
// 主窗口可见,发送内部
else if(qmlapp.mainWin.getVisibility()) {
if(showType=="inside" || showType=="onlyInside") {
notificationPopup.show(title, msg, time)
}
}
// 主窗口不可见,发送外部
else {
if(showType=="inside") {
// 发送外部通知
notificationWindow.show(title, msg, time)
}
}
}
// ========================= 【内部模式】 =========================
Popup {
id: notificationPopup
// 显示通知弹窗
function show(title, msg, time=3000) {
if(opened) { // 已打开,则先关闭
close()
}
nscPopup.show(title, msg, time) // 传入信息
open() // 开始
if(!qmlapp.enabledEffect) { // 无动画时,瞬间出现
nscPopup.y = showY
}
}
// 隐藏通知弹窗
function hide() {
close()
}
// 属性
padding: 0
property real showY: -nscPopup.height*1.5
modal: false // 非模态层(不阻挡下方)
parent: Overlay.overlay
x: Math.round((parent.width - width) / 2)
y: Math.round((parent.height - height))
closePolicy: Popup.NoAutoClose // 不被系统关闭
// 组件
MessageSimpleComp {
id: nscPopup
onHided: notificationPopup.hide // 关闭事件
anchors.horizontalCenter: parent.horizontalCenter
y: notificationPopup.showY
}
// 进入动画
enter: qmlapp.enabledEffect ? enterAnimePop : null
Transition {
id: enterAnimePop
NumberAnimation {
target: nscPopup
property: "y"
duration: 200
from: 0
to: notificationPopup.showY
easing.type: Easing.OutCubic
}
}
// 关闭动画
exit: qmlapp.enabledEffect ? exitAnimePop : null
Transition {
id: exitAnimePop
NumberAnimation {
target: nscPopup
property: "y"
duration: 200
from: notificationPopup.showY
to: 0
easing.type: Easing.InCubic
}
}
}
// ========================= 【外部模式】 =========================
Window {
id: notificationWindow
/* TODO
当:主窗口初始隐藏,弹出通知弹窗,隐藏通知弹窗,恢复主窗口。会报错:
Conflicting properties 'visible' and 'visibility' for Window 'notificationWindow'
似乎不影响使用,待调查。
*/
// 显示通知弹窗
function show(title, msg, time) {
let screenWidth = Screen.width
let screenHeight = Screen.height
x = (screenWidth - width) / 2 // 水平居中
nscWindow.show(title, msg, time) // 传入信息
if(qmlapp.enabledEffect) { // 出现动画
enterAnimeWin.start()
}
else { // 无动画时y瞬间出现
y = screenHeight-showY
}
visible = true
visibility = Window.Windowed
}
// 隐藏通知弹窗
function hide() {
if(qmlapp.enabledEffect) { // 关闭动画
exitAnimeWin.start()
}
else { // 无动画时,瞬间关闭
visible = false
visibility = Window.Hidden
}
}
// 属性
property real showY: nscWindow.height+100 // 显示位置高度
visible: false
flags: Qt.Popup | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnTopHint // 弹出式,无阴影,置顶
color: "#00000000"
width: nscWindow.width+nscWindow.shadowWidth // 长宽要加上阴影宽度
height: nscWindow.height+nscWindow.shadowWidth
Component.onCompleted: y=Screen.height-showY // 初始y值防止theme未加载导致第一次调用异常
MessageSimpleComp {
id: nscWindow
anchors.centerIn: parent
onHided: notificationWindow.hide // 关闭事件
}
// 进入动画
ParallelAnimation {
id: enterAnimeWin
running: false
NumberAnimation {
target: notificationWindow
property: "y"
duration: 200
from: Screen.height
to: Screen.height-notificationWindow.showY
easing.type: Easing.OutCubic
}
}
// 关闭动画
ParallelAnimation {
id: exitAnimeWin
running: false
onStopped: {
notificationWindow.visible = false
notificationWindow.visibility = Window.Hidden
}
NumberAnimation {
target: notificationWindow
property: "y"
duration: 200
from: Screen.height-notificationWindow.showY
to: Screen.height
easing.type: Easing.InCubic
}
}
}
}

View File

@@ -0,0 +1,142 @@
// =============================================
// =============== 简单消息的界面 ===============
// =============================================
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15 // 阴影
import "../Widgets"
Rectangle {
id: spMsg
property var onHided: undefined // 关闭函数,外部传入
function show(title, msg, time=5000, icon="bell") {
textTitle.text = title
textMsg.text = msg
nowTime = 0
allTime = time>0?time:1 // 防止除0
timerProgressBar.percent = 0
iconComp.icon = icon // 图标
timer.start() // 启动计时器
}
property int allTime: 1 // 总时间
property int nowTime: 0 // 当前时间
property int interval: 10 // 间隔刷新
property int shadowWidth: qmlapp.enabledEffect ? size_.spacing*3 : 0 // 边缘阴影宽度
Timer {
id: timer
interval: spMsg.interval // 间隔
running: false
repeat: true // 重复执行
onTriggered: {
spMsg.nowTime += spMsg.interval
timerProgressBar.percent = spMsg.nowTime/spMsg.allTime
if(spMsg.nowTime>=spMsg.allTime) {
timer.stop() // 停止计时器
if(typeof onHided === "function")
onHided() // 调用关闭函数
return
}
}
}
width: size_.line * 20
height: textTitle.height + textMsg.height+size_.spacing*2
color: theme.specialBgColor
radius: qmlapp.enabledEffect ? size_.panelRadius : 0
// 内容组件
Item {
anchors.fill: parent
anchors.margins: size_.spacing
// 倒计时 ▽按钮
HProgressBar {
id: timerProgressBar
anchors.top: parent.top
anchors.right: parent.right
height: size_.smallLine
width: size_.smallLine*2
color: theme.coverColor1
highlightColor: theme.coverColor2
radius: size_.btnRadius
percent: 0
// 鼠标悬浮背景
Rectangle {
id: btnHoverBg
visible: false
anchors.fill: parent
color: theme.coverColor2
radius: size_.btnRadius
}
// 下箭头图标
Icon_ {
anchors.fill: parent
icon: "down"
color: theme.bgColor
}
}
// 标题图标
Icon_ {
id: iconComp
anchors.top: parent.top
anchors.left: parent.left
color: theme.specialTextColor
height: size_.line
width: size_.line
}
// 标题文字
Text_ {
id: textTitle
anchors.top: parent.top
anchors.left: iconComp.right
anchors.leftMargin: size_.line*0.5
height: size_.line
verticalAlignment: Text.AlignVCenter // 垂直居中
}
// 内容文字
Text_ {
id: textMsg
anchors.top: textTitle.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: size_.smallSpacing
color: theme.subTextColor
font.pixelSize: size_.smallText
wrapMode: TextEdit.Wrap // 尽量在单词边界处换行
maximumLineCount: 2 // 限制显示两行
height: text=="" ? 0:undefined // 无文字时高为0有文字时自动高度
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
btnHoverBg.visible = true
}
onExited: {
btnHoverBg.visible = false
}
function mouseClicked() {
timer.stop() // 停止计时器
if(typeof onHided === "function")
onHided() // 调用关闭函数
}
// 单击隐藏弹窗
onClicked: mouseClicked()
// 双击弹出主窗,隐藏弹窗
onDoubleClicked: {
qmlapp.mainWin.setVisibility(true) // 主窗可见
mouseClicked()
}
}
// 边缘阴影
layer.enabled: shadowWidth>0
layer.effect: DropShadow {
transparentBorder: true
color: theme.coverColor4
samples: shadowWidth
}
}

View File

@@ -0,0 +1,73 @@
// =========================================
// =============== 弹窗管理器 ===============
// =========================================
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15 // 阴影
import "../Widgets"
Item {
property string gYesText: qsTr("确认") // 全局确定文本
property string gNoText: qsTr("取消") // 全局取消文本
// ========================= 【对外接口】 =========================
// 显示简单通知,无需确认,计时自动消失
function simple(title, msg, showType="default") {
messageSimple.show(title, msg, showType)
}
// 显示带确认的通知弹窗
// type可选: ""默认, "warning"警告, "error"错误
function message(title, msg, type="") {
messageWin.showMessage(title, msg, type)
}
// 带“不再提示”的通知弹窗
// mid: 唯一标识一个提示
function messageMemory(mid, title, msg, type="") {
messageWin.showMessageMemory(mid, title, msg, type)
}
// 双选项对话窗(确定|取消。需要传入回调函数返回true/false
// argd {"yesText":"确定", "noText": "取消"}
function dialog(title, msg, callback, type="", argd={}) {
let yesText=gYesText, noText=gNoText
if(argd.hasOwnProperty("yesText"))
yesText = argd.yesText
if(argd.hasOwnProperty("noText"))
noText = argd.noText
messageWin.showDialog(title, msg, callback, yesText, noText, type)
}
// 双选项对话窗,带倒计时,结束后调用确定按钮
// argd {"yesText":"确定", "noText": "取消", "time": 10}
function dialogCountdown(title, msg, callback, type="", argd={}) {
let yesText=gYesText, noText=gNoText, time=10000
if(argd.hasOwnProperty("yesText"))
yesText = argd.yesText
if(argd.hasOwnProperty("noText"))
noText = argd.noText
if(argd.hasOwnProperty("time"))
time = argd.time
messageWin.showDialogCountdown(title, msg, callback, yesText, noText, type, time)
}
// 展示遮罩层
function showMask(msg="", id="") {
maskLayer.showMask(msg, id)
}
// 隐藏指定id的遮罩层
function hideMask(id="") {
maskLayer.hideMask(id)
}
// ========================= 【内部】 =========================
MessageBoxWin{ id: messageWin }
MessageSimple { id: messageSimple }
MaskLayer{ id: maskLayer }
}