diff --git a/.gitignore b/.gitignore index 38b0a57eac2721477f0e6fe8c53fbee450077c2d..097d73aecc6d23d634d382d1b10a7e757ac629db 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ yarn-error.log # The following patterns were generated by expo-cli expo-env.d.ts -# @end expo-cli \ No newline at end of file +# @end expo-cli +.claude/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 33f41dd1906ca6da5a24087cb281546ea39795b8..cd06b73fa2d9411d2af58b72b363b3e461be9643 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -34,5 +34,8 @@ "[typescriptreact]": { "editor.defaultFormatter": "vscode.typescript-language-features" }, - "i18n-ally.sourceLanguage": "zh" + "i18n-ally.sourceLanguage": "zh", + "[typescript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + } } diff --git a/src/api/common/http.tsx b/src/api/common/http.tsx index 6d98128cdf43380148da828fb6e9099f1351fb65..e6b6b1190a19f37cfce265c15eaa68aa22481758 100644 --- a/src/api/common/http.tsx +++ b/src/api/common/http.tsx @@ -8,7 +8,7 @@ import axios, { } from 'axios'; import { Platform } from 'react-native'; -import { type ResultData, ResultEnum } from '@/api/types'; +import { type ResultData, ResultEnum, type ResultPage } from '@/api/types'; import { signOut } from '@/lib/auth'; import { getToken, getUniqueId } from '@/lib/auth/utils'; import { error, hideLoading } from '@/lib/message'; @@ -120,6 +120,9 @@ class RequestHttp { ): Promise> { return this.service.post(url, params, _object); } + getGridList(url: string, params?: object, _object = {}): Promise { + return this.service.get(url, { params, ..._object }); + } postForm(url: string, params?: object): Promise { return this.service.postForm(url, params); } diff --git a/src/api/modules/common.ts b/src/api/modules/common.ts index f9a669d969aa54aa2265071052c3a54e419a8e09..9166e588966f67a4c25ae200289fe0e615a7af75 100644 --- a/src/api/modules/common.ts +++ b/src/api/modules/common.ts @@ -1,24 +1,82 @@ +import { + getBrand, + getBundleId, + getDeviceId, + getSystemName, + getSystemVersion, + getVersion, +} from 'react-native-device-info'; + import http from '@/api/common/http'; +import { + type DeviceInfo, + type ResultData, + type VersionInfo, +} from '@/api/types'; import { isWeb } from '@/lib'; -import { DeviceInfo, VersionInfo } from '../types'; -import { getSystemName, getSystemVersion, getBrand, getDeviceId, getBundleId, getVersion } from 'react-native-device-info'; +import { type SmLov } from '@/types'; +/** 查询最新版本信息 */ export const queryLatestVersion = () => { - return http.get( - `/api/SmApplicationVersion/latest` - ); + return http.get('/api/SmApplicationVersion/latest'); }; + +/** 记录设备信息 */ export const recordDevice = async (uniqueId: string) => { - if (uniqueId !== '' && !isWeb) { - let param: DeviceInfo = { - UUID: uniqueId, - Platform: getSystemName(), - Version: getSystemVersion(), - Brand: getBrand(), - Model: getDeviceId(), - BundleId: getBundleId(), - BundleVersion: getVersion(), - }; - await http.post(`api/SmApplicationDevice/Record`, param); - } -}; \ No newline at end of file + if (!uniqueId || isWeb) return; + + const param: DeviceInfo = { + UUID: uniqueId, + Platform: getSystemName(), + Version: getSystemVersion(), + Brand: getBrand(), + Model: getDeviceId(), + BundleId: getBundleId(), + BundleVersion: getVersion(), + }; + + return http.post('/api/SmApplicationDevice/Record', param); +}; + +/** + * 按过滤条件查询 + * @param moduleCode 模块代码 + * @param params 查询参数 + * @param filter 过滤条件 + * @returns 查询结果列表 + */ +export const queryByFilter = ( + moduleCode: string, + params: Record, + filter: any +) => + http.getGridList(`/api/Common/QueryByFilter/${moduleCode}`, params, { + filter, + }); + +/** + * 查询详情 + * @param url 查询URL + * @param id 记录ID + * @returns 查询结果 + */ + +export const queryDetail = ( + url: string, + id: string +): Promise> => { + return http.get(`${url}/${id}`); +}; + +export const queryLov = (code: string) => { + return http.get(`/api/SmLov/QueryByCode/${code}`); +}; + +/** + * 获取下拉表格数据 + * @param params 查询参数 + * @returns 下拉表格数据 + */ +export const getComboGridData = (params: Record) => { + return http.post('/api/Common/GetComboGridData', params); +}; diff --git a/src/api/modules/login.ts b/src/api/modules/login.ts index d170e599e154ca39779c90b08e3a8526bb546ec0..abeb35efc3c68725d36bf197ae2ff4ab91b29560 100644 --- a/src/api/modules/login.ts +++ b/src/api/modules/login.ts @@ -1,25 +1,31 @@ import http from '@/api/common/http'; -// import { ReqLogin, ResLogin } from "@/api/interface/index"; -interface ResLogin { - Token: string; + +/** 用户信息 */ +export interface UserInfo { + UserName: string; + WeekName: string; UserId: string; - UserInfo: UserInfo; + UserType: string; + AvatarFileId: string; } + +/** 登录请求参数 */ export interface ReqLogin { UserAccount: string; Password: string; } -export type UserInfo = { - UserName: string; - WeekName: string; +/** 登录响应结果 */ +interface ResLogin { + Token: string; UserId: string; - UserType: string; - AvatarFileId: string; -}; -export const loginApi = (params: ReqLogin) => { - return http.post(`api/Authorize/Login`, params); -}; -export const currentUserApi = () => { - return http.get(`api/Authorize/CurrentUser`); -}; + UserInfo: UserInfo; +} + +/** 用户登录 */ +export const loginApi = (params: ReqLogin) => + http.post('/api/Authorize/Login', params); + +/** 获取当前用户信息 */ +export const currentUserApi = () => + http.get('/api/Authorize/CurrentUser'); diff --git a/src/api/types.ts b/src/api/types.ts index 8324a90326f7b3fe06738d9ff0c5426fbb4b8264..1b6616255f89eac000943c9cc224794f5a7d1e44 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -21,12 +21,15 @@ export interface DataRow { hasNext: boolean; rows: T[]; } -export interface ResultData { - code: number; - msg: string; - data: T; +export interface ResultPage { + current: number; + data: []; + message: string; + pageSize: number; + total: number; + pageCount: number; + status: number; success: boolean; - time: string; } export interface Result { Status: number; @@ -49,7 +52,6 @@ export type DeviceInfo = { BundleVersion: string; }; - export interface VersionInfo { Platform?: string; VersionNo?: string; @@ -74,4 +76,4 @@ export interface VersionInfo { UpdateTime?: null; ModuleCode?: null; ID?: string; -} \ No newline at end of file +} diff --git a/src/app/(app)/index.tsx b/src/app/(app)/index.tsx index 1af8c73e00ef864a2ab77d9b995f0fc07e65650e..f377415bfcf5ad45de565667f715a8943126ba4b 100644 --- a/src/app/(app)/index.tsx +++ b/src/app/(app)/index.tsx @@ -74,10 +74,8 @@ const Home: React.FC = () => { const userInfo = user.use.userInfo(); const fetchUserInfo = user.use.fetchUserInfo(); useEffect(() => { - if (userInfo == null) { - fetchUserInfo(); - } - }, [userInfo, fetchUserInfo]); + fetchUserInfo(); + }, []); const router = useRouter(); return ( diff --git a/src/app/(repair)/_layout.tsx b/src/app/(repair)/_layout.tsx index f4e20bae7d9c3e0c70448702c014aa002b782cf4..6a25158b04b9b7c38fa608fe5a3219812be14981 100644 --- a/src/app/(repair)/_layout.tsx +++ b/src/app/(repair)/_layout.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { TabLayout, type TabConfig } from '@/components/tabs'; +import { type TabConfig, TabLayout } from '@/components/tabs'; import { Analysis, Equipment, diff --git a/src/app/(repair)/equipment.tsx b/src/app/(repair)/equipment.tsx index 1c859ce30d396f46b652c757063617166643fabf..6a78c24949b04d28bd34a1c44cca263f361ff4fb 100644 --- a/src/app/(repair)/equipment.tsx +++ b/src/app/(repair)/equipment.tsx @@ -1,39 +1,63 @@ -import { LinearGradient } from 'expo-linear-gradient'; +import { Env } from '@env'; import { useRouter } from 'expo-router'; -import React, { useState } from 'react'; -import { ScrollView, TextInput, TouchableOpacity } from 'react-native'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { Image, ScrollView, TextInput, TouchableOpacity } from 'react-native'; +import { queryByFilter } from '@/api'; +import { RefreshListView } from '@/components'; import { NavHeader, Text, View } from '@/components/ui'; -import { FontAwesome } from '@/components/ui/icons'; +import { FontAwesome, GroupEnum } from '@/components/ui/icons'; import { useAppColorScheme } from '@/lib/hooks'; +import { type Equipment, type EquipmentStatus } from '@/types'; + +const PageSize = 10; +const moduleCode = 'EM_EQUIPMENT_INFO_MNG'; -// 筛选标签类型 type FilterType = 'all' | 'running' | 'repairing' | 'fault'; -// 设备状态类型 -type EquipmentStatus = 'running' | 'repairing' | 'fault'; - -// 设备数据类型 -type Equipment = { - id: string; - name: string; - code: string; - icon: string; - location: string; - installDate: string; - status: EquipmentStatus; - health: number; - repairCount: number; - maintenanceInfo: string; - gradientColors: [string, string]; +const STATUS_CONFIG = { + running: { + bg: 'bg-emerald-50', + text: '#059669', + border: 'border-emerald-200', + icon: 'check-circle', + label: '运行中', + }, + repairing: { + bg: 'bg-amber-50', + text: '#d97706', + border: 'border-amber-200', + icon: 'wrench', + label: '维修中', + }, + fault: { + bg: 'bg-red-50', + text: '#dc2626', + border: 'border-red-200', + icon: 'exclamation-triangle', + label: '故障', + }, + default: { + bg: 'bg-gray-100', + text: '#6b7280', + border: 'border-gray-200', + icon: 'question-circle', + label: '未知', + }, +}; + +const getStatusConfig = (status: EquipmentStatus | null | undefined) => { + return ( + STATUS_CONFIG[status as keyof typeof STATUS_CONFIG] || STATUS_CONFIG.default + ); }; -// 筛选按钮组件 type FilterButtonProps = { label: string; count: number; active: boolean; onPress: () => void; + activeColor?: string; }; const FilterButton: React.FC = ({ @@ -41,272 +65,272 @@ const FilterButton: React.FC = ({ count, active, onPress, + activeColor, }) => ( {label} + + {count} - + ); -// 设备卡片组件 -type EquipmentCardProps = { - equipment: Equipment; - onPress: () => void; -}; +export default function EquipmentView() { + const router = useRouter(); + const [searchText, setSearchText] = useState(''); + const [activeFilter, setActiveFilter] = useState('all'); + const { isDark } = useAppColorScheme(); + const [list, setList] = useState([]); + const [hasMore, setHasMore] = useState(true); + const pageNumRef = useRef(1); -const EquipmentCard: React.FC = ({ - equipment, - onPress, -}) => { - const getStatusConfig = (status: EquipmentStatus) => { - switch (status) { - case 'running': - return { - bgColor: 'rgba(82, 196, 26, 0.1)', - textColor: '#52c41a', - icon: 'circle', - label: '运行中', - }; - case 'repairing': - return { - bgColor: 'rgba(250, 173, 20, 0.1)', - textColor: '#faad14', - icon: 'wrench', - label: '维修中', - }; - case 'fault': - return { - bgColor: 'rgba(245, 34, 45, 0.1)', - textColor: '#f5222d', - icon: 'exclamation-circle', - label: '故障', - }; + const loadData = async (append = false) => { + const page = append ? pageNumRef.current + 1 : 1; + pageNumRef.current = page; + + const filter = { + PageIndex: page, + PageSize: PageSize, + Conditions: '', + }; + + const { success, data, total } = await queryByFilter( + moduleCode, + {}, + filter + ); + + if (success && Array.isArray(data)) { + const newHasMore = list.length + data.length < (total || 0); + setHasMore(newHasMore); + setList((prev) => (append ? [...prev, ...data] : data)); + } else { + setHasMore(false); } }; - const statusConfig = getStatusConfig(equipment.status); + useEffect(() => { + loadData(false); + }, []); - return ( - - - {/* 顶部信息 */} - - - - - - - - {equipment.name} + const handleEquipmentPress = (item: Equipment) => { + router.push(`/equipment/${item.ID}`); + }; + + const onRefresh = () => { + pageNumRef.current = 1; + loadData(false); + }; + + const onLoadMore = () => { + if (!hasMore) return; + loadData(true); + }; + + const filterCounts = useMemo( + () => ({ + all: list.length, + running: list.filter((e) => e.Status === 'running').length, + repairing: list.filter((e) => e.Status === 'repairing').length, + fault: list.filter((e) => e.Status === 'fault').length, + }), + [list] + ); + + const filteredList = useMemo(() => { + if (activeFilter === 'all') return list; + return list.filter((e) => e.Status === activeFilter); + }, [list, activeFilter]); + + const renderEquipmentCard = ({ item }: { item: Equipment }) => { + const statusConfig = getStatusConfig(item.Status); + const healthRate = item.Health ?? 0; + const repairCount = item.RepairCount ?? 0; + + return ( + handleEquipmentPress(item)} + activeOpacity={0.7} + > + {/* 状态指示条 */} + + + + {/* 顶部区域:图片 + 信息 + 状态 */} + + {/* 设备图片 */} + + {item.ImageId ? ( + + ) : ( + + + + )} + + + {/* 设备信息 */} + + + {item.MachineName} - - 编号:{equipment.code} + + {item.MachineNo} - - - {equipment.location} - - - - {equipment.installDate} + + + {item.Location || '未设置'} - - - + + {/* 状态标签 */} + {statusConfig.label} - - {/* 底部统计 */} - - - - {equipment.health}% - - - 健康度 - - - - - {equipment.repairCount}次 - - - 本月维修 - - - - - {equipment.maintenanceInfo} - - - {equipment.status === 'repairing' - ? '维修状态' - : equipment.status === 'fault' - ? '优先级' - : '距保养'} - + {/* 分隔线 */} + + + {/* 底部统计区域 */} + + {/* 健康度 */} + + + + {healthRate} + + % + + 健康度 + {/* 健康度进度条 */} + + + + + + {/* 分隔 */} + + + {/* 维修次数 */} + + + + {repairCount} + + + + 本月维修 + + + {/* 分隔 */} + + + {/* 状态相关信息 */} + + + xxxxxx + + + {item.Status === 'repairing' + ? '维修状态' + : item.Status === 'fault' + ? '优先级' + : '距保养'} + + + + ); + }; + + const EmptyState = () => ( + + + - + + 暂无设备信息 + + 下拉刷新或添加新设备 + ); -}; - -const EquipmentList: React.FC = () => { - const router = useRouter(); - const [searchText, setSearchText] = useState(''); - const [activeFilter, setActiveFilter] = useState('all'); - const { isDark } = useAppColorScheme(); - - // 模拟设备数据 - const equipmentList: Equipment[] = [ - { - id: '1', - name: '数控机床 CNC-01', - code: 'EQ-2024-001', - icon: 'database', - location: 'A车间-1号线', - installDate: '2023-05-15', - status: 'running', - health: 98, - repairCount: 2, - maintenanceInfo: '5天', - gradientColors: ['#3b82f6', '#2563eb'], - }, - { - id: '2', - name: '注塑机 IM-03', - code: 'EQ-2024-003', - icon: 'cog', - location: 'B车间-2号线', - installDate: '2022-08-20', - status: 'repairing', - health: 65, - repairCount: 7, - maintenanceInfo: '进行中', - gradientColors: ['#f97316', '#ea580c'], - }, - { - id: '3', - name: '空压机 AC-05', - code: 'EQ-2024-005', - icon: 'map-marker', - location: '动力车间', - installDate: '2021-03-10', - status: 'fault', - health: 35, - repairCount: 15, - maintenanceInfo: '紧急', - gradientColors: ['#ef4444', '#dc2626'], - }, - { - id: '4', - name: '混料机 MX-08', - code: 'EQ-2024-008', - icon: 'flask', - location: 'C车间-配料区', - installDate: '2023-11-05', - status: 'running', - health: 95, - repairCount: 1, - maintenanceInfo: '12天', - gradientColors: ['#22c55e', '#16a34a'], - }, - { - id: '5', - name: '机械手臂 RB-02', - code: 'EQ-2024-012', - icon: 'map-marker', - location: 'A车间-组装线', - installDate: '2024-01-15', - status: 'running', - health: 99, - repairCount: 0, - maintenanceInfo: '30天', - gradientColors: ['#a855f7', '#9333ea'], - }, - ]; - - // 筛选设备 - const filteredEquipment = equipmentList.filter((item) => { - if (activeFilter === 'all') return true; - return item.status === activeFilter; - }); - - const filterCounts = { - all: equipmentList.length, - running: equipmentList.filter((e) => e.status === 'running').length, - repairing: equipmentList.filter((e) => e.status === 'repairing').length, - fault: equipmentList.filter((e) => e.status === 'fault').length, - }; return ( @@ -315,27 +339,23 @@ const EquipmentList: React.FC = () => { title="设备" leftShown={false} right={ - - { - router.push(`/equipment/add`); - }} - > - - - + router.push('/equipment/add')} + > + + } /> - {/* 搜索和筛选 - 固定在顶部 */} - + {/* 搜索和筛选区域 */} + {/* 搜索框 */} - + { style={{ position: 'absolute', left: 12, top: 12, zIndex: 1 }} /> @@ -361,70 +381,61 @@ const EquipmentList: React.FC = () => { {/* 筛选标签 */} - - setActiveFilter('all')} - /> - setActiveFilter('running')} - /> - setActiveFilter('repairing')} - /> - setActiveFilter('fault')} + setActiveFilter('all')} + /> + setActiveFilter('running')} + activeColor={STATUS_CONFIG.running.text} + /> + setActiveFilter('repairing')} + activeColor={STATUS_CONFIG.repairing.text} + /> + setActiveFilter('fault')} + activeColor={STATUS_CONFIG.fault.text} + /> + + - - - - - 筛选 - - - - + + 筛选 + + - {/* 设备列表 - 可滚动 */} - - {/* 设备列表 */} - - {filteredEquipment.map((equipment) => ( - { - router.push(`/equipment/${equipment.id}`); - }} - /> - ))} - - - {/* 底部空间 */} - - + {/* 设备列表 */} + item.ID} + showsVerticalScrollIndicator={false} + hasMore={hasMore} + ListEmptyComponent={EmptyState} + onRefresh={onRefresh} + onLoadMore={onLoadMore} + contentContainerStyle={{ padding: 12 }} + /> ); -}; - -export default EquipmentList; +} diff --git a/src/app/(repair)/index.tsx b/src/app/(repair)/index.tsx index 3a4679140530328fd1d908e3aacb4013d4c6803c..f6f4aec8d10ddf19a1cf86d1265b1e33a5c1ebb2 100644 --- a/src/app/(repair)/index.tsx +++ b/src/app/(repair)/index.tsx @@ -3,10 +3,11 @@ import { useRouter } from 'expo-router'; import React, { useEffect } from 'react'; import { ScrollView, TouchableOpacity } from 'react-native'; -import { NavHeader, Text, View } from '@/components/ui'; -import { FontAwesome } from '@/components/ui/icons'; +import { NavHeader, Text, View, FontAwesome, IconGroupEnum } from '@/components/ui'; + import { useAppColorScheme } from '@/lib/hooks'; import { userInfo as user } from '@/lib/user'; +import { isWeb } from '@/lib'; // 数据统计卡片组件 type StatCardProps = { @@ -196,9 +197,8 @@ const RepairHome: React.FC = () => { const fetchUserInfo = user.use.fetchUserInfo(); const { isDark } = useAppColorScheme(); useEffect(() => { - if (userInfo == null) - fetchUserInfo(); - }, [userInfo, fetchUserInfo]); + fetchUserInfo(); + }, []); const router = useRouter(); @@ -209,19 +209,32 @@ const RepairHome: React.FC = () => { title="首页" leftShown={false} right={ - router.push('/notification')} - > - - - 3 - - + + <> + router.push('/notification')} + > + + + 3 + + + {!isWeb && { + router.push('/qr-scanner') + + }} + > + + } + + } /> diff --git a/src/app/(repair)/repair-order.tsx b/src/app/(repair)/repair-order.tsx index 56b1efbebc0b690eff335375684c6af0c80d7f1f..3d0fe3cc6608963076597fe9e1f20dabc4b9012c 100644 --- a/src/app/(repair)/repair-order.tsx +++ b/src/app/(repair)/repair-order.tsx @@ -1,159 +1,20 @@ -import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import React, { useState } from 'react'; import { ScrollView, TouchableOpacity } from 'react-native'; -import { NavHeader, Text, View } from '@/components/ui'; +import { + FilterButton, + StatCard, + type StatCardData, + type WorkOrder, + WorkOrderCard, + type WorkOrderStatus, +} from '@/components/repair-order'; +import { NavHeader, View } from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; import { useAppColorScheme } from '@/lib/hooks'; -// 工单状态类型 -type WorkOrderStatus = 'urgent' | 'processing' | 'review' | 'completed' | 'all'; - -// 统计卡片数据类型 -type StatCardData = { - label: string; - value: number; - gradientColors: [string, string]; -}; - -// 工单数据类型 -type WorkOrder = { - id: string; - status: WorkOrderStatus; - statusLabel: string; - statusColor: string; - title: string; - equipmentName: string; - description: string; - assignee: string; - timeAgo: string; - deadline?: string; -}; - -// 统计卡片组件 -type StatCardProps = { - label: string; - value: number; - gradientColors: [string, string]; -}; - -const StatCard: React.FC = ({ - label, - value, - gradientColors, -}) => ( - - - {value} - {label} - - -); - -// 筛选按钮组件 -type FilterButtonProps = { - label: string; - count: number; - isActive: boolean; - onPress: () => void; -}; - -const FilterButton: React.FC = ({ - label, - count, - isActive, - onPress, -}) => ( - - - {label} {count} - - -); - -// 工单卡片组件 -type WorkOrderCardProps = { - order: WorkOrder; - onPress: () => void; -}; - -const WorkOrderCard: React.FC = ({ order, onPress }) => ( - - {/* 标题部分 */} - - - - - {order.statusLabel} - - - - {order.title} - - - - 设备:{order.equipmentName} - - - 故障描述:{order.description} - - - - {/* 底部信息 */} - - - - - - {order.assignee} - - - - - - {order.timeAgo} - - - - {order.deadline && ( - - {order.deadline} - - )} - - -); - -const RepairList: React.FC = () => { +const RepairOrder: React.FC = () => { const router = useRouter(); const [activeFilter, setActiveFilter] = useState('all'); const { isDark } = useAppColorScheme(); @@ -256,18 +117,16 @@ const RepairList: React.FC = () => { title="维修" leftShown={false} right={ - - console.log('分享')} - > - - - + router.push('/repair-order/add')} + > + + } /> @@ -342,4 +201,4 @@ const RepairList: React.FC = () => { ); }; -export default RepairList; +export default RepairOrder; diff --git a/src/app/analytics/index.tsx b/src/app/analytics/index.tsx index 840aa1fe1dd75a38034f3c65bd6dbc73a31291fa..f4684274f919b3b93bfb9967dc6eb49be3179e0a 100644 --- a/src/app/analytics/index.tsx +++ b/src/app/analytics/index.tsx @@ -6,9 +6,9 @@ import { OverView, Production, Quality, - Sales + Sales, } from '@/components/analytics'; -import { NavHeader, ScrollView, View, SafeAreaView } from '@/components/ui'; +import { NavHeader, ScrollView, View } from '@/components/ui'; // 图表图例项组件 // type ChartLegendItemProps = { @@ -40,17 +40,8 @@ const Analytics: React.FC = () => { const [activeSegment, setActiveSegment] = useState(0); return ( - - - // - // - // - // - // } - /> + + {/* 分段控制器 */} @@ -68,7 +59,7 @@ const Analytics: React.FC = () => { {activeSegment === 3 && } {activeSegment === 4 && } - + ); }; diff --git a/src/app/equipment/[id].tsx b/src/app/equipment/[id].tsx index ef1005ded6a68abc7dac8e3c39af4109895a315b..317e1dd98b75b819450c8b74a409aca234ba9213 100644 --- a/src/app/equipment/[id].tsx +++ b/src/app/equipment/[id].tsx @@ -1,228 +1,43 @@ +import { Env } from '@env'; import { LinearGradient } from 'expo-linear-gradient'; -// import { useLocalSearchParams, useRouter } from 'expo-router'; -import React from 'react'; -import { ScrollView, TouchableOpacity } from 'react-native'; +import { useLocalSearchParams } from 'expo-router'; +import React, { useEffect, useState } from 'react'; +import { Image, ScrollView, TouchableOpacity } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { NavHeader, Text, View } from '@/components/ui'; +import { queryDetail } from '@/api'; +import { + DocumentItem, + InfoRow, + MaintenancePlanItem, + RepairRecordItem, + StatCard, +} from '@/components/equipment'; +import { ImageGallery, NavHeader, Text, View } from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; import { useAppColorScheme } from '@/lib/hooks'; - -// 信息行组件 -type InfoRowProps = { - label: string; - value: string; - isLast?: boolean; -}; - -const InfoRow: React.FC = ({ label, value, isLast = false }) => ( - - {label} - - {value} - - -); - -// 统计卡片组件 -type StatCardProps = { - value: string; - label: string; - bgColor: string; - textColor: string; - isDark: boolean; -}; - -const StatCard: React.FC = ({ - value, - label, - bgColor, - textColor, - isDark, -}) => { - // 在暗黑模式下使用半透明的深色背景 - const darkBgColor = isDark ? 'rgba(64, 64, 64, 0.5)' : bgColor; - - return ( - - - {value} - - {label} - - ); -}; - -// 维修记录项组件 -type RepairRecordItemProps = { - title: string; - description: string; - assignee: string; - date: string; - status: string; - statusColor: string; - borderColor: string; - bgColor: string; - isDark: boolean; -}; - -const RepairRecordItem: React.FC = ({ - title, - description, - assignee, - date, - status, - statusColor, - borderColor, - bgColor, - isDark, -}) => { - // 在暗黑模式下使用半透明的深色背景 - const darkBgColor = isDark ? 'rgba(64, 64, 64, 0.3)' : bgColor; - - return ( - - - - - {title} - - - {description} - - - - {status} - - - - - - - {assignee} - - - - - - {date} - - - - - ); -}; - -// 保养计划项组件 -type MaintenancePlanItemProps = { - title: string; - date: string; - daysLeft: string; - borderColor: string; - bgColor: string; - textColor: string; - isDark: boolean; -}; - -const MaintenancePlanItem: React.FC = ({ - title, - date, - daysLeft, - borderColor, - bgColor, - textColor, - isDark, -}) => { - // 在暗黑模式下使用半透明的深色背景 - const darkBgColor = isDark ? 'rgba(64, 64, 64, 0.3)' : bgColor; - - return ( - - - - {title} - - - 下次保养时间:{date} - - - - {daysLeft} - - - ); -}; - -// 文档项组件 -type DocumentItemProps = { - icon: string; - iconColor: string; - iconBgColor: string; - title: string; - size: string; - date: string; - onPress: () => void; -}; - -const DocumentItem: React.FC = ({ - icon, - iconColor, - iconBgColor, - title, - size, - date, - onPress, -}) => ( - - - - - - - {title} - - - {size} · {date} - - - - -); +import { formatDateShort, formatFileSize, getFileIconInfo } from '@/lib/utils'; +import { type Equipment } from '@/types'; const EquipmentDetail: React.FC = () => { // const router = useRouter(); // const { id } = useLocalSearchParams(); const { isDark } = useAppColorScheme(); const insets = useSafeAreaInsets(); + const local = useLocalSearchParams<{ id: string }>(); + const [data, setData] = useState({} as Equipment); + + const loadData = async () => { + const { Success, Data } = await queryDetail( + '/api/EmEquipment', + local.id + ); + if (Success) setData(Data); + }; + + useEffect(() => { + loadData(); + }, []); // 快捷操作按钮数据 const quickActions = [ @@ -249,34 +64,6 @@ const EquipmentDetail: React.FC = () => { }, ]; - // 维修统计数据 - const repairStats = [ - { - value: '12次', - label: '累计维修', - bgColor: '#eff6ff', - textColor: '#1890ff', - }, - { - value: '8次', - label: '累计保养', - bgColor: '#f0fdf4', - textColor: '#52c41a', - }, - { - value: '2次', - label: '本月维修', - bgColor: '#fff7ed', - textColor: '#faad14', - }, - { - value: '¥8,500', - label: '维修成本', - bgColor: '#faf5ff', - textColor: '#a855f7', - }, - ]; - // 维修记录数据 const repairRecords = [ { @@ -339,118 +126,107 @@ const EquipmentDetail: React.FC = () => { }, ]; - // 文档数据 - const documents = [ - { - icon: 'file-pdf-o', - iconColor: '#ef4444', - iconBgColor: '#fef2f2', - title: '设备说明书.pdf', - size: '2.5 MB', - date: '2023-05-15', - }, - { - icon: 'file-word-o', - iconColor: '#3b82f6', - iconBgColor: '#eff6ff', - title: '维护手册.docx', - size: '1.8 MB', - date: '2023-05-15', - }, - { - icon: 'file-excel-o', - iconColor: '#22c55e', - iconBgColor: '#f0fdf4', - title: '备件清单.xlsx', - size: '0.3 MB', - date: '2023-05-15', - }, - ]; - return ( {/* 顶部导航 */} router.back()} - // right={ - // - // console.log('分享')} - // > - // - // - // console.log('更多')}> - // - // - // - // } + // onBack={() => router.back()} + right={ + + console.log('分享')} + > + + + console.log('更多')}> + + + + } /> {/* 设备基本信息卡片 */} - - - - - + {data && ( + + + + {/* */} + {data.ImageId ? ( + + ) : ( + + + + )} + + + {data.MachineName} + + + {data.MachineNo} + + + 位置:{data.Location ?? '未设置'} + + - - - 数控机床 CNC-01 - - - 设备编号:EQ-2024-001 + + + + 运行中 - 位置:A车间-1号线 - - - - 运行中 - - - - - - 98% - 健康度 - - - 2,845 - 运行时长(h) - - - 5天 - 距下次保养 + + + 98% + 健康度 + + + + 2,845 + + 运行时长(h) + + + 5天 + 距下次保养 + - - + + )} {/* 快捷操作 */} @@ -479,36 +255,55 @@ const EquipmentDetail: React.FC = () => { ))} - {/* 设备信息 */} - - - - - 设备信息 - - - - - - - - - - - + {/* 设备图片 */} + {data?.ImageIds && data.ImageIds.length > 0 && ( + + + + + + 设备图片 + + + + 共 {data.ImageIds.length} 张 + + + - + )} + {/* 设备信息 */} + {data && ( + + + + + 设备信息 + + + + + + + + + + + + + + )} {/* 维修统计 */} - 维修统计1 + 维修统计 - {repairStats.map((stat, index) => ( + {data.RepairStats?.map((stat, index) => ( { {/* 设备文档 */} - - - - - 设备文档 - - - - {documents.map((doc, index) => ( - console.log('下载', doc.title)} - /> - ))} + {data?.Attachments && data.Attachments.length > 0 && ( + + + + + 设备文档 + + + + {data.Attachments.map((attachment) => { + const iconInfo = getFileIconInfo(attachment.FileExt); + return ( + + console.log('下载', attachment.OriginalFileName) + } + /> + ); + })} + - + )} {/* 底部空间 */} diff --git a/src/app/equipment/add.tsx b/src/app/equipment/add.tsx index ee9f30c447c9d45c6fda73511de9806fc912f0d4..122aef1fcaf2cd14fb79899227770137f53caea9 100644 --- a/src/app/equipment/add.tsx +++ b/src/app/equipment/add.tsx @@ -133,7 +133,7 @@ const ImageUpload: React.FC = () => { export default function AddEquipment() { const router = useRouter(); - const { isDark } = useAppColorScheme(); + // const { isDark } = useAppColorScheme(); const [equipmentType, setEquipmentType] = useState(''); const [department, setDepartment] = useState(''); const insets = useSafeAreaInsets(); diff --git a/src/app/index.tsx b/src/app/index.tsx index 6f1333610e1575e3f803db3e67658add41fb6112..69aab80f251036ca4f49c352bf008ef76f9118fe 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,7 +1,6 @@ import { Redirect } from 'expo-router'; -import { - get as getUserInfoData, -} from '@/lib/user/utils'; + +import { get as getUserInfoData } from '@/lib/user/utils'; export default function Index() { const userInfo = getUserInfoData(); diff --git a/src/app/inventory/[id].tsx b/src/app/inventory/[id].tsx index 9f9de25cbf508e99b9bab8d4bb8aa41ad833cc96..d85ad30da2e4312b50f15445f5047988882607d4 100644 --- a/src/app/inventory/[id].tsx +++ b/src/app/inventory/[id].tsx @@ -1,8 +1,9 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { Dimensions, // Dimensions, TouchableOpacity, + useColorScheme, } from 'react-native'; import { LineChart } from 'react-native-chart-kit'; @@ -145,31 +146,42 @@ const InventoryDetail: React.FC = () => { legend: ['库存量', '安全库存'], }; - const chartConfig = { - backgroundGradientFrom: '#ffffff', - backgroundGradientTo: '#ffffff', - decimalPlaces: 0, - color: () => `rgba(59, 130, 246, 0.6)`, - labelColor: () => `rgba(107, 114, 128, 1)`, - style: { - borderRadius: 16, - }, - propsForDots: { - r: '4', - strokeWidth: '2', - stroke: '#3b82f6', - }, - }; + const colorScheme = useColorScheme(); + const isDark = colorScheme === 'dark'; + + const chartConfig = useMemo(() => { + return { + backgroundGradientFrom: isDark ? '#0b1220' : '#ffffff', + backgroundGradientTo: isDark ? '#0b1220' : '#ffffff', + decimalPlaces: 0, + color: () => + isDark ? `rgba(96, 165, 250, 0.8)` : `rgba(59, 130, 246, 0.6)`, + labelColor: () => + isDark ? `rgba(156, 163, 175, 1)` : `rgba(107, 114, 128, 1)`, + style: { + borderRadius: 16, + }, + propsForDots: { + r: '4', + strokeWidth: '2', + stroke: isDark ? '#60a5fa' : '#3b82f6', + }, + }; + }, [isDark]); const screenWidth = Dimensions.get('window').width - 40; // 考虑内边距 // 详情行组件 const DetailRow = ({ label = '', value = '', isLast = false }) => ( - {label} - {value} + + {label} + + + {value} + ); @@ -217,21 +229,25 @@ const InventoryDetail: React.FC = () => { {records.map((record, index) => ( - {record.type} - {record.date} + + {record.type} + + + {record.date} + 数量: {record.quantity} - + 操作人: {record.operator} - + 关联单据: {record.document} @@ -241,7 +257,9 @@ const InventoryDetail: React.FC = () => { case 'usage': return ( - 近期使用产品 + + 近期使用产品 + {usageProducts.map((product, index) => ( { className="mr-2 text-blue-500" /> - {product.name} - + + {product.name} + + {product.quantityPerUnit} - {product.monthlyConsumption} + + {product.monthlyConsumption} + ))} - 消耗预测 - + + 消耗预测 + + - 预计月消耗 - 约4,500个 + + 预计月消耗 + + + 约4,500个 + - 当前库存可用 - 约1.1个月 + + 当前库存可用 + + + 约1.1个月 + - 建议补货时间 - 2周内 + + 建议补货时间 + + + 2周内 + @@ -289,7 +325,7 @@ const InventoryDetail: React.FC = () => { }; return ( - + { contentContainerClassName="px-4 py-4 pb-20" > {/* 物料基本信息卡片 */} - + - + - {materialData.name} - + + {materialData.name} + + 编号: {materialData.id} - - + + {materialData.category} - - + + {materialData.tag} @@ -332,10 +370,12 @@ const InventoryDetail: React.FC = () => { {/* 库存状态 */} - + - 当前库存 - + + 当前库存 + + {materialData.currentStock.toLocaleString()} {materialData.unit} @@ -343,8 +383,10 @@ const InventoryDetail: React.FC = () => { - 安全库存 - + + 安全库存 + + {materialData.safetyStock.toLocaleString()} {materialData.unit} @@ -352,7 +394,9 @@ const InventoryDetail: React.FC = () => { - 状态 + + 状态 + { {/* 库存趋势图 */} - - 库存趋势 + + + 库存趋势 + { withDots={true} withShadow={false} segments={4} - - // /> {/* 详细信息选项卡 */} - - + + setActiveTab('info')} > 基本信息 @@ -448,7 +492,7 @@ const InventoryDetail: React.FC = () => { onPress={() => setActiveTab('records')} > 出入库记录 @@ -458,7 +502,7 @@ const InventoryDetail: React.FC = () => { onPress={() => setActiveTab('usage')} > 使用情况 @@ -469,11 +513,15 @@ const InventoryDetail: React.FC = () => { {/* 相关物料 */} - + - 相关物料 + + 相关物料 + - 查看全部 + + 查看全部 + @@ -489,24 +537,26 @@ const InventoryDetail: React.FC = () => { return ( - {material.name} - + + {material.name} + + 编号: {material.code} - + {material.quantity.toLocaleString()} - + 安全库存: {material.safetyStock.toLocaleString()} diff --git a/src/app/login.tsx b/src/app/login.tsx index 2830fb70c6bfe8bb62e0ad72895ce09183f4eba3..760eb4d296de1ef2b27041cbab9a26dbd80f22ce 100644 --- a/src/app/login.tsx +++ b/src/app/login.tsx @@ -32,8 +32,7 @@ export default function Login() { }); setUserInfo(Data.UserInfo); - router.replace("/"); - + router.replace('/'); } else error(Message!); }; return ( diff --git a/src/app/material/[id].tsx b/src/app/material/[id].tsx index 46d5358cdf49fa48f00b0809b701be4db22f8cc9..fddc70d05dd72ff942432995580eab89a15f4f25 100644 --- a/src/app/material/[id].tsx +++ b/src/app/material/[id].tsx @@ -180,14 +180,14 @@ const MaterialDetail = () => { // 样式常量 const styles = { - tab: 'text-sm font-medium text-gray-600', - tabActive: 'text-sm font-medium text-blue-600', + tab: 'text-sm font-medium text-gray-600 dark:text-gray-400', + tabActive: 'text-sm font-medium text-blue-600 dark:text-blue-400', tabTouchable: 'flex-1 items-center px-4 py-2', tabTouchableActive: 'flex-1 items-center border-b-2 border-blue-600 px-4 py-2', fastOption: 'items-center justify-center rounded-lg py-2', fastOptionWithMargin: 'mr-2 items-center justify-center rounded-lg py-2', - listItemBorder: 'border-b border-gray-100 py-3', + listItemBorder: 'border-b border-gray-100 py-3 dark:border-gray-700', listItemNoBorder: 'py-3', }; @@ -242,7 +242,7 @@ const MaterialDetail = () => { }; return ( - + { contentContainerStyle={{ padding: 16, paddingBottom: 80 }} > {/* 物料基本信息卡片 */} - + - + { /> - {materialData.name} - + + {materialData.name} + + 编号: {materialData.id} - - + + {materialData.category} - - + + {materialData.tag} @@ -292,37 +294,43 @@ const MaterialDetail = () => { {/* 库存状态 */} - + - 当前库存 + + 当前库存 + - + {materialData.currentStock} - + {materialData.unit} - 安全库存 + + 安全库存 + - + {materialData.safetyStock} - + {materialData.unit} - 状态 + + 状态 + - + {materialData.status} @@ -359,8 +367,8 @@ const MaterialDetail = () => { {/* 详细信息选项卡 */} - - + + { {/* 相关物料 */} - - 相关物料 + + + 相关物料 + {relatedMaterials.map((material, index) => ( ( - {label} - {value} + + {label} + + + {value} + ); @@ -428,11 +444,17 @@ interface RecordListItemProps { } const RecordListItem = ({ record, isLast }: RecordListItemProps) => ( - + - {record.type} - + + {record.type} + + 单号: {record.orderNumber} @@ -440,13 +462,15 @@ const RecordListItem = ({ record, isLast }: RecordListItemProps) => ( {record.quantity} - {record.date} + + {record.date} + @@ -482,7 +506,11 @@ const DocumentListItem = ({ document, isLast }: DocumentListItemProps) => { const iconColor = getDocIconColor(document.icon); return ( - + { group={GroupEnum.FontAwesome5} /> - {document.name} - + + {document.name} + + {document.type} · {document.size} · {document.uploadDate} @@ -515,17 +545,23 @@ const RelatedMaterialItem = ({ isLast, }: RelatedMaterialItemProps) => ( - {material.name} - + + {material.name} + + 编号: {material.code} | 规格: {material.spec} - 库存: {material.stock} + + 库存: {material.stock} + diff --git a/src/app/material/index.tsx b/src/app/material/index.tsx index c47233c5a5f1cf983fa7d1815ae1423a79af75be..8d236a487e8233c0d012d64e454d50c2ee4121d4 100644 --- a/src/app/material/index.tsx +++ b/src/app/material/index.tsx @@ -1,13 +1,9 @@ import { useRouter } from 'expo-router'; import React, { useState } from 'react'; -import { - StyleSheet, - TextInput, - TouchableOpacity, -} from 'react-native'; +import { StyleSheet, TextInput, TouchableOpacity } from 'react-native'; import { RefreshListView } from '@/components'; -import { NavHeader, ScrollView, Text, View, SafeAreaView } from '@/components/ui'; +import { NavHeader, ScrollView, Text, View } from '@/components/ui'; import { FontAwesome, GroupEnum } from '@/components/ui/icons'; type MaterialProps = { @@ -160,29 +156,37 @@ const Materials = () => { // 渲染物料项 const renderMaterialItem = ({ item }: { item: any }) => ( handleMaterialPress(item.id)} > - {item.name} - + + {item.name} + + 编码: {item.code} - + 库存: {item.stock} {item.unit} {item.status} @@ -205,7 +209,7 @@ const Materials = () => { ); return ( - + { } /> {/* 头部 */} - + {/* 搜索框 */} - + { group={GroupEnum.EvilIcons} /> { {categories.map((category) => ( handleCategorySelect(category.name)} > @@ -275,10 +279,7 @@ const Materials = () => { } /> */} {category.name} @@ -308,7 +309,7 @@ const Materials = () => { } /> - + ); }; diff --git a/src/app/notification/[id].tsx b/src/app/notification/[id].tsx index 7a365f982e715af4fbe349e904de6761023da4f1..a8729a8409d92bd1faef23d20135865bf200c3b5 100644 --- a/src/app/notification/[id].tsx +++ b/src/app/notification/[id].tsx @@ -4,6 +4,7 @@ import { StyleSheet, TouchableOpacity } from 'react-native'; import { NavHeader, ScrollView, Text, View } from '@/components/ui'; import { FontAwesome, GroupEnum } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; interface RelatedNotification { id: string; @@ -18,6 +19,7 @@ const NotificationDetail = () => { // const notificationType1 = route.params?.type || 'warning'; const local = useLocalSearchParams<{ type: string }>(); const notificationType = local.type; + const { isDark } = useAppColorScheme(); // 通知详情状态 const [notificationInfo, setNotificationInfo] = useState({ @@ -28,8 +30,8 @@ const NotificationDetail = () => { 'PCB板库存低于安全库存,当前库存数量为25个,安全库存为50个,请及时补货。\n\n低库存可能导致生产计划延误,建议在3天内完成补货。', icon: 'exclamation-triangle', iconBgColor: '#ef4444', // red-500 - badgeBg: 'bg-red-100', - badgeText: 'text-red-800', + badgeBg: 'bg-red-100 dark:bg-red-900/30', + badgeText: 'text-red-800 dark:text-red-300', badgeContent: '预警信息', actionText: '查看物料详情', relatedInfo: [ @@ -71,8 +73,8 @@ const NotificationDetail = () => { title: '库存预警', icon: 'exclamation-triangle', iconBgColor: '#ef4444', // red-500 - badgeBg: 'bg-red-100', - badgeText: 'text-red-800', + badgeBg: 'bg-red-100 dark:bg-red-900/30', + badgeText: 'text-red-800 dark:text-red-300', badgeContent: '预警信息', actionText: '查看物料详情', }; @@ -85,8 +87,8 @@ const NotificationDetail = () => { '您有一个新的生产任务 #PT20231129-02 需要处理。请在今天下午3点前完成相关准备工作。', icon: 'tasks', iconBgColor: '#22c55e', // green-500 - badgeBg: 'bg-green-100', - badgeText: 'text-green-800', + badgeBg: 'bg-green-100 dark:bg-green-900/30', + badgeText: 'text-green-800 dark:text-green-300', badgeContent: '任务提醒', actionText: '处理任务', }; @@ -99,8 +101,8 @@ const NotificationDetail = () => { '订单 #2023112801 已完成生产,等待发货。请安排物流部门进行后续处理。', icon: 'clipboard-check', iconBgColor: '#3b82f6', // blue-500 - badgeBg: 'bg-blue-100', - badgeText: 'text-blue-800', + badgeBg: 'bg-blue-100 dark:bg-blue-900/30', + badgeText: 'text-blue-800 dark:text-blue-300', badgeContent: '订单通知', actionText: '查看订单', }; @@ -113,8 +115,8 @@ const NotificationDetail = () => { '系统将于今晚22:00-23:00进行例行维护,请提前做好准备。维护期间系统将暂停服务。', icon: 'bell', iconBgColor: '#eab308', // yellow-500 - badgeBg: 'bg-yellow-100', - badgeText: 'text-yellow-800', + badgeBg: 'bg-yellow-100 dark:bg-yellow-900/30', + badgeText: 'text-yellow-800 dark:text-yellow-300', badgeContent: '系统通知', actionText: '了解详情', }; @@ -127,8 +129,8 @@ const NotificationDetail = () => { '11月生产效率分析报表已生成,请查看。本月生产效率较上月提升5.2%。', icon: 'chart-line', iconBgColor: '#a855f7', // purple-500 - badgeBg: 'bg-purple-100', - badgeText: 'text-purple-800', + badgeBg: 'bg-purple-100 dark:bg-purple-900/30', + badgeText: 'text-purple-800 dark:text-purple-300', badgeContent: '数据报表', actionText: '查看报表', }; @@ -140,8 +142,8 @@ const NotificationDetail = () => { content: '李工程师已加入您的生产团队,请及时安排工作。', icon: 'user-plus', iconBgColor: '#6366f1', // indigo-500 - badgeBg: 'bg-indigo-100', - badgeText: 'text-indigo-800', + badgeBg: 'bg-indigo-100 dark:bg-indigo-900/30', + badgeText: 'text-indigo-800 dark:text-indigo-300', badgeContent: '团队通知', actionText: '查看团队', }; @@ -154,8 +156,8 @@ const NotificationDetail = () => { '系统已更新至v1.0.5版本,新增多项功能和优化。主要更新内容包括:生产计划优化、库存预警阈值自定义、报表导出功能等。', icon: 'cogs', iconBgColor: '#f97316', // orange-500 - badgeBg: 'bg-orange-100', - badgeText: 'text-orange-800', + badgeBg: 'bg-orange-100 dark:bg-orange-900/30', + badgeText: 'text-orange-800 dark:text-orange-300', badgeContent: '系统更新', actionText: '查看更新', }; @@ -192,16 +194,24 @@ const NotificationDetail = () => { }; return ( - + - + - + } @@ -221,7 +231,7 @@ const NotificationDetail = () => { {/* 通知详情卡片 */} - + { - + {notificationInfo.title} - + {notificationInfo.time} - + {notificationInfo.date} - 详细内容 - + + 详细内容 + + {notificationInfo.content} {/* 相关信息 */} - - 相关信息 + + + 相关信息 + {notificationInfo.relatedInfo.map((item, index) => ( - + - {item.label} + + {item.label} + {item.value} @@ -285,20 +308,24 @@ const NotificationDetail = () => { - 标记为已读 + + 标记为已读 + {/* 相关通知 */} - 相关通知 - + + 相关通知 + + {relatedNotifications.map((item, index) => ( { - {item.title} - {item.time} + + {item.title} + + + {item.time} + - + {item.message} diff --git a/src/app/notification/index.tsx b/src/app/notification/index.tsx index 4cc6599d4789aa9c2afec37523cf3098ec0e3b10..2f9f63e2155c8c78019da3c1eb845a92c6832959 100644 --- a/src/app/notification/index.tsx +++ b/src/app/notification/index.tsx @@ -5,6 +5,7 @@ import { StyleSheet, TouchableOpacity } from 'react-native'; import { SegmentedControl, type SegmentedControlOption } from '@/components'; import { NavHeader, ScrollView, Text, View } from '@/components/ui'; import { FontAwesome, GroupEnum } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; // type TabType = 0 | 1 | 2 | 3; @@ -23,6 +24,7 @@ interface NotificationItem { const Notification = () => { const [selectedTabIndex, setSelectedTabIndex] = useState(0); const router = useRouter(); + const { isDark } = useAppColorScheme(); // 模拟通知数据 const notifications: NotificationItem[] = [ @@ -140,12 +142,8 @@ const Notification = () => { // 渲染通知项 const renderNotificationItem = (item: NotificationItem) => ( - handleViewDetail(item.id)} - > - + handleViewDetail(item.id)}> + { - {item.title} - {item.time} + + {item.title} + + + {item.time} + - {item.message} + + {item.message} + - {/* 查看详情 */} handleIgnore(item.id)} > - 忽略 + + 忽略 + @@ -190,13 +195,17 @@ const Notification = () => { { key: 'process-management1', label: '预警信息' }, ]; return ( - + - + } @@ -215,8 +224,10 @@ const Notification = () => { {/* 今日通知 */} {todayNotifications.length > 0 && ( <> - 今日通知 - + + 今日通知 + + {todayNotifications.map(renderNotificationItem)} @@ -225,8 +236,10 @@ const Notification = () => { {/* 昨日通知 */} {yesterdayNotifications.length > 0 && ( <> - 昨日通知 - + + 昨日通知 + + {yesterdayNotifications.map(renderNotificationItem)} @@ -235,8 +248,10 @@ const Notification = () => { {/* 更早通知 */} {earlierNotifications.length > 0 && ( <> - 更早 - + + 更早 + + {earlierNotifications.map(renderNotificationItem)} diff --git a/src/app/onboarding.tsx b/src/app/onboarding.tsx index 8da7fe76614e456557001bd1289c728f72b9db66..cd3722bf4f477f24b88f4650f550a65aa1284b3e 100644 --- a/src/app/onboarding.tsx +++ b/src/app/onboarding.tsx @@ -7,7 +7,7 @@ import { StatusBar, TouchableOpacity, useWindowDimensions, - ViewToken, + type ViewToken, } from 'react-native'; import { Text, View } from '@/components/ui'; @@ -33,12 +33,7 @@ const onboardingData: OnboardingItem[] = [ description: '数字化生产管控,实时掌握生产全流程', icon: 'industry', gradientColors: ['#3b82f6', '#2563eb'], - features: [ - '实时生产监控', - '生产计划排程', - '工单进度跟踪', - '产能分析优化', - ], + features: ['实时生产监控', '生产计划排程', '工单进度跟踪', '产能分析优化'], }, { id: '2', @@ -46,12 +41,7 @@ const onboardingData: OnboardingItem[] = [ description: '全流程质量管控,确保产品卓越品质', icon: 'check-circle', gradientColors: ['#22c55e', '#16a34a'], - features: [ - '质检流程管理', - '不良品追溯', - '质量数据分析', - '质量报告生成', - ], + features: ['质检流程管理', '不良品追溯', '质量数据分析', '质量报告生成'], }, { id: '3', @@ -59,12 +49,7 @@ const onboardingData: OnboardingItem[] = [ description: '预防性维护,保障设备高效运转', icon: 'cogs', gradientColors: ['#a855f7', '#9333ea'], - features: [ - '设备状态监控', - '预防性维护', - '故障快速响应', - '维修记录管理', - ], + features: ['设备状态监控', '预防性维护', '故障快速响应', '维修记录管理'], }, { id: '4', @@ -147,11 +132,7 @@ const Paginator: React.FC = ({ data, currentIndex }) => ( {data.map((_, index) => ( ))} @@ -214,9 +195,7 @@ export default function Onboarding() { className="absolute right-6 top-12 z-10" activeOpacity={0.7} > - - 跳过 - + 跳过 )} diff --git a/src/app/order/[id].tsx b/src/app/order/[id].tsx index 42f6c992b150686c72bf5913ae0ca83aa36c3f9c..2929bbfe31059bfea85a4ba30fdaba185b9a1612 100644 --- a/src/app/order/[id].tsx +++ b/src/app/order/[id].tsx @@ -1,17 +1,13 @@ import React, { useState } from 'react'; -import { - Image, - ScrollView, - Text, - TouchableOpacity, - View, -} from 'react-native'; +import { Image, ScrollView, Text, TouchableOpacity, View } from 'react-native'; import { NavHeader, SafeAreaView } from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; const OrderDetail = () => { const [activeTab, setActiveTab] = useState('product-list'); + const { isDark } = useAppColorScheme(); // 切换选项卡 const handleTabChange = (tabId: string) => { @@ -19,16 +15,24 @@ const OrderDetail = () => { }; return ( - + - + - + } @@ -36,11 +40,13 @@ const OrderDetail = () => { {/* 订单基本信息 */} - + - 订单 #2023112702 - - + + 订单 #2023112702 + + + 生产中 @@ -48,75 +54,103 @@ const OrderDetail = () => { - 客户 - 北京智能科技有限公司 + + 客户 + + + 北京智能科技有限公司 + - 联系人 - 张经理 + + 联系人 + + + 张经理 + - 联系电话 - 138****5678 + + 联系电话 + + + 138****5678 + - 订单金额 - ¥95,200 + + 订单金额 + + + ¥95,200 + - 订单日期 - 2023-11-27 + + 订单日期 + + + 2023-11-27 + - 交付日期 - 2023-12-10 + + 交付日期 + + + 2023-12-10 + - - 收货地址 - 北京市海淀区中关村科技园区8号楼5层 + + + 收货地址 + + + 北京市海淀区中关村科技园区8号楼5层 + {/* 选项卡 - 分段控制器样式 */} - + handleTabChange('product-list')} > 产品明细 handleTabChange('production-progress')} > 生产进度 handleTabChange('logistics-info')} > 物流信息 handleTabChange('payment-info')} > 付款信息 @@ -126,11 +160,11 @@ const OrderDetail = () => { {/* 选项卡内容区域 */} {/* 产品明细 */} {activeTab === 'product-list' && ( - + {/* 产品项目1 */} - + - + { /> - 智能手表 Pro - + + 智能手表 Pro + + 型号:SW-2023-Pro - ¥1,280 × 50 - ¥64,000 + + ¥1,280 × 50 + + + ¥64,000 + - - + + 规格:黑色 / 1.4英寸 / GPS+蜂窝网络 - + 备注:包装需使用防震材料,单独包装 {/* 产品项目2 */} - + - + { /> - 智能手表充电器 - + + 智能手表充电器 + + 型号:SWC-2023 - ¥120 × 60 - ¥7,200 + + ¥120 × 60 + + + ¥7,200 + - - + + 规格:白色 / 磁吸式 / 5W @@ -189,7 +235,7 @@ const OrderDetail = () => { {/* 产品项目3 */} - + { /> - 智能手表表带 - + + 智能手表表带 + + 型号:SWB-2023 - ¥200 × 120 - ¥24,000 + + ¥200 × 120 + + + ¥24,000 + - - + + 规格:米兰尼斯表带 / 黑色 / 42mm {/* 订单总计 */} - + - 商品总额 - ¥95,200 + + 商品总额 + + + ¥95,200 + - 运费 - ¥0 + + 运费 + + + ¥0 + - 订单总计 - ¥95,200 + + 订单总计 + + + ¥95,200 + @@ -234,15 +298,19 @@ const OrderDetail = () => { {/* 生产进度 */} {activeTab === 'production-progress' && ( - - 生产进度概览 + + + 生产进度概览 + - 总体进度 - 65% + + 总体进度 + + 65% - + { - 100% - 原料采购 + + 100% + + + 原料采购 + - 75% - 生产制造 + + 75% + + + 生产制造 + 0% - 质检包装 + + 质检包装 + - 生产时间线 + + 生产时间线 + {/* 时间线项目 */} - - 订单确认 - 2023-11-27 14:30 + + + 订单确认 + + + 2023-11-27 14:30 + - - 原料采购完成 - 2023-11-29 16:45 + + + 原料采购完成 + + + 2023-11-29 16:45 + - - 生产开始 - 2023-11-30 09:15 + + + 生产开始 + + + 2023-11-30 09:15 + - - 智能手表组装中 - 2023-12-02 11:30 - + + + 智能手表组装中 + + + 2023-12-02 11:30 + + 已完成35台,剩余15台 - - + + 质量检测 @@ -309,8 +405,8 @@ const OrderDetail = () => { - - + + 包装完成 @@ -318,7 +414,7 @@ const OrderDetail = () => { - + 发货 预计 2023-12-08 @@ -327,48 +423,66 @@ const OrderDetail = () => { {/* 物流信息 */} {activeTab === 'logistics-info' && ( - - - + + + 订单尚未发货,预计发货日期:2023-12-08 - + 发货后将更新物流信息 - 收货信息 + + 收货信息 + - 收货人 - 张经理 + + 收货人 + + + 张经理 + - 联系电话 - 138****5678 + + 联系电话 + + + 138****5678 + - 收货地址 - + + 收货地址 + + 北京市海淀区中关村科技园区8号楼5层 - 备注 - 请在工作日送货,并提前电话联系 + + 备注 + + + 请在工作日送货,并提前电话联系 + )} {/* 付款信息 */} {activeTab === 'payment-info' && ( - - 付款状态 + + + 付款状态 + - + { style={{ marginRight: 8 }} /> - 已支付定金 - + + 已支付定金 + + 剩余尾款待发货前支付 @@ -385,32 +501,58 @@ const OrderDetail = () => { - 订单总额 - ¥95,200 + + 订单总额 + + + ¥95,200 + - 已付定金(30%) - ¥28,560 + + 已付定金(30%) + + + ¥28,560 + - 待付尾款 - ¥66,640 + + 待付尾款 + + + ¥66,640 + - 付款记录 + + 付款记录 + - - - 付款时间 - 付款金额 - 付款方式 + + + + 付款时间 + + + 付款金额 + + + 付款方式 + - - 2023-11-27 - ¥28,560 - 银行转账 + + + 2023-11-27 + + + ¥28,560 + + + 银行转账 + diff --git a/src/app/privacy-policy.tsx b/src/app/privacy-policy.tsx index 47e64c1d82251656153d9d1ddd7940efce1db618..c4064da611a778d00be6c3ba6afdf331e76c2366 100644 --- a/src/app/privacy-policy.tsx +++ b/src/app/privacy-policy.tsx @@ -2,11 +2,8 @@ import { Stack, useRouter } from 'expo-router'; import React from 'react'; import { Pressable, ScrollView } from 'react-native'; -import { - SafeAreaView, - Text, - View, -} from '@/components/ui'; + +import { SafeAreaView, Text, View } from '@/components/ui'; export default function PrivacyPolicyScreen() { const router = useRouter(); diff --git a/src/app/production/[id].tsx b/src/app/production/[id].tsx index 7260898c58a4a377d93f8696181b1d7151be0653..355193ee2ddd087abdfae70532ef638a724c27ea 100644 --- a/src/app/production/[id].tsx +++ b/src/app/production/[id].tsx @@ -1,28 +1,33 @@ import React, { useState } from 'react'; -import { - ScrollView, - Text, - TouchableOpacity, - View, -} from 'react-native'; +import { ScrollView, Text, TouchableOpacity, View } from 'react-native'; import { NavHeader, SafeAreaView } from '@/components/ui'; import { FontAwesome, GroupEnum } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; const ProductionDetail = () => { const [activeTab, setActiveTab] = useState('production-process'); + const { isDark } = useAppColorScheme(); return ( - + - + - + } @@ -30,17 +35,17 @@ const ProductionDetail = () => { {/* 生产计划基本信息 */} - + - + 夏季新品连衣裙生产计划 - - + + 进行中 @@ -48,43 +53,71 @@ const ProductionDetail = () => { - + 计划编号: PL20230601 - 负责人 - 张经理 + + 负责人 + + + 张经理 + - 所属订单 - ORD20230520 + + 所属订单 + + + ORD20230520 + - 开始日期 - 2023-06-01 + + 开始日期 + + + 2023-06-01 + - 截止日期 - 2023-06-30 + + 截止日期 + + + 2023-06-30 + - 计划产量 - 2,500件 + + 计划产量 + + + 2,500件 + - 已完成 - 1,875件 (75%) + + 已完成 + + + 1,875件 (75%) + - 生产进度 - 75% + + 生产进度 + + + 75% + - + { - + - 生产说明 - + + 生产说明 + + 本批次连衣裙采用新型面料,需特别注意缝制工艺和质量控制。 @@ -125,11 +160,11 @@ const ProductionDetail = () => { ].map((tab) => ( setActiveTab(tab.id)} > {tab.label} @@ -144,29 +179,39 @@ const ProductionDetail = () => { {activeTab === 'production-process' && ( {/* 工序流程图 */} - - 生产流程 + + + 生产流程 + - - + + - 裁剪 - 已完成 + + 裁剪 + + + 已完成 + - + @@ -178,50 +223,76 @@ const ProductionDetail = () => { /> - 缝制 - 进行中 + + 缝制 + + + 进行中 + - + - + - 装饰 - 待开始 + + 装饰 + + + 待开始 + - + - - + + - 质检 - 待开始 + + 质检 + + + 待开始 + - + - - + + - 包装 - 待开始 + + 包装 + + + 待开始 + {/* 生产时间线 */} - - 生产时间线 + + + 生产时间线 + {[ @@ -282,26 +353,28 @@ const ProductionDetail = () => { ].map((item, index) => ( {index < 8 && ( )} {item.title} {item.time} {item.desc && ( - {item.desc} + + {item.desc} + )} ))} @@ -353,30 +426,38 @@ const ProductionDetail = () => { ].map((task, index) => ( - {task.title} - + + {task.title} + + 任务编号: {task.id} @@ -387,18 +468,22 @@ const ProductionDetail = () => { - 负责人 - {task.manager} + + 负责人 + + + {task.manager} + - + {task.status === '已完成' ? '完成时间' : task.status === '进行中' ? '预计完成' : '计划开始'} - + {task.status === '已完成' ? task.completeTime : task.status === '进行中' @@ -407,18 +492,22 @@ const ProductionDetail = () => { - 计划工时 - {task.planHours} + + 计划工时 + + + {task.planHours} + - + {task.status === '已完成' ? '实际工时' : task.status === '进行中' ? '已用工时' : '计划完成'} - + {task.status === '已完成' ? task.actualHours : task.status === '进行中' @@ -431,10 +520,14 @@ const ProductionDetail = () => { {task.status === '进行中' && ( - 任务进度 - {task.progress}% + + 任务进度 + + + {task.progress}% + - + { {/* 物料使用内容 */} {activeTab === 'material-usage' && ( - - + + 物料使用情况 - - + + 物料名称 - + 规格 - + 计划用量 - + 已用量 - + 剩余量 @@ -507,21 +600,21 @@ const ProductionDetail = () => { ].map((material, index) => ( - + {material.name} - + {material.spec} - + {material.plan} - + {material.used} - + {material.remain} @@ -530,17 +623,21 @@ const ProductionDetail = () => { - - + + 物料消耗分析 - 面料利用率 - 92.5% + + 面料利用率 + + + 92.5% + - + { - 辅料消耗率 - 75% + + 辅料消耗率 + + + 75% + - + { {/* 质量控制内容 */} {activeTab === 'quality-control' && ( - - + + 质量检验标准 - 面料质量 - + + 面料质量 + + 面料平整度≥95%,色差≤±2%,接缝强度≥4.5kg/cm - 缝制质量 - + + 缝制质量 + + 针距均匀度≥98%,接缝平整,无跳针、断线现象 - 外观质量 - + + 外观质量 + + 无明显瑕疵,装饰部件牢固,整体外观符合设计要求 - - + + 质量检验记录 - - + + 批次 - + 检验日期 - + 检验数量 - + 合格率 - + 检验员 @@ -644,17 +751,21 @@ const ProductionDetail = () => { ].map((record, index) => ( - + {record.batch} - {record.date} - + + {record.date} + + {record.count} - {record.rate} - + + {record.rate} + + {record.inspector} diff --git a/src/app/production/equipment/[id].tsx b/src/app/production/equipment/[id].tsx index 0b2930aad3158dd58ddd8b68d22123bbb2ea1d31..19b8cee44dd342c0e56c2ac430910e2394ca2fa2 100644 --- a/src/app/production/equipment/[id].tsx +++ b/src/app/production/equipment/[id].tsx @@ -1,11 +1,19 @@ import React, { useState } from 'react'; import { TouchableOpacity } from 'react-native'; -import { NavHeader, ScrollView, Text, View, SafeAreaView } from '@/components/ui'; +import { + NavHeader, + SafeAreaView, + ScrollView, + Text, + View, +} from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; const EquipmentDetail = () => { const [activeTab, setActiveTab] = useState('running-data'); + const { isDark } = useAppColorScheme(); // 切换选项卡 const handleTabChange = (tabId: string) => { @@ -13,99 +21,131 @@ const EquipmentDetail = () => { }; return ( - + {/* 设备基本信息卡片 */} - + - 贴片机 #SMT-2023-01 - - 运行中 + + 贴片机 #SMT-2023-01 + + + + 运行中 + - 负责人 - 张工程师 + + 负责人 + + + 张工程师 + - 位置 - 生产车间A区 + + 位置 + + + 生产车间A区 + - 品牌型号 - 松下 NPM-W2 + + 品牌型号 + + + 松下 NPM-W2 + - 购入日期 - 2022-05-15 + + 购入日期 + + + 2022-05-15 + - 运行状态 - 正常 + + 运行状态 + + + 正常 + - 今日产能 - 850件 + + 今日产能 + + + 850件 + - + - 设备警报 - 无异常警报 + + 设备警报 + + + 无异常警报 + {/* 选项卡 - 分段控制器样式 */} - + handleTabChange('running-data')} > 运行数据 handleTabChange('maintenance-records')} > 维护记录 handleTabChange('performance-params')} > 性能参数 handleTabChange('fault-records')} > 故障记录 @@ -117,19 +157,23 @@ const EquipmentDetail = () => { {activeTab === 'running-data' && ( {/* 设备监控 */} - - 设备监控 + + + 设备监控 + - 实时监控 - + + 实时监控 + + - + 设备实时监控画面 @@ -138,28 +182,40 @@ const EquipmentDetail = () => { - + 今日运行时长 - 8.5小时 + + 8.5小时 + - 今日产能 - 850件 + + 今日产能 + + + 850件 + - + 平均贴片速度 - 0.1秒/个 + + 0.1秒/个 + - 良品率 - 99.2% + + 良品率 + + + 99.2% + - + { className="mr-2" /> - 设备状态良好 - + + 设备状态良好 + + 下次计划维护时间:2023-12-15 @@ -181,40 +239,48 @@ const EquipmentDetail = () => { {/* 维护记录 */} {activeTab === 'maintenance-records' && ( - - 维护记录 + + + 维护记录 + - - - + + + 日期 - + 维护类型 - + 维护人员 - + 维护内容 - + 状态 - + - 2023-11-15 - 例行保养 - 王工程师 - + + 2023-11-15 + + + 例行保养 + + + 王工程师 + + 清洁、润滑、校准 - - + + 已完成 @@ -222,15 +288,23 @@ const EquipmentDetail = () => { - + - 2023-10-20 - 部件更换 - 李工程师 - 更换吸嘴组件 + + 2023-10-20 + + + 部件更换 + + + 李工程师 + + + 更换吸嘴组件 + - - + + 已完成 @@ -238,17 +312,23 @@ const EquipmentDetail = () => { - + - 2023-09-10 - 例行保养 - 王工程师 - + + 2023-09-10 + + + 例行保养 + + + 王工程师 + + 清洁、润滑、校准 - - + + 已完成 @@ -262,39 +342,61 @@ const EquipmentDetail = () => { {/* 性能参数 */} {activeTab === 'performance-params' && ( - - 性能参数 + + + 性能参数 + - 设备温度 - 42°C + + 设备温度 + + + 42°C + - 电机转速 - 1200 RPM + + 电机转速 + + + 1200 RPM + - 电压 - 220V + + 电压 + + + 220V + - 电流 - 5.2A + + 电流 + + + 5.2A + - + - 设备警报 - 无异常警报 + + 设备警报 + + + 无异常警报 + @@ -303,56 +405,88 @@ const EquipmentDetail = () => { {/* 故障记录 */} {activeTab === 'fault-records' && ( - - 故障记录 + + + 故障记录 + - - - + + + 日期 - + 故障类型 - + 处理人员 - + 处理方法 - + 停机时长 - + - 2023-11-05 - 吸嘴堵塞 - 王工程师 - 清洗吸嘴 - 2小时 + + 2023-11-05 + + + 吸嘴堵塞 + + + 王工程师 + + + 清洗吸嘴 + + + 2小时 + - + - 2023-10-12 - 供料器卡料 - 李工程师 - 调整供料器 - 1.5小时 + + 2023-10-12 + + + 供料器卡料 + + + 李工程师 + + + 调整供料器 + + + 1.5小时 + - + - 2023-09-25 - 视觉系统异常 - 张工程师 - 重新校准 - 3小时 + + 2023-09-25 + + + 视觉系统异常 + + + 张工程师 + + + 重新校准 + + + 3小时 + diff --git a/src/app/production/plan/[id].tsx b/src/app/production/plan/[id].tsx index 4601a7b86ad2b65821f694628b7df584f9c73135..0e7598ecf24f13a8df728644fe393209ce7c0832 100644 --- a/src/app/production/plan/[id].tsx +++ b/src/app/production/plan/[id].tsx @@ -1,16 +1,13 @@ import React, { useState } from 'react'; -import { - ScrollView, - Text, - TouchableOpacity, - View, -} from 'react-native'; +import { ScrollView, Text, TouchableOpacity, View } from 'react-native'; import { NavHeader, SafeAreaView } from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; const ProductionPlanDetail = () => { const [activeTab, setActiveTab] = useState('plan-details'); + const { isDark } = useAppColorScheme(); // 切换选项卡 const handleTabChange = (tabId: string) => { @@ -18,16 +15,24 @@ const ProductionPlanDetail = () => { }; return ( - + - + - + } @@ -35,11 +40,13 @@ const ProductionPlanDetail = () => { {/* 计划基本信息 */} - + - 智能手表主板生产计划 - - + + 智能手表主板生产计划 + + + 进行中 @@ -47,28 +54,50 @@ const ProductionPlanDetail = () => { - 计划编号 - PP20231128-01 + + 计划编号 + + + PP20231128-01 + - 负责人 - 王工程师 + + 负责人 + + + 王工程师 + - 开始日期 - 2023-11-30 + + 开始日期 + + + 2023-11-30 + - 截止日期 - 2023-12-15 + + 截止日期 + + + 2023-12-15 + - 计划产量 - 1000件 + + 计划产量 + + + 1000件 + - 已完成 - + + 已完成 + + 650件 (65%) @@ -76,60 +105,62 @@ const ProductionPlanDetail = () => { - 完成进度 - 65% + 完成进度 + 65% - + - - 计划描述 - + + + 计划描述 + + 智能手表主板生产计划,包括PCB制作、元器件采购、SMT贴片和功能测试等工序。本批次为1000件,优先级高。 {/* 选项卡 - 分段控制器样式 */} - + handleTabChange('plan-details')} > 计划详情 handleTabChange('production-progress')} > 生产进度 handleTabChange('material-info')} > 物料信息 handleTabChange('team-info')} > 团队信息 @@ -139,15 +170,19 @@ const ProductionPlanDetail = () => { {/* 选项卡内容区域 */} {/* 计划详情 */} {activeTab === 'plan-details' && ( - - 工序安排 + + + 工序安排 + {/* 工序1 */} - - - PCB制板 - - + + + + PCB制板 + + + 已完成 @@ -155,27 +190,39 @@ const ProductionPlanDetail = () => { - 负责人 - 李工程师 + + 负责人 + + + 李工程师 + - 完成时间 - 2023-12-02 + + 完成时间 + + + 2023-12-02 + - 备注 - + + 备注 + + PCB制板工序已完成,质检合格率98.5% {/* 工序2 */} - - - SMT贴片 - - + + + + SMT贴片 + + + 进行中 @@ -183,25 +230,39 @@ const ProductionPlanDetail = () => { - 负责人 - 张工程师 + + 负责人 + + + 张工程师 + - 预计完成 - 2023-12-08 + + 预计完成 + + + 2023-12-08 + - 进度 - 已完成650块主板贴片,正在进行中 + + 进度 + + + 已完成650块主板贴片,正在进行中 + {/* 工序3 */} - - - 功能测试 - - + + + + 功能测试 + + + 待开始 @@ -209,16 +270,28 @@ const ProductionPlanDetail = () => { - 负责人 - 赵工程师 + + 负责人 + + + 赵工程师 + - 预计开始 - 2023-12-09 + + 预计开始 + + + 2023-12-09 + - 备注 - 等待SMT贴片完成后进行功能测试 + + 备注 + + + 等待SMT贴片完成后进行功能测试 + @@ -226,85 +299,125 @@ const ProductionPlanDetail = () => { {/* 生产进度 */} {activeTab === 'production-progress' && ( - - 生产进度概览 + + + 生产进度概览 + - 总体进度 - 65% + + 总体进度 + + 65% - + - 100% - PCB制板 + + 100% + + + PCB制板 + - 65% - SMT贴片 + + 65% + + + SMT贴片 + 0% - 功能测试 + + 功能测试 + - 生产时间线 + + 生产时间线 + {/* 时间线项目 */} - - 计划创建 - 2023-11-28 10:30 + + + 计划创建 + + + 2023-11-28 10:30 + - - 物料准备完成 - 2023-11-29 16:45 + + + 物料准备完成 + + + 2023-11-29 16:45 + - - PCB制板开始 - 2023-11-30 09:15 + + + PCB制板开始 + + + 2023-11-30 09:15 + - - PCB制板完成 - 2023-12-02 11:30 + + + PCB制板完成 + + + 2023-12-02 11:30 + - - SMT贴片开始 - 2023-12-03 08:00 + + + SMT贴片开始 + + + 2023-12-03 08:00 + - - SMT贴片进行中 - 2023-12-05 14:30 - + + + SMT贴片进行中 + + + 2023-12-05 14:30 + + 已完成650件,剩余350件 - - + + SMT贴片完成 @@ -312,8 +425,8 @@ const ProductionPlanDetail = () => { - - + + 功能测试 @@ -323,7 +436,7 @@ const ProductionPlanDetail = () => { - + 计划完成 @@ -334,79 +447,123 @@ const ProductionPlanDetail = () => { {/* 物料信息 */} {activeTab === 'material-info' && ( - - 物料清单 + + + 物料清单 + {/* 物料项目1 */} - + - PCB空板 - 已到货 + + PCB空板 + + + 已到货 + - + 规格:4层板 / FR-4 / 1.6mm - 需求数量:1050片 - 库存数量:1100片 + + 需求数量:1050片 + + + 库存数量:1100片 + {/* 物料项目2 */} - + - 主控芯片 - 已到货 + + 主控芯片 + + + 已到货 + - + 型号:STM32F103RCT6 - 需求数量:1000个 - 库存数量:1200个 + + 需求数量:1000个 + + + 库存数量:1200个 + {/* 物料项目3 */} - + - 电容 - 已到货 + + 电容 + + + 已到货 + - + 规格:0402 / 0.1uF - 需求数量:10000个 - 库存数量:12000个 + + 需求数量:10000个 + + + 库存数量:12000个 + {/* 物料项目4 */} - + - 电阻 - 已到货 + + 电阻 + + + 已到货 + - + 规格:0402 / 10K - 需求数量:8000个 - 库存数量:9500个 + + 需求数量:8000个 + + + 库存数量:9500个 + {/* 物料项目5 */} - 蓝牙模块 - 已到货 + + 蓝牙模块 + + + 已到货 + - 型号:BLE4.2 + + 型号:BLE4.2 + - 需求数量:1000个 - 库存数量:1050个 + + 需求数量:1000个 + + + 库存数量:1050个 + @@ -414,68 +571,86 @@ const ProductionPlanDetail = () => { {/* 团队信息 */} {activeTab === 'team-info' && ( - - 团队成员 + + + 团队成员 + {/* 团队成员1 */} - - + + - 王工程师 - 项目负责人 + + 王工程师 + + + 项目负责人 + - + 电子工程部 / 高级工程师 {/* 团队成员2 */} - - + + - 李工程师 - PCB制板负责人 + + 李工程师 + + + PCB制板负责人 + - + 电子工程部 / 工程师 {/* 团队成员3 */} - - + + - 张工程师 - SMT贴片负责人 + + 张工程师 + + + SMT贴片负责人 + - + 生产部 / 高级技术员 {/* 团队成员4 */} - - + + - 赵工程师 - 功能测试负责人 + + 赵工程师 + + + 功能测试负责人 + - + 质量部 / 测试工程师 @@ -483,15 +658,21 @@ const ProductionPlanDetail = () => { {/* 团队成员5 */} - + - 刘经理 - 物料采购 + + 刘经理 + + + 物料采购 + - 采购部 / 采购经理 + + 采购部 / 采购经理 + @@ -500,14 +681,16 @@ const ProductionPlanDetail = () => { {/* 底部操作按钮 */} - + - 编辑计划 + + 编辑计划 + + - + - + } @@ -30,35 +35,51 @@ export default function ProcessDetail() { {/* 工序基本信息 */} - + - 元器件贴装 - - 使用中 + + 元器件贴装 + + + + 使用中 + - + 工序编号:PR20231201-03 - 负责人 - 王工程师 + + 负责人 + + + 王工程师 + - 标准工时 - 45分钟/批次 + + 标准工时 + + + 45分钟/批次 + - 设备利用率 - 85% + + 设备利用率 + + + 85% + - + - - 关联设备 + + + 关联设备 + - SMT贴片机#2 + + SMT贴片机#2 + @@ -90,10 +115,10 @@ export default function ProcessDetail() { setActiveTab(tab)} - className={`mr-2 rounded-full px-4 py-2 ${activeTab === tab ? 'bg-blue-600' : 'bg-gray-200'}`} + className={`mr-2 rounded-full px-4 py-2 ${activeTab === tab ? 'bg-blue-600' : 'bg-gray-200 dark:bg-gray-700'}`} > {tab} @@ -103,56 +128,88 @@ export default function ProcessDetail() { {/* 生产记录 */} {activeTab === '生产记录' && ( - - 生产记录 + + + 生产记录 + - - + + 日期 - + 批次 - + 产量 - + 良品率 - + 操作员 - + - 2023-12-04 - B20231204-01 - 120件 - 98.3% - 张三 + + 2023-12-04 + + + B20231204-01 + + + 120件 + + + 98.3% + + + 张三 + - + - 2023-12-03 - B20231203-02 - 150件 - 97.5% - 李四 + + 2023-12-03 + + + B20231203-02 + + + 150件 + + + 97.5% + + + 李四 + - + - 2023-12-02 - B20231202-01 - 130件 - 99.1% - 王五 + + 2023-12-02 + + + B20231202-01 + + + 130件 + + + 99.1% + + + 王五 + @@ -162,45 +219,65 @@ export default function ProcessDetail() { {/* 工艺参数 */} {activeTab === '工艺参数' && ( - - 工艺参数 + + + 工艺参数 + - 贴片速度 - 0.1秒/个 + + 贴片速度 + + + 0.1秒/个 + - 定位精度 - ±0.02mm + + 定位精度 + + + ±0.02mm + - 最小元件尺寸 - 0201(0.6mm×0.3mm) + + 最小元件尺寸 + + + 0201(0.6mm×0.3mm) + - 最大PCB尺寸 - 330mm×250mm + + 最大PCB尺寸 + + + 330mm×250mm + - + - 注意事项 - + + 注意事项 + + 1. 确保PCB板表面清洁无污染 - + 2. 定期检查吸嘴状态,避免漏吸 - + 3. 元件供料器需定期校准 @@ -211,28 +288,42 @@ export default function ProcessDetail() { {/* 质量控制 */} {activeTab === '质量控制' && ( - - 质量控制 + + + 质量控制 + - 检验标准 - + + 检验标准 + + IPC-A-610G 3级标准 - 检验项目 + + 检验项目 + - - 元件位置 + + + 元件位置 + - - 焊接质量 + + + 焊接质量 + - - 极性方向 + + + 极性方向 + - - 元件完整性 + + + 元件完整性 + @@ -241,21 +332,31 @@ export default function ProcessDetail() { {/* 操作指南 */} {activeTab === '操作指南' && ( - - 操作指南 + + + 操作指南 + - 操作步骤 - - 1. 确认PCB板已完成前道工序 - + + 操作步骤 + + + + 1. 确认PCB板已完成前道工序 + + 2. 检查贴片机程序是否正确加载 - + 3. 确认元件供料器已正确安装 - 4. 启动设备并监控首件生产 - 5. 完成后进行首件检验确认 + + 4. 启动设备并监控首件生产 + + + 5. 完成后进行首件检验确认 + diff --git a/src/app/production/task/[id].tsx b/src/app/production/task/[id].tsx index 52a4a3a460d4240b9b94279898b73979b16fa609..bbc769cd7826d794742c4c55796110a945e2cd9a 100644 --- a/src/app/production/task/[id].tsx +++ b/src/app/production/task/[id].tsx @@ -1,28 +1,33 @@ import React, { useState } from 'react'; -import { - ScrollView, - Text, - TouchableOpacity, - View, -} from 'react-native'; +import { ScrollView, Text, TouchableOpacity, View } from 'react-native'; import { NavHeader, SafeAreaView } from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; const ProductionTaskDetail = () => { const [activeTab, setActiveTab] = useState('basic-info'); + const { isDark } = useAppColorScheme(); return ( - + - + - + } @@ -30,22 +35,24 @@ const ProductionTaskDetail = () => { {/* 任务状态卡片 */} - + - 连衣裙缝制任务 - + + 连衣裙缝制任务 + + 任务编号: T20230601-02 - + - + 进行中 @@ -53,10 +60,12 @@ const ProductionTaskDetail = () => { - 任务进度 - 65% + 任务进度 + + 65% + - + { - 开始时间 - 2023-06-06 08:00 + + 开始时间 + + + 2023-06-06 08:00 + - 预计完成 - 2023-06-20 17:30 + + 预计完成 + + + 2023-06-20 17:30 + - 所属计划 - 夏季新品连衣裙 + + 所属计划 + + + 夏季新品连衣裙 + - 负责人 - 李工 + + 负责人 + + + 李工 + @@ -93,14 +118,14 @@ const ProductionTaskDetail = () => { /> 完成任务 - + - + 暂停任务 @@ -108,13 +133,13 @@ const ProductionTaskDetail = () => { {/* 任务详情选项卡 */} - + setActiveTab('basic-info')} > 基本信息 @@ -124,7 +149,7 @@ const ProductionTaskDetail = () => { onPress={() => setActiveTab('process')} > 工序进度 @@ -134,7 +159,7 @@ const ProductionTaskDetail = () => { onPress={() => setActiveTab('resources')} > 相关资源 @@ -143,60 +168,94 @@ const ProductionTaskDetail = () => { {/* 基本信息内容 */} {activeTab === 'basic-info' && ( - - 任务详情 + + + 任务详情 + - 任务描述 - + + 任务描述 + + 完成夏季新品连衣裙的缝制工作,包括主体缝合、袖口处理和装饰缝制。需确保缝线平整,接缝牢固,符合质量标准。 - 任务类型 - 缝制加工 + + 任务类型 + + + 缝制加工 + - 优先级 - + + 优先级 + + + 高 + - 计划产量 - 2,500件 + + 计划产量 + + + 2,500件 + - 已完成 - 1,625件 + + 已完成 + + + 1,625件 + - 质量要求 + + 质量要求 + - 缝线平整,无跳线、断线 + + 缝线平整,无跳线、断线 + - 接缝牢固,无开线现象 + + 接缝牢固,无开线现象 + - 装饰部分对称美观 + + 装饰部分对称美观 + - 符合尺寸规格要求 + + 符合尺寸规格要求 + - 备注 - 本批次为紧急订单,需优先安排生产,确保按时交付。 + + 备注 + + + 本批次为紧急订单,需优先安排生产,确保按时交付。 + )} @@ -204,8 +263,10 @@ const ProductionTaskDetail = () => { {/* 工序进度内容 */} {activeTab === 'process' && ( - - 工序流程 + + + 工序流程 + { > - - + + - 裁剪 + + 裁剪 + - - + + - 缝制 + + 缝制 + - + - + - 装饰 + + 装饰 + - + - - + + - 质检 + + 质检 + - + - - + + - 包装 + + 包装 + - - 任务日志 + + + 任务日志 + - - + + 2023-06-15 14:30 - - 生产进度更新 - 已完成1,625件,完成率65% - + + + 生产进度更新 + + + 已完成1,625件,完成率65% + + 操作人: 李工 @@ -290,14 +383,18 @@ const ProductionTaskDetail = () => { - - + + 2023-06-10 09:15 - - 质量检查 - 抽检100件,合格率98% - + + + 质量检查 + + + 抽检100件,合格率98% + + 操作人: 王质检 @@ -305,13 +402,17 @@ const ProductionTaskDetail = () => { - + 2023-06-06 08:00 - - 任务开始 - 开始连衣裙缝制任务 - + + + 任务开始 + + + 开始连衣裙缝制任务 + + 操作人: 李工 @@ -324,100 +425,162 @@ const ProductionTaskDetail = () => { {/* 相关资源内容 */} {activeTab === 'resources' && ( - - 使用设备 + + + 使用设备 + - - - + + + - 工业缝纫机 - + + 工业缝纫机 + + 设备编号: EQ-2023-056 - - 正常运行 + + + 正常运行 + - - - + + + - 锁边机 - + + 锁边机 + + 设备编号: EQ-2023-078 - - 正常运行 + + + 正常运行 + - - 材料清单 + + + 材料清单 + - - - + + + - 面料 - + + 面料 + + 库存编号: M-2023-102 - - 充足 + + + 充足 + - - - + + + - 缝线 - + + 缝线 + + 库存编号: M-2023-156 - - 偏低 + + + 偏低 + - - 相关文档 + + + 相关文档 + - - - + + + - 工艺说明书 - PDF文档 · 2.5MB + + 工艺说明书 + + + PDF文档 · 2.5MB + - + - - - + + + - 款式图 - JPG图片 · 1.8MB + + 款式图 + + + JPG图片 · 1.8MB + - + @@ -426,23 +589,27 @@ const ProductionTaskDetail = () => { {/* 底部操作按钮 */} - + - 添加备注 + + 添加备注 + - + - 报告问题 + + 报告问题 + diff --git a/src/app/qr-scanner.tsx b/src/app/qr-scanner.tsx index 54df3e31cd4e85a03eff4bc8c7f8e5a6b4e69e99..81f22222564555fe0a589b24e22406a08e346e78 100644 --- a/src/app/qr-scanner.tsx +++ b/src/app/qr-scanner.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { Alert } from 'react-native'; import { QRCodeScanner } from '@/components/ui/qr-code-scanner'; +import { NavHeader } from '@/components/ui'; export default function QRScannerScreen() { const router = useRouter(); @@ -29,45 +30,16 @@ export default function QRScannerScreen() { let parts = data.split('_'); if (parts.length !== 2) { Alert.alert('扫描成功', `检测到: ${data}`, [ - { text: '确定', onPress: () => {} }, + { text: '确定', onPress: () => { } }, ]); } else { console.log('parts[0]:' + parts[0]); - if (Number(parts[0]) === 1) { - Alert.alert('扫描成功', `检测到打样单,是否确认提交?`, [ - { - text: '确定', - onPress: () => { - // router.push({ - // pathname: '/chat', - // params: { - // actualText: '打样提交', - // text: `prompt:scan_sample(),value:${parts[1]},CurrentTime: ${formatDate(new Date())}`, - // }, - // }); - }, - }, - { text: '取消', onPress: () => {} }, - ]); - } else if (Number(parts[0]) === 2) { - Alert.alert('扫描成功', `检测到排料单,是否确认提交?`, [ - { - text: '确定', - onPress: () => { - // router.push({ - // pathname: '/chat', - // params: { - // actualText: '排料提交', - // text: `prompt:scan_marker(),value:${parts[1]},CurrentTime: ${formatDate(new Date())}`, - // }, - // }); - }, - }, - { text: '取消', onPress: () => {} }, - ]); + if (parts[0] === "Equip") { + router.push({ + pathname: `/equipment/[id]`, + params: { id: parts[1] } + }); } - // - // console.log(parts); // 输出: ['1', '33'] } } }, @@ -78,7 +50,9 @@ export default function QRScannerScreen() { router.back(); }, [router]); - return ; + return <>; } function isValidUrl(string: string): boolean { diff --git a/src/app/quality/[id].tsx b/src/app/quality/[id].tsx index bab66d5556023ce53ba1f68a58e97b5f193ecb86..0b06c1afc454f655d79f0ad75ebd105af45cb5b4 100644 --- a/src/app/quality/[id].tsx +++ b/src/app/quality/[id].tsx @@ -1,63 +1,84 @@ import React, { useState } from 'react'; -import { +import { ScrollView, Text, TouchableOpacity, View } from 'react-native'; - ScrollView, - Text, - TouchableOpacity, - View, -} from 'react-native'; - -import { SafeAreaView, NavHeader } from '@/components/ui'; +import { NavHeader, SafeAreaView } from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; + const QualityDetail = () => { const [activeTab, setActiveTab] = useState(0); + const { isDark } = useAppColorScheme(); // 选项卡内容 const tabs = ['检验结果', '不良品分析', '操作记录', '检验标准']; return ( - + {/* 质检任务基本信息 */} - + - 智能手表主板质检 - - 进行中 + + 智能手表主板质检 + + + + 进行中 + - + 任务编号:QC20231128-01 - 负责人 - 王质检 + + 负责人 + + + 王质检 + - 截止日期 - 2023-12-05 + + 截止日期 + + + 2023-12-05 + - 关联产品 - 智能手表 SW-2023 + + 关联产品 + + + 智能手表 SW-2023 + - 批次号 - BT20231201-A + + 批次号 + + + BT20231201-A + - 完成进度 - 65% + + 完成进度 + + + 65% + - + { - + - 检验说明 - + + 检验说明 + + 本次质检针对智能手表主板进行全面检测,包括电路连通性、元器件焊接质量、功能测试等。 @@ -94,10 +117,10 @@ const QualityDetail = () => { setActiveTab(index)} - className={`rounded-full px-4 py-2 ${activeTab === index ? 'bg-blue-600' : ''}`} + className={`rounded-full px-4 py-2 ${activeTab === index ? 'bg-blue-600' : 'bg-white dark:bg-gray-800'}`} > {tab} @@ -108,30 +131,48 @@ const QualityDetail = () => { {/* 检验结果 */} {activeTab === 0 && ( - - 检验结果统计 + + + 检验结果统计 + - 650 - 已检测 + + 650 + + + 已检测 + - 637 - 合格 + + 637 + + + 合格 + - 13 - 不合格 + + 13 + + + 不合格 + - 合格率 - 98.0% + + 合格率 + + + 98.0% + - + { - - + + 检验项目 - + 标准值 - + 合格率 - + 状态 - + - 电路连通性 - 100% - 99.5% + + 电路连通性 + + + 100% + + + 99.5% + - - 合格 + + + 合格 + - + - 元器件焊接 - ≥98% - 97.2% + + 元器件焊接 + + + ≥98% + + + 97.2% + - - 不合格 + + + 不合格 + - + - 功能测试 - ≥99% - 99.1% + + 功能测试 + + + ≥99% + + + 99.1% + - - 合格 + + + 合格 + - + - 外观检查 - ≥95% - 98.3% + + 外观检查 + + + ≥95% + + + 98.3% + - - 合格 + + + 合格 + @@ -214,75 +287,111 @@ const QualityDetail = () => { {/* 不良品分析 */} {activeTab === 1 && ( - - 不良品分析 + + + 不良品分析 + - - 2.0% - 不良品率 + + + 2.0% + + + 不良品率 + - 主要不良原因: + + 主要不良原因: + - 焊接不良 - 61.5% + + 焊接不良 + + + 61.5% + - 元器件损坏 - 23.1% + + 元器件损坏 + + + 23.1% + - 电路异常 - 15.4% + + 电路异常 + + + 15.4% + - 不良品标签 + + 不良品标签 + - - 虚焊 + + + 虚焊 + - - 焊接过热 + + + 焊接过热 + - - 电容损坏 + + + 电容损坏 + - - 电阻偏差 + + + 电阻偏差 + - - 短路 + + + 短路 + - - 开路 + + + 开路 + - + - 改进建议 - + + 改进建议 + + 1. 调整焊接温度参数,避免虚焊和过热问题 - + 2. 加强元器件进料检验,提高元器件质量 - + 3. 优化电路设计,增强抗干扰能力 @@ -293,75 +402,95 @@ const QualityDetail = () => { {/* 操作记录 */} {activeTab === 2 && ( - - 操作记录 + + + 操作记录 + - + - 开始检验 + + 开始检验 + - 2023-12-01 09:30 + + 2023-12-01 09:30 + - + 王质检开始对批次BT20231201-A进行质量检验 - + - 发现不良品 + + 发现不良品 + - 2023-12-01 11:45 + + 2023-12-01 11:45 + - + 发现5件焊接不良产品,已标记并隔离 - + - 暂停检验 + + 暂停检验 + - 2023-12-01 12:30 + + 2023-12-01 12:30 + - 午休暂停检验 + + 午休暂停检验 + - + - 继续检验 + + 继续检验 + - 2023-12-01 13:30 + + 2023-12-01 13:30 + - + 继续对批次BT20231201-A进行质量检验 @@ -372,14 +501,18 @@ const QualityDetail = () => { - 生成中期报告 + + 生成中期报告 + - 2023-12-02 17:00 + + 2023-12-02 17:00 + - + 生成检验中期报告,当前合格率98.0% @@ -388,42 +521,62 @@ const QualityDetail = () => { {/* 检验标准 */} {activeTab === 3 && ( - - 检验标准 + + + 检验标准 + - 参考标准 - IPC-A-610G 3级标准 + + 参考标准 + + + IPC-A-610G 3级标准 + - 检验项目 + + 检验项目 + - - 电路连通性 + + + 电路连通性 + - - 元器件焊接 + + + 元器件焊接 + - - 功能测试 + + + 功能测试 + - - 外观检查 + + + 外观检查 + - 抽样方案 - + + 抽样方案 + + GB/T 2828.1-2012 正常检验 II 级 - 合格判定 - + + 合格判定 + + AQL=1.0,批量1000pcs,抽检数量80pcs,接收数2,拒收数3 @@ -434,14 +587,16 @@ const QualityDetail = () => { {/* 底部操作按钮 */} - + - 导出报告 + + 导出报告 + { const router = useRouter(); + const { isDark } = useAppColorScheme(); // const navigation = useNavigation(); const [activeMainTab, setActiveMainTab] = useState('quality-overview'); @@ -39,15 +37,15 @@ const Quality = () => { // 渲染主选项卡 const renderMainTabs = () => ( - + {mainTabs.map((tab) => ( setActiveMainTab(tab.id)} > {tab.title} @@ -67,11 +65,11 @@ const Quality = () => { {subTabs.map((tab) => ( setActiveSubTab(tab)} > {tab} @@ -92,11 +90,11 @@ const Quality = () => { {inspectionTabs.map((tab) => ( setActiveInspectionTab(tab)} > {tab} @@ -112,33 +110,51 @@ const Quality = () => { {renderSubTabs()} {/* 质量概览卡片 */} - - 质量概览 + + + 质量概览 + - 24 - 今日质检 + + 24 + + + 今日质检 + - 98.5% - 合格率 + + 98.5% + + + 合格率 + - 5 - 待处理 + + 5 + + + 待处理 + - + - 本月质量趋势 + + 本月质量趋势 + - 良好 + + 良好 + ↑ 1.2% @@ -146,30 +162,42 @@ const Quality = () => { {/* 质检任务列表 */} - 质检任务 + + 质检任务 + {/* 任务项目1 */} router.push(`/quality/1`)}> - + - 智能手表主板质检 - - 进行中 + + 智能手表主板质检 + + + + 进行中 + - + 任务编号:QC20231128-01 - - 负责人:王质检 - 截止日期:2023-12-05 + + + 负责人:王质检 + + + 截止日期:2023-12-05 + - 完成进度 - 65% + 完成进度 + + 65% + - + { - 计划检测: - 1000 - 已完成: - 650 + + 计划检测: + + + 1000 + + + 已完成: + + + 650 + - 详情 + 详情 @@ -192,26 +228,36 @@ const Quality = () => { {/* 任务项目2 */} router.push(`/quality/1`)}> - + - 智能音箱外壳质检 - - 已完成 + + 智能音箱外壳质检 + + + + 已完成 + - + 任务编号:QC20231125-03 - - 负责人:李质检 - 完成日期:2023-11-28 + + + 负责人:李质检 + + + 完成日期:2023-11-28 + - 合格率 - 99.2% + 合格率 + + 99.2% + - + { - 检测数量: - 2000 - 不合格: - 16 + + 检测数量: + + + 2000 + + + 不合格: + + + 16 + - 查看报告 + 查看报告 {/* 不良品分析 */} - 不良品分析 - + + 不良品分析 + + - - 1.5% - 不良品率 + + + 1.5% + + + 不良品率 + - 主要不良原因: + + 主要不良原因: + - 焊接不良 - 45% + + 焊接不良 + + + 45% + - 外观瑕疵 - 30% + + 外观瑕疵 + + + 30% + - 功能异常 - 25% + + 功能异常 + + + 25% + - 查看详细分析 + + 查看详细分析 + {/* 质量改进建议 */} - 质量改进建议 - - + + 质量改进建议 + + + - 优化焊接工艺参数 - 2023-11-28 + + 优化焊接工艺参数 + + + 2023-11-28 + - + 针对焊接不良问题,建议调整焊接温度和时间参数,并加强操作人员培训。 - + - 更新外观检测标准 - 2023-11-25 + + 更新外观检测标准 + + + 2023-11-25 + - + 建议更新外观检测标准,增加细节检查项,并引入自动化视觉检测系统辅助人工检测。 - 加强功能测试覆盖率 - 2023-11-22 + + 加强功能测试覆盖率 + + + 2023-11-22 + - + 针对功能异常问题,建议增加测试用例覆盖率,并在生产线上增加中间测试环节。 @@ -303,33 +393,49 @@ const Quality = () => { const renderQualityInspection = () => ( {/* 质量检验概览 */} - - 质量检验概览 + + + 质量检验概览 + - 15 - 待检验 + + 15 + + + 待检验 + - 8 - 检验中 + + 8 + + + 检验中 + - 42 - 已完成 + + 42 + + + 已完成 + - + - 本周检验完成率 + + 本周检验完成率 + - + 92.5% ↑ 3.2% @@ -340,25 +446,31 @@ const Quality = () => { {/* 检验任务筛选 */} - 检验任务 + + 检验任务 + - + - 筛选 + + 筛选 + - + - 排序 + + 排序 + @@ -367,54 +479,78 @@ const Quality = () => { {renderInspectionTabs()} {/* 检验任务1 */} - + - 智能手表主板检验 - - 待检验 + + 智能手表主板检验 + + + + 待检验 + - + 任务编号:QI20231201-01 - - 负责人:王质检 - 截止日期:2023-12-05 + + + 负责人:王质检 + + + 截止日期:2023-12-05 + - 批次号: - BT20231201-A - 计划数量: - 500 + 批次号: + + BT20231201-A + + + 计划数量: + + + 500 + - 开始检验 + 开始检验 {/* 检验任务2 */} - + - 智能音箱电路板检验 - - 检验中 + + 智能音箱电路板检验 + + + + 检验中 + - + 任务编号:QI20231130-02 - - 负责人:李质检 - 截止日期:2023-12-03 + + + 负责人:李质检 + + + 截止日期:2023-12-03 + - 完成进度 - 70% + 完成进度 + + 70% + - + { - 已检验: - 350 - 不合格: - 12 + 已检验: + + 350 + + + 不合格: + + + 12 + - 继续检验 + 继续检验 @@ -439,9 +581,15 @@ const Quality = () => { // 渲染缺陷管理内容(简化版) const renderDefectManagement = () => ( - - 缺陷管理 - + + + 缺陷管理 + + 此部分内容正在开发中,敬请期待 @@ -450,9 +598,15 @@ const Quality = () => { // 渲染质量报表内容(简化版) const renderQualityReport = () => ( - - 质量报表 - + + + 质量报表 + + 此部分内容正在开发中,敬请期待 @@ -473,9 +627,10 @@ const Quality = () => { return renderQualityOverview(); } }; + const insets = useSafeAreaInsets(); return ( - + @@ -484,6 +639,7 @@ const Quality = () => { {/* 内容区域 */} {renderContent()} + {/* 浮动按钮 */} @@ -493,7 +649,7 @@ const Quality = () => { > - + ); }; diff --git a/src/app/repair-order/[id].tsx b/src/app/repair-order/[id].tsx index ce5ac908e7655fca7d61c7dd69004bdc01e6a10e..a69c34e9480b4c92f3f5e5bba24f6d3a1f5d7fdb 100644 --- a/src/app/repair-order/[id].tsx +++ b/src/app/repair-order/[id].tsx @@ -4,113 +4,12 @@ import React from 'react'; import { Image, ScrollView, TouchableOpacity } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { InfoRow } from '@/components/equipment'; +import { ProgressItem, SparePartItem } from '@/components/repair-order'; import { NavHeader, Text, View } from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; import { useAppColorScheme } from '@/lib/hooks'; -// 信息行组件 -type InfoRowProps = { - label: string; - value: string; - isLast?: boolean; -}; - -const InfoRow: React.FC = ({ label, value, isLast = false }) => ( - - {label} - - {value} - - -); - -// 进度项组件 -type ProgressItemProps = { - icon: string; - iconColor: string; - bgColor: string; - title: string; - time?: string; - description?: string; - isCompleted: boolean; -}; - -const ProgressItem: React.FC = ({ - icon, - iconColor, - bgColor, - title, - time, - description, - isCompleted, -}) => ( - - - - - - - - {title} - - {time && ( - - {time} - - )} - - {description && ( - - {description} - - )} - - -); - -// 备件项组件 -type SparePartItemProps = { - name: string; - model: string; - quantity: number; - price: string; -}; - -const SparePartItem: React.FC = ({ - name, - model, - quantity, - price, -}) => ( - - - - {name} - - - 型号:{model} - - - - - ×{quantity} - - {price} - - -); - const RepairOrderDetail: React.FC = () => { // const router = useRouter(); // const { id } = useLocalSearchParams(); diff --git a/src/app/repair-order/add.tsx b/src/app/repair-order/add.tsx index 8e18998824b9fdd652b04b1ecbc140d3600f44c6..6ae4129ed1d00c9853de117b8d64d3fe40f98c99 100644 --- a/src/app/repair-order/add.tsx +++ b/src/app/repair-order/add.tsx @@ -1,5 +1,5 @@ import { useRouter } from 'expo-router'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Alert, ScrollView, @@ -8,32 +8,21 @@ import { TouchableOpacity, } from 'react-native'; -import { NavHeader, Text, View } from '@/components/ui'; -import { FontAwesome } from '@/components/ui/icons'; -import { useAppColorScheme } from '@/lib/hooks'; +import { queryLov } from '@/api'; +import http from '@/api/common/http'; +import { PriorityButton, RadioButton } from '@/components/repair-order'; +import { + DatePickerInput, + FontAwesome, + NavHeader, + Text, + View, +} from '@/components/ui'; +import { QRCodeScanner } from '@/components/ui/qr-code-scanner'; +import { isWeb } from '@/lib'; import { error, info } from '@/lib/message'; - -// 设备选项类型 -type Equipment = { - id: string; - name: string; - code: string; - location: string; -}; - -// 设备数据 -const equipmentList: Equipment[] = [ - { id: '1', name: '数控机床', code: 'CNC-01', location: 'A车间-1号线' }, - { id: '2', name: '数控机床', code: 'CNC-03', location: 'A车间-3号线' }, - { id: '3', name: '注塑机', code: 'IM-03', location: 'B车间-2号线' }, - { id: '4', name: '注塑机', code: 'IM-05', location: 'B车间-5号线' }, - { id: '5', name: '空压机', code: 'AC-01', location: '动力车间' }, - { id: '6', name: '空压机', code: 'AC-02', location: '动力车间' }, - { id: '7', name: '空压机', code: 'AC-05', location: '动力车间' }, - { id: '8', name: '传送带', code: 'CB-01', location: 'C车间-包装区' }, - { id: '9', name: '传送带', code: 'CB-02', location: 'C车间-包装区' }, - { id: '10', name: '混料机', code: 'MX-08', location: 'C车间-配料区' }, -]; +import { type Equipment, type SmLov } from '@/types'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; // 维修人员数据 const technicianList = [ @@ -44,98 +33,19 @@ const technicianList = [ { id: '5', name: '孙七', level: '初级技师' }, ]; -// 单选按钮组件 -type RadioButtonProps = { - label: string; - selected: boolean; - onPress: () => void; - icon?: string; - color?: string; -}; - -const RadioButton: React.FC = ({ - label, - selected, - onPress, - icon, - color = '#1890ff', -}) => ( - - {icon && ( - - - - )} - - {label} - - -); - -// 优先级按钮组件 -type PriorityButtonProps = { - label: string; - icon: string; - color: string; - selected: boolean; - onPress: () => void; -}; - -const PriorityButton: React.FC = ({ - label, - icon, - color, - selected, - onPress, -}) => ( - - - - {label} - - -); - const AddRepairOrder: React.FC = () => { const router = useRouter(); - const { isDark } = useAppColorScheme(); + const insets = useSafeAreaInsets(); // 表单状态 const [selectedEquipment, setSelectedEquipment] = useState(''); const [faultType, setFaultType] = useState(''); + const [faultTypes, setFaultTypes] = useState([]); + const [equipmentList, setEquipmentList] = useState([]); const [priority, setPriority] = useState(''); const [impact, setImpact] = useState(''); const [faultDescription, setFaultDescription] = useState(''); - const [expectedTime, setExpectedTime] = useState(''); + const [expectedTime, setExpectedTime] = useState(); const [assignedTechnician, setAssignedTechnician] = useState(''); const [remarks, setRemarks] = useState(''); const [needShutdown, setNeedShutdown] = useState(false); @@ -143,13 +53,14 @@ const AddRepairOrder: React.FC = () => { // 显示设备选择器 const [showEquipmentPicker, setShowEquipmentPicker] = useState(false); const [showTechnicianPicker, setShowTechnicianPicker] = useState(false); + const [showScanner, setShowScanner] = useState(false); // 获取选中的设备 const getSelectedEquipmentText = () => { if (!selectedEquipment) return '请选择需要维修的设备'; - const equipment = equipmentList.find((e) => e.id === selectedEquipment); + const equipment = equipmentList.find((e) => e.ID === selectedEquipment); return equipment - ? `${equipment.name} ${equipment.code} (${equipment.location})` + ? `${equipment.MachineName} ${equipment.MachineNo} (${equipment.Location})` : '请选择需要维修的设备'; }; @@ -162,6 +73,49 @@ const AddRepairOrder: React.FC = () => { : '系统自动分配'; }; + const loadFaultType = async () => { + const { Success, Data } = await queryLov('EquipmentFaultType'); + if (Success) setFaultTypes(Data); + }; + const loadEquipment = async () => { + const { Success, Data } = await http.get( + '/api/EmRepairOrder/GetEquipment' + ); + if (Success) setEquipmentList(Data); + }; + + useEffect(() => { + loadFaultType(); + loadEquipment(); + }, []); + + // 处理扫码结果 + const handleScanResult = (data: string) => { + setShowScanner(false); + let parts = data.split('_'); + console.log() + if (parts.length !== 2) { + info(`无效的设备二维码!`); + return; + } + + // 根据扫描结果查找设备 + const equipment = equipmentList.find( + (e) => e.MachineNo === parts[1] || e.ID === parts[1] + ); + if (equipment) { + setSelectedEquipment(equipment.ID); + info(`已选择设备: ${equipment.MachineName}`); + } else { + error(`无效的设备二维码!`); + } + }; + + // 取消扫码 + const handleCancelScan = () => { + setShowScanner(false); + }; + // 验证表单 const validateForm = () => { if (!selectedEquipment) { @@ -191,37 +145,54 @@ const AddRepairOrder: React.FC = () => { return true; }; - // 保存草稿 - const handleSaveDraft = () => { - Alert.alert('保存草稿', '是否保存为草稿?', [ - { text: '取消', style: 'cancel' }, - { - text: '确定', - onPress: () => { - info('已保存为草稿'); - router.back(); - }, - }, - ]); - }; - // 提交表单 - const handleSubmit = () => { + const handleSubmit = async () => { if (!validateForm()) return; - Alert.alert('提交工单', '确认提交维修工单吗?', [ - { text: '取消', style: 'cancel' }, - { - text: '确定', - onPress: () => { - const workOrderNo = `WO-${Date.now()}`; - info(`维修工单提交成功!工单号:${workOrderNo}`); + let data = { + EquipmentId: selectedEquipment, + faultType, + priority, + impact, + FaultDesc: faultDescription, + ExpectedCompleteTime: expectedTime, + remark: remarks, + }; + if (isWeb) { + const confirmed = window.confirm('确认提交维修工单吗?'); + if (confirmed) { + const { Success } = await http.post('/api/EmRepairOrder', data); + if (Success) { router.back(); + info(`维修工单提交成功!`); + } + } + } else + Alert.alert('提交工单', '确认提交维修工单吗?', [ + { text: '取消', style: 'cancel' }, + { + text: '确定', + onPress: async () => { + const { Success } = await http.post( + '/api/EmRepairOrder', + data + ); + if (Success) { + router.back(); + info(`维修工单提交成功!`); + } + }, }, - }, - ]); + ]); }; + // 如果显示扫码界面,渲染 QRCodeScanner + if (showScanner) { + return ( + + ); + } + return ( {/* 顶部导航 */} @@ -265,22 +236,19 @@ const AddRepairOrder: React.FC = () => { {equipmentList.map((equipment) => ( { - setSelectedEquipment(equipment.id); + setSelectedEquipment(equipment.ID); setShowEquipmentPicker(false); }} - className={`border-b border-gray-100 p-3 dark:border-neutral-600 ${selectedEquipment === equipment.id - ? 'bg-blue-50 dark:bg-blue-950/30' - : '' - }`} + className={`border-b border-gray-100 p-3 dark:border-neutral-600 ${selectedEquipment === equipment.ID ? 'bg-blue-50 dark:bg-blue-950/30' : ''}`} activeOpacity={0.7} > - {equipment.name} {equipment.code} + {equipment.MachineName} {equipment.MachineNo} - {equipment.location} + {equipment.Location} ))} @@ -291,8 +259,8 @@ const AddRepairOrder: React.FC = () => { {/* 或扫码选择 */} info('扫码功能开发中')} + className="flex-row items-center justify-center space-x-2 rounded-lg border-2 border-dashed border-primary-500 py-3 mt-4" + onPress={() => setShowScanner(true)} activeOpacity={0.7} > @@ -314,45 +282,26 @@ const AddRepairOrder: React.FC = () => { {/* 故障类型 */} - - - 故障类型 * - - - - - setFaultType('mechanical')} - /> - - - setFaultType('electrical')} - /> - - - - - setFaultType('hydraulic')} - /> - - - setFaultType('other')} - /> + {faultTypes && faultTypes.length > 0 && ( + + + 故障类型 * + + + + {faultTypes.map((faultType1, index) => ( + + setFaultType(faultType1.value)} + /> + + ))} - + )} {/* 优先级 */} @@ -483,15 +432,19 @@ const AddRepairOrder: React.FC = () => { 期望完成时间 * info('日期选择器开发中')} activeOpacity={0.7} > - {expectedTime || '请选择期望完成时间'} - + */} + setExpectedTime(date)} + /> @@ -519,10 +472,7 @@ const AddRepairOrder: React.FC = () => { setAssignedTechnician(''); setShowTechnicianPicker(false); }} - className={`border-b border-gray-100 p-3 dark:border-neutral-600 ${!assignedTechnician - ? 'bg-blue-50 dark:bg-blue-950/30' - : '' - }`} + className={`border-b border-gray-100 p-3 dark:border-neutral-600 ${!assignedTechnician ? 'bg-blue-50 dark:bg-blue-950/30' : ''}`} activeOpacity={0.7} > @@ -536,10 +486,7 @@ const AddRepairOrder: React.FC = () => { setAssignedTechnician(technician.id); setShowTechnicianPicker(false); }} - className={`border-b border-gray-100 p-3 dark:border-neutral-600 ${assignedTechnician === technician.id - ? 'bg-blue-50 dark:bg-blue-950/30' - : '' - }`} + className={`border-b border-gray-100 p-3 dark:border-neutral-600 ${assignedTechnician === technician.id ? 'bg-blue-50 dark:bg-blue-950/30' : ''}`} activeOpacity={0.7} > @@ -642,27 +589,17 @@ const AddRepairOrder: React.FC = () => { • 上传故障图片可帮助维修人员提前准备工具和备件 - • 维修工单创建后可在"维修管理"中查看进度 + • 维修工单创建后可在"维修管理"中查看进度 {/* 底部固定操作栏 */} - + - - - - - 存为草稿 - - - void; +}; + +const FilterButton: React.FC = ({ + label, + count, + active, + onPress, +}) => ( + + + {label} + + {count} + + + +); + +// 设备卡片组件 +type EquipmentCardProps = { + equipment: Equipment; + onPress: () => void; +}; + +const EquipmentCard: React.FC = ({ + equipment, + onPress, +}) => { + const getStatusConfig = (status: EquipmentStatus) => { + switch (status) { + case 'running': + return { + bgColor: 'rgba(82, 196, 26, 0.1)', + textColor: '#52c41a', + icon: 'circle', + label: '运行中', + }; + case 'repairing': + return { + bgColor: 'rgba(250, 173, 20, 0.1)', + textColor: '#faad14', + icon: 'wrench', + label: '维修中', + }; + case 'fault': + return { + bgColor: 'rgba(245, 34, 45, 0.1)', + textColor: '#f5222d', + icon: 'exclamation-circle', + label: '故障', + }; + } + }; + + const statusConfig = getStatusConfig(equipment.status); + + return ( + + + {/* 顶部信息 */} + + + + + + + + {equipment.name} + + + 编号:{equipment.code} + + + + + {equipment.location} + + + + {equipment.installDate} + + + + + + + + + {statusConfig.label} + + + + + + {/* 底部统计 */} + + + + {equipment.health}% + + + 健康度 + + + + + {equipment.repairCount}次 + + + 本月维修 + + + + + {equipment.maintenanceInfo} + + + {equipment.status === 'repairing' + ? '维修状态' + : equipment.status === 'fault' + ? '优先级' + : '距保养'} + + + + + + ); +}; + +const EquipmentList: React.FC = () => { + const router = useRouter(); + const [searchText, setSearchText] = useState(''); + const [activeFilter, setActiveFilter] = useState('all'); + const { isDark } = useAppColorScheme(); + + // 模拟设备数据 + const equipmentList: Equipment[] = [ + { + id: '1', + name: '数控机床 CNC-01', + code: 'EQ-2024-001', + icon: 'database', + location: 'A车间-1号线', + installDate: '2023-05-15', + status: 'running', + health: 98, + repairCount: 2, + maintenanceInfo: '5天', + gradientColors: ['#3b82f6', '#2563eb'], + }, + { + id: '2', + name: '注塑机 IM-03', + code: 'EQ-2024-003', + icon: 'cog', + location: 'B车间-2号线', + installDate: '2022-08-20', + status: 'repairing', + health: 65, + repairCount: 7, + maintenanceInfo: '进行中', + gradientColors: ['#f97316', '#ea580c'], + }, + { + id: '3', + name: '空压机 AC-05', + code: 'EQ-2024-005', + icon: 'map-marker', + location: '动力车间', + installDate: '2021-03-10', + status: 'fault', + health: 35, + repairCount: 15, + maintenanceInfo: '紧急', + gradientColors: ['#ef4444', '#dc2626'], + }, + { + id: '4', + name: '混料机 MX-08', + code: 'EQ-2024-008', + icon: 'flask', + location: 'C车间-配料区', + installDate: '2023-11-05', + status: 'running', + health: 95, + repairCount: 1, + maintenanceInfo: '12天', + gradientColors: ['#22c55e', '#16a34a'], + }, + { + id: '5', + name: '机械手臂 RB-02', + code: 'EQ-2024-012', + icon: 'map-marker', + location: 'A车间-组装线', + installDate: '2024-01-15', + status: 'running', + health: 99, + repairCount: 0, + maintenanceInfo: '30天', + gradientColors: ['#a855f7', '#9333ea'], + }, + ]; + + // 筛选设备 + const filteredEquipment = equipmentList.filter((item) => { + if (activeFilter === 'all') return true; + return item.status === activeFilter; + }); + + const filterCounts = { + all: equipmentList.length, + running: equipmentList.filter((e) => e.status === 'running').length, + repairing: equipmentList.filter((e) => e.status === 'repairing').length, + fault: equipmentList.filter((e) => e.status === 'fault').length, + }; + + return ( + + {/* 顶部导航 */} + + { + router.push(`/equipment/add`); + }} + > + + + + } + /> + + {/* 搜索和筛选 - 固定在顶部 */} + + {/* 搜索框 */} + + + + + + + + + + + {/* 筛选标签 */} + + + setActiveFilter('all')} + /> + setActiveFilter('running')} + /> + setActiveFilter('repairing')} + /> + setActiveFilter('fault')} + /> + + + + + 筛选 + + + + + + + + {/* 设备列表 - 可滚动 */} + + {/* 设备列表 */} + + {filteredEquipment.map((equipment) => ( + { + router.push(`/equipment/${equipment.id}`); + }} + /> + ))} + + + {/* 底部空间 */} + + + + ); +}; + +export default EquipmentList; diff --git a/src/app/user-agreement.tsx b/src/app/user-agreement.tsx index 32b0defdfcbf53a31cab6f1118f0071cd6c87b2b..a79da85ee3dea1fd26fdd72a09d53cf90cce4cbe 100644 --- a/src/app/user-agreement.tsx +++ b/src/app/user-agreement.tsx @@ -2,11 +2,8 @@ import { Stack, useRouter } from 'expo-router'; import React from 'react'; import { Pressable, ScrollView } from 'react-native'; -import { - SafeAreaView, - Text, - View, -} from '@/components/ui'; + +import { SafeAreaView, Text, View } from '@/components/ui'; export default function UserAgreementScreen() { const router = useRouter(); diff --git a/src/components/analytics/inventory.tsx b/src/components/analytics/inventory.tsx index 60110e60b9df9e7c43755d97786977cc77feb50b..53007138fd7d87a934a8da151385529109047b79 100644 --- a/src/components/analytics/inventory.tsx +++ b/src/components/analytics/inventory.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Dimensions, - SafeAreaView, ScrollView, Text, TouchableOpacity, @@ -9,6 +8,8 @@ import { } from 'react-native'; import { LineChart, PieChart } from 'react-native-chart-kit'; +import { useAppColorScheme } from '@/lib'; + // 获取屏幕宽度用于图表响应式设计 const screenWidth = Dimensions.get('window').width; @@ -28,8 +29,8 @@ const KpiCard: React.FC = ({ trendUp = true, color, }) => ( - - {title} + + {title} {value} @@ -56,20 +57,20 @@ const InventoryAlertItem: React.FC = ({ safetyStock, level, }) => { - let bgColor = 'bg-red-50'; + let bgColor = 'bg-red-50 dark:bg-red-900/20'; let borderColor = 'border-red-500'; - let textColor = 'text-red-600'; + let textColor = 'text-red-600 dark:text-red-400'; let levelText = '紧急'; if (level === 'warning') { - bgColor = 'bg-yellow-50'; + bgColor = 'bg-yellow-50 dark:bg-yellow-900/20'; borderColor = 'border-yellow-500'; - textColor = 'text-yellow-600'; + textColor = 'text-yellow-600 dark:text-yellow-400'; levelText = '注意'; } else if (level === 'normal') { - bgColor = 'bg-green-50'; + bgColor = 'bg-green-50 dark:bg-green-900/20'; borderColor = 'border-green-500'; - textColor = 'text-green-600'; + textColor = 'text-green-600 dark:text-green-400'; levelText = '正常'; } @@ -78,8 +79,10 @@ const InventoryAlertItem: React.FC = ({ className={`flex-row items-center justify-between p-3 ${bgColor} rounded-lg border-l-4 ${borderColor} mb-2`} > - {name} - + + {name} + + 当前库存: {currentStock} | 安全库存: {safetyStock} @@ -89,13 +92,16 @@ const InventoryAlertItem: React.FC = ({ }; // 图表通用配置 -const chartConfig = { - backgroundColor: '#ffffff', - backgroundGradientFrom: '#ffffff', - backgroundGradientTo: '#ffffff', +const getChartConfig = (isDark: boolean) => ({ + backgroundColor: isDark ? '#1f2937' : '#ffffff', + backgroundGradientFrom: isDark ? '#1f2937' : '#ffffff', + backgroundGradientTo: isDark ? '#1f2937' : '#ffffff', decimalPlaces: 0, color: (opacity = 1) => `rgba(59, 130, 246, ${opacity})`, - labelColor: (opacity = 1) => `rgba(107, 114, 128, ${opacity})`, + labelColor: (opacity = 1) => + isDark + ? `rgba(156, 163, 175, ${opacity})` + : `rgba(107, 114, 128, ${opacity})`, style: { borderRadius: 16, }, @@ -104,9 +110,12 @@ const chartConfig = { strokeWidth: '2', stroke: '#3b82f6', }, -}; +}); export const Inventory: React.FC = () => { + const { isDark } = useAppColorScheme(); + const chartConfig = getChartConfig(isDark); + // 库存周转率趋势数据 const inventoryTurnoverData = { labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月'], @@ -126,40 +135,48 @@ export const Inventory: React.FC = () => { name: '正常', population: 65, color: '#10b981', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '偏低', population: 20, color: '#f59e0b', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '预警', population: 15, color: '#ef4444', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, ]; return ( - + {/* 时间选择器 */} - 库存数据 - - - + + 库存数据 + + + + + 日 + - - + + + 月 + - + + 年 + @@ -197,11 +214,15 @@ export const Inventory: React.FC = () => { {/* 库存周转率趋势 */} - + - 库存周转率趋势 + + 库存周转率趋势 + - 详情 + + 详情 + { {/* 库存状态分布 */} - + - 库存状态分布 + + 库存状态分布 + - 详情 + + 详情 + @@ -241,18 +266,30 @@ export const Inventory: React.FC = () => { - 正常 - 65% + + 正常 + + + 65% + - 偏低 - 20% + + 偏低 + + + 20% + - 预警 - 15% + + 预警 + + + 15% + @@ -260,11 +297,15 @@ export const Inventory: React.FC = () => { {/* 库存预警列表 */} - + - 库存预警 + + 库存预警 + - 查看全部 + + 查看全部 + @@ -289,6 +330,6 @@ export const Inventory: React.FC = () => { - + ); }; diff --git a/src/components/analytics/kpi-card.tsx b/src/components/analytics/kpi-card.tsx index a3b6c8f89be646e568dbe6992bc966da3eab490f..f91de871f39f6adfc3af3e8252c85bafc3d4d6df 100644 --- a/src/components/analytics/kpi-card.tsx +++ b/src/components/analytics/kpi-card.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Text, View } from '@/components/ui'; + // KPI卡片组件 type KpiCardProps = { title: string; @@ -17,8 +18,8 @@ export const KpiCard: React.FC = ({ trendUp = true, color, }) => ( - - {title} + + {title} {value} diff --git a/src/components/analytics/overview.tsx b/src/components/analytics/overview.tsx index d65cb401b813b5c8d1017fa4f22b162dafaf58e4..e92b8370236c04dac167095f2dd0958ecd0f01e4 100644 --- a/src/components/analytics/overview.tsx +++ b/src/components/analytics/overview.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { Dimensions, Text, TouchableOpacity, View } from 'react-native'; import { BarChart, LineChart, PieChart } from 'react-native-chart-kit'; +import { useAppColorScheme } from '@/lib'; + import { KpiCard } from './kpi-card'; import { ReportItem } from './report-item'; @@ -39,45 +41,48 @@ const productionOrderData = { }; // 产品类别分布数据 -const productCategoryData = [ +const getProductCategoryData = (isDark: boolean) => [ { name: '智能手表', population: 45, color: '#3b82f6', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '智能音箱', population: 25, color: '#22c55e', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '智能门锁', population: 15, color: '#f59e0b', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '其他产品', population: 15, color: '#ef4444', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, ]; // 图表通用配置 -const chartConfig = { - backgroundColor: '#ffffff', - backgroundGradientFrom: '#ffffff', - backgroundGradientTo: '#ffffff', +const getChartConfig = (isDark: boolean) => ({ + backgroundColor: isDark ? '#1f2937' : '#ffffff', + backgroundGradientFrom: isDark ? '#1f2937' : '#ffffff', + backgroundGradientTo: isDark ? '#1f2937' : '#ffffff', decimalPlaces: 0, color: (opacity = 1) => `rgba(59, 130, 246, ${opacity})`, - labelColor: (opacity = 1) => `rgba(107, 114, 128, ${opacity})`, + labelColor: (opacity = 1) => + isDark + ? `rgba(156, 163, 175, ${opacity})` + : `rgba(107, 114, 128, ${opacity})`, style: { borderRadius: 16, }, @@ -86,23 +91,29 @@ const chartConfig = { strokeWidth: '2', stroke: '#3b82f6', }, -}; +}); export const OverView = () => { + const { isDark } = useAppColorScheme(); + const chartConfig = getChartConfig(isDark); + const productCategoryData = getProductCategoryData(isDark); + return ( {/* 时间选择器 */} - 本月数据 - - - + + 本月数据 + + + + - - + + - + @@ -140,11 +151,15 @@ export const OverView = () => { {/* 销售趋势图 - 使用BarChart替换自定义图表 */} - + - 销售趋势 + + 销售趋势 + - 详情 + + 详情 + { {/* 生产与订单对比 - 使用LineChart替换自定义图表 */} - + - 生产与订单对比 + + 生产与订单对比 + - 详情 + + 详情 + { {/* 产品类别分布 - 使用PieChart替换自定义图表 */} - + - 产品类别分布 + + 产品类别分布 + - 详情 + + 详情 + { {/* 数据报表下载 */} - - 数据报表 + + + 数据报表 + {}} + onPress={() => { }} /> {}} + onPress={() => { }} /> {}} + onPress={() => { }} /> diff --git a/src/components/analytics/production.tsx b/src/components/analytics/production.tsx index efb3c455244eec006a21adf21a1feb41e10bb2db..6afc879fb57346d2454d8b02e45a076a660bbc45 100644 --- a/src/components/analytics/production.tsx +++ b/src/components/analytics/production.tsx @@ -1,9 +1,7 @@ import React from 'react'; import { Dimensions, - SafeAreaView, ScrollView, - StatusBar, Text, TouchableOpacity, View, @@ -11,6 +9,7 @@ import { import { LineChart, ProgressChart } from 'react-native-chart-kit'; import { FontAwesome } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; import { KpiCard } from './kpi-card'; @@ -36,34 +35,36 @@ const DeviceStatusItem: React.FC = ({ switch (status) { case 'normal': return { - container: 'bg-green-50 border-l-4 border-green-500', - iconBg: 'bg-green-100', - iconColor: 'text-green-600', - statusText: 'text-green-600', + container: + 'bg-green-50 border-l-4 border-green-500 dark:bg-green-900/20', + iconBg: 'bg-green-100 dark:bg-green-900/30', + iconColor: '#16a34a', + statusText: 'text-green-600 dark:text-green-400', statusLabel: '正常', }; case 'warning': return { - container: 'bg-yellow-50 border-l-4 border-yellow-500', - iconBg: 'bg-yellow-100', - iconColor: 'text-yellow-600', - statusText: 'text-yellow-600', + container: + 'bg-yellow-50 border-l-4 border-yellow-500 dark:bg-yellow-900/20', + iconBg: 'bg-yellow-100 dark:bg-yellow-900/30', + iconColor: '#ca8a04', + statusText: 'text-yellow-600 dark:text-yellow-400', statusLabel: '注意', }; case 'error': return { - container: 'bg-red-50 border-l-4 border-red-500', - iconBg: 'bg-red-100', - iconColor: 'text-red-600', - statusText: 'text-red-600', + container: 'bg-red-50 border-l-4 border-red-500 dark:bg-red-900/20', + iconBg: 'bg-red-100 dark:bg-red-900/30', + iconColor: '#dc2626', + statusText: 'text-red-600 dark:text-red-400', statusLabel: '故障', }; default: return { - container: 'bg-gray-50 border-l-4 border-gray-500', - iconBg: 'bg-gray-100', - iconColor: 'text-gray-600', - statusText: 'text-gray-600', + container: 'bg-gray-50 border-l-4 border-gray-500 dark:bg-gray-700', + iconBg: 'bg-gray-100 dark:bg-gray-600', + iconColor: '#4b5563', + statusText: 'text-gray-600 dark:text-gray-400', statusLabel: '未知', }; } @@ -79,15 +80,15 @@ const DeviceStatusItem: React.FC = ({ - + - {name} - {info} + + {name} + + + {info} + @@ -98,13 +99,16 @@ const DeviceStatusItem: React.FC = ({ }; // 图表通用配置 -const chartConfig = { - backgroundColor: '#ffffff', - backgroundGradientFrom: '#ffffff', - backgroundGradientTo: '#ffffff', +const getChartConfig = (isDark: boolean) => ({ + backgroundColor: isDark ? '#1f2937' : '#ffffff', + backgroundGradientFrom: isDark ? '#1f2937' : '#ffffff', + backgroundGradientTo: isDark ? '#1f2937' : '#ffffff', decimalPlaces: 0, color: (opacity = 1) => `rgba(59, 130, 246, ${opacity})`, - labelColor: (opacity = 1) => `rgba(107, 114, 128, ${opacity})`, + labelColor: (opacity = 1) => + isDark + ? `rgba(156, 163, 175, ${opacity})` + : `rgba(107, 114, 128, ${opacity})`, style: { borderRadius: 16, }, @@ -113,9 +117,12 @@ const chartConfig = { strokeWidth: '2', stroke: '#3b82f6', }, -}; +}); export const Production: React.FC = () => { + const { isDark } = useAppColorScheme(); + const chartConfig = getChartConfig(isDark); + // 生产效率趋势数据 const efficiencyData = { labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月'], @@ -135,22 +142,28 @@ export const Production: React.FC = () => { }; return ( - - - + {/* 时间选择器 */} - 生产数据 - - - + + 生产数据 + + + + + 日 + - - + + + 月 + - + + 年 + @@ -188,11 +201,15 @@ export const Production: React.FC = () => { {/* 生产效率趋势 */} - + - 生产效率趋势 + + 生产效率趋势 + - 详情 + + 详情 + { {/* 设备运行状态 */} - + - 设备运行状态 + + 设备运行状态 + - 详情 + + 详情 + @@ -240,10 +261,14 @@ export const Production: React.FC = () => { {/* 产能利用率 */} - + - 产能利用率 - 78.5% + + 产能利用率 + + + 78.5% + { /> - + ); }; diff --git a/src/components/analytics/quality.tsx b/src/components/analytics/quality.tsx index 86bf47ba7b4bd1c59ef6376b1981a0d0e67f331a..205cce8059d56ca93a8c4288fd1aff7ea7b6345b 100644 --- a/src/components/analytics/quality.tsx +++ b/src/components/analytics/quality.tsx @@ -4,6 +4,7 @@ import { LineChart, PieChart } from 'react-native-chart-kit'; import { KpiCard } from '@/components/analytics/kpi-card'; import { FontAwesome, GroupEnum } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; // 获取屏幕宽度用于图表响应式设计 const screenWidth = Dimensions.get('window').width; @@ -22,10 +23,10 @@ const ProductQualityItem: React.FC = ({ }) => ( - {name} - {rate} + {name} + {rate} - + = ({ dueDate, icon, color, -}) => ( - +}) => { + const getBgClass = () => { + switch (color) { + case 'blue': + return 'bg-blue-50 dark:bg-blue-900/20'; + case 'green': + return 'bg-green-50 dark:bg-green-900/20'; + case 'purple': + return 'bg-purple-50 dark:bg-purple-900/20'; + default: + return 'bg-gray-50 dark:bg-gray-700'; + } + }; + + const getIconBgClass = () => { + switch (color) { + case 'blue': + return 'bg-blue-100 dark:bg-blue-900/30'; + case 'green': + return 'bg-green-100 dark:bg-green-900/30'; + case 'purple': + return 'bg-purple-100 dark:bg-purple-900/30'; + default: + return 'bg-gray-100 dark:bg-gray-600'; + } + }; + + const iconColor = + color === 'blue' ? '#0066ff' : color === 'green' ? '#22c55e' : '#8b5cf6'; + + return ( - - - - {title} - - 进度: {progress} | 预计完成: {dueDate} - - - - + + + + + + {title} + + + 进度: {progress} | 预计完成: {dueDate} + + + + + - -); + ); +}; // 图表通用配置 -const chartConfig = { - backgroundColor: '#ffffff', - backgroundGradientFrom: '#ffffff', - backgroundGradientTo: '#ffffff', +const getChartConfig = (isDark: boolean) => ({ + backgroundColor: isDark ? '#1f2937' : '#ffffff', + backgroundGradientFrom: isDark ? '#1f2937' : '#ffffff', + backgroundGradientTo: isDark ? '#1f2937' : '#ffffff', decimalPlaces: 1, color: (opacity = 1) => `rgba(59, 130, 246, ${opacity})`, - labelColor: (opacity = 1) => `rgba(107, 114, 128, ${opacity})`, + labelColor: (opacity = 1) => + isDark + ? `rgba(156, 163, 175, ${opacity})` + : `rgba(107, 114, 128, ${opacity})`, style: { borderRadius: 16, }, @@ -100,9 +135,12 @@ const chartConfig = { strokeWidth: '2', stroke: '#3b82f6', }, -}; +}); export const Quality: React.FC = () => { + const { isDark } = useAppColorScheme(); + const chartConfig = getChartConfig(isDark); + // 质量合格率趋势数据 const qualityTrendData = { labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月'], @@ -122,35 +160,35 @@ export const Quality: React.FC = () => { name: '组装不良', population: 35, color: '#ef4444', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '材料缺陷', population: 20, color: '#f59e0b', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '功能异常', population: 20, color: '#3b82f6', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '外观瑕疵', population: 15, color: '#10b981', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '其他问题', population: 10, color: '#6b7280', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, ]; @@ -159,16 +197,18 @@ export const Quality: React.FC = () => { <> {/* 时间选择器 */} - 质量数据 - - - + + 质量数据 + + + + - - + + - + @@ -206,11 +246,15 @@ export const Quality: React.FC = () => { {/* 质量合格率趋势 */} - + - 质量合格率趋势 + + 质量合格率趋势 + - 详情 + + 详情 + { borderRadius: 16, }} /> - + 注:图表显示的是不良品率,数值越低越好 {/* 不良品分析 */} - + - 不良品分析 + + 不良品分析 + - 详情 + + 详情 + @@ -257,28 +305,48 @@ export const Quality: React.FC = () => { - 组装不良 - 35% + + 组装不良 + + + 35% + - 材料缺陷 - 20% + + 材料缺陷 + + + 20% + - 功能异常 - 20% + + 功能异常 + + + 20% + - 外观瑕疵 - 15% + + 外观瑕疵 + + + 15% + - 其他问题 - 10% + + 其他问题 + + + 10% + @@ -286,11 +354,15 @@ export const Quality: React.FC = () => { {/* 产品质量对比 */} - + - 产品质量对比 + + 产品质量对比 + - 详情 + + 详情 + @@ -302,11 +374,15 @@ export const Quality: React.FC = () => { {/* 质量改进计划 */} - + - 质量改进计划 + + 质量改进计划 + - 详情 + + 详情 + diff --git a/src/components/analytics/report-item.tsx b/src/components/analytics/report-item.tsx index f205c801af6eaaf7d07ad33d9362d4e01330e53c..09c7d062000b4e19689a53acf1e3df4e3ee8921b 100644 --- a/src/components/analytics/report-item.tsx +++ b/src/components/analytics/report-item.tsx @@ -3,6 +3,7 @@ import { TouchableOpacity } from 'react-native'; import { Text, View } from '@/components/ui'; import { FontAwesome } from '@/components/ui/icons'; +import { useAppColorScheme } from '@/lib'; // 报表项组件 type ReportItemProps = { @@ -19,22 +20,34 @@ export const ReportItem: React.FC = ({ title, date, onPress, -}) => ( - - - - - {title} - {date} +}) => { + const { isDark } = useAppColorScheme(); + + return ( + + + + + + {title} + + + {date} + + + + + - - - - -); + ); +}; diff --git a/src/components/analytics/sales.tsx b/src/components/analytics/sales.tsx index e3969898074df351b29fe8ec178a4ad983ae6efb..82b0aad237a4385e81e924bd87d980cfbdb915c5 100644 --- a/src/components/analytics/sales.tsx +++ b/src/components/analytics/sales.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { Dimensions, Text, TouchableOpacity, View } from 'react-native'; import { BarChart, PieChart } from 'react-native-chart-kit'; +import { useAppColorScheme } from '@/lib'; + // 获取屏幕宽度用于图表响应式设计 const screenWidth = Dimensions.get('window').width; @@ -21,8 +23,8 @@ const KpiCard: React.FC = ({ trendUp = true, color, }) => ( - - {title} + + {title} {value} @@ -36,13 +38,16 @@ const KpiCard: React.FC = ({ ); // 图表通用配置 -const chartConfig = { - backgroundColor: '#ffffff', - backgroundGradientFrom: '#ffffff', - backgroundGradientTo: '#ffffff', +const getChartConfig = (isDark: boolean) => ({ + backgroundColor: isDark ? '#1f2937' : '#ffffff', + backgroundGradientFrom: isDark ? '#1f2937' : '#ffffff', + backgroundGradientTo: isDark ? '#1f2937' : '#ffffff', decimalPlaces: 0, color: (opacity = 1) => `rgba(59, 130, 246, ${opacity})`, - labelColor: (opacity = 1) => `rgba(107, 114, 128, ${opacity})`, + labelColor: (opacity = 1) => + isDark + ? `rgba(156, 163, 175, ${opacity})` + : `rgba(107, 114, 128, ${opacity})`, style: { borderRadius: 16, }, @@ -51,9 +56,12 @@ const chartConfig = { strokeWidth: '2', stroke: '#3b82f6', }, -}; +}); export const Sales: React.FC = () => { + const { isDark } = useAppColorScheme(); + const chartConfig = getChartConfig(isDark); + // 销售趋势数据 const salesData = { labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月'], @@ -73,28 +81,28 @@ export const Sales: React.FC = () => { name: '华东地区', population: 45, color: '#3b82f6', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '华南地区', population: 25, color: '#22c55e', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '华北地区', population: 15, color: '#f59e0b', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, { name: '其他地区', population: 15, color: '#ef4444', - legendFontColor: '#7F7F7F', + legendFontColor: isDark ? '#9ca3af' : '#7F7F7F', legendFontSize: 12, }, ]; @@ -103,16 +111,18 @@ export const Sales: React.FC = () => { <> {/* 时间选择器 */} - 销售数据 - - - + + 销售数据 + + + + - - + + - + @@ -150,11 +160,15 @@ export const Sales: React.FC = () => { {/* 销售趋势图 */} - + - 销售趋势 + + 销售趋势 + - 详情 + + 详情 + { {/* 客户地区分布 */} - + - 客户地区分布 + + 客户地区分布 + - 详情 + + 详情 + diff --git a/src/components/equipment/document-item.tsx b/src/components/equipment/document-item.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b3cf417e63150f87430af899ea287f67c3e09cd2 --- /dev/null +++ b/src/components/equipment/document-item.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { TouchableOpacity } from 'react-native'; + +import { Text, View } from '@/components/ui'; +import { FontAwesome } from '@/components/ui/icons'; + +export type DocumentItemProps = { + icon: string; + iconColor: string; + iconBgColor: string; + title: string; + size: string; + date: string; + onPress: () => void; +}; + +export const DocumentItem: React.FC = ({ + icon, + iconColor, + iconBgColor, + title, + size, + date, + onPress, +}) => ( + + + + + + + {title} + + + {size} · {date} + + + + +); diff --git a/src/components/equipment/index.tsx b/src/components/equipment/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..62f55c3b773f5612946b07a830ea111cb1b55b00 --- /dev/null +++ b/src/components/equipment/index.tsx @@ -0,0 +1,5 @@ +export * from './document-item'; +export * from './info-row'; +export * from './maintenance-plan-item'; +export * from './repair-record-item'; +export * from './stat-card'; diff --git a/src/components/equipment/info-row.tsx b/src/components/equipment/info-row.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c634ba120599ac517c40f30ac858bf0f4f4b140f --- /dev/null +++ b/src/components/equipment/info-row.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +import { Text, View } from '@/components/ui'; + +export type InfoRowProps = { + label: string; + value?: string; + isLast?: boolean; +}; + +export const InfoRow: React.FC = ({ + label, + value, + isLast = false, +}) => ( + + {label} + + {value} + + +); diff --git a/src/components/equipment/maintenance-plan-item.tsx b/src/components/equipment/maintenance-plan-item.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5dd669043bd5aef8d3c01ac0048b4be8fcbf7e35 --- /dev/null +++ b/src/components/equipment/maintenance-plan-item.tsx @@ -0,0 +1,49 @@ +import React from 'react'; + +import { Text, View } from '@/components/ui'; + +export type MaintenancePlanItemProps = { + title: string; + date: string; + daysLeft: string; + borderColor: string; + bgColor: string; + textColor: string; + isDark: boolean; +}; + +export const MaintenancePlanItem: React.FC = ({ + title, + date, + daysLeft, + borderColor, + bgColor, + textColor, + isDark, +}) => { + // 在暗黑模式下使用半透明的深色背景 + const darkBgColor = isDark ? 'rgba(64, 64, 64, 0.3)' : bgColor; + + return ( + + + + {title} + + + 下次保养时间:{date} + + + + {daysLeft} + + + ); +}; diff --git a/src/components/equipment/repair-record-item.tsx b/src/components/equipment/repair-record-item.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ed31027c9257bedb6b11b06bb825db888733d0c1 --- /dev/null +++ b/src/components/equipment/repair-record-item.tsx @@ -0,0 +1,73 @@ +import React from 'react'; + +import { Text, View } from '@/components/ui'; +import { FontAwesome } from '@/components/ui/icons'; + +export type RepairRecordItemProps = { + title: string; + description: string; + assignee: string; + date: string; + status: string; + statusColor: string; + borderColor: string; + bgColor: string; + isDark: boolean; +}; + +export const RepairRecordItem: React.FC = ({ + title, + description, + assignee, + date, + status, + statusColor, + borderColor, + bgColor, + isDark, +}) => { + // 在暗黑模式下使用半透明的深色背景 + const darkBgColor = isDark ? 'rgba(64, 64, 64, 0.3)' : bgColor; + + return ( + + + + + {title} + + + {description} + + + + {status} + + + + + + + {assignee} + + + + + + {date} + + + + + ); +}; diff --git a/src/components/equipment/stat-card.tsx b/src/components/equipment/stat-card.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6571d7a786c9258785b99dccccfdb5e99349a1d2 --- /dev/null +++ b/src/components/equipment/stat-card.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +import { Text, View } from '@/components/ui'; + +export type StatCardProps = { + value: string; + label: string; + bgColor: string; + textColor: string; + isDark: boolean; +}; + +export const StatCard: React.FC = ({ + value, + label, + bgColor, + textColor, + isDark, +}) => { + // 在暗黑模式下使用半透明的深色背景 + const darkBgColor = isDark ? 'rgba(64, 64, 64, 0.5)' : bgColor; + + return ( + + + {value} + + {label} + + ); +}; diff --git a/src/components/inventory/item.tsx b/src/components/inventory/item.tsx index 8c0c4b010702ed87aca349d2d5364b09a6f5a62c..c3749e8208fd0230795a77dcf356e0a78540e8a7 100644 --- a/src/components/inventory/item.tsx +++ b/src/components/inventory/item.tsx @@ -39,17 +39,19 @@ const Item = ({ className="mr-2 size-2 rounded-full" style={{ backgroundColor: indicatorColor }} /> - {name} + + {name} + - 编号:{code} + + 编号:{code} + - - {quantity} + {quantity} + + 安全库存:{safetyStock} - 安全库存:{safetyStock} @@ -57,4 +59,3 @@ const Item = ({ }; export default Item; - diff --git a/src/components/production/equipment.tsx b/src/components/production/equipment.tsx index 204faee1c304266e489540611912520c0916216f..b40ff28dce979be8a18279afe55035a2a85b6e06 100644 --- a/src/components/production/equipment.tsx +++ b/src/components/production/equipment.tsx @@ -140,16 +140,28 @@ export const Equipment = () => { - 32 - 设备总数 + + 32 + + + 设备总数 + - 25 - 运行中 + + 25 + + + 运行中 + - 2 - 故障 + + 2 + + + 故障 + @@ -160,7 +172,9 @@ export const Equipment = () => { className="mr-3" /> - 设备综合效率(OEE) + + 设备综合效率(OEE) + 87.2% @@ -172,7 +186,9 @@ export const Equipment = () => { {/* 设备列表标题 */} - 设备列表 + + 设备列表 + {/* 设备列表 - 使用map函数循环渲染设备数据 */} {equipmentData.map((equipment) => ( @@ -201,24 +217,36 @@ export const Equipment = () => { {/* 设备参数信息 - 温度、转速、电压、电流 */} - 设备温度 + + 设备温度 + {equipment.temperature} - 电机转速 + + 电机转速 + {equipment.motorSpeed} - 电压 - {equipment.voltage} + + 电压 + + + {equipment.voltage} + - 电流 - {equipment.current} + + 电流 + + + {equipment.current} + diff --git a/src/components/production/plan.tsx b/src/components/production/plan.tsx index 491c15a8b80e3280486d54e8cb27ae8e219312b1..904a35b324f2d579995f2346b93ceb3951d6004f 100644 --- a/src/components/production/plan.tsx +++ b/src/components/production/plan.tsx @@ -56,7 +56,9 @@ export const PlanItem: React.FC = ({ {/* 标题和状态区域 - 使用弹性布局和对齐方式 */} - {title} + + {title} + = ({ {/* 计划编号 - 使用灰色文本和下边距 */} - 计划编号:{code} + + 计划编号:{code} + {/* 负责人和截止日期 - 使用弹性布局和间距 */} - 负责人:{manager} - 截止日期:{deadline} + + 负责人:{manager} + + + 截止日期:{deadline} + {/* 进度条区域 - 使用下边距和嵌套布局 */} - 完成进度 - {progress}% + + 完成进度 + + + {progress}% + @@ -85,10 +97,18 @@ export const PlanItem: React.FC = ({ {/* 底部统计和详情链接 - 使用弹性布局和对齐 */} - 计划产量: - {total} - 已完成: - {completed} + + 计划产量: + + + {total} + + + 已完成: + + + {completed} + @@ -156,16 +176,28 @@ export const Plans = () => { {/* 统计数据网格 */} - 15 - 计划总数 + + 15 + + + 计划总数 + - 8 - 进行中 + + 8 + + + 进行中 + - 3 - 待开始 + + 3 + + + 待开始 + @@ -178,7 +210,9 @@ export const Plans = () => { className="mr-3" /> - 本月生产完成率 + + 本月生产完成率 + 78.5% diff --git a/src/components/production/process-node.tsx b/src/components/production/process-node.tsx index ef629f1ef6f013300060d5669397938ef53780c0..911d7d6e1b787ae3522b6e8687638f95707d7b5f 100644 --- a/src/components/production/process-node.tsx +++ b/src/components/production/process-node.tsx @@ -75,7 +75,9 @@ export const ProcessNode: React.FC = ({ {statusIcon} {/* 节点标签 */} - {label} + + {label} + {/* 节点状态文本 - 根据状态显示不同颜色 */} { 18 - 工序总数 + + 工序总数 + 10 - 使用中 + + 使用中 + 2 - 待优化 + + 待优化 + @@ -126,7 +132,9 @@ export const Process = () => { {/* 工序流程图 - 显示生产工序的流程和进度 */} - 智能手表生产工序流程 + + 智能手表生产工序流程 + 当前批次: @@ -178,7 +186,9 @@ export const Process = () => { {/* 工序列表标题 */} - 工序列表 + + 工序列表 + {/* 工序列表 - 使用map函数循环渲染工序数据 */} {processData.map((process) => ( @@ -207,11 +217,17 @@ export const Process = () => { {/* 工序基本信息 - 负责人和标准工时 */} - 负责人 - {process.responsible} + + 负责人 + + + {process.responsible} + - 标准工时 + + 标准工时 + {process.standardTime} @@ -221,15 +237,21 @@ export const Process = () => { {/* 设备利用率进度条 */} - 设备利用率 - {process.utilizationRate}% + + 设备利用率 + + + {process.utilizationRate}% + {/* 关联设备信息 */} - 关联设备 + + 关联设备 + { color={process.equipment.iconColor} style={{ marginRight: 8 }} /> - {process.equipment.name} + + {process.equipment.name} + {/* 工序项目底部 - 良品率和详情链接 */} - 良品率: + + 良品率: + {process.yieldRate} diff --git a/src/components/production/report.tsx b/src/components/production/report.tsx index fb08dfd9197d9abd132ac151301f42f3a2eab407..0fa2371efbf76a05d2be0f49da73bcc37256a2cb 100644 --- a/src/components/production/report.tsx +++ b/src/components/production/report.tsx @@ -120,7 +120,9 @@ export const Report = () => { {/* 报表概览 - 标题和日期选择器 */} - 生产报表 + + 生产报表 + @@ -128,26 +130,44 @@ export const Report = () => { 生产数据 - 2023-12-05 + + 2023-12-05 + {/* 生产数据统计网格 */} - 1,250 - 计划产量 + + 1,250 + + + 计划产量 + - 1,180 - 实际产量 + + 1,180 + + + 实际产量 + - 94.4% - 计划完成率 + + 94.4% + + + 计划完成率 + - 98.3% - 良品率 + + 98.3% + + + 良品率 + @@ -165,11 +185,15 @@ export const Report = () => { - 计划 + + 计划 + - 实际 + + 实际 + @@ -254,26 +278,44 @@ export const Report = () => { 设备运行报表 - 2023-12-05 + + 2023-12-05 + {/* 设备运行数据统计网格 */} - 32 - 设备总数 + + 32 + + + 设备总数 + - 28 - 运行设备 + + 28 + + + 运行设备 + - 87.5% - 设备利用率 + + 87.5% + + + 设备利用率 + - 4.2h - 平均运行 + + 4.2h + + + 平均运行 + @@ -331,8 +373,12 @@ export const Report = () => { {/* 中心白色圆 */} - 32台 - 设备 + + 32台 + + + 设备 + @@ -393,7 +439,9 @@ export const Report = () => { color="#ef4444" style={{ marginRight: 12 }} /> - 下载本月生产报表 + + 下载本月生产报表 + diff --git a/src/components/production/task.tsx b/src/components/production/task.tsx index 77e55b4ea7ca330d57de0c290146170eb8306151..71ce907585235db1cac56c85a07ec811eca8d1ef 100644 --- a/src/components/production/task.tsx +++ b/src/components/production/task.tsx @@ -103,19 +103,25 @@ export const Task = () => { 24 - 任务总数 + + 任务总数 + 12 - 进行中 + + 进行中 + 5 - 待分配 + + 待分配 + @@ -126,7 +132,9 @@ export const Task = () => { style={{ marginRight: 12 }} /> - 员工任务完成率 + + 员工任务完成率 + 82.3% @@ -179,8 +187,12 @@ export const Task = () => { {/* 完成进度条 */} - 完成进度 - {task.progress}% + + 完成进度 + + + {task.progress}% + { {/* 所属计划和详情按钮 */} - 所属计划: - {task.planNo} + + 所属计划: + + + {task.planNo} + diff --git a/src/components/refresh-list-view.tsx b/src/components/refresh-list-view.tsx index 5e3fa60ae339993dd79324a9c991b272d1fa0152..d3742c804905547ae533fdd080eca81e19ab2d1c 100644 --- a/src/components/refresh-list-view.tsx +++ b/src/components/refresh-list-view.tsx @@ -1,6 +1,12 @@ import { FlashList, type FlashListProps } from '@shopify/flash-list'; import React, { useMemo, useState } from 'react'; -import { ActivityIndicator, RefreshControl, View } from 'react-native'; +import { + ActivityIndicator, + RefreshControl, + View, + type ViewStyle, +} from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; /** * RefreshListView 组件属性接口 @@ -29,6 +35,7 @@ interface RefreshListViewProps onLoadMore?: () => void /** 是否正在刷新中 */; refreshing?: boolean /** 是否还有更多数据可加载 */; hasMore?: boolean; + style?: ViewStyle; } /** @@ -60,6 +67,7 @@ export const RefreshListView = (props: RefreshListViewProps) => { hasMore, ...restProps } = props; // 控制加载更多状态,防止重复触发 + const insets = useSafeAreaInsets(); const [isLoadMore, setIsLoadMore] = useState(false); /** * 处理滚动到底部的事件,触发加载更多 @@ -75,7 +83,7 @@ export const RefreshListView = (props: RefreshListViewProps) => { }; const footer = useMemo(() => { - if (!hasMore) return null; + if (!hasMore) return ; return ( diff --git a/src/components/repair-order/filter-button.tsx b/src/components/repair-order/filter-button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c84b056e273ecc92d078446ddc6cf82d1facefa6 --- /dev/null +++ b/src/components/repair-order/filter-button.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { TouchableOpacity } from 'react-native'; + +import { Text } from '@/components/ui'; + +type FilterButtonProps = { + label: string; + count: number; + isActive: boolean; + onPress: () => void; +}; + +export const FilterButton: React.FC = ({ + label, + count, + isActive, + onPress, +}) => ( + + + {label} {count} + + +); diff --git a/src/components/repair-order/index.ts b/src/components/repair-order/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d0765b7779146f2b164a7df4d08bb4e5e6852c99 --- /dev/null +++ b/src/components/repair-order/index.ts @@ -0,0 +1,7 @@ +export * from './filter-button'; +export * from './priority-button'; +export * from './progress-item'; +export * from './radio-button'; +export * from './spare-part-item'; +export * from './stat-card'; +export * from './work-order-card'; diff --git a/src/components/repair-order/priority-button.tsx b/src/components/repair-order/priority-button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9b8f77514482b4258925b09b8fee7cfaf04d7e8b --- /dev/null +++ b/src/components/repair-order/priority-button.tsx @@ -0,0 +1,44 @@ +import { TouchableOpacity } from 'react-native'; + +import { Text } from '../ui'; +import { FontAwesome } from '../ui/icons'; + +// 优先级按钮组件 +type PriorityButtonProps = { + label: string; + icon: string; + color: string; + selected: boolean; + onPress: () => void; +}; + +export const PriorityButton: React.FC = ({ + label, + icon, + color, + selected, + onPress, +}) => ( + + + + {label} + + +); diff --git a/src/components/repair-order/progress-item.tsx b/src/components/repair-order/progress-item.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bae4c8e099e32d873d42b6c362b46da7a9d28e5a --- /dev/null +++ b/src/components/repair-order/progress-item.tsx @@ -0,0 +1,57 @@ +import { View } from 'react-native'; + +import { Text } from '../ui'; +import { FontAwesome } from '../ui/icons'; + +// 进度项组件 +type ProgressItemProps = { + icon: string; + iconColor: string; + bgColor: string; + title: string; + time?: string; + description?: string; + isCompleted: boolean; +}; + +export const ProgressItem: React.FC = ({ + icon, + iconColor, + bgColor, + title, + time, + description, + isCompleted, +}) => ( + + + + + + + + {title} + + {time && ( + + {time} + + )} + + {description && ( + + {description} + + )} + + +); diff --git a/src/components/repair-order/radio-button.tsx b/src/components/repair-order/radio-button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5268aff0c29b03b8f8ca8da059dba578d2ce0768 --- /dev/null +++ b/src/components/repair-order/radio-button.tsx @@ -0,0 +1,41 @@ +import { TouchableOpacity, View } from 'react-native'; + +import { Text } from '../ui'; +import { FontAwesome } from '../ui/icons'; + +// 单选按钮组件 +type RadioButtonProps = { + label: string; + selected: boolean; + onPress: () => void; + icon?: string; + color?: string; +}; + +export const RadioButton: React.FC = ({ + label, + selected, + onPress, + icon, + color = '#1890ff', +}) => ( + + {icon && ( + + + + )} + + {label} + + +); diff --git a/src/components/repair-order/spare-part-item.tsx b/src/components/repair-order/spare-part-item.tsx new file mode 100644 index 0000000000000000000000000000000000000000..59122cdc87a22f4fe37d373085a8c4c76006496c --- /dev/null +++ b/src/components/repair-order/spare-part-item.tsx @@ -0,0 +1,33 @@ +import { Text, View } from '../ui'; + +// 备件项组件 +type SparePartItemProps = { + name: string; + model: string; + quantity: number; + price: string; +}; + +export const SparePartItem: React.FC = ({ + name, + model, + quantity, + price, +}) => ( + + + + {name} + + + 型号:{model} + + + + + ×{quantity} + + {price} + + +); diff --git a/src/components/repair-order/stat-card.tsx b/src/components/repair-order/stat-card.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5525f3a1bbfc686f847f7f9d52d18ab690b27aa0 --- /dev/null +++ b/src/components/repair-order/stat-card.tsx @@ -0,0 +1,33 @@ +import { LinearGradient } from 'expo-linear-gradient'; +import React from 'react'; + +import { Text, View } from '@/components/ui'; + +type StatCardProps = { + label: string; + value: number; + gradientColors: [string, string]; +}; +// 统计卡片数据类型 +export type StatCardData = { + label: string; + value: number; + gradientColors: [string, string]; +}; +export const StatCard: React.FC = ({ + label, + value, + gradientColors, +}) => ( + + + {value} + {label} + + +); diff --git a/src/components/repair-order/work-order-card.tsx b/src/components/repair-order/work-order-card.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aef429bc488e05c70f7bd3a593239bca6823fe4d --- /dev/null +++ b/src/components/repair-order/work-order-card.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { TouchableOpacity } from 'react-native'; + +import { Text, View } from '@/components/ui'; +import { FontAwesome } from '@/components/ui/icons'; + +export type WorkOrderStatus = + | 'urgent' + | 'processing' + | 'review' + | 'completed' + | 'all'; + +export type WorkOrder = { + id: string; + status: WorkOrderStatus; + statusLabel: string; + statusColor: string; + title: string; + equipmentName: string; + description: string; + assignee: string; + timeAgo: string; + deadline?: string; +}; + +type WorkOrderCardProps = { + order: WorkOrder; + onPress: () => void; +}; + +export const WorkOrderCard: React.FC = ({ + order, + onPress, +}) => ( + + + + + + {order.statusLabel} + + + + {order.title} + + + + 设备:{order.equipmentName} + + + 故障描述:{order.description} + + + + + + + + + {order.assignee} + + + + + + {order.timeAgo} + + + + {order.deadline && ( + + {order.deadline} + + )} + + +); diff --git a/src/components/ui/icons/font-awesome.tsx b/src/components/ui/icons/font-awesome.tsx index bafb786861dbcda269a57bc8724057561ff9c444..4a129f52ce1e7fc09edbc3166250a825997444d9 100644 --- a/src/components/ui/icons/font-awesome.tsx +++ b/src/components/ui/icons/font-awesome.tsx @@ -24,6 +24,16 @@ export enum GroupEnum { Feather = 'Feather', } +export enum IconGroupEnum { + FontAwesome = 'FontAwesome', + Entypo = 'Entypo', + FontAwesome5 = 'FontAwesome5', + EvilIcons = 'EvilIcons', + AntDesign = 'AntDesign', + MaterialCommunityIcons = 'MaterialCommunityIcons', + Feather = 'Feather', +} + /** * 图标组类型 * 用于限制可用的图标库类型 @@ -35,7 +45,14 @@ export type IconGroup = | GroupEnum.EvilIcons | GroupEnum.AntDesign | GroupEnum.MaterialCommunityIcons - | GroupEnum.Feather; + | GroupEnum.Feather + | IconGroupEnum.Entypo + | IconGroupEnum.FontAwesome5 + | IconGroupEnum.FontAwesome + | IconGroupEnum.EvilIcons + | IconGroupEnum.AntDesign + | IconGroupEnum.MaterialCommunityIcons + | IconGroupEnum.Feather; /** * 各图标库的图标名称类型 @@ -178,19 +195,25 @@ export const FontAwesome: React.FC = ({ // 其次根据指定的组类型选择图标库 switch (group) { case GroupEnum.Entypo: + case IconGroupEnum.Entypo: return ; case GroupEnum.FontAwesome5: + case IconGroupEnum.FontAwesome5: return ( ); case GroupEnum.EvilIcons: + case IconGroupEnum.EvilIcons: return ; case GroupEnum.AntDesign: + case IconGroupEnum.AntDesign: return ; - case GroupEnum.Feather: + case GroupEnum.AntDesign: + case IconGroupEnum.Feather: return ; case GroupEnum.MaterialCommunityIcons: + case IconGroupEnum.MaterialCommunityIcons: return ( = ({ imageIds }) => { + const [modalVisible, setModalVisible] = useState(false); + const [selectedIndex, setSelectedIndex] = useState(0); + + const openImage = (index: number) => { + setSelectedIndex(index); + setModalVisible(true); + }; + + if (!imageIds || imageIds.length === 0) { + return null; + } + + return ( + <> + + {imageIds.map((imageId, index) => ( + openImage(index)} + activeOpacity={0.8} + style={{ + marginRight: (index + 1) % 3 === 0 ? 0 : 12, + marginBottom: 12, + }} + > + + + ))} + + + {/* 图片预览 Modal */} + setModalVisible(false)} + > + + setModalVisible(false)} + > + + + + + {imageIds.map((imageId) => ( + + + + ))} + + + + {imageIds.map((_, index) => ( + + ))} + + + + + ); +}; diff --git a/src/components/ui/index.tsx b/src/components/ui/index.tsx index b8b4b9a953d7f40db9a1f2cdc1ccdddd0103074f..cf759a568fbb63571fd46cc4d9114321dc1559a6 100644 --- a/src/components/ui/index.tsx +++ b/src/components/ui/index.tsx @@ -6,7 +6,9 @@ export * from './checkbox'; export { default as colors } from './colors'; export * from './date-picker'; export * from './focus-aware-status-bar'; +export * from './icons/font-awesome'; export * from './image'; +export * from './image-gallery'; export * from './input'; export * from './input-date'; export * from './input-with-icon'; diff --git a/src/components/ui/nav-header.tsx b/src/components/ui/nav-header.tsx index 99e5a21070f4813829412c661ded8eac334a1724..a2275f6633fc7c4c96a03ad12e7c92c96747b891 100644 --- a/src/components/ui/nav-header.tsx +++ b/src/components/ui/nav-header.tsx @@ -1,10 +1,14 @@ -import { Stack, usePathname, useRouter } from 'expo-router'; +import { + Stack, + // usePathname, + useRouter +} from 'expo-router'; import React from 'react'; import { StatusBar, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { isWeb } from '@/lib'; import { useAppColorScheme } from '@/lib/hooks'; -import type { TxKeyPath } from '@/lib/i18n'; -import { translate } from '@/lib/i18n'; +import { translate, type TxKeyPath } from '@/lib/i18n'; import { FontAwesome, GroupEnum } from './icons'; @@ -25,7 +29,7 @@ export const NavHeader = ({ tx, }: NavHeaderProps) => { const router = useRouter(); - const pathName = usePathname(); + // const pathName = usePathname(); const { isDark } = useAppColorScheme(); return ( @@ -65,7 +69,7 @@ export const NavHeader = ({ leftShown && ( router.back()} - className={pathName == '/chat' ? 'ml-4' : ''} + className={isWeb ? 'ml-4' : ''} > { - console.log('scanning:', scanning); - console.log('scanningValue:', scanningValue.current); if (!scanningValue.current) return; scanningValue.current = false; - setScanning(false); onScan(data); }, - [scanning, onScan] + [onScan] ); if (!permission) { - // 相机权限正在加载 return ( @@ -39,7 +35,6 @@ export function QRCodeScanner({ onScan, onCancel }: Props) { } if (!permission.granted) { - // 没有相机权限 return ( @@ -63,7 +58,8 @@ export function QRCodeScanner({ onScan, onCancel }: Props) { } return ( - + + {/* 摄像头全屏 */} - {/* 扫描框覆盖层 */} - - {/* 顶部区域 */} - + /> - {/* 中间扫描区域 */} - - - - {/* 扫描框四个角 */} - - - - - - - + {/* 覆盖层 UI —— 注意:这是 CameraView 的兄弟,不是子元素 */} + + {/* 顶部遮罩 */} + - {/* 底部区域 */} - - - 将二维码放入扫描框内 - - - 取消 - + {/* 中间扫描区域 */} + + + + {/* 扫描框四个角 */} + + + + + + + + {/* 底部区域 */} + + + 将二维码放入扫描框内 + + + 取消 + - + ); -} +} \ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 1d6391a38da4159b6f16d49628018797d8922a47..cca7bfd274db40c116a77ac388d4a9f424c58b43 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -243,3 +243,51 @@ export function compareVersions( return 0; // 完全相等 } + +// 根据文件扩展名获取图标信息 +export const getFileIconInfo = (fileExt?: string) => { + const ext = fileExt?.toLowerCase()?.replace('.', '') || ''; + switch (ext) { + case 'pdf': + return { icon: 'file-pdf-o', iconColor: '#ef4444', iconBgColor: '#fef2f2' }; + case 'doc': + case 'docx': + return { icon: 'file-word-o', iconColor: '#3b82f6', iconBgColor: '#eff6ff' }; + case 'xls': + case 'xlsx': + return { icon: 'file-excel-o', iconColor: '#22c55e', iconBgColor: '#f0fdf4' }; + case 'ppt': + case 'pptx': + return { icon: 'file-powerpoint-o', iconColor: '#f97316', iconBgColor: '#fff7ed' }; + case 'jpg': + case 'jpeg': + case 'png': + case 'gif': + return { icon: 'file-image-o', iconColor: '#8b5cf6', iconBgColor: '#f5f3ff' }; + case 'zip': + case 'rar': + case '7z': + return { icon: 'file-archive-o', iconColor: '#eab308', iconBgColor: '#fefce8' }; + case 'txt': + return { icon: 'file-text-o', iconColor: '#6b7280', iconBgColor: '#f9fafb' }; + default: + return { icon: 'file-o', iconColor: '#6b7280', iconBgColor: '#f9fafb' }; + } +}; + +// 格式化文件大小 +export const formatFileSize = (size?: string) => { + if (!size) return ''; + const bytes = parseInt(size, 10); + if (isNaN(bytes)) return size; + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; +}; + +// 格式化日期(简短格式) +export const formatDateShort = (date?: Date) => { + if (!date) return ''; + const d = new Date(date); + return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; +}; diff --git a/src/types/attachment.ts b/src/types/attachment.ts new file mode 100644 index 0000000000000000000000000000000000000000..32066e49f0c70d5b4cb33babe0e8ee94c3bfde23 --- /dev/null +++ b/src/types/attachment.ts @@ -0,0 +1,24 @@ +export type Attachment = { + MasterId?: string; + OriginalFileName?: string; + FileName?: string; + FileExt?: string; + Path?: string; + Length?: string; + ImageType?: string; + IsDeleted?: boolean; + IsActive?: boolean; + ImportDataId?: null; + ModificationNum?: number; + Tag?: number; + GroupId?: string; + CompanyId?: string; + AuditStatus?: string; + CurrentNode?: null; + CreatedBy?: string; + CreatedTime?: Date; + UpdateBy?: null; + UpdateTime?: null; + ModuleCode?: null; + ID: string; +}; diff --git a/src/types/equipment.ts b/src/types/equipment.ts new file mode 100644 index 0000000000000000000000000000000000000000..42af91134ecce8981e39f6f6af6b8741cad3affa --- /dev/null +++ b/src/types/equipment.ts @@ -0,0 +1,59 @@ +import { type Attachment } from './attachment'; + +export type EquipmentStatus = 'running' | 'repairing' | 'fault'; + +export type Equipment = { + ImageIds?: string[]; + Attachments?: Attachment[]; + RepairCount?: number; + CurrentMonthRepairCount?: number; + MaintenanceCount?: number; + MaintenanceOrder?: string; + RepairOrder?: string; + UseManagerName?: string; + StartDate1?: string; + DeptName?: string; + Runtime?: string; + Health?: number; + StatusText?: string; + MachineTypeId?: string; + MachineNo?: string; + MachineName?: string; + Status: EquipmentStatus; + UseDeptId?: string; + UseManeageId?: string; + RepairManeageId?: string; + BrandModel?: string; + Manufacturer?: string; + Supplier?: string; + Location?: string; + AnnualInspection?: boolean; + AnnualInspectionDate?: Date; + CommissioningDate?: Date; + StopDate?: Date; + ImageId?: string; + Remark?: string; + IsDeleted?: boolean; + IsActive?: boolean; + ImportDataId?: string; + ModificationNum?: number; + Tag?: number; + GroupId?: string; + CompanyId?: string; + AuditStatus?: string; + CurrentNode?: string; + CreatedBy?: string; + CreatedTime?: Date; + UpdateBy?: string; + UpdateTime?: Date; + ModuleCode?: string; + MachineType?: string; + ID: string; + RepairStats: EquipmentRepairStats[]; +}; +export type EquipmentRepairStats = { + value: string; + label: string; + bgColor: string; + textColor: string; +}; diff --git a/src/types/index.ts b/src/types/index.ts index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..adf2d373022d7c40ef4553814edb81e6f7e3ca5f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -0,0 +1,3 @@ +export * from './attachment'; +export * from './equipment'; +export * from './lov'; diff --git a/src/types/lov.ts b/src/types/lov.ts new file mode 100644 index 0000000000000000000000000000000000000000..492ef7be1c9d72e41338eb366c771b590b281137 --- /dev/null +++ b/src/types/lov.ts @@ -0,0 +1,4 @@ +export type SmLov = { + value: string; + label: string; +};