Commit a0aeefab12d97eb09bd4322c7c5d7f1f25f132b5

Authored by “wangming”
1 parent 940fb6ea

好了,设计现在需要给美国那边看,估计还有修改吧

美国版/Food Labeling Management App UniApp/src/components/AppIcon.vue
... ... @@ -53,6 +53,8 @@ const icons: Record<string, string> = {
53 53 lock: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>',
54 54 chevronDown: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>',
55 55 chevronUp: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>',
  56 + squares: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>',
  57 + list: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>',
56 58 }
57 59  
58 60 const svgContent = computed(() => icons[props.name] || icons.food)
... ...
美国版/Food Labeling Management App UniApp/src/locales/en.ts
... ... @@ -4,7 +4,7 @@ export default {
4 4 appName: 'Food Label System',
5 5 employeePortal: 'Employee Portal',
6 6 email: 'Email',
7   - emailPlaceholder: 'your.email@company.com',
  7 + emailPlaceholder: "your.email{'@'}company.com",
8 8 password: 'Password',
9 9 passwordPlaceholder: 'Enter your password',
10 10 rememberMe: 'Remember me',
... ...
美国版/Food Labeling Management App UniApp/src/locales/zh.ts
... ... @@ -4,7 +4,7 @@ export default {
4 4 appName: '食品标签系统',
5 5 employeePortal: '员工门户',
6 6 email: '电子邮件',
7   - emailPlaceholder: 'your.email@company.com',
  7 + emailPlaceholder: "your.email{'@'}company.com",
8 8 password: '密码',
9 9 passwordPlaceholder: '输入您的密码',
10 10 rememberMe: '记住我',
... ...
美国版/Food Labeling Management App UniApp/src/pages/labels/labels.vue
... ... @@ -103,7 +103,7 @@
103 103 <text class="type-badge-text">{{ product.labelTypes.length }} Types</text>
104 104 </view>
105 105 </view>
106   - <text class="food-name">{{ product.name }}</text>
  106 + <text class="food-name">{{ getDisplayName(product) }}</text>
107 107 <text class="food-desc">{{ product.templateName }}</text>
108 108 </view>
109 109 </view>
... ... @@ -117,7 +117,7 @@
117 117 <view v-if="showSubTypeModal" class="modal-mask" @click="showSubTypeModal = false">
118 118 <view class="modal-body" @click.stop>
119 119 <text class="modal-title">Select Label Type</text>
120   - <text class="modal-desc">{{ selectedProduct ? selectedProduct.name : '' }}</text>
  120 + <text class="modal-desc">{{ selectedProduct ? getDisplayName(selectedProduct) : '' }}</text>
121 121 <view class="subtype-list">
122 122 <view
123 123 v-for="lt in currentLabelTypes"
... ... @@ -396,6 +396,14 @@ const productCategoriesByLabel: Record&lt;string, ProductCategory[]&gt; = {
396 396 ],
397 397 }
398 398  
  399 +// 按详情页规则:chicken→Chicken,2"x6"/2"x4"→Cheese Burger Deluxe,其余→Syrup(图片仍用产品图)
  400 +function getDisplayName(product: Product): string {
  401 + if (product.id === 'chicken') return 'Chicken'
  402 + const size = product.templateSize || ''
  403 + if (size.indexOf('2"x6"') >= 0 || size.indexOf('2"x4"') >= 0) return 'Cheese Burger Deluxe'
  404 + return 'Syrup'
  405 +}
  406 +
399 407 const filteredProductCategories = computed(() => {
400 408 const cats = productCategoriesByLabel[selectedCategory.value] || []
401 409 const s = searchTerm.value.toLowerCase()
... ... @@ -405,7 +413,8 @@ const filteredProductCategories = computed(() =&gt; {
405 413 return {
406 414 ...cat,
407 415 products: cat.products.filter(function (p) {
408   - return p.name.toLowerCase().indexOf(s) >= 0
  416 + const displayName = getDisplayName(p)
  417 + return p.name.toLowerCase().indexOf(s) >= 0 || displayName.toLowerCase().indexOf(s) >= 0
409 418 }),
410 419 }
411 420 })
... ...
美国版/Food Labeling Management App UniApp/src/pages/labels/preview.vue
... ... @@ -54,6 +54,10 @@
54 54  
55 55 <view class="info-row">
56 56 <view class="info-item">
  57 + <text class="info-label">Label ID</text>
  58 + <text class="info-value">{{ labelId }}</text>
  59 + </view>
  60 + <view class="info-item">
57 61 <text class="info-label">Last Edited</text>
58 62 <text class="info-value">{{ lastEdited }}</text>
59 63 </view>
... ... @@ -94,6 +98,10 @@
94 98 <view class="modal-label-inner">
95 99 <image :src="labelImage" class="modal-label-img" mode="widthFix" />
96 100 </view>
  101 + <view class="modal-label-id">
  102 + <text class="modal-label-id-label">Label ID</text>
  103 + <text class="modal-label-id-value">{{ labelId }}</text>
  104 + </view>
97 105 </view>
98 106 </view>
99 107 </view>
... ... @@ -109,6 +117,8 @@ import AppIcon from &#39;../../components/AppIcon.vue&#39;
109 117 import SideMenu from '../../components/SideMenu.vue'
110 118 import LocationPicker from '../../components/LocationPicker.vue'
111 119 import { getStatusBarHeight } from '../../utils/statusBar'
  120 +import { generateNextLabelId } from '../../utils/printLog'
  121 +import chickenLabelImg from '../../static/chicken-lable.png'
112 122  
113 123 const statusBarHeight = getStatusBarHeight()
114 124 const isPrinting = ref(false)
... ... @@ -119,6 +129,7 @@ const showPreviewModal = ref(false)
119 129 const btConnected = ref(false)
120 130 const btDeviceName = ref('')
121 131  
  132 +const productId = ref('chicken')
122 133 const productName = ref('Chicken')
123 134 const productCategory = ref('Meat')
124 135 const labelTypeName = ref('')
... ... @@ -126,8 +137,12 @@ const templateSize = ref(&#39;2&quot;x2&quot;&#39;)
126 137 const templateName = ref('Basic')
127 138 const lastEdited = ref('2025.12.03 11:45')
128 139 const locationName = ref('Location A')
  140 +const labelId = ref('')
129 141  
130 142 const labelImage = computed(() => {
  143 + if (productId.value === 'chicken') {
  144 + return chickenLabelImg
  145 + }
131 146 const size = templateSize.value
132 147 if (size.indexOf('2"x6"') >= 0 || size.indexOf('2"x4"') >= 0) {
133 148 return '/static/lable2.png'
... ... @@ -135,10 +150,13 @@ const labelImage = computed(() =&gt; {
135 150 return '/static/lable1.png'
136 151 })
137 152  
138   -// 产品名称按标签模板固定:lable1 → Syrup,lable2 → Cheese Burger Deluxe
139   -const displayProductName = computed(() =>
140   - labelImage.value.includes('lable1') ? 'Syrup' : 'Cheese Burger Deluxe'
141   -)
  153 +// 按详情规则与产品列表一致:chicken→Chicken,lable2→Cheese Burger Deluxe,lable1→Syrup
  154 +const displayProductName = computed(() => {
  155 + if (productId.value === 'chicken') return 'Chicken'
  156 + const size = templateSize.value
  157 + if (size.indexOf('2"x6"') >= 0 || size.indexOf('2"x4"') >= 0) return 'Cheese Burger Deluxe'
  158 + return 'Syrup'
  159 +})
142 160  
143 161 onShow(() => {
144 162 const name = uni.getStorageSync('btDeviceName')
... ... @@ -178,6 +196,7 @@ onLoad((opts: any) =&gt; {
178 196  
179 197 const product = productMap[pid]
180 198 if (product) {
  199 + productId.value = pid
181 200 productName.value = product.name
182 201 productCategory.value = product.category
183 202 templateSize.value = product.templateSize
... ... @@ -194,6 +213,7 @@ onLoad((opts: any) =&gt; {
194 213  
195 214 const storeId = uni.getStorageSync('storeId') || '001'
196 215 locationName.value = 'Location A'
  216 + labelId.value = generateNextLabelId()
197 217 })
198 218  
199 219 const increment = () => { if (printQty.value < 99) printQty.value++ }
... ... @@ -442,18 +462,22 @@ const handlePrint = () =&gt; {
442 462  
443 463 .info-row {
444 464 display: flex;
445   - gap: 16rpx;
446 465 margin-bottom: 24rpx;
447 466 }
448 467  
449 468 .info-item {
450 469 flex: 1;
  470 + min-width: 0;
451 471 background: #fff;
452 472 padding: 20rpx 24rpx;
453 473 border-radius: 16rpx;
454 474 box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
455 475 }
456 476  
  477 +.info-item + .info-item {
  478 + margin-left: 10rpx;
  479 +}
  480 +
457 481 .info-label {
458 482 font-size: 22rpx;
459 483 color: #9ca3af;
... ... @@ -614,6 +638,28 @@ const handlePrint = () =&gt; {
614 638 padding: 24rpx;
615 639 }
616 640  
  641 +.modal-label-id {
  642 + margin-top: 20rpx;
  643 + padding-top: 20rpx;
  644 + border-top: 1rpx solid #e5e7eb;
  645 + display: flex;
  646 + align-items: center;
  647 + justify-content: space-between;
  648 + gap: 16rpx;
  649 +}
  650 +
  651 +.modal-label-id-label {
  652 + font-size: 26rpx;
  653 + color: #6b7280;
  654 + font-weight: 500;
  655 +}
  656 +
  657 +.modal-label-id-value {
  658 + font-size: 28rpx;
  659 + color: #111827;
  660 + font-weight: 600;
  661 +}
  662 +
617 663 .modal-top {
618 664 display: flex;
619 665 justify-content: space-between;
... ...
美国版/Food Labeling Management App UniApp/src/pages/login/login.vue
1 1 <template>
2 2 <view class="login-page">
3   - <view class="header-hero" :style="{ paddingTop: (statusBarHeight + 40) + 'px' }">
  3 + <view class="header-hero" :style="{ paddingTop: (statusBarHeight + 16) + 'px' }">
4 4 <view class="logo-section">
5 5 <image class="login-logo" src="/static/logo_us.png" mode="aspectFit" />
6 6 <text class="hero-sub">{{ t('login.employeePortal') }}</text>
... ... @@ -14,6 +14,7 @@
14 14 <input
15 15 v-model="email"
16 16 type="text"
  17 + inputmode="email"
17 18 class="input"
18 19 :placeholder="t('login.emailPlaceholder')"
19 20 placeholder-class="placeholder"
... ... @@ -77,14 +78,14 @@ const handleLogin = () =&gt; {
77 78 .login-page {
78 79 min-height: 100vh;
79 80 background: #f9fafb;
80   - display: flex;
81   - flex-direction: column;
  81 + padding-bottom: 48rpx;
82 82 }
83 83  
84 84 .header-hero {
85 85 background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-dark));
86   - padding: 0 48rpx 64rpx;
  86 + padding: 0 48rpx 24rpx;
87 87 text-align: center;
  88 + flex-shrink: 0;
88 89 }
89 90  
90 91 .logo-section {
... ... @@ -94,9 +95,9 @@ const handleLogin = () =&gt; {
94 95 }
95 96  
96 97 .login-logo {
97   - width: 420rpx;
98   - height: 140rpx;
99   - margin-bottom: 20rpx;
  98 + width: 360rpx;
  99 + height: 120rpx;
  100 + margin-bottom: 12rpx;
100 101 background: rgba(255,255,255,0.92);
101 102 border-radius: 16rpx;
102 103 padding: 16rpx;
... ... @@ -108,9 +109,7 @@ const handleLogin = () =&gt; {
108 109 }
109 110  
110 111 .form-wrap {
111   - flex: 1;
112   - padding: 48rpx;
113   - margin-top: -32rpx;
  112 + padding: 24rpx 48rpx 48rpx;
114 113 }
115 114  
116 115 .form-card {
... ... @@ -134,10 +133,14 @@ const handleLogin = () =&gt; {
134 133  
135 134 .input {
136 135 height: 96rpx;
  136 + min-height: 48px;
137 137 padding: 0 24rpx;
138 138 background: #f3f4f6;
139 139 border-radius: 16rpx;
140 140 font-size: 32rpx;
  141 + display: block;
  142 + width: 100%;
  143 + box-sizing: border-box;
141 144 }
142 145  
143 146 .placeholder {
... ...
美国版/Food Labeling Management App UniApp/src/pages/more/label-report.vue
... ... @@ -15,6 +15,33 @@
15 15 </view>
16 16 </view>
17 17  
  18 + <view class="period-bar">
  19 + <view
  20 + v-for="p in periodOptions"
  21 + :key="p.value"
  22 + class="period-btn"
  23 + :class="{ active: period === p.value }"
  24 + @click="selectPeriod(p.value)"
  25 + >
  26 + <text class="period-text">{{ p.label }}</text>
  27 + </view>
  28 + </view>
  29 +
  30 + <view v-if="period === 'custom'" class="custom-date-bar">
  31 + <view class="date-field">
  32 + <text class="date-label">Start</text>
  33 + <picker mode="date" :value="customStart" :end="customEnd || '2099-12-31'" @change="onStartChange">
  34 + <view class="date-picker-btn">{{ customStart || 'Select' }}</view>
  35 + </picker>
  36 + </view>
  37 + <view class="date-field">
  38 + <text class="date-label">End</text>
  39 + <picker mode="date" :value="customEnd" :start="customStart || '2000-01-01'" @change="onEndChange">
  40 + <view class="date-picker-btn">{{ customEnd || 'Select' }}</view>
  41 + </picker>
  42 + </view>
  43 + </view>
  44 +
18 45 <scroll-view class="content" scroll-y>
19 46 <!-- 指标卡 -->
20 47 <view class="metrics-grid">
... ... @@ -104,6 +131,42 @@ import { getStatusBarHeight } from &#39;../../utils/statusBar&#39;
104 131  
105 132 const statusBarHeight = getStatusBarHeight()
106 133 const isMenuOpen = ref(false)
  134 +const period = ref('7d')
  135 +const customStart = ref('')
  136 +const customEnd = ref('')
  137 +
  138 +const periodOptions = [
  139 + { value: '7d', label: 'Last 7 Days' },
  140 + { value: '30d', label: 'Last 30 Days' },
  141 + { value: '90d', label: 'Last 90 Days' },
  142 + { value: 'custom', label: 'Custom' },
  143 +]
  144 +
  145 +function selectPeriod(val: string) {
  146 + period.value = val
  147 + if (val === 'custom') {
  148 + const today = formatDate(new Date())
  149 + const weekAgo = new Date()
  150 + weekAgo.setDate(weekAgo.getDate() - 7)
  151 + customStart.value = customStart.value || formatDate(weekAgo)
  152 + customEnd.value = customEnd.value || today
  153 + }
  154 +}
  155 +
  156 +function formatDate(d: Date): string {
  157 + const y = d.getFullYear()
  158 + const m = String(d.getMonth() + 1).padStart(2, '0')
  159 + const day = String(d.getDate()).padStart(2, '0')
  160 + return `${y}-${m}-${day}`
  161 +}
  162 +
  163 +function onStartChange(e: any) {
  164 + customStart.value = e.detail.value
  165 +}
  166 +
  167 +function onEndChange(e: any) {
  168 + customEnd.value = e.detail.value
  169 +}
107 170  
108 171 const categoryDataRaw = [
109 172 { name: 'Dairy', value: 450 },
... ... @@ -187,6 +250,67 @@ const goBack = () =&gt; {
187 250 color: #fff;
188 251 }
189 252  
  253 +.period-bar {
  254 + display: flex;
  255 + gap: 12rpx;
  256 + padding: 16rpx 28rpx;
  257 + background: #fff;
  258 + border-bottom: 1rpx solid #e5e7eb;
  259 +}
  260 +
  261 +.period-btn {
  262 + flex: 1;
  263 + padding: 16rpx 24rpx;
  264 + border-radius: 12rpx;
  265 + background: #f3f4f6;
  266 + text-align: center;
  267 + transition: all 0.2s;
  268 +}
  269 +
  270 +.period-btn.active {
  271 + background: linear-gradient(135deg, #1F3A8A, #1447E6);
  272 +}
  273 +
  274 +.period-btn.active .period-text {
  275 + color: #fff;
  276 +}
  277 +
  278 +.period-text {
  279 + font-size: 26rpx;
  280 + font-weight: 500;
  281 + color: #6b7280;
  282 +}
  283 +
  284 +.custom-date-bar {
  285 + display: flex;
  286 + gap: 24rpx;
  287 + padding: 20rpx 28rpx;
  288 + background: #fff;
  289 + border-bottom: 1rpx solid #e5e7eb;
  290 +}
  291 +
  292 +.date-field {
  293 + flex: 1;
  294 + display: flex;
  295 + flex-direction: column;
  296 + gap: 8rpx;
  297 +}
  298 +
  299 +.date-label {
  300 + font-size: 24rpx;
  301 + color: #6b7280;
  302 + font-weight: 500;
  303 +}
  304 +
  305 +.date-picker-btn {
  306 + padding: 20rpx 24rpx;
  307 + background: #f3f4f6;
  308 + border-radius: 12rpx;
  309 + font-size: 28rpx;
  310 + color: #111827;
  311 + border: 2rpx solid #e5e7eb;
  312 +}
  313 +
190 314 .content {
191 315 flex: 1;
192 316 padding: 24rpx 28rpx 40rpx;
... ... @@ -378,10 +502,10 @@ const goBack = () =&gt; {
378 502 font-size: 24rpx;
379 503 }
380 504  
381   -.col-name { flex: 1; min-width: 0; }
382   -.col-cat { flex: 0 0 140rpx; }
383   -.col-total { flex: 0 0 120rpx; text-align: right; }
384   -.col-pct { flex: 0 0 88rpx; text-align: right; }
  505 +.col-name { flex: 1; min-width: 0; text-align: left; }
  506 +.col-cat { flex: 0 0 140rpx; text-align: left; }
  507 +.col-total { flex: 0 0 120rpx; text-align: left; }
  508 +.col-pct { flex: 0 0 88rpx; text-align: left; }
385 509  
386 510 .product-row:not(.header) .col-name { color: #111827; font-weight: 500; }
387 511 .product-row:not(.header) .col-cat { color: #6b7280; }
... ...
美国版/Food Labeling Management App UniApp/src/pages/more/print-detail.vue
... ... @@ -27,6 +27,10 @@
27 27 <text class="detail-section-title">Print Info</text>
28 28 <view class="info-card">
29 29 <view class="info-row">
  30 + <text class="info-label">Label ID</text>
  31 + <text class="info-value">{{ record.labelId }}</text>
  32 + </view>
  33 + <view class="info-row">
30 34 <text class="info-label">Print Time</text>
31 35 <text class="info-value">{{ record.dateFull }} {{ record.time }}</text>
32 36 </view>
... ... @@ -74,7 +78,8 @@ import AppIcon from &#39;../../components/AppIcon.vue&#39;
74 78 import SideMenu from '../../components/SideMenu.vue'
75 79 import LocationPicker from '../../components/LocationPicker.vue'
76 80 import { getStatusBarHeight } from '../../utils/statusBar'
77   -import { getRecordById } from '../../utils/printRecords'
  81 +import { getRecordByLabelId } from '../../utils/printLog'
  82 +import chickenLabelImg from '../../static/chicken-lable.png'
78 83  
79 84 const statusBarHeight = getStatusBarHeight()
80 85 const isMenuOpen = ref(false)
... ... @@ -83,6 +88,9 @@ const record = ref&lt;any&gt;(null)
83 88 const labelImage = computed(() => {
84 89 const r = record.value
85 90 if (!r) return '/static/lable1.png'
  91 + if (r.productName === 'Chicken') {
  92 + return chickenLabelImg
  93 + }
86 94 const size = r.templateSize || ''
87 95 if (size.indexOf('2"x6"') >= 0 || size.indexOf('2"x4"') >= 0) {
88 96 return '/static/lable2.png'
... ... @@ -90,15 +98,20 @@ const labelImage = computed(() =&gt; {
90 98 return '/static/lable1.png'
91 99 })
92 100  
93   -// 产品名称按标签模板固定:lable1 → Syrup,lable2 → Cheese Burger Deluxe
94   -const displayProductName = computed(() =>
95   - labelImage.value.includes('lable1') ? 'Syrup' : 'Cheese Burger Deluxe'
96   -)
  101 +// 与产品列表、详情一致:按标签图 Chicken/Syrup/Cheese Burger Deluxe
  102 +const displayProductName = computed(() => {
  103 + const r = record.value
  104 + if (!r) return ''
  105 + if (r.productName === 'Chicken') return 'Chicken'
  106 + const size = r.templateSize || ''
  107 + if (size.indexOf('2"x6"') >= 0 || size.indexOf('2"x4"') >= 0) return 'Cheese Burger Deluxe'
  108 + return 'Syrup'
  109 +})
97 110  
98 111 onLoad((opts: any) => {
99   - const id = opts && opts.id
100   - if (id) {
101   - record.value = getRecordById(id)
  112 + const labelId = opts && opts.labelId
  113 + if (labelId) {
  114 + record.value = getRecordByLabelId(labelId)
102 115 }
103 116 })
104 117  
... ...
美国版/Food Labeling Management App UniApp/src/pages/more/print-log.vue
... ... @@ -15,8 +15,28 @@
15 15 </view>
16 16 </view>
17 17  
  18 + <view class="view-toggle">
  19 + <view
  20 + class="toggle-btn"
  21 + :class="{ active: viewMode === 'card' }"
  22 + @click="viewMode = 'card'"
  23 + >
  24 + <AppIcon name="squares" size="sm" :color="viewMode === 'card' ? 'white' : 'gray'" />
  25 + <text class="toggle-text">Card</text>
  26 + </view>
  27 + <view
  28 + class="toggle-btn"
  29 + :class="{ active: viewMode === 'list' }"
  30 + @click="viewMode = 'list'"
  31 + >
  32 + <AppIcon name="list" size="sm" :color="viewMode === 'list' ? 'white' : 'gray'" />
  33 + <text class="toggle-text">List</text>
  34 + </view>
  35 + </view>
  36 +
18 37 <scroll-view class="content" scroll-y>
19   - <view class="log-list">
  38 + <!-- 卡片视图 -->
  39 + <view v-if="viewMode === 'card'" class="log-list">
20 40 <view
21 41 v-for="row in printLogData"
22 42 :key="row.labelId"
... ... @@ -56,6 +76,34 @@
56 76 </view>
57 77 </view>
58 78 </view>
  79 +
  80 + <!-- 列表视图 -->
  81 + <view v-else class="log-table-wrap">
  82 + <view class="log-table">
  83 + <view class="log-table-header">
  84 + <text class="th th-product">Product</text>
  85 + <text class="th th-id">Label ID</text>
  86 + <text class="th th-printed">Printed At</text>
  87 + <text class="th th-expires">Expires</text>
  88 + <text class="th th-action">Action</text>
  89 + </view>
  90 + <view
  91 + v-for="row in printLogData"
  92 + :key="row.labelId"
  93 + class="log-table-row"
  94 + >
  95 + <text class="td td-product">{{ row.productName }}</text>
  96 + <text class="td td-id">{{ row.labelId }}</text>
  97 + <text class="td td-printed">{{ row.printedAt }}</text>
  98 + <text class="td td-expires">{{ row.expiryDate }}</text>
  99 + <view class="td td-action">
  100 + <view class="reprint-btn-sm" @click="handleReprint(row)">
  101 + <AppIcon name="printer" size="sm" color="white" />
  102 + </view>
  103 + </view>
  104 + </view>
  105 + </view>
  106 + </view>
59 107 </scroll-view>
60 108  
61 109 <SideMenu v-model="isMenuOpen" />
... ... @@ -68,20 +116,16 @@ import AppIcon from &#39;../../components/AppIcon.vue&#39;
68 116 import SideMenu from '../../components/SideMenu.vue'
69 117 import LocationPicker from '../../components/LocationPicker.vue'
70 118 import { getStatusBarHeight } from '../../utils/statusBar'
  119 +import { printLogList } from '../../utils/printLog'
71 120  
72 121 const statusBarHeight = getStatusBarHeight()
73 122 const isMenuOpen = ref(false)
  123 +const viewMode = ref<'card' | 'list'>('card')
74 124  
75   -const printLogData = ref([
76   - { labelId: '1-251201', productName: 'Whole Milk', category: 'Dairy', template: '2"x2" Basic', printedAt: '2024-03-20 09:30 AM', printedBy: 'Alice Johnson', location: 'Downtown Store (101)', expiryDate: '2024-03-27' },
77   - { labelId: '2-251201', productName: 'Ground Beef', category: 'Meat', template: '2"x2" Basic', printedAt: '2024-03-20 10:15 AM', printedBy: 'Bob Smith', location: 'Uptown Store (102)', expiryDate: '2024-03-23' },
78   - { labelId: '3-251201', productName: 'Croissant', category: 'Bakery', template: '2"x2" Basic', printedAt: '2024-03-19 14:00 PM', printedBy: 'Charlie Brown', location: 'Downtown Store (101)', expiryDate: '2024-03-20' },
79   - { labelId: '4-251201', productName: 'Caesar Salad', category: 'Deli', template: '2"x6" G\'n\'G !!!', printedAt: '2024-03-18 11:45 AM', printedBy: 'Alice Johnson', location: 'Downtown Store (101)', expiryDate: '2024-03-21' },
80   - { labelId: '5-251201', productName: 'Orange Juice', category: 'Beverage', template: '2"x2" Basic', printedAt: '2024-03-18 08:20 AM', printedBy: 'Bob Smith', location: 'Airport Kiosk (201)', expiryDate: '2024-03-25' },
81   -])
  125 +const printLogData = ref(printLogList)
82 126  
83 127 const handleReprint = (row: any) => {
84   - uni.showToast({ title: 'Reprint: ' + row.productName, icon: 'none' })
  128 + uni.navigateTo({ url: `/pages/more/print-detail?labelId=${encodeURIComponent(row.labelId)}` })
85 129 }
86 130  
87 131 const goBack = () => {
... ... @@ -250,4 +294,125 @@ const goBack = () =&gt; {
250 294 font-weight: 600;
251 295 color: #fff;
252 296 }
  297 +
  298 +.view-toggle {
  299 + display: flex;
  300 + gap: 12rpx;
  301 + padding: 16rpx 28rpx;
  302 + background: #fff;
  303 + border-bottom: 1rpx solid #e5e7eb;
  304 +}
  305 +
  306 +.toggle-btn {
  307 + flex: 1;
  308 + display: flex;
  309 + align-items: center;
  310 + justify-content: center;
  311 + gap: 8rpx;
  312 + padding: 16rpx 24rpx;
  313 + border-radius: 12rpx;
  314 + background: #f3f4f6;
  315 + transition: all 0.2s;
  316 +}
  317 +
  318 +.toggle-btn.active {
  319 + background: linear-gradient(135deg, #1F3A8A, #1447E6);
  320 +}
  321 +
  322 +.toggle-btn.active .toggle-text {
  323 + color: #fff;
  324 +}
  325 +
  326 +.toggle-text {
  327 + font-size: 26rpx;
  328 + font-weight: 500;
  329 + color: #6b7280;
  330 +}
  331 +
  332 +.log-table-wrap {
  333 + background: #fff;
  334 + border-radius: 20rpx;
  335 + overflow-x: auto;
  336 + overflow-y: hidden;
  337 + -webkit-overflow-scrolling: touch;
  338 + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
  339 + border: 1rpx solid #e5e7eb;
  340 +}
  341 +
  342 +/* 确保横向滚动条可见(H5/浏览器) */
  343 +.log-table-wrap::-webkit-scrollbar {
  344 + height: 6px;
  345 +}
  346 +.log-table-wrap::-webkit-scrollbar-thumb {
  347 + background: #d1d5db;
  348 + border-radius: 3px;
  349 +}
  350 +.log-table-wrap::-webkit-scrollbar-track {
  351 + background: #f3f4f6;
  352 +}
  353 +
  354 +.log-table {
  355 + width: max-content;
  356 + min-width: 100%;
  357 +}
  358 +
  359 +.log-table-header {
  360 + display: flex;
  361 + padding: 24rpx 28rpx;
  362 + gap: 16rpx;
  363 + background: #fafbfc;
  364 + border-bottom: 1rpx solid #e5e7eb;
  365 + font-size: 24rpx;
  366 + font-weight: 600;
  367 + color: #6b7280;
  368 +}
  369 +
  370 +.log-table-row {
  371 + display: flex;
  372 + padding: 24rpx 28rpx;
  373 + gap: 16rpx;
  374 + border-bottom: 1rpx solid #e5e7eb;
  375 + align-items: center;
  376 + font-size: 26rpx;
  377 +}
  378 +
  379 +.log-table-row:last-child {
  380 + border-bottom: none;
  381 +}
  382 +
  383 +.th, .td {
  384 + flex-shrink: 0;
  385 + white-space: nowrap;
  386 +}
  387 +
  388 +.th-product, .td-product { flex: 0 0 auto; min-width: 140rpx; }
  389 +.th-id, .td-id { flex: 0 0 auto; min-width: 140rpx; }
  390 +.th-printed, .td-printed { flex: 0 0 auto; min-width: 200rpx; }
  391 +.th-expires, .td-expires { flex: 0 0 auto; min-width: 200rpx; }
  392 +.th-action, .td-action {
  393 + flex: 0 0 80rpx;
  394 + width: 80rpx;
  395 + min-width: 80rpx;
  396 + display: flex;
  397 + align-items: center;
  398 + justify-content: center;
  399 +}
  400 +
  401 +.td-product { color: #111827; font-weight: 500; }
  402 +.td-id { color: #6b7280; font-size: 24rpx; }
  403 +.td-printed, .td-expires { color: #4b5563; }
  404 +
  405 +.reprint-btn-sm {
  406 + display: inline-flex;
  407 + align-items: center;
  408 + justify-content: center;
  409 + width: 64rpx;
  410 + height: 64rpx;
  411 + background: linear-gradient(135deg, #1F3A8A, #1447E6);
  412 + border-radius: 12rpx;
  413 +}
  414 +
  415 +.reprint-btn-sm:active {
  416 + opacity: 0.9;
  417 +}
253 418 </style>
... ...
美国版/Food Labeling Management App UniApp/src/pages/store-select/store-select.vue
... ... @@ -48,7 +48,7 @@
48 48 </view>
49 49 </view>
50 50  
51   - <view class="bottom-bar" :style="{ paddingBottom: (bottomSafeArea + 20) + 'px' }">
  51 + <view class="bottom-bar" :style="{ paddingBottom: (bottomSafeArea + 24) + 'px' }">
52 52 <view
53 53 class="confirm-btn"
54 54 :class="{ disabled: !selectedStore }"
... ... @@ -96,10 +96,12 @@ const handleConfirm = () =&gt; {
96 96 .page {
97 97 min-height: 100vh;
98 98 background: #f9fafb;
99   - padding-bottom: 200rpx;
  99 + display: flex;
  100 + flex-direction: column;
100 101 }
101 102  
102 103 .header-hero {
  104 + flex-shrink: 0;
103 105 background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-dark));
104 106 padding: 16rpx 32rpx 32rpx;
105 107 }
... ... @@ -136,7 +138,11 @@ const handleConfirm = () =&gt; {
136 138 }
137 139  
138 140 .list {
  141 + flex: 1;
  142 + min-height: 0;
  143 + overflow-y: auto;
139 144 padding: 32rpx;
  145 + padding-bottom: 24rpx;
140 146 }
141 147  
142 148 .card {
... ... @@ -214,12 +220,8 @@ const handleConfirm = () =&gt; {
214 220 }
215 221  
216 222 .bottom-bar {
217   - position: fixed;
218   - bottom: 0;
219   - left: 0;
220   - right: 0;
221   - padding: 32rpx 48rpx;
222   - padding-bottom: 48rpx;
  223 + flex-shrink: 0;
  224 + padding: 24rpx 48rpx;
223 225 background: #ffffff;
224 226 border-top: 1rpx solid #e5e7eb;
225 227 }
... ...
美国版/Food Labeling Management App UniApp/src/static/chicken lable.png 0 → 100644

14.5 KB

美国版/Food Labeling Management App UniApp/src/static/chicken-lable.png 0 → 100644

14.5 KB

美国版/Food Labeling Management App UniApp/src/utils/printLog.ts 0 → 100644
  1 +/**
  2 + * Print Log 为唯一数据源,labelId 格式: X-YYMMDD
  3 + */
  4 +export interface PrintLogRecord {
  5 + labelId: string
  6 + productName: string
  7 + category: string
  8 + template: string
  9 + templateSize: string
  10 + templateName: string
  11 + printedAt: string
  12 + printedBy: string
  13 + location: string
  14 + expiryDate: string
  15 + qty: number
  16 + dateFull: string
  17 + time: string
  18 + labelType?: string
  19 + printer: string
  20 +}
  21 +
  22 +export const printLogList: PrintLogRecord[] = [
  23 + { labelId: '1-251201', productName: 'Whole Milk', category: 'Dairy', template: '2"x2" Basic', templateSize: '2"x2"', templateName: 'Basic', printedAt: '2024-03-20 09:30 AM', printedBy: 'Alice Johnson', location: 'Downtown Store (101)', expiryDate: '2024-03-27 14:30', qty: 1, dateFull: 'Mar 20, 2024', time: '09:30 AM', printer: 'Zebra ZD421' },
  24 + { labelId: '2-251201', productName: 'Ground Beef', category: 'Meat', template: '2"x2" Basic', templateSize: '2"x2"', templateName: 'Basic', printedAt: '2024-03-20 10:15 AM', printedBy: 'Bob Smith', location: 'Uptown Store (102)', expiryDate: '2024-03-23 09:45', qty: 1, dateFull: 'Mar 20, 2024', time: '10:15 AM', printer: 'Zebra ZD421' },
  25 + { labelId: '3-251201', productName: 'Croissant', category: 'Bakery', template: '2"x2" Basic', templateSize: '2"x2"', templateName: 'Basic', printedAt: '2024-03-19 14:00 PM', printedBy: 'Charlie Brown', location: 'Downtown Store (101)', expiryDate: '2024-03-20 16:00', qty: 1, dateFull: 'Mar 19, 2024', time: '02:00 PM', printer: 'Zebra ZD421' },
  26 + { labelId: '4-251201', productName: 'Caesar Salad', category: 'Deli', template: '2"x6" G\'n\'G !!!', templateSize: '2"x6"', templateName: "G'n'G", printedAt: '2024-03-18 11:45 AM', printedBy: 'Alice Johnson', location: 'Downtown Store (101)', expiryDate: '2024-03-21 11:30', qty: 1, dateFull: 'Mar 18, 2024', time: '11:45 AM', printer: 'Brother QL-820NWB' },
  27 + { labelId: '5-251201', productName: 'Orange Juice', category: 'Beverage', template: '2"x2" Basic', templateSize: '2"x2"', templateName: 'Basic', printedAt: '2024-03-18 08:20 AM', printedBy: 'Bob Smith', location: 'Airport Kiosk (201)', expiryDate: '2024-03-25 08:00', qty: 1, dateFull: 'Mar 18, 2024', time: '08:20 AM', printer: 'Brother QL-820NWB' },
  28 +]
  29 +
  30 +export function getRecordByLabelId(labelId: string): PrintLogRecord | undefined {
  31 + return printLogList.find(r => r.labelId === labelId)
  32 +}
  33 +
  34 +/** 生成下一个 labelId,格式与 print log 一致 X-YYMMDD */
  35 +export function generateNextLabelId(): string {
  36 + const now = new Date()
  37 + const yy = String(now.getFullYear()).slice(-2)
  38 + const mm = String(now.getMonth() + 1).padStart(2, '0')
  39 + const dd = String(now.getDate()).padStart(2, '0')
  40 + const datePart = yy + mm + dd
  41 + const todayRecords = printLogList.filter(r => {
  42 + const parts = r.labelId.split('-')
  43 + return parts.length === 2 && parts[1] === datePart
  44 + })
  45 + const maxSeq = todayRecords.length > 0
  46 + ? Math.max(...todayRecords.map(r => parseInt(r.labelId.split('-')[0], 10)))
  47 + : 0
  48 + return `${maxSeq + 1}-${datePart}`
  49 +}
... ...
美国版/Food Labeling Management App UniApp/src/utils/printRecords.ts
1 1 export interface PrintRecord {
2 2 id: string
  3 + labelId: string
3 4 productName: string
4 5 category: string
5 6 qty: number
... ... @@ -14,14 +15,14 @@ export interface PrintRecord {
14 15 }
15 16  
16 17 export const printRecordsList: PrintRecord[] = [
17   - { id: '1', productName: 'Chicken Sandwich', category: 'Sandwich', qty: 2, userName: 'John Smith', date: '12/04', dateFull: 'Dec 4, 2025', time: '10:45 AM', templateSize: '2"x6"', templateName: "G'n'G", printer: 'Zebra ZD421' },
18   - { id: '2', productName: 'Chicken', category: 'Meat', qty: 1, userName: 'John Smith', date: '12/04', dateFull: 'Dec 4, 2025', time: '10:32 AM', labelType: 'Defrost', templateSize: '2"x2"', templateName: 'Basic', printer: 'Zebra ZD421' },
19   - { id: '3', productName: 'Caesar Salad', category: 'Salads', qty: 3, userName: 'Jane Doe', date: '12/04', dateFull: 'Dec 4, 2025', time: '09:15 AM', templateSize: '2"x4"', templateName: "G'n'G", printer: 'Brother QL-820NWB' },
20   - { id: '4', productName: 'Beef', category: 'Meat', qty: 1, userName: 'John Smith', date: '12/03', dateFull: 'Dec 3, 2025', time: '4:20 PM', labelType: 'Heated', templateSize: '2"x2"', templateName: 'Basic', printer: 'Zebra ZD421' },
21   - { id: '5', productName: 'Cheese Burger', category: 'Sandwich', qty: 2, userName: 'Jane Doe', date: '12/03', dateFull: 'Dec 3, 2025', time: '3:45 PM', templateSize: '2"x6"', templateName: "G'n'G", printer: 'Brother QL-820NWB' },
22   - { id: '6', productName: 'Ice Cream', category: 'Frozen', qty: 1, userName: 'John Smith', date: '12/03', dateFull: 'Dec 3, 2025', time: '2:30 PM', labelType: 'Vanilla', templateSize: '2"x2"', templateName: 'Storage', printer: 'Epson TM-T88VI' },
23   - { id: '7', productName: 'Milk', category: 'Dairy', qty: 1, userName: 'Jane Doe', date: '12/03', dateFull: 'Dec 3, 2025', time: '11:00 AM', templateSize: '2"x2"', templateName: 'Basic', printer: 'Zebra ZD421' },
24   - { id: '8', productName: 'Turkey Club', category: 'Sandwich', qty: 1, userName: 'John Smith', date: '12/02', dateFull: 'Dec 2, 2025', time: '1:20 PM', templateSize: '2"x6"', templateName: "G'n'G", printer: 'Brother QL-820NWB' },
  18 + { id: '1', labelId: '1-251204', productName: 'Chicken Sandwich', category: 'Sandwich', qty: 2, userName: 'John Smith', date: '12/04', dateFull: 'Dec 4, 2025', time: '10:45 AM', templateSize: '2"x6"', templateName: "G'n'G", printer: 'Zebra ZD421' },
  19 + { id: '2', labelId: '2-251204', productName: 'Chicken', category: 'Meat', qty: 1, userName: 'John Smith', date: '12/04', dateFull: 'Dec 4, 2025', time: '10:32 AM', labelType: 'Defrost', templateSize: '2"x2"', templateName: 'Basic', printer: 'Zebra ZD421' },
  20 + { id: '3', labelId: '3-251204', productName: 'Caesar Salad', category: 'Salads', qty: 3, userName: 'Jane Doe', date: '12/04', dateFull: 'Dec 4, 2025', time: '09:15 AM', templateSize: '2"x4"', templateName: "G'n'G", printer: 'Brother QL-820NWB' },
  21 + { id: '4', labelId: '4-251203', productName: 'Beef', category: 'Meat', qty: 1, userName: 'John Smith', date: '12/03', dateFull: 'Dec 3, 2025', time: '4:20 PM', labelType: 'Heated', templateSize: '2"x2"', templateName: 'Basic', printer: 'Zebra ZD421' },
  22 + { id: '5', labelId: '5-251203', productName: 'Cheese Burger', category: 'Sandwich', qty: 2, userName: 'Jane Doe', date: '12/03', dateFull: 'Dec 3, 2025', time: '3:45 PM', templateSize: '2"x6"', templateName: "G'n'G", printer: 'Brother QL-820NWB' },
  23 + { id: '6', labelId: '6-251203', productName: 'Ice Cream', category: 'Frozen', qty: 1, userName: 'John Smith', date: '12/03', dateFull: 'Dec 3, 2025', time: '2:30 PM', labelType: 'Vanilla', templateSize: '2"x2"', templateName: 'Storage', printer: 'Epson TM-T88VI' },
  24 + { id: '7', labelId: '7-251203', productName: 'Milk', category: 'Dairy', qty: 1, userName: 'Jane Doe', date: '12/03', dateFull: 'Dec 3, 2025', time: '11:00 AM', templateSize: '2"x2"', templateName: 'Basic', printer: 'Zebra ZD421' },
  25 + { id: '8', labelId: '8-251202', productName: 'Turkey Club', category: 'Sandwich', qty: 1, userName: 'John Smith', date: '12/02', dateFull: 'Dec 2, 2025', time: '1:20 PM', templateSize: '2"x6"', templateName: "G'n'G", printer: 'Brother QL-820NWB' },
25 26 ]
26 27  
27 28 export function getRecordById(id: string): PrintRecord | undefined {
... ...