diff --git a/index.html b/index.html
index 8176ea4..9a9a81c 100644
--- a/index.html
+++ b/index.html
@@ -260,28 +260,36 @@
¥
{{ boardMain.int }}
{{ boardMain.frac }}
+
+
+
+ +{{ money(fullBonusEarned) }}
+ 满勤奖
+
{{ currentMonth + 1 }}月下半月工时 {{ fmt(monthStats.h2) }}h{{ money(monthStats.h2 * settings.rate) }}
-
{{ currentMonth + 1 }}月奖惩结算{{ signed(monthStats.adjustNet) }}
+
{{ currentMonth + 1 }}月奖惩结算{{ signed(monthStats.reviewNet) }}
- 奖惩口径
+ 奖惩状态
{{ monthStats.finalized ? '已定稿' : '预估(未申报)' }}
@@ -588,7 +592,8 @@
- 距离满勤还差 {{ settings.fullDays - monthStats.workDays }} 天,达成可得满勤奖约 {{ money(monthStats.totalHours * settings.fullBonusPerHour) }}({{ nextLabel.month + 1 }}月{{ settings.payDayB }}号发)。
+ 距离满勤还差 {{ settings.fullDays - monthStats.workDays }} 天。
+ 若达成,满勤奖为{{ money(monthStats.totalHours * settings.fullBonusPerHour) }}(当月总工时 {{ fmt(monthStats.totalHours) }}h × {{ settings.fullBonusPerHour }} 元/h),下月{{ settings.payDayB }}号单独到账,不计入当月工资。
已达成 {{ settings.fullDays }} 天满勤,满勤奖 +{{ money(monthStats.fullBonus) }} 将于 {{ nextLabel.month + 1 }}月{{ settings.payDayB }}号发放。
@@ -678,6 +683,21 @@
班次默认工时
+
+
打卡默认班次
+
+
+
+
+
-
存草稿不改变发放口径;定稿后按终稿金额计算。日常打卡记录不受影响。
+
存草稿不改变发放金额;定稿后按终稿金额计算。日常打卡记录不受影响。
@@ -949,7 +969,8 @@ createApp({
payDayA: 22, // 上半月工时 → 当月该日发
payDayB: 10, // 下半月工时 + 当月奖惩 → 次月该日发
dayHours: 8, // 白班默认工时
- nightHours: 10 // 夜班默认工时
+ nightHours: 10, // 夜班默认工时
+ defaultShift: 'day' // 打卡默认班次:'day' 白班 / 'night' 夜班
};
const settings = reactive({ ...DEFAULTS });
@@ -999,7 +1020,8 @@ createApp({
payDayA: Math.min(31, Math.max(1, Math.round(sn(settings.payDayA, DEFAULTS.payDayA)))),
payDayB: Math.min(31, Math.max(1, Math.round(sn(settings.payDayB, DEFAULTS.payDayB)))),
dayHours: clampHours(sn(settings.dayHours, DEFAULTS.dayHours), DEFAULTS.dayHours),
- nightHours: clampHours(sn(settings.nightHours, DEFAULTS.nightHours), DEFAULTS.nightHours)
+ nightHours: clampHours(sn(settings.nightHours, DEFAULTS.nightHours), DEFAULTS.nightHours),
+ defaultShift: settings.defaultShift === 'night' ? 'night' : 'day'
}));
// ===== 页签 =====
@@ -1044,7 +1066,7 @@ createApp({
penalty_reason: ''
};
}
- const draftRecord = ref(newDraft('day'));
+ const draftRecord = ref(newDraft(cfg.value.defaultShift));
// ===== 日期辅助 =====
const daysInMonth = computed(() => new Date(currentYear.value, currentMonth.value + 1, 0).getDate());
@@ -1163,7 +1185,9 @@ createApp({
const cashTotal = computed(() => payoutThisB.value + payoutThisA.value);
// 「挣得」拆成两笔落地时间:上半月工时随本月 payDayA;下半月工时 + 奖惩结转次月 payDayB。
// 这一行是两套口径之间唯一的桥,解释了为何挣得 ≠ 进账。
- const earnedSplit = computed(() => ({ paid: payoutThisA.value, carry: payoutNextB.value }));
+ // 注意:结转那笔只用「已确认的奖惩」,满勤奖月底才确定,不计入挣得(另作预估展示)。
+ const earnedCarry = computed(() => Math.max(0, monthStats.value.h2 * cfg.value.rate + monthStats.value.reviewNet));
+ const earnedSplit = computed(() => ({ paid: payoutThisA.value, carry: earnedCarry.value }));
// 上半月那笔到没到账,只在看当前月份时才有「待发」说法;翻历史月份一律算已发。
const payDayAPassed = computed(() => {
if (!isCurrentMonth.value) {
@@ -1174,7 +1198,7 @@ createApp({
return new Date().getDate() >= cfg.value.payDayA;
});
// 处罚吃穿下半月工时时,结转那笔被 ¥0 兰住,于是 paid+carry > earnedTotal。页面需要明说。
- const earnedFloored = computed(() => monthStats.value.h2 * cfg.value.rate + monthStats.value.adjustNet < 0);
+ const earnedFloored = computed(() => monthStats.value.h2 * cfg.value.rate + monthStats.value.reviewNet < 0);
const penaltyDetails = computed(() => {
const c = cfg.value;
@@ -1198,8 +1222,14 @@ createApp({
const recordCount = computed(() => Object.keys(records.value).length);
- // 看板主数字:本月两笔到账合计
- const boardMain = computed(() => moneyParts(earnedTotal.value));
+ // 看板主数字(打卡页顶部「X月挣得」):只含「本月工时收入 + 已达标满勤奖」。
+ // 满勤奖未达标(< fullDays 天)绝不计入;日常查获/漏查等奖惩也不混入这个大数字
+ //(它们有专门的奖惩胶囊/汇总卡展示,避免顶部数字忽高忽低、误导「还在挣」的直觉)。
+ // 满勤达标后满勤奖才以「可确认的奖励」身份加入,并在页面上显著标注。
+ const fullBonusEarned = computed(() => monthStats.value.workDays >= cfg.value.fullDays
+ ? monthStats.value.fullBonus : 0);
+ const boardEarned = computed(() => monthStats.value.basePay + fullBonusEarned.value);
+ const boardMain = computed(() => moneyParts(boardEarned.value));
const hasPenalty = (day) => {
const r = getRecord(day);
@@ -1277,7 +1307,7 @@ createApp({
const openDayDrawer = (day) => {
selectedDay.value = day;
const existing = getRecord(day);
- draftRecord.value = existing ? JSON.parse(JSON.stringify(existing)) : newDraft('day');
+ draftRecord.value = existing ? JSON.parse(JSON.stringify(existing)) : newDraft(cfg.value.defaultShift);
isDrawerOpen.value = true;
};
@@ -1345,7 +1375,7 @@ createApp({
delete monthAdjust.value[monthKeyOf(t.year, t.month)];
persistAdjust();
adjustOpen.value = false;
- showToast('已撑销定稿,恢复日常累计口径');
+ showToast('已撑销定稿,恢复日常累计值');
};
const selectShift = (type) => {
@@ -1408,21 +1438,21 @@ createApp({
`上半月 ${fmt(s.h1)}h → ${money(s.h1 * c.rate)} 下半月 ${fmt(s.h2)}h → ${money(s.h2 * c.rate)}`,
``,
`【${m}月挣得】${money(earnedTotal.value)}`,
- `工时收入 ${money(s.basePay)}(确定) | 奖惩净额 ${signed(s.adjustNet)}(${s.finalized ? '已定稿' : '预估·待审核'})`,
+ `工时收入 ${money(s.basePay)}(确定) | 奖惩净额 ${signed(s.reviewNet)}(${s.finalized ? '已定稿' : '预估·待审核'},不含满勤奖)`,
`何时到手:${m}/${settings.payDayA} ${money(earnedSplit.value.paid)}(上半月工时) | ${nm}/${settings.payDayB} ${money(earnedSplit.value.carry)}(结转:下半月工时+奖惩)`,
``,
`【${m}月奖惩明细·次月${settings.payDayB}号结算】`,
`满勤奖 ${s.fullBonus > 0 ? '+' + money(s.fullBonus) : '¥0(未满勤)'}`,
`查获 +${money(s.foundBonus)}(${s.foundCount}件) | 漏查 -${money(s.missedPenalty)}(${s.missedCount}件) | 其他 -${money(s.otherPenalties)}`,
`日常累计(待申报)${signed(s.rawReview)}${s.finalized ? ` → 审核终稿 ${signed(s.reviewAmount)}` : ''}`,
- `奖惩净额 ${signed(s.adjustNet)}`,
+ `奖惩净额 ${signed(s.reviewNet)}(不含满勤奖)`,
``,
- `【${m}月进账·钱包口径】${money(cashTotal.value)}`,
+ `【${m}月进账·实际到账】${money(cashTotal.value)}`,
`${m}月${settings.payDayB}日 ${money(payoutThisB.value)}(归属${pm}月:下半月 ${fmt(p.h2)}h + 奖惩 ${signed(p.adjustNet)})`,
`${m}月${settings.payDayA}日 ${money(payoutThisA.value)}(归属${m}月:上半月 ${fmt(s.h1)}h)`,
`合计 ${money(cashTotal.value)}(含${pm}月结转 ${money(payoutThisB.value)})`,
``,
- `【${nm}月${settings.payDayB}日预估·结转】${money(payoutNextB.value)}(${m}月下半月 ${fmt(s.h2)}h + 奖惩 ${signed(s.adjustNet)})`
+ `【${nm}月${settings.payDayB}日预估·结转】${money(earnedCarry.value)}(${m}月下半月 ${fmt(s.h2)}h + 奖惩 ${signed(s.reviewNet)},不含满勤奖)`
].join('\n');
};
@@ -1551,6 +1581,7 @@ createApp({
monthStats, prevStats, prevLabel, nextLabel,
payoutThisA, payoutThisB, payoutNextB, penaltyDetails,
earnedTotal, cashTotal, earnedSplit, earnedFloored, payDayAPassed,
+ fullBonusEarned, boardEarned,
fmt, money, signed, moneyParts, boardMain, buildReport, applyImport,
isToday, isCurrentMonth, getRecord, hasPenalty, cellClass, isCutEdge,
payDayKind, selectedPayInfo,