feat:首页添加任务、产品展示模块

master
黄伟杰 3 weeks ago
parent 22179e1eac
commit d3dfc45074

@ -1,7 +1,8 @@
// 应用全局配置
const config = {
// baseUrl: 'http://47.106.185.127:48080',127.0.0.1
baseUrl: 'https://besure.ngsk.tech:7001',
// baseUrl: 'https://besure.ngsk.tech:7001',
baseUrl: 'http://192.168.5.167:48081',
// 应用信息
appInfo: {
// 应用名称
@ -25,4 +26,4 @@ const config = {
}
}
export default config
export default config

@ -0,0 +1,371 @@
export default {
common: {
submit: 'Submit',
close: 'Close',
exit: 'Exit',
notice: 'Notice',
moduleBuilding: 'This module is under construction',
updateSuccess: 'Updated successfully',
confirmLogout: 'Are you sure you want to log out?',
languageSwitched: 'Language switched'
},
tab: {
home: 'Home',
report: 'Reports',
work: 'Manage',
mine: 'Mine'
},
nav: {
home: 'Home',
mine: 'Profile',
avatar: 'Edit Avatar',
info: 'Profile',
editInfo: 'Edit Profile',
pwd: 'Change Password',
setting: 'App Settings',
help: 'FAQ',
about: 'About'
},
dashboard: {
welcome: 'Welcome to',
subtitle: 'Besure Digital Intelligent Control Platform',
functionNav: 'Function Navigation',
productionOverview: 'Production Overview',
productionPlan: 'Production Plans',
collapseList: 'Collapse',
viewMore: 'View More ',
productName: 'Product',
pipeline: 'Production Line',
planNumber: 'Planned Qty',
planStart: 'Start',
planEnd: 'End',
back: 'Back',
todoTitle: 'To-do Tasks',
noTodo: 'No pending tasks',
taskCode: 'Task Code: {value}',
taskType: 'Task Type: {value}',
taskTarget: 'Target: {value}',
createTime: 'Created At: {value}',
all: 'Total',
pending: 'Pending',
running: 'In Progress',
finished: 'Done',
mold: 'Mold',
equipment: 'Equipment',
keypart: 'Key Part',
spare: 'Spare Part',
product: 'Product Material',
statusScheduled: 'Scheduled',
statusTrial: 'Trial',
statusMass: 'Mass',
statusPause: 'Paused',
statusWaitStockIn: 'Waiting Stock-in',
viewPlan: 'View Plan: {code}',
filterTask: 'Task',
filterProduct: 'Product',
dateRange: 'Date Range',
startDate: 'Start Time',
endDate: 'End Time',
baogongNum: 'Reported',
passNum: 'Qualified',
noPassNum: 'Unqualified',
passRate: 'Pass Rate',
trendChart: 'Trend',
taskPlaceholder: 'Task mode under development',
totalTask: 'Total Tasks',
waitingProduction: 'Waiting',
producing: 'Producing',
completed: 'Completed',
taskTrend: 'Task Trend',
rangeYear: 'Last Year',
rangeMonth: 'This Month',
rangeWeek: 'This Week',
rangeToday: 'Today',
rangeCustom: 'Custom',
monday: 'Mon',
tuesday: 'Tue',
wednesday: 'Wed',
thursday: 'Thu',
friday: 'Fri',
saturday: 'Sat',
sunday: 'Sun'
},
functionCommon: {
search: 'Search',
cancel: 'Cancel',
save: 'Save',
loading: 'Loading...',
loadingMore: 'Loading more...',
noMoreData: 'No more data',
noIdView: 'Missing ID, cannot view details',
noIdEdit: 'Missing ID, cannot edit',
noIdDelete: 'Missing ID, cannot delete',
loadFailed: 'Load failed',
deleteSuccess: 'Deleted successfully',
deleteFailed: 'Delete failed',
saveFailed: 'Save failed',
createSuccess: 'Created successfully',
updateSuccess: 'Updated successfully',
confirmDelete: 'Confirm deletion',
uploading: 'Uploading',
uploadImageFailed: 'Image upload failed',
yes: 'Yes',
no: 'No'
},
moldGet: {
moduleName: 'Mold Stock-out',
subTitle: 'Filter quickly by no and status',
detailTitle: 'Mold Stock-out Detail',
basicInfo: 'Basic Info',
outNo: 'Stock-out No',
outType: 'Stock-out Type',
outTime: 'Stock-out Time',
outTimeSingle: 'Stock-out Date',
outTimePlaceholder: 'Select stock-out date',
warehouse: 'Warehouse',
allWarehouse: 'All Warehouses',
warehousePlaceholder: 'Select warehouse',
creator: 'Creator',
status: 'Status',
allStatus: 'All Status',
remark: 'Remark',
itemRemark: 'Item Remark',
attachment: 'Attachment',
fileUrlPlaceholder: 'Enter attachment URL',
remarkPlaceholder: 'Enter remark',
moldName: 'Mold',
moldCode: 'Mold Code',
moldStatus: 'Mold Status',
moldUseTime: 'Use Time',
machineName: 'Machine',
isEnable: 'Enabled',
createTime: 'Created At',
searchNo: 'Enter stock-out no',
searchMold: 'Enter mold code or name',
searchCode: 'Enter mold code',
searchName: 'Enter mold name',
itemListTitle: 'Item List',
selectMold: 'Select Mold',
noItems: 'No items',
count: 'Count',
noAuto: 'Generated automatically',
createTitle: 'Create Mold Stock-out',
editTitle: 'Edit Mold Stock-out',
edit: 'Edit',
delete: 'Delete',
approve: 'Approve',
empty: 'No mold stock-out data',
noMoldData: 'No mold options',
loadEditFailed: 'Failed to load edit data',
validatorOutTimeRequired: 'Stock-out date is required',
validatorWarehouseRequired: 'Warehouse is required',
validatorItemRequired: 'Select at least one mold',
validatorCountRequired: 'Count must be greater than 0',
confirmDelete: 'Delete stock-out {no}?',
confirmApprove: 'Approve stock-out {no}?',
approveSuccess: 'Approved successfully'
},
moldReturn: {
moduleName: 'Mold Stock-in',
subTitle: 'Filter quickly by no and status',
detailTitle: 'Mold Stock-in Detail',
basicInfo: 'Basic Info',
inNo: 'Stock-in No',
inType: 'Stock-in Type',
inTime: 'Stock-in Time',
inTimeSingle: 'Stock-in Date',
inTimePlaceholder: 'Select stock-in date',
warehouse: 'Warehouse',
allWarehouse: 'All Warehouses',
warehousePlaceholder: 'Select warehouse',
creator: 'Creator',
status: 'Status',
allStatus: 'All Status',
remark: 'Remark',
itemRemark: 'Item Remark',
attachment: 'Attachment',
fileUrlPlaceholder: 'Enter attachment URL',
remarkPlaceholder: 'Enter remark',
moldName: 'Mold',
moldCode: 'Mold Code',
moldStatus: 'Mold Status',
moldUseTime: 'Use Time',
searchNo: 'Enter stock-in no',
searchCode: 'Enter mold code',
searchName: 'Enter mold name',
itemListTitle: 'Item List',
selectMold: 'Select Mold',
noItems: 'No items',
count: 'Count',
noAuto: 'Generated automatically',
createTitle: 'Create Mold Stock-in',
editTitle: 'Edit Mold Stock-in',
approve: 'Approve',
empty: 'No mold stock-in data',
noMoldData: 'No mold options',
loadEditFailed: 'Failed to load edit data',
validatorInTimeRequired: 'Stock-in date is required',
validatorWarehouseRequired: 'Warehouse is required',
validatorItemRequired: 'Select at least one mold',
validatorCountRequired: 'Count must be greater than 0',
confirmDelete: 'Delete stock-in {no}?',
confirmApprove: 'Approve stock-in {no}?',
approveSuccess: 'Approved successfully'
},
moldOperate: {
moduleName: 'Mold Operate',
subTitle: 'Mold mounting and dismounting records',
detailTitle: 'Mold Operate Detail',
basicInfo: 'Basic Info',
tabUp: 'Mount',
tabDown: 'Dismount',
operateType: 'Operation Type',
mold: 'Mold',
lowerMold: 'Dismount Mold',
selectMold: 'Select Mold',
noSelectedMold: 'No selected molds',
allMold: 'All Molds',
searchRemark: 'Enter remark keyword',
searchCode: 'Enter mold code',
searchName: 'Enter mold name',
moldCode: 'Mold Code',
moldName: 'Mold Name',
device: 'Device',
deviceName: 'Device Name',
creatorName: 'Creator',
remark: 'Remark',
createTime: 'Created At',
createTitle: 'Create Mold Operate',
editTitle: 'Edit Mold Operate',
empty: 'No mold operate data',
noMoldData: 'No mold options',
placeholderDevice: 'Select device',
placeholderRemark: 'Enter remark',
validatorOperateTypeRequired: 'Operation type is required',
validatorDeviceRequired: 'Device is required',
validatorMoldRequired: 'Select at least one mold for mounting',
validatorLowerMoldRequired: 'Select at least one mold for dismounting',
loadEditFailed: 'Failed to load edit data',
confirmDelete: 'Confirm delete this mold operate record?'
},
moldInspectionItems: {
moduleName: 'Inspection Items',
subTitle: 'Mold management inspection items',
detailTitle: 'Inspection Item Detail',
basicInfo: 'Basic Info',
code: 'Item Code',
name: 'Item Name',
inspectionMethod: 'Inspection Method',
valueType: 'Value Type',
isEnable: 'Enabled',
judgmentCriteria: 'Judgment Criteria',
creatorName: 'Creator',
createTime: 'Created At',
searchPlaceholder: 'Enter code/name/criteria',
createTitle: 'Create Inspection Item',
editTitle: 'Edit Inspection Item',
empty: 'No inspection items',
loadEditFailed: 'Failed to load edit data',
confirmDelete: 'Confirm delete this inspection item?',
placeholderCode: 'Enter item code',
placeholderName: 'Enter item name',
placeholderInspectionMethod: 'Select inspection method',
placeholderValueType: 'Select value type',
placeholderIsEnable: 'Select enabled status',
placeholderJudgmentCriteria: 'Enter judgment criteria',
validatorSubjectCodeRequired: 'Item code is required',
validatorSubjectNameRequired: 'Item name is required',
validatorInspectionMethodRequired: 'Inspection method is required',
validatorValueTypeRequired: 'Value type is required',
validatorIsEnableRequired: 'Enabled status is required',
validatorJudgmentCriteriaRequired: 'Judgment criteria is required'
},
mine: {
clickLogin: 'Tap to sign in',
username: 'Username: {name}',
profile: 'Profile',
feedback: 'Feedback',
service: 'Support',
changePassword: 'Change Password',
logout: 'Log Out',
editProfile: 'Edit Profile',
faq: 'FAQ',
about: 'About',
appSettings: 'Settings'
},
setting: {
language: 'System Language',
currentLanguage: 'Current: {language}',
switchLanguage: 'Switch Language',
checkUpdate: 'Check Updates',
cleanCache: 'Clear Cache',
logout: 'Log Out',
zhCN: 'Chinese',
enUS: 'English'
},
about: {
appName: 'Besure Production System',
version: 'Version',
email: 'Official Email',
hotline: 'Service Hotline',
website: 'Website'
},
help: {
appUserQuestion: 'App User Questions',
otherQuestion: 'Other Questions',
appFeatureQuestion: 'What business features are supported by the app?',
appFeatureAnswer: 'Plan start, production report, material feeding records, production records',
reportQuestion: 'How can I submit a production report?',
reportAnswer: 'You can submit it on the report page.',
planQuestion: 'How is plan management handled?',
planAnswer: 'Use the plan management module.',
materialQuestion: 'How to manage feeding records?',
materialAnswer: 'Use the feeding record module.',
logoutQuestion: 'How do I log out?',
logoutAnswer: 'Go to [Mine] - [App Settings] - [Log Out] to sign out.',
avatarQuestion: 'How do I change my avatar?',
avatarAnswer: 'Go to [Mine] - [Choose Avatar] - [Submit] to update your avatar.',
passwordQuestion: 'How do I change my login password?',
passwordAnswer: 'Go to [Mine] - [App Settings] - [Change Password] to update your password.'
},
info: {
username: 'Username',
nickname: 'Nickname',
gender: 'Gender',
male: 'Male',
female: 'Female',
phone: 'Mobile',
email: 'Email',
createdAt: 'Created At'
},
editInfo: {
nickname: 'Nickname',
nicknamePlaceholder: 'Enter nickname',
phone: 'Mobile',
phonePlaceholder: 'Enter mobile number',
email: 'Email',
emailPlaceholder: 'Enter email',
gender: 'Gender',
nicknameRequired: 'Nickname is required',
phoneRequired: 'Mobile number is required',
phoneInvalid: 'Please enter a valid mobile number',
emailRequired: 'Email is required',
emailInvalid: 'Please enter a valid email'
},
pwd: {
oldPassword: 'Current Password',
newPassword: 'New Password',
confirmPassword: 'Confirm Password',
oldPasswordPlaceholder: 'Enter current password',
newPasswordPlaceholder: 'Enter new password',
confirmPasswordPlaceholder: 'Confirm new password',
oldPasswordRequired: 'Current password is required',
newPasswordRequired: 'New password is required',
passwordLength: 'Length must be between 6 and 20 characters',
confirmPasswordRequired: 'Please confirm password',
passwordNotMatch: 'The two passwords do not match'
},
avatar: {
chooseAvatar: 'Choose Avatar'
}
}

@ -1,696 +1,14 @@
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN'
import enUS from './en-US'
const LOCALE_STORAGE_KEY = 'app_locale'
const LOCALE_CHANGE_EVENT = 'app-locale-changed'
const DEFAULT_LOCALE = 'zh-CN'
const messages = {
'zh-CN': {
common: {
submit: '提交',
close: '关闭',
exit: '退出',
notice: '通知',
moduleBuilding: '模块建设中~',
updateSuccess: '修改成功',
confirmLogout: '确定注销并退出系统吗',
languageSwitched: '语言已切换'
},
tab: {
home: '首页',
report: '报表',
work: '管理',
mine: '我的'
},
nav: {
home: '首页',
mine: '个人中心',
avatar: '修改头像',
info: '个人信息',
editInfo: '编辑资料',
pwd: '修改密码',
setting: '应用设置',
help: '常见问题',
about: '关于我们'
},
dashboard: {
welcome: '欢迎您使用',
subtitle: '必硕数字化智能中控平台',
functionNav: '功能导航',
productionOverview: '生产整体概况',
productionPlan: '生产计划',
collapseList: '收起列表',
viewMore: '查看更多 ',
productName: '产品名称',
pipeline: '生产线',
planNumber: '计划数量',
planStart: '计划开始',
planEnd: '计划结束',
back: '返回',
todoTitle: '待办任务',
noTodo: '暂无待办任务',
taskCode: '任务编号:{value}',
taskType: '任务类型:{value}',
taskTarget: '目标:{value}',
createTime: '创建时间:{value}',
all: '总数',
pending: '未开工',
running: '生产中',
finished: '完工',
mold: '模具',
equipment: '设备',
keypart: '关键件',
spare: '备件',
product: '产品物料',
statusScheduled: '已排产',
statusTrial: '试产',
statusMass: '量产',
statusPause: '暂停',
statusWaitStockIn: '待入库',
viewPlan: '查看计划: {code}'
},
functionCommon: {
search: '查询',
cancel: '取消',
save: '保存',
loading: '加载中...',
loadingMore: '正在加载更多...',
noMoreData: '没有更多数据了',
noIdView: '缺少ID无法查看详情',
noIdEdit: '缺少ID无法编辑',
noIdDelete: '缺少ID无法删除',
loadFailed: '加载失败',
deleteSuccess: '删除成功',
deleteFailed: '删除失败',
saveFailed: '保存失败',
createSuccess: '新增成功',
updateSuccess: '更新成功',
confirmDelete: '确认删除',
uploading: '上传中',
uploadImageFailed: '图片上传失败',
yes: '是',
no: '否'
},
moldGet: {
moduleName: '模具出库',
subTitle: '按出库单号与状态快速筛选',
detailTitle: '模具出库详情',
basicInfo: '基础信息',
outNo: '出库单号',
outType: '出库类型',
outTime: '出库时间',
outTimeSingle: '出库日期',
outTimePlaceholder: '请选择出库日期',
warehouse: '仓库',
allWarehouse: '全部仓库',
warehousePlaceholder: '请选择仓库',
creator: '创建人',
status: '状态',
allStatus: '全部状态',
remark: '备注',
itemRemark: '明细备注',
attachment: '附件',
fileUrlPlaceholder: '请输入附件地址',
remarkPlaceholder: '请输入备注',
moldName: '模具',
moldCode: '模具编码',
moldStatus: '模具状态',
moldUseTime: '使用次数',
machineName: '使用设备',
isEnable: '是否启用',
createTime: '创建时间',
searchNo: '请输入出库单号',
searchMold: '请输入模具编码或名称',
searchCode: '请输入模具编码',
searchName: '请输入模具名称',
itemListTitle: '出库明细',
selectMold: '选择模具',
noItems: '暂无出库明细',
count: '数量',
noAuto: '系统自动生成',
createTitle: '新增模具出库',
editTitle: '编辑模具出库',
edit: '编辑',
delete: '删除',
approve: '审批',
empty: '暂无模具出库数据',
noMoldData: '暂无可选模具',
loadEditFailed: '加载编辑数据失败',
validatorOutTimeRequired: '出库日期不能为空',
validatorWarehouseRequired: '仓库不能为空',
validatorItemRequired: '请至少选择一个模具',
validatorCountRequired: '数量必须大于0',
confirmDelete: '确认删除出库单 {no} 吗?',
confirmApprove: '确认审批出库单 {no} 吗?',
approveSuccess: '审批成功'
},
moldReturn: {
moduleName: '模具入库',
subTitle: '按入库单号与状态快速筛选',
detailTitle: '模具入库详情',
basicInfo: '基础信息',
inNo: '入库单号',
inType: '入库类型',
inTime: '入库时间',
inTimeSingle: '入库日期',
inTimePlaceholder: '请选择入库日期',
warehouse: '仓库',
allWarehouse: '全部仓库',
warehousePlaceholder: '请选择仓库',
creator: '创建人',
status: '状态',
allStatus: '全部状态',
remark: '备注',
itemRemark: '明细备注',
attachment: '附件',
fileUrlPlaceholder: '请输入附件地址',
remarkPlaceholder: '请输入备注',
moldName: '模具',
moldCode: '模具编码',
moldStatus: '模具状态',
moldUseTime: '使用次数',
searchNo: '请输入入库单号',
searchCode: '请输入模具编码',
searchName: '请输入模具名称',
itemListTitle: '入库明细',
selectMold: '选择模具',
noItems: '暂无入库明细',
count: '数量',
noAuto: '系统自动生成',
createTitle: '新增模具入库',
editTitle: '编辑模具入库',
approve: '审批',
empty: '暂无模具入库数据',
noMoldData: '暂无可选模具',
loadEditFailed: '加载编辑数据失败',
validatorInTimeRequired: '入库日期不能为空',
validatorWarehouseRequired: '仓库不能为空',
validatorItemRequired: '请至少选择一个模具',
validatorCountRequired: '数量必须大于0',
confirmDelete: '确认删除入库单 {no} 吗?',
confirmApprove: '确认审批入库单 {no} 吗?',
approveSuccess: '审批成功'
},
moldOperate: {
moduleName: '上下模',
subTitle: '模具上模/下模操作记录',
detailTitle: '上下模详情',
basicInfo: '基础信息',
tabUp: '上模',
tabDown: '下模',
operateType: '操作类型',
mold: '模具',
lowerMold: '下模模具',
selectMold: '选择模具',
noSelectedMold: '暂无已选模具',
allMold: '全部模具',
searchRemark: '请输入备注关键字',
searchCode: '请输入模具编码',
searchName: '请输入模具名称',
moldCode: '模具编码',
moldName: '模具名称',
device: '设备',
deviceName: '设备名称',
creatorName: '创建人',
remark: '备注',
createTime: '创建时间',
createTitle: '新增上下模',
editTitle: '编辑上下模',
empty: '暂无上下模数据',
noMoldData: '暂无可选模具',
placeholderDevice: '请选择设备',
placeholderRemark: '请输入备注',
validatorOperateTypeRequired: '操作类型不能为空',
validatorDeviceRequired: '设备不能为空',
validatorMoldRequired: '请至少选择一个上模模具',
validatorLowerMoldRequired: '请至少选择一个下模模具',
loadEditFailed: '加载编辑数据失败',
confirmDelete: '确认删除该上下模记录吗?'
},
moldInspectionItems: {
moduleName: '点检项库',
subTitle: '模具管理点检项维护',
detailTitle: '点检项库详情',
basicInfo: '基础信息',
code: '项目编码',
name: '项目名称',
inspectionMethod: '检验方式',
valueType: '值类型',
isEnable: '是否启用',
judgmentCriteria: '判定基准',
creatorName: '创建人',
createTime: '创建时间',
searchPlaceholder: '请输入编码/名称/判定基准',
createTitle: '新增点检项',
editTitle: '编辑点检项',
empty: '暂无点检项数据',
loadEditFailed: '加载编辑数据失败',
confirmDelete: '确认删除该点检项吗?',
placeholderCode: '请输入项目编码',
placeholderName: '请输入项目名称',
placeholderInspectionMethod: '请选择检验方式',
placeholderValueType: '请选择值类型',
placeholderIsEnable: '请选择是否启用',
placeholderJudgmentCriteria: '请输入判定基准',
validatorSubjectCodeRequired: '项目编码不能为空',
validatorSubjectNameRequired: '项目名称不能为空',
validatorInspectionMethodRequired: '检验方式不能为空',
validatorValueTypeRequired: '值类型不能为空',
validatorIsEnableRequired: '是否启用不能为空',
validatorJudgmentCriteriaRequired: '判定基准不能为空'
},
mine: {
clickLogin: '点击登录',
username: '用户名:{name}',
profile: '个人信息',
feedback: '反馈中心',
service: '在线客服',
changePassword: '修改密码',
logout: '退出登录',
editProfile: '编辑资料',
faq: '常见问题',
about: '关于我们',
appSettings: '应用设置'
},
setting: {
language: '系统语言',
currentLanguage: '当前语言:{language}',
switchLanguage: '切换语言',
checkUpdate: '检查更新',
cleanCache: '清理缓存',
logout: '退出登录',
zhCN: '中文',
enUS: '英文'
},
about: {
appName: '必硕生管系统',
version: '版本信息',
email: '官方邮箱',
hotline: '服务热线',
website: '公司网站'
},
help: {
appUserQuestion: 'APP用户问题',
otherQuestion: '其他问题',
appFeatureQuestion: 'APP支持的功能业务有哪些',
appFeatureAnswer: '计划开工、生产报工、投料记录、生产记录',
reportQuestion: '生产报工如何报工?',
reportAnswer: '可以',
planQuestion: '计划管理如何进行?',
planAnswer: '计划管理',
materialQuestion: '投料记录如何进行?',
materialAnswer: '投料记录',
logoutQuestion: '如何退出登录?',
logoutAnswer: '请点击[我的] - [应用设置] - [退出登录]即可退出登录',
avatarQuestion: '如何修改用户头像?',
avatarAnswer: '请点击[我的] - [选择头像] - [点击提交]即可更换用户头像',
passwordQuestion: '如何修改登录密码?',
passwordAnswer: '请点击[我的] - [应用设置] - [修改密码]即可修改登录密码'
},
info: {
username: '用户名称',
nickname: '昵称',
gender: '性别',
male: '男',
female: '女',
phone: '手机号码',
email: '邮箱',
createdAt: '创建日期'
},
editInfo: {
nickname: '用户昵称',
nicknamePlaceholder: '请输入昵称',
phone: '手机号码',
phonePlaceholder: '请输入手机号码',
email: '邮箱',
emailPlaceholder: '请输入邮箱',
gender: '性别',
nicknameRequired: '用户昵称不能为空',
phoneRequired: '手机号码不能为空',
phoneInvalid: '请输入正确的手机号码',
emailRequired: '邮箱地址不能为空',
emailInvalid: '请输入正确的邮箱地址'
},
pwd: {
oldPassword: '旧密码',
newPassword: '新密码',
confirmPassword: '确认密码',
oldPasswordPlaceholder: '请输入旧密码',
newPasswordPlaceholder: '请输入新密码',
confirmPasswordPlaceholder: '请确认新密码',
oldPasswordRequired: '旧密码不能为空',
newPasswordRequired: '新密码不能为空',
passwordLength: '长度在 6 到 20 个字符',
confirmPasswordRequired: '确认密码不能为空',
passwordNotMatch: '两次输入的密码不一致'
},
avatar: {
chooseAvatar: '选择头像'
}
},
'en-US': {
common: {
submit: 'Submit',
close: 'Close',
exit: 'Exit',
notice: 'Notice',
moduleBuilding: 'This module is under construction',
updateSuccess: 'Updated successfully',
confirmLogout: 'Are you sure you want to log out?',
languageSwitched: 'Language switched'
},
tab: {
home: 'Home',
report: 'Reports',
work: 'Manage',
mine: 'Mine'
},
nav: {
home: 'Home',
mine: 'Profile',
avatar: 'Edit Avatar',
info: 'Profile',
editInfo: 'Edit Profile',
pwd: 'Change Password',
setting: 'App Settings',
help: 'FAQ',
about: 'About'
},
dashboard: {
welcome: 'Welcome to',
subtitle: 'Besure Digital Intelligent Control Platform',
functionNav: 'Function Navigation',
productionOverview: 'Production Overview',
productionPlan: 'Production Plans',
collapseList: 'Collapse',
viewMore: 'View More ',
productName: 'Product',
pipeline: 'Production Line',
planNumber: 'Planned Qty',
planStart: 'Start',
planEnd: 'End',
back: 'Back',
todoTitle: 'To-do Tasks',
noTodo: 'No pending tasks',
taskCode: 'Task Code: {value}',
taskType: 'Task Type: {value}',
taskTarget: 'Target: {value}',
createTime: 'Created At: {value}',
all: 'Total',
pending: 'Pending',
running: 'In Progress',
finished: 'Done',
mold: 'Mold',
equipment: 'Equipment',
keypart: 'Key Part',
spare: 'Spare Part',
product: 'Product Material',
statusScheduled: 'Scheduled',
statusTrial: 'Trial',
statusMass: 'Mass',
statusPause: 'Paused',
statusWaitStockIn: 'Waiting Stock-in',
viewPlan: 'View Plan: {code}'
},
functionCommon: {
search: 'Search',
cancel: 'Cancel',
save: 'Save',
loading: 'Loading...',
loadingMore: 'Loading more...',
noMoreData: 'No more data',
noIdView: 'Missing ID, cannot view details',
noIdEdit: 'Missing ID, cannot edit',
noIdDelete: 'Missing ID, cannot delete',
loadFailed: 'Load failed',
deleteSuccess: 'Deleted successfully',
deleteFailed: 'Delete failed',
saveFailed: 'Save failed',
createSuccess: 'Created successfully',
updateSuccess: 'Updated successfully',
confirmDelete: 'Confirm deletion',
uploading: 'Uploading',
uploadImageFailed: 'Image upload failed',
yes: 'Yes',
no: 'No'
},
moldGet: {
moduleName: 'Mold Stock-out',
subTitle: 'Filter quickly by no and status',
detailTitle: 'Mold Stock-out Detail',
basicInfo: 'Basic Info',
outNo: 'Stock-out No',
outType: 'Stock-out Type',
outTime: 'Stock-out Time',
outTimeSingle: 'Stock-out Date',
outTimePlaceholder: 'Select stock-out date',
warehouse: 'Warehouse',
allWarehouse: 'All Warehouses',
warehousePlaceholder: 'Select warehouse',
creator: 'Creator',
status: 'Status',
allStatus: 'All Status',
remark: 'Remark',
itemRemark: 'Item Remark',
attachment: 'Attachment',
fileUrlPlaceholder: 'Enter attachment URL',
remarkPlaceholder: 'Enter remark',
moldName: 'Mold',
moldCode: 'Mold Code',
moldStatus: 'Mold Status',
moldUseTime: 'Use Time',
machineName: 'Machine',
isEnable: 'Enabled',
createTime: 'Created At',
searchNo: 'Enter stock-out no',
searchMold: 'Enter mold code or name',
searchCode: 'Enter mold code',
searchName: 'Enter mold name',
itemListTitle: 'Item List',
selectMold: 'Select Mold',
noItems: 'No items',
count: 'Count',
noAuto: 'Generated automatically',
createTitle: 'Create Mold Stock-out',
editTitle: 'Edit Mold Stock-out',
edit: 'Edit',
delete: 'Delete',
approve: 'Approve',
empty: 'No mold stock-out data',
noMoldData: 'No mold options',
loadEditFailed: 'Failed to load edit data',
validatorOutTimeRequired: 'Stock-out date is required',
validatorWarehouseRequired: 'Warehouse is required',
validatorItemRequired: 'Select at least one mold',
validatorCountRequired: 'Count must be greater than 0',
confirmDelete: 'Delete stock-out {no}?',
confirmApprove: 'Approve stock-out {no}?',
approveSuccess: 'Approved successfully'
},
moldReturn: {
moduleName: 'Mold Stock-in',
subTitle: 'Filter quickly by no and status',
detailTitle: 'Mold Stock-in Detail',
basicInfo: 'Basic Info',
inNo: 'Stock-in No',
inType: 'Stock-in Type',
inTime: 'Stock-in Time',
inTimeSingle: 'Stock-in Date',
inTimePlaceholder: 'Select stock-in date',
warehouse: 'Warehouse',
allWarehouse: 'All Warehouses',
warehousePlaceholder: 'Select warehouse',
creator: 'Creator',
status: 'Status',
allStatus: 'All Status',
remark: 'Remark',
itemRemark: 'Item Remark',
attachment: 'Attachment',
fileUrlPlaceholder: 'Enter attachment URL',
remarkPlaceholder: 'Enter remark',
moldName: 'Mold',
moldCode: 'Mold Code',
moldStatus: 'Mold Status',
moldUseTime: 'Use Time',
searchNo: 'Enter stock-in no',
searchCode: 'Enter mold code',
searchName: 'Enter mold name',
itemListTitle: 'Item List',
selectMold: 'Select Mold',
noItems: 'No items',
count: 'Count',
noAuto: 'Generated automatically',
createTitle: 'Create Mold Stock-in',
editTitle: 'Edit Mold Stock-in',
approve: 'Approve',
empty: 'No mold stock-in data',
noMoldData: 'No mold options',
loadEditFailed: 'Failed to load edit data',
validatorInTimeRequired: 'Stock-in date is required',
validatorWarehouseRequired: 'Warehouse is required',
validatorItemRequired: 'Select at least one mold',
validatorCountRequired: 'Count must be greater than 0',
confirmDelete: 'Delete stock-in {no}?',
confirmApprove: 'Approve stock-in {no}?',
approveSuccess: 'Approved successfully'
},
moldOperate: {
moduleName: 'Mold Operate',
subTitle: 'Mold mounting and dismounting records',
detailTitle: 'Mold Operate Detail',
basicInfo: 'Basic Info',
tabUp: 'Mount',
tabDown: 'Dismount',
operateType: 'Operation Type',
mold: 'Mold',
lowerMold: 'Dismount Mold',
selectMold: 'Select Mold',
noSelectedMold: 'No selected molds',
allMold: 'All Molds',
searchRemark: 'Enter remark keyword',
searchCode: 'Enter mold code',
searchName: 'Enter mold name',
moldCode: 'Mold Code',
moldName: 'Mold Name',
device: 'Device',
deviceName: 'Device Name',
creatorName: 'Creator',
remark: 'Remark',
createTime: 'Created At',
createTitle: 'Create Mold Operate',
editTitle: 'Edit Mold Operate',
empty: 'No mold operate data',
noMoldData: 'No mold options',
placeholderDevice: 'Select device',
placeholderRemark: 'Enter remark',
validatorOperateTypeRequired: 'Operation type is required',
validatorDeviceRequired: 'Device is required',
validatorMoldRequired: 'Select at least one mold for mounting',
validatorLowerMoldRequired: 'Select at least one mold for dismounting',
loadEditFailed: 'Failed to load edit data',
confirmDelete: 'Delete this mold operate record?'
},
moldInspectionItems: {
moduleName: 'Inspection Items',
subTitle: 'Mold inspection item maintenance',
detailTitle: 'Inspection Item Detail',
basicInfo: 'Basic Info',
code: 'Item Code',
name: 'Item Name',
inspectionMethod: 'Inspection Method',
valueType: 'Value Type',
isEnable: 'Enabled',
judgmentCriteria: 'Judgment Criteria',
creatorName: 'Creator',
createTime: 'Created At',
searchPlaceholder: 'Enter code/name/criteria',
createTitle: 'Create Inspection Item',
editTitle: 'Edit Inspection Item',
empty: 'No inspection item data',
loadEditFailed: 'Failed to load edit data',
confirmDelete: 'Delete this inspection item?',
placeholderCode: 'Enter item code',
placeholderName: 'Enter item name',
placeholderInspectionMethod: 'Select inspection method',
placeholderValueType: 'Select value type',
placeholderIsEnable: 'Select enabled status',
placeholderJudgmentCriteria: 'Enter judgment criteria',
validatorSubjectCodeRequired: 'Item code is required',
validatorSubjectNameRequired: 'Item name is required',
validatorInspectionMethodRequired: 'Inspection method is required',
validatorValueTypeRequired: 'Value type is required',
validatorIsEnableRequired: 'Enabled status is required',
validatorJudgmentCriteriaRequired: 'Judgment criteria is required'
},
mine: {
clickLogin: 'Tap to sign in',
username: 'Username: {name}',
profile: 'Profile',
feedback: 'Feedback',
service: 'Support',
changePassword: 'Change Password',
logout: 'Log Out',
editProfile: 'Edit Profile',
faq: 'FAQ',
about: 'About',
appSettings: 'Settings'
},
setting: {
language: 'System Language',
currentLanguage: 'Current: {language}',
switchLanguage: 'Switch Language',
checkUpdate: 'Check Updates',
cleanCache: 'Clear Cache',
logout: 'Log Out',
zhCN: 'Chinese',
enUS: 'English'
},
about: {
appName: 'Besure Production System',
version: 'Version',
email: 'Official Email',
hotline: 'Service Hotline',
website: 'Website'
},
help: {
appUserQuestion: 'App User Questions',
otherQuestion: 'Other Questions',
appFeatureQuestion: 'What business features are supported by the app?',
appFeatureAnswer: 'Plan start, production report, material feeding records, production records',
reportQuestion: 'How can I submit a production report?',
reportAnswer: 'You can submit it on the report page.',
planQuestion: 'How is plan management handled?',
planAnswer: 'Use the plan management module.',
materialQuestion: 'How to manage feeding records?',
materialAnswer: 'Use the feeding record module.',
logoutQuestion: 'How do I log out?',
logoutAnswer: 'Go to [Mine] - [App Settings] - [Log Out] to sign out.',
avatarQuestion: 'How do I change my avatar?',
avatarAnswer: 'Go to [Mine] - [Choose Avatar] - [Submit] to update your avatar.',
passwordQuestion: 'How do I change my login password?',
passwordAnswer: 'Go to [Mine] - [App Settings] - [Change Password] to update your password.'
},
info: {
username: 'Username',
nickname: 'Nickname',
gender: 'Gender',
male: 'Male',
female: 'Female',
phone: 'Mobile',
email: 'Email',
createdAt: 'Created At'
},
editInfo: {
nickname: 'Nickname',
nicknamePlaceholder: 'Enter nickname',
phone: 'Mobile',
phonePlaceholder: 'Enter mobile number',
email: 'Email',
emailPlaceholder: 'Enter email',
gender: 'Gender',
nicknameRequired: 'Nickname is required',
phoneRequired: 'Mobile number is required',
phoneInvalid: 'Please enter a valid mobile number',
emailRequired: 'Email is required',
emailInvalid: 'Please enter a valid email'
},
pwd: {
oldPassword: 'Current Password',
newPassword: 'New Password',
confirmPassword: 'Confirm Password',
oldPasswordPlaceholder: 'Enter current password',
newPasswordPlaceholder: 'Enter new password',
confirmPasswordPlaceholder: 'Confirm new password',
oldPasswordRequired: 'Current password is required',
newPasswordRequired: 'New password is required',
passwordLength: 'Length must be between 6 and 20 characters',
confirmPasswordRequired: 'Please confirm password',
passwordNotMatch: 'The two passwords do not match'
},
avatar: {
chooseAvatar: 'Choose Avatar'
}
}
'zh-CN': zhCN,
'en-US': enUS
}
function normalizeLocale(locale) {

@ -0,0 +1,371 @@
export default {
common: {
submit: '提交',
close: '关闭',
exit: '退出',
notice: '通知',
moduleBuilding: '模块建设中~',
updateSuccess: '修改成功',
confirmLogout: '确定注销并退出系统吗',
languageSwitched: '语言已切换'
},
tab: {
home: '首页',
report: '报表',
work: '管理',
mine: '我的'
},
nav: {
home: '首页',
mine: '个人中心',
avatar: '修改头像',
info: '个人信息',
editInfo: '编辑资料',
pwd: '修改密码',
setting: '应用设置',
help: '常见问题',
about: '关于我们'
},
dashboard: {
welcome: '欢迎您使用',
subtitle: '必硕数字化智能中控平台',
functionNav: '功能导航',
productionOverview: '生产整体概况',
productionPlan: '生产计划',
collapseList: '收起列表',
viewMore: '查看更多 ',
productName: '产品名称',
pipeline: '生产线',
planNumber: '计划数量',
planStart: '计划开始',
planEnd: '计划结束',
back: '返回',
todoTitle: '待办任务',
noTodo: '暂无待办任务',
taskCode: '任务编号:{value}',
taskType: '任务类型:{value}',
taskTarget: '目标:{value}',
createTime: '创建时间:{value}',
all: '总数',
pending: '未开工',
running: '生产中',
finished: '完工',
mold: '模具',
equipment: '设备',
keypart: '关键件',
spare: '备件',
product: '产品物料',
statusScheduled: '已排产',
statusTrial: '试产',
statusMass: '量产',
statusPause: '暂停',
statusWaitStockIn: '待入库',
viewPlan: '查看计划: {code}',
filterTask: '任务',
filterProduct: '产品',
dateRange: '日期范围',
startDate: '开始时间',
endDate: '结束时间',
baogongNum: '报工数',
passNum: '合格数',
noPassNum: '不合格数',
passRate: '合格率',
trendChart: '趋势图',
taskPlaceholder: '任务模式开发中',
totalTask: '任务总数',
waitingProduction: '待生产',
producing: '生产中',
completed: '已完成',
taskTrend: '任务趋势',
rangeYear: '近一年',
rangeMonth: '本月',
rangeWeek: '本周',
rangeToday: '今日',
rangeCustom: '自定义',
monday: '周一',
tuesday: '周二',
wednesday: '周三',
thursday: '周四',
friday: '周五',
saturday: '周六',
sunday: '周日'
},
functionCommon: {
search: '查询',
cancel: '取消',
save: '保存',
loading: '加载中...',
loadingMore: '正在加载更多...',
noMoreData: '没有更多数据了',
noIdView: '缺少ID无法查看详情',
noIdEdit: '缺少ID无法编辑',
noIdDelete: '缺少ID无法删除',
loadFailed: '加载失败',
deleteSuccess: '删除成功',
deleteFailed: '删除失败',
saveFailed: '保存失败',
createSuccess: '新增成功',
updateSuccess: '更新成功',
confirmDelete: '确认删除',
uploading: '上传中',
uploadImageFailed: '图片上传失败',
yes: '是',
no: '否'
},
moldGet: {
moduleName: '模具出库',
subTitle: '按出库单号与状态快速筛选',
detailTitle: '模具出库详情',
basicInfo: '基础信息',
outNo: '出库单号',
outType: '出库类型',
outTime: '出库时间',
outTimeSingle: '出库日期',
outTimePlaceholder: '请选择出库日期',
warehouse: '仓库',
allWarehouse: '全部仓库',
warehousePlaceholder: '请选择仓库',
creator: '创建人',
status: '状态',
allStatus: '全部状态',
remark: '备注',
itemRemark: '明细备注',
attachment: '附件',
fileUrlPlaceholder: '请输入附件地址',
remarkPlaceholder: '请输入备注',
moldName: '模具',
moldCode: '模具编码',
moldStatus: '模具状态',
moldUseTime: '使用次数',
machineName: '使用设备',
isEnable: '是否启用',
createTime: '创建时间',
searchNo: '请输入出库单号',
searchMold: '请输入模具编码或名称',
searchCode: '请输入模具编码',
searchName: '请输入模具名称',
itemListTitle: '出库明细',
selectMold: '选择模具',
noItems: '暂无出库明细',
count: '数量',
noAuto: '系统自动生成',
createTitle: '新增模具出库',
editTitle: '编辑模具出库',
edit: '编辑',
delete: '删除',
approve: '审批',
empty: '暂无模具出库数据',
noMoldData: '暂无可选模具',
loadEditFailed: '加载编辑数据失败',
validatorOutTimeRequired: '出库日期不能为空',
validatorWarehouseRequired: '仓库不能为空',
validatorItemRequired: '请至少选择一个模具',
validatorCountRequired: '数量必须大于0',
confirmDelete: '确认删除出库单 {no} 吗?',
confirmApprove: '确认审批出库单 {no} 吗?',
approveSuccess: '审批成功'
},
moldReturn: {
moduleName: '模具入库',
subTitle: '按入库单号与状态快速筛选',
detailTitle: '模具入库详情',
basicInfo: '基础信息',
inNo: '入库单号',
inType: '入库类型',
inTime: '入库时间',
inTimeSingle: '入库日期',
inTimePlaceholder: '请选择入库日期',
warehouse: '仓库',
allWarehouse: '全部仓库',
warehousePlaceholder: '请选择仓库',
creator: '创建人',
status: '状态',
allStatus: '全部状态',
remark: '备注',
itemRemark: '明细备注',
attachment: '附件',
fileUrlPlaceholder: '请输入附件地址',
remarkPlaceholder: '请输入备注',
moldName: '模具',
moldCode: '模具编码',
moldStatus: '模具状态',
moldUseTime: '使用次数',
searchNo: '请输入入库单号',
searchCode: '请输入模具编码',
searchName: '请输入模具名称',
itemListTitle: '入库明细',
selectMold: '选择模具',
noItems: '暂无入库明细',
count: '数量',
noAuto: '系统自动生成',
createTitle: '新增模具入库',
editTitle: '编辑模具入库',
approve: '审批',
empty: '暂无模具入库数据',
noMoldData: '暂无可选模具',
loadEditFailed: '加载编辑数据失败',
validatorInTimeRequired: '入库日期不能为空',
validatorWarehouseRequired: '仓库不能为空',
validatorItemRequired: '请至少选择一个模具',
validatorCountRequired: '数量必须大于0',
confirmDelete: '确认删除入库单 {no} 吗?',
confirmApprove: '确认审批入库单 {no} 吗?',
approveSuccess: '审批成功'
},
moldOperate: {
moduleName: '上下模',
subTitle: '模具上模/下模操作记录',
detailTitle: '上下模详情',
basicInfo: '基础信息',
tabUp: '上模',
tabDown: '下模',
operateType: '操作类型',
mold: '模具',
lowerMold: '下模模具',
selectMold: '选择模具',
noSelectedMold: '暂无已选模具',
allMold: '全部模具',
searchRemark: '请输入备注关键字',
searchCode: '请输入模具编码',
searchName: '请输入模具名称',
moldCode: '模具编码',
moldName: '模具名称',
device: '设备',
deviceName: '设备名称',
creatorName: '创建人',
remark: '备注',
createTime: '创建时间',
createTitle: '新增上下模',
editTitle: '编辑上下模',
empty: '暂无上下模数据',
noMoldData: '暂无可选模具',
placeholderDevice: '请选择设备',
placeholderRemark: '请输入备注',
validatorOperateTypeRequired: '操作类型不能为空',
validatorDeviceRequired: '设备不能为空',
validatorMoldRequired: '请至少选择一个上模模具',
validatorLowerMoldRequired: '请至少选择一个下模模具',
loadEditFailed: '加载编辑数据失败',
confirmDelete: '确认删除该上下模记录吗?'
},
moldInspectionItems: {
moduleName: '点检项库',
subTitle: '模具管理点检项维护',
detailTitle: '点检项库详情',
basicInfo: '基础信息',
code: '项目编码',
name: '项目名称',
inspectionMethod: '检验方式',
valueType: '值类型',
isEnable: '是否启用',
judgmentCriteria: '判定基准',
creatorName: '创建人',
createTime: '创建时间',
searchPlaceholder: '请输入编码/名称/判定基准',
createTitle: '新增点检项',
editTitle: '编辑点检项',
empty: '暂无点检项数据',
loadEditFailed: '加载编辑数据失败',
confirmDelete: '确认删除该点检项吗?',
placeholderCode: '请输入项目编码',
placeholderName: '请输入项目名称',
placeholderInspectionMethod: '请选择检验方式',
placeholderValueType: '请选择值类型',
placeholderIsEnable: '请选择是否启用',
placeholderJudgmentCriteria: '请输入判定基准',
validatorSubjectCodeRequired: '项目编码不能为空',
validatorSubjectNameRequired: '项目名称不能为空',
validatorInspectionMethodRequired: '检验方式不能为空',
validatorValueTypeRequired: '值类型不能为空',
validatorIsEnableRequired: '是否启用不能为空',
validatorJudgmentCriteriaRequired: '判定基准不能为空'
},
mine: {
clickLogin: '点击登录',
username: '用户名:{name}',
profile: '个人信息',
feedback: '反馈中心',
service: '在线客服',
changePassword: '修改密码',
logout: '退出登录',
editProfile: '编辑资料',
faq: '常见问题',
about: '关于我们',
appSettings: '应用设置'
},
setting: {
language: '系统语言',
currentLanguage: '当前语言:{language}',
switchLanguage: '切换语言',
checkUpdate: '检查更新',
cleanCache: '清理缓存',
logout: '退出登录',
zhCN: '中文',
enUS: '英文'
},
about: {
appName: '必硕生管系统',
version: '版本信息',
email: '官方邮箱',
hotline: '服务热线',
website: '公司网站'
},
help: {
appUserQuestion: 'APP用户问题',
otherQuestion: '其他问题',
appFeatureQuestion: 'APP支持的功能业务有哪些',
appFeatureAnswer: '计划开工、生产报工、投料记录、生产记录',
reportQuestion: '生产报工如何报工?',
reportAnswer: '可以',
planQuestion: '计划管理如何进行?',
planAnswer: '计划管理',
materialQuestion: '投料记录如何进行?',
materialAnswer: '投料记录',
logoutQuestion: '如何退出登录?',
logoutAnswer: '请点击[我的] - [应用设置] - [退出登录]即可退出登录',
avatarQuestion: '如何修改用户头像?',
avatarAnswer: '请点击[我的] - [选择头像] - [点击提交]即可更换用户头像',
passwordQuestion: '如何修改登录密码?',
passwordAnswer: '请点击[我的] - [应用设置] - [修改密码]即可修改登录密码'
},
info: {
username: '用户名称',
nickname: '昵称',
gender: '性别',
male: '男',
female: '女',
phone: '手机号码',
email: '邮箱',
createdAt: '创建日期'
},
editInfo: {
nickname: '用户昵称',
nicknamePlaceholder: '请输入昵称',
phone: '手机号码',
phonePlaceholder: '请输入手机号码',
email: '邮箱',
emailPlaceholder: '请输入邮箱',
gender: '性别',
nicknameRequired: '用户昵称不能为空',
phoneRequired: '手机号码不能为空',
phoneInvalid: '请输入正确的手机号码',
emailRequired: '邮箱地址不能为空',
emailInvalid: '请输入正确的邮箱地址'
},
pwd: {
oldPassword: '旧密码',
newPassword: '新密码',
confirmPassword: '确认密码',
oldPasswordPlaceholder: '请输入旧密码',
newPasswordPlaceholder: '请输入新密码',
confirmPasswordPlaceholder: '请确认新密码',
oldPasswordRequired: '旧密码不能为空',
newPasswordRequired: '新密码不能为空',
passwordLength: '长度在 6 到 20 个字符',
confirmPasswordRequired: '确认密码不能为空',
passwordNotMatch: '两次输入的密码不一致'
},
avatar: {
chooseAvatar: '选择头像'
}
}

@ -504,6 +504,12 @@
"navigationBarTitleText": "点检项库详情",
"navigationStyle": "custom"
}
},
{
"path": "planList/index",
"style": {
"navigationBarTitleText": "生产计划"
}
}
]
}

