csm xiugai

master
2358328281@qq.com 5 days ago
parent d3248a3dae
commit f29c3dc589

@ -133,6 +133,7 @@ export default {
confirmPassword: 'Confirm Password', confirmPassword: 'Confirm Password',
hospital: 'Hospital Name', hospital: 'Hospital Name',
hospitalName: 'Hospital Name', hospitalName: 'Hospital Name',
hospitalSearch: 'Hospital Name',
contact: 'Contact', contact: 'Contact',
contactPerson: 'Contact Person', contactPerson: 'Contact Person',
contactDept: 'Contact Department', contactDept: 'Contact Department',
@ -148,6 +149,7 @@ export default {
acceptanceReportTip: 'Support uploading PDF format files (required)', acceptanceReportTip: 'Support uploading PDF format files (required)',
account: 'Account', account: 'Account',
accountType: 'Account Type', accountType: 'Account Type',
nameAccount: 'Name/Account',
password: 'Password', password: 'Password',
gender: 'Gender', gender: 'Gender',
role: 'Role', role: 'Role',
@ -176,6 +178,7 @@ export default {
search: 'Please input', search: 'Please input',
keyword: 'Search title/ticket no.', keyword: 'Search title/ticket no.',
account: 'Enter account', account: 'Enter account',
nameOrAccount: 'Search by name or account',
password: 'Enter password', password: 'Enter password',
newPassword: 'Enter new password (at least 6 chars)', newPassword: 'Enter new password (at least 6 chars)',
confirmPassword: 'Confirm new password', confirmPassword: 'Confirm new password',
@ -199,6 +202,7 @@ export default {
cancelSuccess: 'Ticket cancelled successfully!', cancelSuccess: 'Ticket cancelled successfully!',
closeSuccess: 'Ticket closed successfully!', closeSuccess: 'Ticket closed successfully!',
passwordChangeSuccess: 'Password changed successfully!', passwordChangeSuccess: 'Password changed successfully!',
statusChangeSuccess: 'Status changed successfully!',
languageChangeSuccess: 'Language changed successfully!', languageChangeSuccess: 'Language changed successfully!',
themeChangeSuccess: 'Theme changed successfully!', themeChangeSuccess: 'Theme changed successfully!',
hospitalAddSuccess: 'Hospital added successfully!', hospitalAddSuccess: 'Hospital added successfully!',

@ -1,473 +1,477 @@
export default { export default {
// 公共 // 公共
common: { common: {
confirm: '确认', confirm: "确认",
cancel: '取消', cancel: "取消",
save: '保存', save: "保存",
submit: '提交', submit: "提交",
reset: '重置', reset: "重置",
query: '查询', query: "查询",
search: '搜索', search: "搜索",
add: '添加', add: "添加",
edit: '编辑', edit: "编辑",
delete: '删除', delete: "删除",
view: '查看', view: "查看",
close: '关闭', close: "关闭",
back: '返回', back: "返回",
export: '导出', export: "导出",
download: '下载', download: "下载",
upload: '上传', upload: "上传",
more: '更多', more: "更多",
all: '全部', all: "全部",
yes: '是', yes: "是",
no: '否', no: "否",
enabled: '启用', enabled: "启用",
disabled: '禁用', disabled: "禁用",
pleaseSelect: '请选择', pleaseSelect: "请选择",
pleaseInput: '请输入', pleaseInput: "请输入",
success: '成功', success: "成功",
failed: '失败', failed: "失败",
operating: '操作', operating: "操作",
remark: '备注', remark: "备注",
time: '时间', time: "时间",
today: '今日', today: "今日",
yesterday: '昨日', yesterday: "昨日",
thisWeek: '本周', thisWeek: "本周",
thisMonth: '本月', thisMonth: "本月",
thisQuarter: '本季度', thisQuarter: "本季度",
thisYear: '本年', thisYear: "本年",
customDate: '自定义日期', customDate: "自定义日期",
startDate: '开始日期', startDate: "开始日期",
endDate: '结束日期', endDate: "结束日期",
language: '语言', language: "语言",
}, },
// 登录页 // 登录页
login: { login: {
title: '欢迎登录', title: "欢迎登录",
name: '康策智能CSM系统', name: "康策智能CSM系统",
headerTitle: '康策智能CSM系统', headerTitle: "康策智能CSM系统",
userName: '请输入账号', userName: "请输入账号",
passWord: '请输入密码', passWord: "请输入密码",
code: '请输入验证码', code: "请输入验证码",
jizhu: '记住账号', jizhu: "记住账号",
denglu: '登 录', denglu: "登 录",
copyright: '上海康策软件有限公司版权所有', copyright: "上海康策软件有限公司版权所有",
hotline: '服务热线021-60713139', hotline: "服务热线021-60713139",
loginSuccess: '登录成功', loginSuccess: "登录成功",
loginFailed: '登录失败', loginFailed: "登录失败",
unknownUserType: '未知用户类型,无法登录', unknownUserType: "未知用户类型,无法登录",
pleaseEnterUserName: '请输入账号', pleaseEnterUserName: "请输入账号",
pleaseEnterPassWord: '请输入密码', pleaseEnterPassWord: "请输入密码",
languageLabel: '语言:', languageLabel: "语言:",
}, },
// 导航菜单 // 导航菜单
nav: { nav: {
workbench: '我的工作台', workbench: "我的工作台",
submit: '我的工单', submit: "我的工单",
points: '我的积分', points: "我的积分",
quickSubmit: '快速提交', quickSubmit: "快速提交",
workorders: '服务工单', workorders: "服务工单",
reports: '服务报告', reports: "服务报告",
hospitals: '医院信息', hospitals: "医院信息",
customer: '客户服务', customer: "客户服务",
managerPoints: '客户经理积分', managerPoints: "客户经理积分",
account: '账号管理', account: "账号管理",
grades: '绩效管理', grades: "绩效管理",
}, },
// 页面标题 // 页面标题
title: { title: {
workbench: '我的工作台', workbench: "我的工作台",
submit: '我的工单', submit: "我的工单",
points: '我的积分', points: "我的积分",
workorders: '服务工单', workorders: "服务工单",
reports: '服务报告', reports: "服务报告",
hospitals: '医院信息', hospitals: "医院信息",
customer: '客户服务', customer: "客户服务",
managerPoints: '客户经理积分', managerPoints: "客户经理积分",
account: '账号管理', account: "账号管理",
grades: '绩效管理', grades: "绩效管理",
changePassword: '修改密码', changePassword: "修改密码",
}, },
// 按钮文本 // 按钮文本
btn: { btn: {
quickSubmit: '快速提交', quickSubmit: "快速提交",
view: '查看', view: "查看",
edit: '编辑', edit: "编辑",
cancel: '取消', cancel: "取消",
close: '关闭', close: "关闭",
detail: '详情', detail: "详情",
process: '处理', process: "处理",
submit: '提交工单', submit: "提交工单",
save: '保存修改', save: "保存修改",
cancelModal: '取消', cancelModal: "取消",
saveChange: '保存修改', saveChange: "保存修改",
query: '查询', query: "查询",
reset: '重置', reset: "重置",
export: '导出', export: "导出",
download: '下载', download: "下载",
upload: '上传', upload: "上传",
generate: '生成', generate: "生成",
add: '添加', add: "添加",
delete: '删除', delete: "删除",
addOrder: '+ 新建工单', addOrder: "+ 新建工单",
addHospital: '+ 添加医院', addHospital: "+ 添加医院",
addAccount: '+ 添加账号', addAccount: "+ 添加账号",
quickAddOrder: '+ 快速提交工单', quickAddOrder: "+ 快速提交工单",
back: '← 返回', back: "← 返回",
preview: '查看报告', preview: "查看报告",
downloadPdf: '下载PDF报告', downloadPdf: "下载PDF报告",
confirm: '确认修改', confirm: "确认修改",
downloadReport: '下载报告', downloadReport: "下载报告",
}, },
// 表单标签 // 表单标签
label: { label: {
title: '问题名称', title: "问题名称",
description: '问题描述', description: "问题描述",
attachment: '附件', attachment: "附件",
dept: '提出科室', dept: "提出科室",
name: '提出人员', name: "提出人员",
priority: '优先级', priority: "优先级",
type: '服务类型', type: "服务类型",
oldPassword: '原密码', oldPassword: "原密码",
newPassword: '新密码', newPassword: "新密码",
confirmPassword: '确认密码', confirmPassword: "确认密码",
hospital: '医院名称', hospital: "医院名称",
hospitalName: '医院名称', hospitalName: "医院名称",
contact: '联系人', hospitalSearch: "医院名称",
contactPerson: '联系人', contact: "联系人",
contactDept: '联系科室', contactPerson: "联系人",
contactPosition: '职务', contactDept: "联系科室",
contactPhone: '联系电话', contactPosition: "职务",
category: '客户分类', contactPhone: "联系电话",
signDate: '签约时间', category: "客户分类",
acceptDate: '验收时间', signDate: "签约时间",
years: '年限', acceptDate: "验收时间",
maintenanceEndDate: '维保截止时间', years: "年限",
manager: '客户经理', maintenanceEndDate: "维保截止时间",
acceptanceReport: '验收报告', manager: "客户经理",
acceptanceReportTip: '支持上传PDF格式文件必填', acceptanceReport: "请上传验收报告",
account: '账号', acceptanceReportTip: "支持上传PDF格式文件必填",
accountType: '账号类型', account: "账号",
password: '密码', accountType: "账号类型",
gender: '性别', nameAccount: "姓名/账号",
role: '角色', password: "密码",
status: '状态', gender: "性别",
renewal: '续保完成', role: "角色",
contractSigned: '合同签署', status: "状态",
paymentPaid: '款项支付', renewal: "续保完成",
visited: '回访状态', contractSigned: "合同签署",
visitResult: '回访结果', paymentPaid: "款项支付",
createTime: '创建时间', visited: "回访状态",
colorTag: '颜色标签', visitResult: "回访结果",
currentPassword: '当前密码', createTime: "创建时间",
}, colorTag: "颜色标签",
// 表单 placeholder currentPassword: "当前密码",
placeholder: { },
title: '请输入问题名称', // 表单 placeholder
description: '请详细描述问题,并可以编辑器直接贴图', placeholder: {
dept: '请输入提出科室', title: "请输入问题名称",
name: '请输入提出人员', description: "请详细描述问题,并可以编辑器直接贴图",
type: '请选择服务类型', dept: "请输入提出科室",
hospital: '请输入医院名称', name: "请输入提出人员",
contactPerson: '请输入联系人', type: "请选择服务类型",
contactDept: '请输入联系科室', hospital: "请输入医院名称",
contactPosition: '请输入职务', contactPerson: "请输入联系人",
contactPhone: '请输入联系电话', contactDept: "请输入联系科室",
search: '请输入', contactPosition: "请输入职务",
keyword: '搜索标题/工单号', contactPhone: "请输入联系电话",
account: '请输入账号', search: "请输入",
password: '请输入密码', keyword: "搜索标题/工单号",
newPassword: '请输入新密码至少6位', account: "请输入账号",
confirmPassword: '请再次输入新密码', nameOrAccount: "搜索姓名或账号",
currentPassword: '请输入当前密码', password: "请输入密码",
remark: '请输入备注信息...', newPassword: "请输入新密码至少6位",
}, confirmPassword: "请再次输入新密码",
// 提示消息 currentPassword: "请输入当前密码",
msg: { remark: "请输入备注信息...",
pleaseInputTitle: '请输入问题名称', },
pleaseInputDescription: '请输入问题描述', // 提示消息
pleaseSelectPriority: '请选择优先级', msg: {
pleaseSelectType: '请选择服务类型', pleaseInputTitle: "请输入问题名称",
pleaseSelectHospital: '请选择医院', pleaseInputDescription: "请输入问题描述",
pleaseInputDept: '请输入提出科室', pleaseSelectPriority: "请选择优先级",
pleaseInputName: '请输入提出人员', pleaseSelectType: "请选择服务类型",
passwordTooShort: '新密码至少需要6位', pleaseSelectHospital: "请选择医院",
passwordMismatch: '两次输入的新密码不一致!', pleaseInputDept: "请输入提出科室",
pleaseFillRequired: '请填写必填项!', pleaseInputName: "请输入提出人员",
submitSuccess: '工单提交成功!', passwordTooShort: "新密码至少需要6位",
editSuccess: '工单修改成功!', passwordMismatch: "两次输入的新密码不一致!",
cancelSuccess: '工单已成功取消!', pleaseFillRequired: "请填写必填项!",
closeSuccess: '工单已成功关闭!', submitSuccess: "工单提交成功!",
passwordChangeSuccess: '密码修改成功!', editSuccess: "工单修改成功!",
languageChangeSuccess: '语言切换成功!', cancelSuccess: "工单已成功取消!",
themeChangeSuccess: '主题切换成功!', closeSuccess: "工单已成功关闭!",
hospitalAddSuccess: '医院添加成功!', passwordChangeSuccess: "密码修改成功!",
hospitalEditSuccess: '医院信息修改成功!', statusChangeSuccess: "状态切换成功!",
accountAddSuccess: '账号添加成功!', languageChangeSuccess: "语言切换成功!",
accountEditSuccess: '账号更新成功!', themeChangeSuccess: "主题切换成功!",
passwordResetSuccess: '密码重置成功!', hospitalAddSuccess: "医院添加成功!",
reportGenerateSuccess: '报告生成成功!', hospitalEditSuccess: "医院信息修改成功!",
confirmCancelOrder: '确定要取消工单 {orderId} 吗?取消后将无法恢复。', accountAddSuccess: "账号添加成功!",
confirmCloseOrder: '确定要关闭工单 {orderId} 吗?', accountEditSuccess: "账号更新成功!",
}, passwordResetSuccess: "密码重置成功!",
// 统计 reportGenerateSuccess: "报告生成成功!",
stat: { confirmCancelOrder: "确定要取消工单 {orderId} 吗?取消后将无法恢复。",
total: '累计提交工单', confirmCloseOrder: "确定要关闭工单 {orderId} 吗?",
completed: '已完成工单', },
pending: '待处理工单', // 统计
highPriority: '高优先级工单', stat: {
totalOrders: '总工单', total: "累计提交工单",
pendingOrders: '待处理', completed: "已完成工单",
completedOrders: '已完成', pending: "待处理工单",
highPriorityOrders: '高优先级', highPriority: "高优先级工单",
high: '高优先级', totalOrders: "总工单",
medium: '中优先级', pendingOrders: "待处理",
low: '低优先级', completedOrders: "已完成",
mediumPriority: '中优先级', highPriorityOrders: "高优先级",
lowPriority: '低优先级', high: "高优先级",
processing: '处理中', medium: "中优先级",
}, low: "低优先级",
// 表格列 mediumPriority: "中优先级",
table: { lowPriority: "低优先级",
id: '工单号', processing: "处理中",
priority: '优先级', },
title: '问题名称', // 表格列
type: '服务类型', table: {
status: '状态', id: "工单号",
time: '提交时间', priority: "优先级",
submitter: '提交人', title: "问题名称",
action: '操作', type: "服务类型",
hospital: '医院名称', status: "状态",
handler: '处理人', time: "提交时间",
handleTime: '处理时间', submitter: "提交人",
handleDesc: '处理说明', action: "操作",
registrar: '登记人', hospital: "医院名称",
dept: '提出科室', handler: "处理人",
category: '客户分类', handleTime: "处理时间",
contact: '联系人', handleDesc: "处理说明",
phone: '联系电话', registrar: "登记人",
manager: '客户经理', dept: "提出科室",
signDate: '签约日期', category: "客户分类",
usageYears: '客户使用年限', contact: "联系人",
maintenanceEnd: '维保截止时间', phone: "联系电话",
acceptanceReport: '验收报告', manager: "客户经理",
account: '账号', signDate: "签约日期",
name: '姓名', usageYears: "客户使用年限",
gender: '性别', maintenanceEnd: "维保截止时间",
role: '角色', acceptanceReport: "验收报告",
createTime: '创建时间', account: "账号",
hospitalName: '医院名称', name: "姓名",
}, gender: "性别",
// 状态 role: "角色",
status: { createTime: "创建时间",
pending: '待处理', hospitalName: "医院名称",
processing: '处理中', },
completed: '已完成', // 状态
closed: '已关闭', status: {
active: '活跃', pending: "待处理",
inactive: '不活跃', processing: "处理中",
normal: '正常', completed: "已完成",
warning: '预警', closed: "已关闭",
urgent: '紧急', active: "活跃",
expired: '已过期', inactive: "不活跃",
renewed: '已续保', normal: "正常",
notRenewed: '未续保', warning: "预警",
paid: '已付款', urgent: "紧急",
notPaid: '未付款', expired: "已过期",
}, renewed: "已续保",
// 优先级 notRenewed: "未续保",
priority: { paid: "已付款",
high: '🔴 高', notPaid: "未付款",
medium: '🟠 中', },
low: '🟢 低', // 优先级
}, priority: {
// 类型 high: "🔴 高",
type: { medium: "🟠 中",
issue: '故障问题', low: "🟢 低",
consult: '使用咨询', },
feature: '功能需求', // 类型
other: '其他', type: {
}, issue: "故障问题",
// 图表 consult: "使用咨询",
chart: { feature: "功能需求",
title: '工单趋势', other: "其他",
submit: '提交工单', },
complete: '完成工单', // 图表
bar: '柱状图', chart: {
line: '折线图', title: "工单趋势",
}, submit: "提交工单",
// 日期按钮 complete: "完成工单",
dateBtn: { bar: "柱状图",
thisMonth: '本月', line: "折线图",
thisQuarter: '本季度', },
thisYear: '本年', // 日期按钮
customDate: '自定义日期', dateBtn: {
}, thisMonth: "本月",
// 主题 thisQuarter: "本季度",
theme: { thisYear: "本年",
blue: '穹宇蓝', customDate: "自定义日期",
purple: '星轨紫', },
green: '青峦绿', // 主题
title: '更换主题', theme: {
}, blue: "穹宇蓝",
// 语言 purple: "星轨紫",
languageSwitch: { green: "青峦绿",
title: '更换语言', title: "更换主题",
zhCN: '中文简体', },
zhTW: '中文繁体', // 语言
enUS: 'English', languageSwitch: {
}, title: "更换语言",
// 模态框 zhCN: "中文简体",
modal: { zhTW: "中文繁体",
detail: '工单详情', enUS: "English",
edit: '编辑工单', },
submit: '快速提交工单', // 模态框
changePassword: '修改密码', modal: {
changeTheme: '更换主题', detail: "工单详情",
changeLanguage: '更换语言', edit: "编辑工单",
addAccount: '添加账号', submit: "快速提交工单",
editAccount: '编辑账号', changePassword: "修改密码",
resetPassword: '重置密码', changeTheme: "更换主题",
addHospital: '添加医院', changeLanguage: "更换语言",
editHospital: '编辑医院', addAccount: "添加账号",
detailHospital: '医院详情', editAccount: "编辑账号",
generateReport: '生成服务报告', resetPassword: "重置密码",
}, addHospital: "添加医院",
// 用户菜单 editHospital: "编辑医院",
userMenu: { detailHospital: "医院详情",
changePassword: '修改密码', generateReport: "生成服务报告",
changeTheme: '更换主题', },
changeLanguage: '更换语言', // 用户菜单
logout: '退出登录', userMenu: {
}, changePassword: "修改密码",
// 客户分类 changeTheme: "更换主题",
category: { changeLanguage: "更换语言",
A: 'A类活跃', logout: "退出登录",
B: 'B类不活跃', },
C: 'C类待挽回', // 客户分类
AShort: 'A类', category: {
BShort: 'B类', A: "A类活跃",
CShort: 'C类', B: "B类不活跃",
}, C: "C类待挽回",
// 角色 AShort: "A类",
role: { BShort: "B类",
super: '超级管理员', CShort: "C类",
manager: '客户经理', },
}, // 角色
// 性别 role: {
gender: { super: "超级管理员",
male: '男', manager: "客户经理",
female: '女', },
}, // 性别
// 文件上传 gender: {
upload: { male: "男",
text: '点击或拖拽文件到此处添加', female: "女",
hint: '支持上传不超过50M的文件', },
uploaded: '已上传文件', // 文件上传
remove: '删除', upload: {
rename: '重命名', text: "点击或拖拽文件到此处添加",
maxSize50M: '(不超过50M)', hint: "支持上传不超过50M的文件",
}, uploaded: "已上传文件",
// 积分页面 remove: "删除",
points: { rename: "重命名",
title: '🎁 我的积分', maxSize50M: "(不超过50M)",
unit: '分', },
rewards: '已获得15次服务奖励', // 积分页面
detail: '积分明细', points: {
points: '积分', title: "🎁 我的积分",
pointsTime: '时间', unit: "分",
pointsSource: '来源', rewards: "已获得15次服务奖励",
pointsOrder: '工单号', detail: "积分明细",
pointsChange: '积分变动', points: "积分",
pointsDesc: '说明', pointsTime: "时间",
}, pointsSource: "来源",
// 报告页面 pointsOrder: "工单号",
report: { pointsChange: "积分变动",
list: '服务报告列表', pointsDesc: "说明",
generate: '生成服务报告', },
view: '查看', // 报告页面
download: '下载', report: {
remark: '备注', list: "服务报告列表",
search: '输入医院名称搜索', generate: "生成服务报告",
typeAll: '全部类型', view: "查看",
quarterly: '季度报告', download: "下载",
annual: '年度报告', remark: "备注",
period: '报告周期', search: "输入医院名称搜索",
quarter: '季度', typeAll: "全部类型",
year: '年度', quarterly: "季度报告",
querySuccess: '查询完成,找到 {count} 条报告记录。', annual: "年度报告",
}, period: "报告周期",
// 工单详情 quarter: "季度",
orderDetail: { year: "年度",
submitTime: '提交时间', querySuccess: "查询完成,找到 {count} 条报告记录。",
statusChange: '状态变更日志', },
operator: '操作人', // 工单详情
beforeChange: '变更前', orderDetail: {
afterChange: '变更后', submitTime: "提交时间",
noAttachment: '暂无附件', statusChange: "状态变更日志",
currentStatus: '当前状态', operator: "操作人",
dept: '提出科室', beforeChange: "变更前",
name: '提出人员', afterChange: "变更后",
handler: '负责人员', noAttachment: "暂无附件",
}, currentStatus: "当前状态",
// 修改密码 dept: "提出科室",
password: { name: "提出人员",
title: '修改密码', handler: "负责人员",
oldPassword: '原密码', },
newPassword: '新密码', // 修改密码
confirmPassword: '确认密码', password: {
submit: '提交', title: "修改密码",
cancel: '取消', oldPassword: "原密码",
success: '密码已修改', newPassword: "新密码",
pleaseInputOld: '请输入原密码', confirmPassword: "确认密码",
pleaseInputNew: '请输入新密码', submit: "提交",
pleaseInputConfirm: '请再次输入新密码', cancel: "取消",
passwordTooShort: '密码长度至少 6 位', success: "密码已修改",
passwordMismatch: '两次密码不一致', pleaseInputOld: "请输入原密码",
oldPasswordError: '原密码错误', pleaseInputNew: "请输入新密码",
accountNotFound: '账号不存在', pleaseInputConfirm: "请再次输入新密码",
}, passwordTooShort: "密码长度至少 6 位",
// 账号管理 passwordMismatch: "两次密码不一致",
account: { oldPasswordError: "原密码错误",
hospitalAccount: '医院账号', accountNotFound: "账号不存在",
adminAccount: '管理员账号', },
list: '账号列表', // 账号管理
addHospital: '+ 添加医院账号', account: {
addAdmin: '+ 添加管理员账号', hospitalAccount: "医院账号",
enable: '启用', adminAccount: "管理员账号",
disable: '禁用', list: "账号列表",
enableSuccess: '账号 {id} 已启用', addHospital: "+ 添加医院账号",
disableSuccess: '账号 {id} 已禁用', addAdmin: "+ 添加管理员账号",
resetPassword: '重置密码', enable: "启用",
defaultPassword: '重置后密码将变为默认密码123456', disable: "禁用",
confirmReset: '确定要重置账号 {id} 的密码吗?', enableSuccess: "账号 {id} 已启用",
resetSuccess: '账号 {id} 的密码已重置为默认密码: 123456', disableSuccess: "账号 {id} 已禁用",
pleaseInputName: '请输入姓名!', resetPassword: "重置密码",
}, defaultPassword: "重置后密码将变为默认密码123456",
// 客户服务 confirmReset: "确定要重置账号 {id} 的密码吗?",
service: { resetSuccess: "账号 {id} 的密码已重置为默认密码: 123456",
title: '客户服务', pleaseInputName: "请输入姓名!",
monthlyView: '📅 月视图', },
prevMonth: '← 上月', // 客户服务
nextMonth: '下月 →', service: {
phoneVisit: '📞 电话回访', title: "客户服务",
onSiteInspection: '🔍 现场巡检', monthlyView: "📅 月视图",
training: '📚 培训记录', prevMonth: "← 上月",
gift: '🎁 纪念品记录', nextMonth: "下月 →",
expiring: '本月到期提醒', phoneVisit: "📞 电话回访",
searchHospital: '按医院名称搜索', onSiteInspection: "🔍 现场巡检",
noRecords: '暂无记录', training: "📚 培训记录",
}, gift: "🎁 纪念品记录",
// 通用验证 expiring: "本月到期提醒",
validation: { searchHospital: "按医院名称搜索",
required: '此项必填', noRecords: "暂无记录",
}, },
// 通用日期范围 // 通用验证
dateRange: { validation: {
today: '今日', required: "此项必填",
yesterday: '昨天', },
thisWeek: '本周', // 通用日期范围
thisMonth: '本月', dateRange: {
thisQuarter: '本季度', today: "今日",
}, yesterday: "昨天",
} thisWeek: "本周",
thisMonth: "本月",
thisQuarter: "本季度",
},
};

@ -133,6 +133,7 @@ export default {
confirmPassword: '確認密碼', confirmPassword: '確認密碼',
hospital: '醫院名稱', hospital: '醫院名稱',
hospitalName: '醫院名稱', hospitalName: '醫院名稱',
hospitalSearch: '醫院名稱',
contact: '聯繫人', contact: '聯繫人',
contactPerson: '聯繫人', contactPerson: '聯繫人',
contactDept: '聯繫科室', contactDept: '聯繫科室',
@ -148,6 +149,7 @@ export default {
acceptanceReportTip: '支援上傳PDF格式檔案必填', acceptanceReportTip: '支援上傳PDF格式檔案必填',
account: '帳號', account: '帳號',
accountType: '帳號類型', accountType: '帳號類型',
nameAccount: '姓名/帳號',
password: '密碼', password: '密碼',
gender: '性別', gender: '性別',
role: '角色', role: '角色',
@ -176,6 +178,7 @@ export default {
search: '請輸入', search: '請輸入',
keyword: '搜索標題/工單號', keyword: '搜索標題/工單號',
account: '請輸入帳號', account: '請輸入帳號',
nameOrAccount: '搜索姓名或帳號',
password: '請輸入密碼', password: '請輸入密碼',
newPassword: '請輸入新密碼至少6位', newPassword: '請輸入新密碼至少6位',
confirmPassword: '請再次輸入新密碼', confirmPassword: '請再次輸入新密碼',
@ -199,6 +202,7 @@ export default {
cancelSuccess: '工單已成功取消!', cancelSuccess: '工單已成功取消!',
closeSuccess: '工單已成功關閉!', closeSuccess: '工單已成功關閉!',
passwordChangeSuccess: '密碼修改成功!', passwordChangeSuccess: '密碼修改成功!',
statusChangeSuccess: '狀態切換成功!',
languageChangeSuccess: '語言切換成功!', languageChangeSuccess: '語言切換成功!',
themeChangeSuccess: '主題切換成功!', themeChangeSuccess: '主題切換成功!',
hospitalAddSuccess: '醫院添加成功!', hospitalAddSuccess: '醫院添加成功!',

@ -8,8 +8,8 @@
<el-option label="医院端" value="Hospital" /> <el-option label="医院端" value="Hospital" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="$t('placeholder.keyword')"> <el-form-item :label="$t('label.nameAccount')">
<el-input v-model="filters.keyword" :placeholder="$t('placeholder.keyword')" clearable style="width:200px" /> <el-input v-model="filters.keyword" :placeholder="$t('placeholder.nameOrAccount')" clearable style="width:200px" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="onSearch">{{ $t('btn.query') }}</el-button> <el-button type="primary" @click="onSearch">{{ $t('btn.query') }}</el-button>
@ -118,7 +118,7 @@ const toggleStatus = async (row) => {
row.__loading = true; row.__loading = true;
try { try {
await toggleAdminAccountStatus(row.id); await toggleAdminAccountStatus(row.id);
ElMessage.success($t('msg.passwordChangeSuccess')); ElMessage.success($t('msg.statusChangeSuccess'));
loadList(); loadList();
} finally { } finally {
row.__loading = false; row.__loading = false;

@ -24,9 +24,9 @@
<el-descriptions-item :label="$t('label.contactPhone')">{{ detail.contactPhone }}</el-descriptions-item> <el-descriptions-item :label="$t('label.contactPhone')">{{ detail.contactPhone }}</el-descriptions-item>
<el-descriptions-item :label="$t('table.signDate')">{{ formatDate(detail.signDate) }}</el-descriptions-item> <el-descriptions-item :label="$t('table.signDate')">{{ formatDate(detail.signDate) }}</el-descriptions-item>
<el-descriptions-item :label="$t('label.acceptDate')">{{ formatDate(detail.acceptDate) }}</el-descriptions-item> <el-descriptions-item :label="$t('label.acceptDate')">{{ formatDate(detail.acceptDate) }}</el-descriptions-item>
<el-descriptions-item :label="$t('label.years')">{{ detail.contractYears }} {{ $t('common.time') }}</el-descriptions-item> <el-descriptions-item :label="$t('label.years')">{{ formatUsageYears(detail.signDate) }}</el-descriptions-item>
<el-descriptions-item :label="$t('table.maintenanceEnd')">{{ formatDate(detail.maintenanceEnd) }}</el-descriptions-item> <el-descriptions-item :label="$t('table.maintenanceEnd')">{{ formatDate(detail.maintenanceEnd) }}</el-descriptions-item>
<el-descriptions-item :label="$t('table.manager')">{{ detail.managerId }}</el-descriptions-item> <el-descriptions-item :label="$t('table.manager')">{{ detail.managerName }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<div class="section"> <div class="section">
@ -51,6 +51,18 @@ const detail = ref({});
const formatDate = (v) => (v ? dayjs(v).format("YYYY-MM-DD") : "—"); const formatDate = (v) => (v ? dayjs(v).format("YYYY-MM-DD") : "—");
// 使"XX"
const formatUsageYears = (signDate) => {
if (!signDate) return "—";
const start = dayjs(signDate);
const now = dayjs();
if (start.isAfter(now)) return "—";
const totalDays = now.diff(start, "day");
const years = Math.floor(totalDays / 365);
const days = totalDays - years * 365;
return `${years}${days}`;
};
const loadDetail = async () => { const loadDetail = async () => {
loading.value = true; loading.value = true;
try { try {

@ -7,7 +7,9 @@
class="page-header" class="page-header"
> >
<template #content> <template #content>
<span class="page-title">{{ isEdit ? $t('modal.editHospital') : $t('modal.addHospital') }}</span> <span class="page-title">{{
isEdit ? $t("modal.editHospital") : $t("modal.addHospital")
}}</span>
</template> </template>
</el-page-header> </el-page-header>
@ -20,7 +22,10 @@
style="max-width: 800px" style="max-width: 800px"
> >
<el-form-item :label="$t('label.hospitalName')" prop="name"> <el-form-item :label="$t('label.hospitalName')" prop="name">
<el-input v-model="form.name" :placeholder="$t('placeholder.hospital')" /> <el-input
v-model="form.name"
:placeholder="$t('placeholder.hospital')"
/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('label.category')" prop="customerCategory"> <el-form-item :label="$t('label.category')" prop="customerCategory">
<el-select <el-select
@ -28,9 +33,9 @@
:placeholder="$t('label.category')" :placeholder="$t('label.category')"
style="width: 200px" style="width: 200px"
> >
<el-option label="A类" value="A" /> <el-option label="A类客户(活跃)" value="A" />
<el-option label="B类" value="B" /> <el-option label="B类客户(不活跃)" value="B" />
<el-option label="C类" value="C" /> <el-option label="C类客户(待挽回)" value="C" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="$t('label.contactDept')"> <el-form-item :label="$t('label.contactDept')">
@ -66,7 +71,10 @@
<el-form-item :label="$t('label.years')" prop="contractYears"> <el-form-item :label="$t('label.years')" prop="contractYears">
<el-input-number v-model="form.contractYears" :min="1" :max="20" /> <el-input-number v-model="form.contractYears" :min="1" :max="20" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('label.maintenanceEndDate')" prop="maintenanceEnd"> <el-form-item
:label="$t('label.maintenanceEndDate')"
prop="maintenanceEnd"
>
<el-date-picker <el-date-picker
v-model="form.maintenanceEnd" v-model="form.maintenanceEnd"
type="date" type="date"
@ -99,9 +107,11 @@
:on-exceed="handleReportExceed" :on-exceed="handleReportExceed"
accept=".pdf" accept=".pdf"
> >
<el-button type="primary" plain>{{ $t('btn.upload') }}</el-button> <el-button type="primary" plain>{{ $t("btn.upload") }}</el-button>
<template #tip> <template #tip>
<div class="el-upload__tip">{{ $t('label.acceptanceReportTip') }}</div> <div class="el-upload__tip">
{{ $t("label.acceptanceReportTip") }}
</div>
</template> </template>
</el-upload> </el-upload>
</el-form-item> </el-form-item>
@ -110,9 +120,9 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :loading="submitting" @click="submit"> <el-button type="primary" :loading="submitting" @click="submit">
{{ isEdit ? $t('common.save') : $t('common.add') }} {{ isEdit ? $t("common.save") : $t("common.add") }}
</el-button> </el-button>
<el-button @click="goBack">{{ $t('common.cancel') }}</el-button> <el-button @click="goBack">{{ $t("common.cancel") }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@ -157,19 +167,35 @@ const form = reactive({
}); });
const rules = { const rules = {
name: [{ required: true, message: () => $t('placeholder.hospital'), trigger: "blur" }], name: [
{
required: true,
message: () => $t("placeholder.hospital"),
trigger: "blur",
},
],
customerCategory: [ customerCategory: [
{ required: true, message: () => $t('label.category'), trigger: "change" }, { required: true, message: () => $t("label.category"), trigger: "change" },
],
signDate: [
{ required: true, message: () => $t("label.signDate"), trigger: "change" },
], ],
signDate: [{ required: true, message: () => $t('label.signDate'), trigger: "change" }],
acceptDate: [ acceptDate: [
{ required: true, message: () => $t('label.acceptDate'), trigger: "change" }, {
required: true,
message: () => $t("label.acceptDate"),
trigger: "change",
},
], ],
contractYears: [ contractYears: [
{ required: true, message: () => $t('label.years'), trigger: "blur" }, { required: true, message: () => $t("label.years"), trigger: "blur" },
], ],
maintenanceEnd: [ maintenanceEnd: [
{ required: true, message: () => $t('label.maintenanceEndDate'), trigger: "change" }, {
required: true,
message: () => $t("label.maintenanceEndDate"),
trigger: "change",
},
], ],
}; };
@ -187,7 +213,9 @@ const buildHospitalFormData = () => {
if (value === undefined || value === null || value === "") return; if (value === undefined || value === null || value === "") return;
fd.append(key, value); fd.append(key, value);
}); });
const file = (reportFileList.value || []).map((f) => f.raw).filter(Boolean)[0]; const file = (reportFileList.value || [])
.map((f) => f.raw)
.filter(Boolean)[0];
if (file) fd.append("files", file); if (file) fd.append("files", file);
return fd; return fd;
}; };
@ -216,7 +244,7 @@ const submit = async () => {
await formRef.value.validate(async (valid) => { await formRef.value.validate(async (valid) => {
if (!valid) return; if (!valid) return;
if (!reportFileList.value?.length) { if (!reportFileList.value?.length) {
ElMessage.warning($t('label.acceptanceReport')); ElMessage.warning($t("label.acceptanceReport"));
return; return;
} }
submitting.value = true; submitting.value = true;
@ -224,10 +252,10 @@ const submit = async () => {
const fd = buildHospitalFormData(); const fd = buildHospitalFormData();
if (isEdit.value) { if (isEdit.value) {
await updateAdminHospital(route.params.id, fd); await updateAdminHospital(route.params.id, fd);
ElMessage.success($t('msg.hospitalEditSuccess')); ElMessage.success($t("msg.hospitalEditSuccess"));
} else { } else {
await createAdminHospital(fd); await createAdminHospital(fd);
ElMessage.success($t('msg.hospitalAddSuccess')); ElMessage.success($t("msg.hospitalAddSuccess"));
} }
router.replace("/admin/hospitals"); router.replace("/admin/hospitals");
} finally { } finally {

@ -2,7 +2,7 @@
<div class="hospital-list"> <div class="hospital-list">
<el-card shadow="never"> <el-card shadow="never">
<el-form :inline="true" :model="filters" class="filter-form"> <el-form :inline="true" :model="filters" class="filter-form">
<el-form-item :label="$t('placeholder.keyword')"> <el-form-item :label="$t('label.hospitalSearch')">
<el-input <el-input
v-model="filters.keyword" v-model="filters.keyword"
:placeholder="$t('placeholder.hospital')" :placeholder="$t('placeholder.hospital')"
@ -17,9 +17,9 @@
clearable clearable
style="width: 140px" style="width: 140px"
> >
<el-option label="A类" value="A" /> <el-option label="A类客户(活跃)" value="A" />
<el-option label="B类" value="B" /> <el-option label="B类客户(不活跃)" value="B" />
<el-option label="C类" value="C" /> <el-option label="C类客户(待挽回)" value="C" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -41,7 +41,9 @@
<el-table-column :label="$t('table.contact')" prop="contactPerson" width="100" /> <el-table-column :label="$t('table.contact')" prop="contactPerson" width="100" />
<el-table-column :label="$t('table.phone')" prop="contactPhone" width="140" /> <el-table-column :label="$t('table.phone')" prop="contactPhone" width="140" />
<el-table-column :label="$t('table.manager')" prop="managerName" width="140" /> <el-table-column :label="$t('table.manager')" prop="managerName" width="140" />
<el-table-column :label="$t('table.usageYears')" prop="contractYears" width="140" /> <el-table-column :label="$t('table.usageYears')" width="140">
<template #default="{ row }">{{ formatUsageYears(row.signDate) }}</template>
</el-table-column>
<el-table-column :label="$t('table.acceptanceReport')" width="140"> <el-table-column :label="$t('table.acceptanceReport')" width="140">
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -118,6 +120,18 @@ const filters = reactive({ keyword: "", category: "" });
const formatDate = (v) => (v ? dayjs(v).format("YYYY-MM-DD") : "—"); const formatDate = (v) => (v ? dayjs(v).format("YYYY-MM-DD") : "—");
// 使"XX"
const formatUsageYears = (signDate) => {
if (!signDate) return "—";
const start = dayjs(signDate);
const now = dayjs();
if (start.isAfter(now)) return "—";
const totalDays = now.diff(start, "day");
const years = Math.floor(totalDays / 365);
const days = totalDays - years * 365;
return `${years}${days}`;
};
const loadList = async () => { const loadList = async () => {
loading.value = true; loading.value = true;
try { try {

@ -30,7 +30,7 @@
<el-descriptions-item :label="$t('table.type')">{{ detail.serviceType }}</el-descriptions-item> <el-descriptions-item :label="$t('table.type')">{{ detail.serviceType }}</el-descriptions-item>
<el-descriptions-item :label="$t('table.submitter')">{{ detail.submitter }}</el-descriptions-item> <el-descriptions-item :label="$t('table.submitter')">{{ detail.submitter }}</el-descriptions-item>
<el-descriptions-item :label="$t('label.dept')">{{ detail.department }}</el-descriptions-item> <el-descriptions-item :label="$t('label.dept')">{{ detail.department }}</el-descriptions-item>
<el-descriptions-item :label="$t('table.time')">{{ detail.createdAt }}</el-descriptions-item> <el-descriptions-item :label="$t('table.time')">{{ detail.createdAt?.split('T').join(' ') }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<div class="section"> <div class="section">
@ -42,16 +42,16 @@
<div class="section-title">{{ $t('label.attachment') }} ({{ (detail.attachments || []).length }})</div> <div class="section-title">{{ $t('label.attachment') }} ({{ (detail.attachments || []).length }})</div>
<el-empty v-if="!detail.attachments?.length" :description="$t('orderDetail.noAttachment')" :image-size="80" /> <el-empty v-if="!detail.attachments?.length" :description="$t('orderDetail.noAttachment')" :image-size="80" />
<div v-else class="files"> <div v-else class="files">
<el-link <el-button
v-for="(f, i) in detail.attachments" v-for="(f, i) in detail.attachments"
:key="i" :key="i"
:href="getAttachmentUrl(f.fileName)" link
target="_blank"
type="primary" type="primary"
class="file-item" class="file-item"
@click="viewReport(f.filePath)"
> >
<el-icon><Document /></el-icon>{{ f.fileName }} <el-icon><Document /></el-icon>{{ f.fileName }}
</el-link> </el-button>
</div> </div>
</div> </div>
@ -70,6 +70,21 @@
</el-timeline> </el-timeline>
</div> </div>
</el-card> </el-card>
<el-dialog
v-model="previewVisible"
:title="$t('btn.preview')"
width="80%"
top="5vh"
destroy-on-close
:before-close="closePreview"
>
<iframe
v-if="previewUrl"
:src="previewUrl"
class="preview-iframe"
/>
</el-dialog>
</div> </div>
</template> </template>
@ -77,8 +92,8 @@
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { ArrowLeft, Document } from "@element-plus/icons-vue"; import { ArrowLeft, Document } from "@element-plus/icons-vue";
import { getAdminOrderDetail } from "@/service/modular/admin"; import { ElMessage } from "element-plus";
import { getAttachmentUrl } from "@/service/modular/upload"; import { getAdminOrderDetail, getUploadFile } from "@/service/modular/admin";
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -101,6 +116,34 @@ const loadDetail = async () => {
const goBack = () => router.back(); const goBack = () => router.back();
const goProcess = () => router.push(`/admin/orders/process/${route.params.id}`); const goProcess = () => router.push(`/admin/orders/process/${route.params.id}`);
const previewVisible = ref(false);
const previewUrl = ref("");
const cleanupPreviewUrl = () => {
if (previewUrl.value) {
URL.revokeObjectURL(previewUrl.value);
previewUrl.value = "";
}
};
const closePreview = () => {
previewVisible.value = false;
cleanupPreviewUrl();
};
const viewReport = async (attachmentPath) => {
if (!attachmentPath) return;
const fileName = String(attachmentPath);
try {
const blob = await getUploadFile(fileName);
cleanupPreviewUrl();
previewUrl.value = URL.createObjectURL(blob);
previewVisible.value = true;
} catch (e) {
ElMessage.error(e?.message || $t('msg.failed'));
}
};
onMounted(loadDetail); onMounted(loadDetail);
</script> </script>
@ -157,4 +200,9 @@ onMounted(loadDetail);
margin-top: 4px; margin-top: 4px;
} }
} }
.preview-iframe {
width: 100%;
height: 80vh;
border: 0;
}
</style> </style>

@ -394,7 +394,7 @@ const filters = reactive({
const loadHospitals = async () => { const loadHospitals = async () => {
try { try {
const res = await getAdminHospitals({ page: 1, pageSize: 1000 }); const res = await getAdminHospitals({ page: 1, pageSize: 1000 });
hospitals.value = res?.list.map || []; hospitals.value = res?.list || [];
} catch (_) { } catch (_) {
hospitals.value = []; hospitals.value = [];
} }

Loading…
Cancel
Save