chore: initialize mini program frontend
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
const api = require('../../utils/api')
|
||||
const util = require('../../utils/util')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
rows: [],
|
||||
page: 1,
|
||||
pageInput: '1',
|
||||
totalPages: 1,
|
||||
refreshing: false,
|
||||
filterVoided: true,
|
||||
},
|
||||
onLoad(options) {
|
||||
const defaultDate = util.today()
|
||||
this.setData({
|
||||
startDate: options.startDate || defaultDate,
|
||||
endDate: options.endDate || defaultDate,
|
||||
})
|
||||
},
|
||||
onShow() {
|
||||
this.load()
|
||||
},
|
||||
async load(page = this.data.page) {
|
||||
try {
|
||||
const result = await api.listDashboard({
|
||||
startDate: this.data.startDate,
|
||||
endDate: this.data.endDate,
|
||||
page,
|
||||
pageSize: 8,
|
||||
includeVoided: !this.data.filterVoided,
|
||||
})
|
||||
this.setData({
|
||||
rows: result.rows,
|
||||
page: result.page,
|
||||
pageInput: String(result.page || 1),
|
||||
totalPages: result.totalPages,
|
||||
})
|
||||
} catch (error) {
|
||||
wx.showToast({ title: error.message, icon: 'none' })
|
||||
}
|
||||
},
|
||||
async onPullDownRefresh() {
|
||||
this.setData({ refreshing: true })
|
||||
try {
|
||||
await this.load(1)
|
||||
} finally {
|
||||
this.setData({ refreshing: false })
|
||||
if (wx.stopPullDownRefresh) {
|
||||
wx.stopPullDownRefresh()
|
||||
}
|
||||
}
|
||||
},
|
||||
onStartChange(e) {
|
||||
this.setData({ startDate: e.detail.value, page: 1 })
|
||||
this.load(1)
|
||||
},
|
||||
onEndChange(e) {
|
||||
this.setData({ endDate: e.detail.value, page: 1 })
|
||||
this.load(1)
|
||||
},
|
||||
shiftRangeDate(field, days) {
|
||||
const otherField = field === 'startDate' ? 'endDate' : 'startDate'
|
||||
const current = this.data[field]
|
||||
const other = this.data[otherField]
|
||||
const base = current || other || util.today()
|
||||
const updates = {
|
||||
[field]: util.addDays(base, days),
|
||||
page: 1,
|
||||
}
|
||||
if (!current && !other) {
|
||||
updates[otherField] = base
|
||||
}
|
||||
this.setData(updates)
|
||||
this.load(1)
|
||||
},
|
||||
decreaseStartDate() {
|
||||
this.shiftRangeDate('startDate', -1)
|
||||
},
|
||||
increaseEndDate() {
|
||||
this.shiftRangeDate('endDate', 1)
|
||||
},
|
||||
toggleFilterVoided() {
|
||||
this.setData({ filterVoided: !this.data.filterVoided, page: 1 })
|
||||
this.load(1)
|
||||
},
|
||||
onFilterVoidedChange(e) {
|
||||
this.setData({ filterVoided: !!e.detail.value, page: 1 })
|
||||
this.load(1)
|
||||
},
|
||||
prevPage() {
|
||||
if (this.data.page > 1) {
|
||||
this.load(this.data.page - 1)
|
||||
}
|
||||
},
|
||||
nextPage() {
|
||||
if (this.data.page < this.data.totalPages) {
|
||||
this.load(this.data.page + 1)
|
||||
}
|
||||
},
|
||||
onPageInput(e) {
|
||||
this.setData({ pageInput: e.detail.value })
|
||||
},
|
||||
jumpToPage(e) {
|
||||
wx.hideKeyboard()
|
||||
const inputValue = e && e.detail && e.detail.value !== undefined ? e.detail.value : this.data.pageInput
|
||||
const target = Math.max(1, Math.min(Number(inputValue) || this.data.page, this.data.totalPages))
|
||||
if (target === this.data.page) {
|
||||
this.setData({ pageInput: String(this.data.page || 1) })
|
||||
return
|
||||
}
|
||||
this.load(target)
|
||||
},
|
||||
async exportFile() {
|
||||
wx.showLoading({ title: '导出中' })
|
||||
try {
|
||||
const filePath = await api.exportDashboard({
|
||||
startDate: this.data.startDate,
|
||||
endDate: this.data.endDate,
|
||||
includeVoided: !this.data.filterVoided,
|
||||
})
|
||||
wx.openDocument({
|
||||
filePath,
|
||||
fileType: 'xlsx',
|
||||
showMenu: true,
|
||||
})
|
||||
} catch (error) {
|
||||
wx.showToast({ title: error.message, icon: 'none' })
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"navigationBarTitleText": "报工看板"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<scroll-view
|
||||
class="page safe-bottom"
|
||||
scroll-y
|
||||
type="list"
|
||||
refresher-enabled
|
||||
refresher-triggered="{{refreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
>
|
||||
<view class="header">
|
||||
<view class="dashboard-header-row">
|
||||
<view class="dashboard-header-main">
|
||||
<text class="title">报工看板</text>
|
||||
<text class="subtitle">同人同日同项目、产品、工序、冲压方式的已审核报工自动合并</text>
|
||||
</view>
|
||||
<view class="void-filter-switch">
|
||||
<text>过滤作废</text>
|
||||
<switch checked="{{filterVoided}}" color="#1463ff" bindchange="onFilterVoidedChange" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-bar date-filter-bar">
|
||||
<view class="date-filter-cell">
|
||||
<button class="date-step-btn" bindtap="decreaseStartDate">-</button>
|
||||
<picker mode="date" value="{{startDate}}" bindchange="onStartChange">
|
||||
<view class="picker">{{startDate || '开始日期'}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="date-filter-cell end">
|
||||
<button class="date-step-btn" bindtap="increaseEndDate">+</button>
|
||||
<picker mode="date" value="{{endDate}}" bindchange="onEndChange">
|
||||
<view class="picker">{{endDate || '结束日期'}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
<button class="btn primary full export-btn" bindtap="exportFile">导出看板数据</button>
|
||||
|
||||
<view wx:if="{{!rows.length}}" class="list-empty">暂无已审核报工</view>
|
||||
<view wx:for="{{rows}}" wx:key="id" class="card dashboard-card {{item.isSystemAutoSubmitted ? 'system-auto' : ''}} {{item.isVoided ? 'voided' : ''}}">
|
||||
<text wx:if="{{item.isVoided}}" class="voided-watermark">作废</text>
|
||||
<text wx:elif="{{item.correctionPairs.length}}" class="modified-watermark">更改</text>
|
||||
<text wx:if="{{item.isSystemAutoSubmitted}}" class="timeout-watermark">超时-系统自动提交</text>
|
||||
<text wx:if="{{item.isCleaning}}" class="cleaning-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.correctionPairs.length ? 'lower-watermark' : ''}}">清洗</text>
|
||||
<text wx:if="{{item.isMisc}}" class="misc-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.correctionPairs.length ? 'lower-watermark' : ''}}">包含杂活</text>
|
||||
<text wx:if="{{item.isMultiPerson}}" class="multi-person-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.correctionPairs.length || item.isMisc || item.isCleaning ? 'lower-watermark' : ''}}">多人协作</text>
|
||||
<text wx:if="{{item.isContinuousDie}}" class="continuous-die-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.correctionPairs.length || item.isMisc || item.isCleaning || item.isMultiPerson ? 'lower-watermark' : ''}} {{item.isMultiPerson && (item.isSystemAutoSubmitted || item.isVoided || item.correctionPairs.length || item.isMisc || item.isCleaning) ? 'third-watermark' : ''}}">连续模</text>
|
||||
<view class="row">
|
||||
<view class="row-left">
|
||||
<text class="value">{{item.employeeName}} · {{item.reportDate}}</text>
|
||||
<text class="subtitle">考勤点 {{item.attendancePointName || '-'}}</text>
|
||||
<text class="subtitle">{{item.employeePhone}} · {{item.projectNo}} / {{item.productName}}</text>
|
||||
<text class="subtitle">{{item.processDisplayName || item.productName || '-'}}</text>
|
||||
<text class="subtitle">{{item.isMisc ? '杂活事项' : '工序'}} {{item.processName || '-'}} · 冲压方式 {{item.stampingMethod || '-'}} · {{item.reportCount}} 条报工</text>
|
||||
<text wx:if="{{!item.isCleaning && !item.isMisc}}" class="subtitle">材料库存批次号 {{item.rawMaterialBatchNo || '-'}}</text>
|
||||
<text wx:if="{{item.shiftDistributionText}}" class="subtitle">{{item.shiftDistributionText}}</text>
|
||||
<text wx:else class="subtitle">有效工时 {{item.effectiveMinutes}} 分钟</text>
|
||||
<view wx:if="{{item.correctionPairs.length}}" class="correction-list">
|
||||
<view wx:for="{{item.correctionPairs}}" wx:key="key" wx:for-item="pair" class="correction-pair">
|
||||
<text>{{pair.label}} </text>
|
||||
<text class="old-value">{{pair.oldValue}}</text>
|
||||
<text> {{pair.newValue}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="pill success">已合并</text>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="board-grid">
|
||||
<view>
|
||||
<text class="label">{{item.isMisc ? '处理杂活' : '成品数量'}}</text>
|
||||
<text class="value {{item.workloadCompareClass}}">
|
||||
{{item.isMisc ? '不计产量' : item.totalGood}}
|
||||
<text wx:if="{{item.workloadReason}}" class="metric-reason">{{item.workloadReason}}</text>
|
||||
</text>
|
||||
</view>
|
||||
<view wx:if="{{!item.isMisc}}">
|
||||
<text class="label">不良数量</text>
|
||||
<text class="value">{{item.totalDefect}}</text>
|
||||
</view>
|
||||
<view wx:if="{{!item.isMisc}}">
|
||||
<text class="label">标准工作量</text>
|
||||
<text class="value">{{item.expectedWorkload}}</text>
|
||||
</view>
|
||||
<view wx:if="{{!item.isMisc}}">
|
||||
<text class="label">实际节拍</text>
|
||||
<text class="value {{item.beatCompareClass}}">
|
||||
{{item.actualBeat}} 秒
|
||||
<text wx:if="{{item.beatReason}}" class="metric-reason">{{item.beatReason}}</text>
|
||||
</text>
|
||||
</view>
|
||||
<view wx:if="{{!item.isMisc}}">
|
||||
<text class="label">标准节拍</text>
|
||||
<text class="value">{{item.standardBeat}} 秒</text>
|
||||
</view>
|
||||
<view wx:if="{{!item.isMisc}}">
|
||||
<text class="label">节拍对比</text>
|
||||
<text class="value {{item.beatCompareClass}}">{{item.paceText}}</text>
|
||||
</view>
|
||||
<view wx:if="{{!item.isMisc}}">
|
||||
<text class="label">工作量对比</text>
|
||||
<text class="value {{item.workloadCompareClass}}">{{item.workloadText}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.isMisc}}">
|
||||
<text class="label">工时</text>
|
||||
<text class="value">{{item.effectiveMinutes}} 分钟</text>
|
||||
</view>
|
||||
<view wx:if="{{!item.isCleaning && !item.isMisc}}">
|
||||
<text class="label">材料库存批次号</text>
|
||||
<text class="value">{{item.rawMaterialBatchNo || '-'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.isContinuousDie}}">
|
||||
<text class="label">换料次数</text>
|
||||
<text class="value">{{item.changeoverCount || 0}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{item.reviewRemark}}" class="dashboard-remark">
|
||||
<text class="label">备注</text>
|
||||
<text class="value">{{item.reviewRemark}}</text>
|
||||
</view>
|
||||
<view class="salary-divider"></view>
|
||||
<view class="salary-row">
|
||||
<text class="label">参考工资</text>
|
||||
<text class="salary-value">¥{{item.referenceWage || 0}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="button-row pager-row" wx:if="{{rows.length}}">
|
||||
<button class="btn secondary pager-nav" bindtap="prevPage" disabled="{{page <= 1}}">上一页</button>
|
||||
<view class="page-jump-control">
|
||||
<input class="page-jump-input" type="number" confirm-type="done" value="{{pageInput}}" bindinput="onPageInput" bindconfirm="jumpToPage" />
|
||||
<text class="page-total-label">/ {{totalPages}}</text>
|
||||
<text class="page-jump-action" bindtap="jumpToPage">跳</text>
|
||||
</view>
|
||||
<button class="btn secondary pager-nav" bindtap="nextPage" disabled="{{page >= totalPages}}">下一页</button>
|
||||
</view>
|
||||
</scroll-view>
|
||||
@@ -0,0 +1,79 @@
|
||||
.dashboard-header-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.dashboard-header-main {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.dashboard-header-row .void-filter-switch {
|
||||
align-self: flex-end;
|
||||
width: 240rpx;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cleaning-watermark {
|
||||
position: absolute;
|
||||
right: 28rpx;
|
||||
top: 18rpx;
|
||||
z-index: 1;
|
||||
padding: 8rpx 18rpx;
|
||||
border: 2rpx solid rgba(18, 163, 117, 0.55);
|
||||
border-radius: 12rpx;
|
||||
color: rgba(8, 116, 67, 0.72);
|
||||
background: rgba(226, 255, 243, 0.82);
|
||||
font-size: 30rpx;
|
||||
font-weight: 900;
|
||||
transform: rotate(10deg);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.board-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 18rpx;
|
||||
padding-top: 4rpx;
|
||||
}
|
||||
|
||||
.salary-divider {
|
||||
height: 2rpx;
|
||||
margin: 20rpx 0 16rpx;
|
||||
background: #e1e8f2;
|
||||
}
|
||||
|
||||
.dashboard-remark {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
margin-top: 18rpx;
|
||||
padding: 16rpx;
|
||||
border: 1rpx solid #dfe7f2;
|
||||
border-radius: 12rpx;
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.dashboard-remark .value {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.salary-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
.salary-value {
|
||||
color: #c2410c;
|
||||
font-size: 34rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
Reference in New Issue
Block a user