@ -23,6 +23,7 @@
</view>
<view class="content-section">
<!-- 功能导航 -->
<view class="nav-section">
<view class="section-title">{{ t('dashboard.functionNav') }}</view>
<view class="nav-grid">
@ -35,6 +36,7 @@
</view>
</view>
<!-- 生产概览统计 -->
<view class="stats-section">
<view class="section-title">{{ t('dashboard.productionOverview') }}</view>
<view class="stats-grid">
@ -45,45 +47,104 @@
</view>
</view>
<!-- 生产计划 -->
<view class="plan-section">
<view class="section-header">
<text class="section-title">{{ t('dashboard.productionPlan') }}</text>
<text v-if="hasMorePlans || isShowAllPlans" class="section-more" @click="viewMorePlans">
{{ isShowAllPlans ? t('dashboard.collapseList') : t('dashboard.viewMore') }}
<text class="section-more" @click="viewMorePlans">
{{ t('dashboard.viewMore') }}
</text>
</view>
<view class="plan-list">
<view v-for="(plan, index) in displayPlanList" :key="index" class="plan-card"
@click="handlePlanClick(plan)">
<view class="plan-header">
<text class="plan-code">{{ plan.code }}</text>
<view class="plan-status" :class="'status-' + plan.statusType">
<text>{{ plan.status }}</text>
</view>
<view class="filter-bar">
<view class="filter-select" @click="showFilterPicker = true">
<text class="filter-text">{{ currentFilterLabel }}</text>
<text class="filter-arrow"></text>
</view>
<view class="filter-select" @click="showRangePicker = true">
<text class="filter-text">{{ currentRangeLabel }}</text>
<text class="filter-arrow"></text>
</view>
</view>
<view v-if="currentRange === 'custom'" class="filter-date-wrap">
<view class="date-picker-item">
<up-icon name="calendar" size="18" color="#999" />
<up-datetime-picker ref="startPickerRef" hasInput v-model="dateRange.start" mode="datetime"
:placeholder="t('dashboard.startDate')" :formatter="datetimeFormatter"
closeOnClickOverlay @confirm="onStartDateConfirm" @close="onPickerClose" @cancel="onPickerClose" />
</view>
<view class="date-picker-item">
<up-icon name="calendar" size="18" color="#999" />
<up-datetime-picker ref="endPickerRef" hasInput v-model="dateRange.end" mode="datetime"
:placeholder="t('dashboard.endDate')" :formatter="datetimeFormatter"
closeOnClickOverlay @confirm="onEndDateConfirm" @close="onPickerClose" @cancel="onPickerClose" />
</view>
</view>
<view v-if="currentFilter === 'product'" class="trend-content">
<view class="trend-stats">
<view class="trend-stat-card">
<text class="trend-stat-value">{{ formatNumber(trendData.baogongNum) }}</text>
<text class="trend-stat-label">{{ t('dashboard.baogongNum') }}</text>
</view>
<view class="plan-body">
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.productName') }}</text>
<text class="plan-value">{{ plan.productName }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.pipeline') }}</text>
<text class="plan-value">{{ plan.feedingPipelineName }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.planNumber') }}</text>
<text class="plan-value plan-num">{{ plan.planNumber }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.planStart') }}</text>
<text class="plan-value">{{ plan.planStartTimeText }}</text>
<view class="trend-stat-card">
<text class="trend-stat-value pass">{{ formatNumber(trendData.passNum) }}</text>
<text class="trend-stat-label">{{ t('dashboard.passNum') }}</text>
</view>
<view class="trend-stat-card">
<text class="trend-stat-value fail">{{ formatNumber(trendData.noPassNum) }}</text>
<text class="trend-stat-label">{{ t('dashboard.noPassNum') }}</text>
</view>
<view class="trend-stat-card">
<text class="trend-stat-value rate">{{ formatPercent(trendData.passRate) }}</text>
<text class="trend-stat-label">{{ t('dashboard.passRate') }}</text>
</view>
</view>
<view v-if="currentRange !== 'custom'" class="trend-chart">
<text class="chart-title">{{ t('dashboard.baogongNum') }}</text>
<scroll-view scroll-x class="chart-scroll">
<view class="chart-box" :style="{ width: chartWidth }">
<qiun-data-charts type="line" :chartData="baogongChartData" :canvas2d="false"
:opts="{ legend: { show: false }, xAxis: { disableGrid: true }, yAxis: { gridType: 'dash', dashLength: 2 }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" />
</view>
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.planEnd') }}</text>
<text class="plan-value">{{ plan.planEndTimeText }}</text>
</scroll-view>
</view>
<view v-if="currentRange !== 'custom'" class="trend-chart">
<text class="chart-title">{{ t('dashboard.passRate') }}</text>
<scroll-view scroll-x class="chart-scroll">
<view class="chart-box" :style="{ width: chartWidth }">
<qiun-data-charts type="line" :chartData="passRateChartData" :canvas2d="false"
:opts="{ legend: { show: false }, xAxis: { disableGrid: true }, yAxis: { gridType: 'dash', dashLength: 2, data: [{ min: 0, max: 100 }] }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" />
</view>
</scroll-view>
</view>
</view>
<view v-else class="trend-content">
<view class="trend-stats">
<view class="trend-stat-card">
<text class="trend-stat-value">{{ formatNumber(taskTrendData.totalNum) }}</text>
<text class="trend-stat-label">{{ t('dashboard.totalTask') }}</text>
</view>
<view class="trend-stat-card">
<text class="trend-stat-value pending">{{ formatNumber(taskTrendData.waitingProductionNum) }}</text>
<text class="trend-stat-label">{{ t('dashboard.waitingProduction') }}</text>
</view>
<view class="trend-stat-card">
<text class="trend-stat-value running">{{ formatNumber(taskTrendData.producingNum) }}</text>
<text class="trend-stat-label">{{ t('dashboard.producing') }}</text>
</view>
<view class="trend-stat-card">
<text class="trend-stat-value pass">{{ formatNumber(taskTrendData.completedNum) }}</text>
<text class="trend-stat-label">{{ t('dashboard.completed') }}</text>
</view>
</view>
<view v-if="currentRange !== 'custom'" class="trend-chart">
<text class="chart-title">{{ t('dashboard.taskTrend') }}</text>
<scroll-view scroll-x class="chart-scroll">
<view class="chart-box" :style="{ width: chartWidth }">
<qiun-data-charts type="line" :chartData="taskChartData" :canvas2d="false"
:opts="{ legend: { show: false }, xAxis: { disableGrid: true }, yAxis: { gridType: 'dash', dashLength: 2 }, extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } }" />
</view>
</scroll-view>
</view>
</view>
</view>
</view>
@ -93,6 +154,12 @@
<text class="go-top-icon"></text>
</view>
<up-picker :show="showFilterPicker" :columns="filterColumns" @confirm="onFilterConfirm"
@cancel="showFilterPicker = false" @close="showFilterPicker = false" closeOnClickOverlay />
<up-picker :show="showRangePicker" :columns="rangeColumns" @confirm="onRangeConfirm"
@cancel="showRangePicker = false" @close="showRangePicker = false" closeOnClickOverlay />
<uni-popup ref="todoPopup" type="right" background-color="#fff">
<view class="todo-popup">
<view class="todo-header">
@ -130,6 +197,7 @@ import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import request from '@/utils/request'
import { onLocaleChange, offLocaleChange, setNavigationTitle } from '@/locales'
import { formatNumber, formatPercent } from '@/utils/format'
const { t } = useI18n()
@ -138,7 +206,6 @@ const todoCount = ref(0);
const scrollTop = ref(0)
const currentScrollTop = ref(0)
const showGoTop = ref(false)
const isShowAllPlans = ref(false)
const badgeVisible = computed(() => Number(todoCount.value) > 0)
const badgeText = computed(() => {
const count = Number(todoCount.value)
@ -162,14 +229,349 @@ const statsData = reactive([
{ labelKey: 'finished', type: 'finished' }
]);
const planList = reactive([]);
const hasMorePlans = computed(() => planList.length > 3)
const displayPlanList = computed(() => {
if (isShowAllPlans.value) return planList
return planList.slice(0, 3)
const todoList = reactive([]);
const showFilterPicker = ref(false)
const showRangePicker = ref(false)
const startPickerRef = ref(null)
const endPickerRef = ref(null)
const currentFilter = ref('task')
const currentRange = ref('month')
const filterColumns = computed(() => [
[
{ text: t('dashboard.filterTask'), value: 'task' },
{ text: t('dashboard.filterProduct'), value: 'product' }
]
])
const currentFilterLabel = computed(() => {
return currentFilter.value === 'task' ? t('dashboard.filterTask') : t('dashboard.filterProduct')
})
const todoList = reactive([]);
const rangeColumns = computed(() => [
[
{ text: t('dashboard.rangeYear'), value: 'year' },
{ text: t('dashboard.rangeMonth'), value: 'month' },
{ text: t('dashboard.rangeWeek'), value: 'week' },
{ text: t('dashboard.rangeToday'), value: 'today' },
{ text: t('dashboard.rangeCustom'), value: 'custom' }
]
])
const rangeLabelMap = computed(() => ({
year: t('dashboard.rangeYear'),
month: t('dashboard.rangeMonth'),
week: t('dashboard.rangeWeek'),
today: t('dashboard.rangeToday'),
custom: t('dashboard.rangeCustom')
}))
const currentRangeLabel = computed(() => {
return rangeLabelMap.value[currentRange.value] || t('dashboard.rangeMonth')
})
const dateRange = reactive({ start: '', end: '' })
const trendData = reactive({
baogongNum: 0,
passNum: 0,
noPassNum: 0,
passRate: 0
})
const taskTrendData = reactive({
totalNum: 0,
issuedNum: 0,
partialScheduledNum: 0,
waitingProductionNum: 0,
producingNum: 0,
completedNum: 0
})
const baogongChartData = reactive({
categories: [],
series: [
{ name: t('dashboard.baogongNum'), data: [] }
]
})
const passRateChartData = reactive({
categories: [],
series: [
{ name: t('dashboard.passRate'), data: [] }
]
})
const taskChartData = reactive({
categories: [],
series: [
{ name: t('dashboard.taskTrend'), data: [] }
]
})
const chartWidth = computed(() => {
const count = baogongChartData.categories.length
if (count <= 7) return '100%'
return Math.max(count * 50, 300) + 'rpx'
})
function getDateRange(type) {
const now = new Date()
const pad2 = (n) => String(n).padStart(2, '0')
const fmt = (d) => `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`
const end = fmt(now)
if (type === 'year') {
const start = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate())
return { start: fmt(start), end }
}
if (type === 'month') {
const start = new Date(now.getFullYear(), now.getMonth(), 1)
return { start: fmt(start), end }
}
if (type === 'week') {
const day = now.getDay() || 7
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - day + 1)
return { start: fmt(start), end }
}
if (type === 'today') {
return { start: end, end }
}
return { start: '', end: '' }
}
const weekdayKeys = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']
function transformChartData(dayTrend, rangeType) {
if (!dayTrend || dayTrend.length === 0) {
return { categories: [], baogongData: [], passRateData: [] }
}
if (rangeType === 'year') {
const monthMap = new Map()
dayTrend.forEach((item) => {
const m = item.day ? item.day.substring(0, 7) : ''
if (!m) return
if (!monthMap.has(m)) {
monthMap.set(m, { baogongNum: 0, passRateSum: 0, count: 0 })
}
const entry = monthMap.get(m)
entry.baogongNum += item.baogongNum ?? 0
entry.passRateSum += item.passRate ?? 0
entry.count += 1
})
const categories = []
const baogongData = []
const passRateData = []
monthMap.forEach((val, key) => {
categories.push(key)
baogongData.push(val.baogongNum)
passRateData.push(val.count > 0 ? Math.round((val.passRateSum / val.count) * 100) / 100 : 0)
})
return { categories, baogongData, passRateData }
}
if (rangeType === 'week') {
const categories = dayTrend.map((item) => {
const d = new Date(item.day)
const dayIdx = d.getDay()
return t(`dashboard.${weekdayKeys[dayIdx]}`)
})
return {
categories,
baogongData: dayTrend.map((item) => item.baogongNum ?? 0),
passRateData: dayTrend.map((item) => item.passRate ?? 0)
}
}
if (rangeType === 'today') {
const categories = dayTrend.map((item) => {
const dayStr = item.day || ''
return dayStr.substring(5)
})
return {
categories,
baogongData: dayTrend.map((item) => item.baogongNum ?? 0),
passRateData: dayTrend.map((item) => item.passRate ?? 0)
}
}
const categories = dayTrend.map((item) => {
const dayStr = item.day || ''
return dayStr.substring(5)
})
return {
categories,
baogongData: dayTrend.map((item) => item.baogongNum ?? 0),
passRateData: dayTrend.map((item) => item.passRate ?? 0)
}
}
function onFilterConfirm(e) {
const val = e.value[0]?.value || e.value[0]
currentFilter.value = val
showFilterPicker.value = false
if (val === 'product') {
loadTrendData()
} else if (val === 'task') {
loadTaskTrendData()
}
}
function onRangeConfirm(e) {
const val = e.value[0]?.value || e.value[0]
currentRange.value = val
showRangePicker.value = false
if (val !== 'custom') {
const range = getDateRange(val)
dateRange.start = range.start
dateRange.end = range.end
if (currentFilter.value === 'product') {
loadTrendData()
} else if (currentFilter.value === 'task') {
loadTaskTrendData()
}
}
}
function formatTimestamp(ts) {
const d = new Date(ts)
const pad = (n) => String(n).padStart(2, '0')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
}
function datetimeFormatter(type, value) {
const unitMap = { year: '年', month: '月', day: '日', hour: '时', minute: '分' }
return value + (unitMap[type] || '')
}
function onStartDateConfirm(e) {
dateRange.start = formatTimestamp(e.value)
if (dateRange.start && dateRange.end) {
if (currentFilter.value === 'product') {
loadTrendData()
} else if (currentFilter.value === 'task') {
loadTaskTrendData()
}
}
}
function onEndDateConfirm(e) {
dateRange.end = formatTimestamp(e.value)
if (dateRange.start && dateRange.end) {
if (currentFilter.value === 'product') {
loadTrendData()
} else if (currentFilter.value === 'task') {
loadTaskTrendData()
}
}
}
function onPickerClose() {
if (startPickerRef.value) startPickerRef.value.showByClickInput = false
if (endPickerRef.value) endPickerRef.value.showByClickInput = false
}
async function loadTrendData() {
const trendTypeMap = { year: 1, month: 2, week: 3, today: 4, custom: 5 }
const params = { trendType: trendTypeMap[currentRange.value] || 2 }
if (currentRange.value === 'custom') {
if (dateRange.start) params.beginBaogongTime = dateRange.start
if (dateRange.end) params.endBaogongTime = dateRange.end
}
const res = await request({
url: '/admin-api/mes/baogong-record/trend',
method: 'get',
params
})
const data = res?.data || {}
trendData.baogongNum = data.baogongNum ?? 0
trendData.passNum = data.passNum ?? 0
trendData.noPassNum = data.noPassNum ?? 0
trendData.passRate = data.passRate ?? 0
if (currentRange.value === 'custom') {
baogongChartData.categories = []
baogongChartData.series = [{ name: t('dashboard.baogongNum'), data: [] }]
passRateChartData.categories = []
passRateChartData.series = [{ name: t('dashboard.passRate'), data: [] }]
return
}
const dayTrend = data.dayTrend || []
const transformed = transformChartData(dayTrend, currentRange.value)
baogongChartData.categories = transformed.categories
baogongChartData.series = [
{ name: t('dashboard.baogongNum'), data: transformed.baogongData }
]
passRateChartData.categories = transformed.categories
passRateChartData.series = [
{ name: t('dashboard.passRate'), data: transformed.passRateData }
]
}
async function loadTaskTrendData() {
const trendTypeMap = { year: 1, month: 2, week: 3, today: 4, custom: 5 }
const params = { trendType: trendTypeMap[currentRange.value] || 2 }
if (currentRange.value === 'custom') {
if (dateRange.start) params.beginTime = dateRange.start
if (dateRange.end) params.endTime = dateRange.end
}
const res = await request({
url: '/admin-api/mes/task/trend',
method: 'get',
params
})
const data = res?.data || {}
taskTrendData.totalNum = data.totalNum ?? 0
taskTrendData.issuedNum = data.issuedNum ?? 0
taskTrendData.partialScheduledNum = data.partialScheduledNum ?? 0
taskTrendData.waitingProductionNum = data.waitingProductionNum ?? 0
taskTrendData.producingNum = data.producingNum ?? 0
taskTrendData.completedNum = data.completedNum ?? 0
if (currentRange.value === 'custom') {
taskChartData.categories = []
taskChartData.series = [{ name: t('dashboard.taskTrend'), data: [] }]
return
}
const dayTrend = data.dayTrend || []
const categories = []
const taskData = []
if (currentRange.value === 'year') {
const monthMap = new Map()
dayTrend.forEach((item) => {
const m = item.day ? item.day.substring(0, 7) : ''
if (!m) return
if (!monthMap.has(m)) {
monthMap.set(m, 0)
}
monthMap.set(m, (monthMap.get(m) ?? 0) + (item.count ?? 0))
})
monthMap.forEach((val, key) => {
categories.push(key)
taskData.push(val)
})
} else if (currentRange.value === 'week') {
const weekdayKeys = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']
dayTrend.forEach((item) => {
const d = new Date(item.day)
const dayIdx = d.getDay()
categories.push(t(`dashboard.${weekdayKeys[dayIdx]}`))
taskData.push(item.count ?? 0)
})
} else {
dayTrend.forEach((item) => {
const dayStr = item.day || ''
categories.push(dayStr.substring(5))
taskData.push(item.count ?? 0)
})
}
taskChartData.categories = categories
taskChartData.series = [{ name: t('dashboard.taskTrend'), data: taskData }]
}
function showTodoList() {
todoPopup.value.open();
@ -199,16 +601,8 @@ function handleNavClick(item) {
}
}
function handlePlanClick(plan) {
uni.showToast({
title: t('dashboard.viewPlan', { code: plan.code }),
icon: 'none'
});
}
function viewMorePlans() {
if (!hasMorePlans.value && !isShowAllPlans.value) return
isShowAllPlans.value = !isShowAllPlans.value
uni.navigateTo({ url: '/pages_function/pages/planList/index' })
}
function onScroll(e) {
@ -235,26 +629,6 @@ function formatDate(ms) {
return `${y}-${m}-${d}`
}
const getPlanStatusLabel = (value) => {
const v = value === '' || value === null || value === undefined ? undefined : String(value)
if (v == '1') return t('dashboard.statusScheduled')
if (v == '6') return t('dashboard.statusTrial')
if (v == '2') return t('dashboard.statusMass')
if (v == '3') return t('dashboard.statusPause')
if (v == '4') return t('dashboard.statusWaitStockIn')
return '-'
}
function mapPlanStatus(status) {
const v = status === '' || status === null || status === undefined ? undefined : String(status)
if (v == '1') return { status: getPlanStatusLabel(v), statusType: 'pending' }
if (v == '6') return { status: getPlanStatusLabel(v), statusType: 'running' }
if (v == '2') return { status: getPlanStatusLabel(v), statusType: 'running' }
if (v == '3') return { status: getPlanStatusLabel(v), statusType: 'pending' }
if (v == '4') return { status: getPlanStatusLabel(v), statusType: 'finished' }
return { status: getPlanStatusLabel(v), statusType: 'pending' }
}
async function loadProductionStats() {
const res = await request({ url: '/admin-api/mes/dashboard/getProduction', method: 'get' })
const taskItems = (res?.data?.taskItems || []).map((i) => ({
@ -272,29 +646,6 @@ async function loadProductionStats() {
})
}
async function loadPlanList() {
const res = await request({ url: '/admin-api/mes/dashboard/getPlan', method: 'get' })
const raw = Array.isArray(res?.data) ? res.data : (res?.data ? [res.data] : [])
const mapped = raw.map((p) => {
const statusInfo = mapPlanStatus(p?.status)
return {
id: p?.id,
code: p?.code ?? '-',
status: statusInfo.status,
statusType: statusInfo.statusType,
productName: p?.productName ?? '-',
feedingPipelineName: p?.feedingPipelineName ?? '-',
planNumber: p?.planNumber ?? 0,
planStartTimeText: formatDate(p?.planStartTime),
planEndTimeText: formatDate(p?.planEndTime),
}
})
planList.splice(0, planList.length, ...mapped)
if (mapped.length <= 3) {
isShowAllPlans.value = false
}
}
async function loadTodoList() {
const res = await request({ url: '/admin-api/mes/dashboard/getTodoList', method: 'get' })
const data = res?.data || []
@ -303,7 +654,7 @@ async function loadTodoList() {
}
async function loadDashboard() {
await Promise.allSettled([loadProductionStats(), loadPlanList(), loadTodoList()])
await Promise.allSettled([loadProductionStats(), loadTodoList(), loadTrendData()])
}
onMounted(() => {
@ -609,89 +960,165 @@ onUnmounted(() => {
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.plan-list {
.filter-bar {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 24rpx;
}
.plan-card {
background: #f8fafc;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.filter-select {
display: flex;
align-items: center;
padding: 12rpx 24rpx;
background: #f0f2f5;
border-radius: 12rpx;
margin-right: 20rpx;
&:active {
background: #e8f4ff;
background: #e8ecf0;
}
}
.plan-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.plan-code {
font-size: 28rpx;
font-weight: 600;
.filter-text {
font-size: 26rpx;
color: #1a3a5c;
font-weight: 500;
margin-right: 8rpx;
}
.plan-status {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 22rpx;
.filter-arrow {
font-size: 20rpx;
color: #999999;
}
.status-pending {
background: rgba(255, 140, 0, 0.15);
color: #ff8c00;
.filter-date-wrap {
display: flex;
gap: 16rpx;
margin-bottom: 24rpx;
flex-wrap: wrap;
}
.status-running {
background: rgba(24, 188, 55, 0.15);
color: #18bc37;
}
.date-picker-item {
flex: 1;
min-width: 200rpx;
display: flex;
align-items: center;
position: relative;
background: #f8fafc;
border-radius: 12rpx;
padding: 6rpx 12rpx;
border: 2rpx solid #e0e6ed;
transition: border-color 0.3s ease;
&:focus-within {
border-color: #4a90c2;
background: #ffffff;
}
:deep(.u-datetime-picker) {
flex: 1;
.u-input {
padding-left: 40rpx;
background: transparent;
border: none;
}
}
.status-finished {
background: rgba(74, 144, 194, 0.15);
color: #4a90c2;
.up-icon {
position: absolute;
left: 12rpx;
z-index: 1;
color: #4a90c2;
}
}
.plan-body {
display: flex;
flex-direction: column;
.trend-content {
margin-top: 8rpx;
}
.plan-row {
.trend-stats {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
margin-bottom: 24rpx;
}
.trend-stat-card {
flex: 1;
background: #f8fafc;
border-radius: 12rpx;
padding: 20rpx 12rpx;
margin: 0 6rpx;
text-align: center;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-bottom: 0;
margin-right: 0;
}
}
.plan-label {
font-size: 26rpx;
.trend-stat-value {
display: block;
font-size: 36rpx;
font-weight: bold;
color: #1a3a5c;
margin-bottom: 6rpx;
&.pass {
color: #18bc37;
}
&.fail {
color: #ff4d4f;
}
&.rate {
color: #4a90c2;
}
}
.trend-stat-label {
display: block;
font-size: 22rpx;
color: #999999;
}
.plan-value {
font-size: 26rpx;
color: #333333;
.trend-chart {
margin-top: 8rpx;
}
.plan-num {
font-weight: 600;
.chart-title {
display: block;
font-size: 28rpx;
font-weight: 500;
color: #1a3a5c;
margin-bottom: 16rpx;
}
.chart-scroll {
width: 100%;
white-space: nowrap;
}
.chart-box {
width: 100%;
height: 450rpx;
min-width: 100%;
}
.task-placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300rpx;
}
.placeholder-text {
font-size: 28rpx;
color: #999999;
}
.todo-popup {

@ -0,0 +1,219 @@
<template>
<view class="page-container">
<view class="plan-list">
<view v-for="(plan, index) in planList" :key="index" class="plan-card" @click="handlePlanClick(plan)">
<view class="plan-header">
<text class="plan-code">{{ plan.code }}</text>
<view class="plan-status" :class="'status-' + plan.statusType">
<text>{{ plan.status }}</text>
</view>
</view>
<view class="plan-body">
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.productName') }}</text>
<text class="plan-value">{{ plan.productName }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.pipeline') }}</text>
<text class="plan-value">{{ plan.feedingPipelineName }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.planNumber') }}</text>
<text class="plan-value plan-num">{{ plan.planNumber }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.planStart') }}</text>
<text class="plan-value">{{ plan.planStartTimeText }}</text>
</view>
<view class="plan-row">
<text class="plan-label">{{ t('dashboard.planEnd') }}</text>
<text class="plan-value">{{ plan.planEndTimeText }}</text>
</view>
</view>
</view>
</view>
<view v-if="planList.length === 0" class="empty-state">
<text class="empty-text">{{ t('functionCommon.loadFailed') }}</text>
</view>
</view>
</template>
<script setup>
import { onMounted, reactive } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import request from '@/utils/request'
import { setNavigationTitle } from '@/locales'
const { t } = useI18n()
const planList = reactive([])
function formatDate(ms) {
if (!ms) return '-'
const date = new Date(ms)
if (Number.isNaN(date.getTime())) return '-'
const pad2 = (n) => String(n).padStart(2, '0')
const y = date.getFullYear()
const m = pad2(date.getMonth() + 1)
const d = pad2(date.getDate())
return `${y}-${m}-${d}`
}
const getPlanStatusLabel = (value) => {
const v = value === '' || value === null || value === undefined ? undefined : String(value)
if (v == '1') return t('dashboard.statusScheduled')
if (v == '6') return t('dashboard.statusTrial')
if (v == '2') return t('dashboard.statusMass')
if (v == '3') return t('dashboard.statusPause')
if (v == '4') return t('dashboard.statusWaitStockIn')
return '-'
}
function mapPlanStatus(status) {
const v = status === '' || status === null || status === undefined ? undefined : String(status)
if (v == '1') return { status: getPlanStatusLabel(v), statusType: 'pending' }
if (v == '6') return { status: getPlanStatusLabel(v), statusType: 'running' }
if (v == '2') return { status: getPlanStatusLabel(v), statusType: 'running' }
if (v == '3') return { status: getPlanStatusLabel(v), statusType: 'pending' }
if (v == '4') return { status: getPlanStatusLabel(v), statusType: 'finished' }
return { status: getPlanStatusLabel(v), statusType: 'pending' }
}
function handlePlanClick(plan) {
uni.showToast({
title: t('dashboard.viewPlan', { code: plan.code }),
icon: 'none'
})
}
async function loadPlanList() {
const res = await request({ url: '/admin-api/mes/dashboard/getPlan', method: 'get' })
const raw = Array.isArray(res?.data) ? res.data : (res?.data ? [res.data] : [])
const mapped = raw.map((p) => {
const statusInfo = mapPlanStatus(p?.status)
return {
id: p?.id,
code: p?.code ?? '-',
status: statusInfo.status,
statusType: statusInfo.statusType,
productName: p?.productName ?? '-',
feedingPipelineName: p?.feedingPipelineName ?? '-',
planNumber: p?.planNumber ?? 0,
planStartTimeText: formatDate(p?.planStartTime),
planEndTimeText: formatDate(p?.planEndTime),
}
})
planList.splice(0, planList.length, ...mapped)
}
onMounted(() => {
loadPlanList()
})
onShow(() => {
setNavigationTitle('nav.home')
})
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #f0f2f5;
padding: 24rpx;
}
.plan-list {
display: flex;
flex-direction: column;
}
.plan-card {
background: #ffffff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
&:active {
background: #e8f4ff;
}
}
.plan-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.plan-code {
font-size: 28rpx;
font-weight: 600;
color: #1a3a5c;
}
.plan-status {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 22rpx;
}
.status-pending {
background: rgba(255, 140, 0, 0.15);
color: #ff8c00;
}
.status-running {
background: rgba(24, 188, 55, 0.15);
color: #18bc37;
}
.status-finished {
background: rgba(74, 144, 194, 0.15);
color: #4a90c2;
}
.plan-body {
display: flex;
flex-direction: column;
}
.plan-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
}
.plan-label {
font-size: 26rpx;
color: #999999;
}
.plan-value {
font-size: 26rpx;
color: #333333;
}
.plan-num {
font-weight: 600;
color: #1a3a5c;
}
.empty-state {
display: flex;
align-items: center;
justify-content: center;
height: 400rpx;
.empty-text {
font-size: 28rpx;
color: #999999;
}
}
</style>

@ -0,0 +1,39 @@
import { getCurrentLocale } from '@/locales'
const ZH_UNITS = [
{ value: 1e8, symbol: '亿' },
{ value: 1e6, symbol: '百万' },
{ value: 1e4, symbol: '万' }
]
const EN_UNITS = [
{ value: 1e9, symbol: 'B' },
{ value: 1e6, symbol: 'M' },
{ value: 1e3, symbol: 'K' }
]
function formatWithUnits(num, units, decimals) {
for (const unit of units) {
if (num >= unit.value) {
const formatted = num / unit.value
const rounded = parseFloat(formatted.toFixed(decimals))
return rounded + unit.symbol
}
}
return num.toLocaleString()
}
export function formatNumber(num, options = {}) {
if (num === null || num === undefined || isNaN(num)) return '-'
const { decimals = 1, locale } = options
const currentLocale = locale || getCurrentLocale()
const isZh = currentLocale.startsWith('zh')
const units = isZh ? ZH_UNITS : EN_UNITS
return formatWithUnits(Number(num), units, decimals)
}
export function formatPercent(num, options = {}) {
if (num === null || num === undefined || isNaN(num)) return '-'
const { decimals = 1 } = options
return parseFloat(Number(num).toFixed(decimals)) + '%'
}

@ -9,7 +9,7 @@ export default defineConfig(() => {
outDir: 'dist',
},
server: {
port: '80'
port: 9000
},
plugins: [
uni()
@ -18,4 +18,4 @@ export default defineConfig(() => {
/\/README\.md$/,
]
}
})
})

Loading…
Cancel
Save