167 Commits

Author SHA1 Message Date
aixianling
a3ea6d9c51 fix(xumu): 修复理赔申请页面的耳标选择和数据加载问题
- 优化耳标选择功能,将选中的耳标数据格式化后赋值给 detail.detailList
- 初始化 detail 对象时添加 list 属性,确保数据结构一致性
- 优化数据加载逻辑,处理可能的空值情况
- 调整页面布局和样式,提高可读性
2025-01-17 11:07:20 +08:00
aixianling
5fa30a0e0e refactor(xumu): 优化 AppLoanApply 添加页面代码
- 在 submit 方法中添加 earNumberList 数组,用于存储生物芯片耳号
- 优化代码结构,提高可读性和可维护性
2025-01-17 10:46:51 +08:00
aixianling
6b030791bb feat(AppInsuranceAudit): 优化耳标号选择和养殖场查询功能
- 在 add.vue 中增加 targetUser 字段,用于查询养殖场
- 修改 AiEartagPicker 组件,增加 valueKey 属性默认值
- 优化代码格式和样式
2025-01-17 10:36:41 +08:00
aixianling
d0922c5309 feat(AppLoanAudit): 优化审核页面功能和样式
- 新增 AiAudit 组件用于审批状态选择
- 调整审批状态字段的验证规则
- 优化页面布局和样式,统一代码格式
- 引入新的组件和 API 接口
2025-01-17 10:17:55 +08:00
aixianling
d46bb82086 refactor(xumu): 解除 AppInsuranceAudit 页面的代码注释
- 移除了 handleAudit 方法中对 API 提交逻辑的注释
- 恢复了代码的原始功能,允许用户提交保险审核申请
2025-01-17 10:11:21 +08:00
aixianling
1ead1b2e9a refactor(xumu): 优化保险申请和审核功能
- 修改保险产品显示逻辑,使用字典标签展示
- 临时注释审计提交功能
- 优化审计状态选择器配置
- 条件渲染保单订单号输入框
2025-01-17 10:10:54 +08:00
aixianling
c5f4f9b356 feat(AppInsuranceAudit): 新增产品类型字典支持
- 在 AppInsuranceAudit 组件中加载 productType 字典
- 在 add 组件中使用 productType 字典显示投保产品名称
- 优化 add 组件中的表单布局
2025-01-17 09:51:05 +08:00
aixianling
3fbde7f193 feat(AppInsuranceAudit): 添加审批组件并优化投保页面
- 新增 AiAudit 组件用于审批操作
- 在投保页面引入并使用 AiAudit 组件
- 优化投保页面布局和样式
2025-01-17 09:25:50 +08:00
aixianling
e5abd1514b feat(AppInsuranceAudit): 添加保险资料审批功能
- 在保险资料卡片中增加审批选项
- 添加审批状态选择框,包括"同意"和"不同意"两个选项
- 审批选项为必选项,增加了验证规则
2025-01-17 09:13:17 +08:00
aixianling
33ed4fe5d5 style(xumu): 格式化 AppInsuranceAudit/add.vue 文件
- 使用 ESLint 规则格式化了代码
- 调整了缩进、空格和换行
- 修复了一些小的语法问题
2025-01-17 09:10:23 +08:00
aixianling
4051b31101 refactor(xumu): 优化理赔申请列表展示逻辑
- 根据 remarks 字段判断是否可申请理赔,而非拼接 status 和 auditStatus
- 简化 permit 字段的使用,直接用于判断是否显示理赔按钮
2025-01-16 10:49:13 +08:00
aixianling
94b1788e8c refactor(xumu): 更新保险申请审核状态字典
- 将审核状态字典从 'auditStatus' 修改为 'insuranceAuditStatus'- 在 add.vue、AppInsuranceApply.vue 和 list.vue 文件中进行了相应更新
2025-01-16 09:58:18 +08:00
aixianling
7944a1a8e3 refactor(xumu): 优化保险申请和审核功能
- 修改身份证和营业执照等文件的上传规则,要求在添加或编辑时必须上传
- 在保险申请提交时添加耳号列表参数
- 修改保险审核列表的审批按钮显示逻辑,仅当状态为"01"时显示
2025-01-16 09:36:38 +08:00
aixianling
17b28025d8 fix(AiEartagPicker): 修复确认选择后原始数据丢失的问题
- 添加 origin 数组以保存初始获取的数据
- 修改 handleConfirm 方法,确保选中的数据来自原始数据
2025-01-15 15:59:58 +08:00
aixianling
0a6b77943f feat(xumu): 优化耳标号选择功能
- 在 AppInsuranceApply 和 AppLoanApply 组件中添加 value-key 属性
- 在 AiEartagPicker 组件中增加对 valueKey 属性的支持- 优化 AiEartagPicker 组件的样式和布局
2025-01-15 15:51:09 +08:00
aixianling
a599566b20 refactor(535): 调整耳标照片上传逻辑
- 将 earPhoto 属性改为 picture
- 添加 upload 配置项,限制上传数量为 1- 优化 columns 数据结构,移除冗余定义
2025-01-14 11:58:59 +08:00
aixianling
1a7031342f refactor(536): 优化 AiTable 组件的输入框使用
- 将 el-input 替换为 ai-input 组件,统一输入框样式
- 为数字类型输入框添加 type="number" 属性,提高输入准确性
2025-01-14 11:55:14 +08:00
aixianling
516a8edb5e refactor(537): 优化体重输入框类型
- 将体重输入框的类型从默认改为 number,确保只能输入数字
-移除 v-model.number 修饰符,因 type="number" 已
2025-01-14 11:52:50 +08:00
aixianling
f4e2100882 fix(542): 修复添加页面接口参数和布局
- 在 AppDeathManage 和 AppOutManage 的 add.vue 文件中,为 ai-eartag-remote 组件添加 class="row",优化布局
- 在 AppOutManage 的 add.vue 文件中,修正出栏接口参数 outTime 之前错误使用 deathTime
- 在 AiEartagRemote 组件中,简化 API 请求参数结构
2025-01-14 11:50:45 +08:00
aixianling
9f6998e042 refactor(BUG 541): 优化治疗时间初始化
- 在 AppImmunityManage 和 AppTreatmentManage 组件中,移除了治疗时间的自动初始化
- 在 AppTreatmentManage 组件中,将治疗时间初始化为当前时间的字符串格式
2025-01-14 11:37:19 +08:00
aixianling
f730850d93 feat(BUG 540): 新增免疫登记功能
- 添加免疫登记对话框和相关表单
- 实现免疫信息的添加和更新功能
- 优化表单样式,调整标签宽度和对齐方式
2025-01-14 11:28:46 +08:00
aixianling
0ef83a945f refactor(BUG 538): 调整字典数据加载时机
- 移除 add.vue 中的 created 钩子中的字典加载逻辑
- 在 AppWeightManage.vue 中添加 created 钩子,统一处理字典加载
2025-01-14 11:26:01 +08:00
aixianling
cbfd68f50c refactor(BUG 539): 为药物和疫苗的剂量添加单位 ml- 在 AppBreedArchive、AppImmunityManage 和 AppTreatmentManage组件中
- 将"药量"列的标题修改为"药量(ml)"
- 此修改提高了数据的准确性和可读性
2025-01-14 11:10:02 +08:00
aixianling
7762eb6d24 fix(xumu): 修复死亡管理和出栏管理中的图片无法回显的问题
- 在 AppDeathManage 和 AppOutManage 组件中,将图片数据转换为 JSON 字符串后提交
- 修复了后端接口对图片数据的接收问题
2025-01-09 16:43:02 +08:00
aixianling
db1a323921 refactor(xumu): 移除表单照片的验证规则
移除了 AppDeathManage 和 AppOutManage 组件中照片上传的验证规则。这个改动统一了照片上传的规则,提高了代码的一致性和可维护性。

受影响的主要文件:
- project/xumu/AppDeathManage/add.vue
- project/xumu/AppOutManage/add.vue

具体改动:
- 删除了 formImages 数组中每个对象的 rules 属性
- 优化了部分代码格式,如空格和换行
2025-01-09 16:39:23 +08:00
aixianling
1a61475f57 fix(xumu): 修复耳标号查询接口的参数传递方式- 将参数从第三个参数移到第二个参数位置,以符合 Axios库的正确用法
-这个修改可以解决耳标号查询功能不工作的问题
2025-01-09 16:36:36 +08:00
aixianling
356e6438e0 fix(xumu): 修复保险申请和治疗管理页面的数据加载问题- 在 AppInsuranceApply 组件中,将 detailList 的默认值改为 detailList || [],以确保数据正确加载
- 在 AppTreatmentManage 组件中,将 detailList 的
2025-01-09 15:48:02 +08:00
aixianling
5fe82a68ef fix(xumu): 修复耳标号查询功能
- 更新 API 请求参数,添加 pageSize 和 pageNum
- 修改数据处理逻辑,使用 records[0] 而不是直接使用 data
- 优化代码格式,删除多余的空格和换行
2025-01-09 15:41:39 +08:00
aixianling
9b6e02810a fix(xumu): 修复死亡管理和淘汰管理页面的查看模式显示逻辑
- 在 AppDeathManage 和 AppOutManage 组件中,为死亡日期、死亡原因、淘汰日期和淘汰原因字段添加 v-else指令
- 这样在查看模式下只显示文本,而不显示输入框
2025-01-09 15:11:27 +08:00
aixianling
f0a32ad868 feat(xumu): 添加远程耳标号查询功能
- 在 AppDeathManage 和 AppOutManage 组件中集成 AiEartagRemote 组件
- 实现耳标号远程查询和自动填充功能
-优化用户输入体验,提高数据准确性
2025-01-09 15:09:28 +08:00
aixianling
ce64bfd54d style(xumu): 优化 AppTreatmentManage 组件中的表单项
- 为 formItems 循环渲染的表单项添加 key 属性,以提高渲染性能和稳定性
2025-01-09 14:51:58 +08:00
aixianling
a53d25fae2 refactor(xumu): 为 el-date-picker 组件添加 value-format 属性
- 在多个组件中,为 el-date-picker 组件添加 value-format="yyyy-MM-dd HH:mm:ss" 属性
- 这样做可以确保日期时间的格式一致性,避免潜在的时间格式问题
2025-01-09 14:39:56 +08:00
aixianling
08d5e0e4fd fix(AiEartagPicker): 修复耳标选择器组件
- 优化代码格式和缩进
- 修复 penId 属性未使用的问题
- 改进 getEartag 方法,确保正确处理返回数据
- 更新模板中的点击事件和样式
2025-01-09 11:55:59 +08:00
aixianling
b23f8325f4 refactor(xumu): 优化字典数据加载和组件结构
- 移动字典数据加载到父组件 AppDeathManage
- 修复死亡日期和死亡原因的验证规则
- 优化死亡原因的显示逻辑
- 移除子组件中的重复字典数据加载
2025-01-09 11:43:16 +08:00
aixianling
0adb2b1486 feat(AppWeightManage): 更新体重信息显示逻辑
- 在"最近称重时间"字段中,优先显示 todayCreateTime,若为空则显示 lastCreateTime
- 在"最新体重(公斤)"字段中,优先显示 todayWeight,若为空则显示 lastWeight
2025-01-09 11:37:26 +08:00
aixianling
38082397a9 style(xumu): 将只读属性改为禁用
- 在 AppInsuranceAudit 和 AppLoanAudit 组件
2025-01-09 10:40:12 +08:00
aixianling
4d527dc8ec feat(BUG 534 533): 添加身份证远程查询功能
- 在 AppInsuranceAudit 和 AppLoanAudit 组件中集成 AiIdcardRemote 组件
- 实现身份证信息查询和自动填充功能
- 优化用户信息获取逻辑,提高用户体验
2025-01-09 10:38:41 +08:00
aixianling
d90aca62e4 feat(AppAccountManage): 添加身份证号输入框
- 在表单中新增身份证号字段,设置为必填项
- 使用 ai-input组件替代 el-input 组件,以适应特殊输入需求
2025-01-09 09:16:12 +08:00
aixianling
44f11be05c fix(BUG 517): 修改账号管理对话框标题
- 将编辑状态下的对话框标题从"功能分配"改为"编辑账号"
- 优化用户体验,使对话框标题与操作内容更加匹配
2025-01-08 16:27:59 +08:00
aixianling
7a21ab3804 feat(AppAccountConfigManage):BUG 519 修改对话框标题
- 将对话框标题从"认证材料"修改为"场地配置"
- 此修改提高了界面文案的准确性,更好地反映了对话框内容的主题
2025-01-08 16:24:22 +08:00
aixianling
6bb4802f15 refactor(store):BUG 524 530 重构 signOut 逻辑
- 移除了 state 中的冗余代码
-优化了 signOut mutation 的逻辑结构
- 使用 Promise 处理异步操作,提高了代码可读性
- 简化了登录页面 URL 的拼接方式
2025-01-08 16:22:44 +08:00
aixianling
cfc8f3c8e0 feat(AppSign):BUG 525 添加登录失败错误提示
- 在登录接口返回数据中增加错误信息展示
- 使用 $message.error 方法显示错误提示
2025-01-08 16:01:46 +08:00
aixianling
3f26b8b6df refactor(AppRoleRightsManager): BUG 526 优化角色用户展示和编辑功能- 重新设计角色用户列表的展示方式,使用标签替代原有格式
- 调整角色名称列宽,提高可读性- 编辑角色时增加角色类型参数,可能为后续流程使用
2025-01-08 16:00:05 +08:00
aixianling
350ca644cf refactor(AppAccountManage):BUG 527 将“账户”改为“账号”
- 修改了 AppAccountManage 组件中的一个字段标签
- 将“账户”改为“账号”,以更准确地描述用户登录凭证
2025-01-08 15:43:20 +08:00
aixianling
99b507657f refactor(xumu): 优化基础信息输入逻辑
- 将耳标号输入框的 v-model绑定到新的 filter 属性- 在输入框中添加 @keyup.enter.native 事件,调用 getDetail(filter) 方法
- 优化了数据结构和事件处理方式,提高了用户体验
2025-01-08 15:23:26 +08:00
aixianling
2e93f1465f refactor(xumu): 重构耳标选择器组件
- 新增计算属性 api,用于生成请求 URL
- 将 watch 目标从 action 改为 api
- 优化 getEartag 方法,使用计算属性 api 替代手动构建 URL
2025-01-08 15:18:37 +08:00
aixianling
07a4d0637f refactor(xumu): 优化体重录入对话框样式和功能
- 调整对话框宽度为 50vw,提高可读性和美观性
- 为日期选择器添加 value-format 属性,确保日期格式一致性
- 将普通输入框替换为自定义的 ai-input 组件,提升用户体验
2025-01-08 15:05:55 +08:00
aixianling
66a721c1eb feat(xumu): 优化耳标编辑功能
- 在提交时,如果处于编辑状态,将当前详情数据构建成 detailList 数组- 保留原有逻辑,确保新增和编辑操作都能正常进行
2025-01-08 15:02:37 +08:00
aixianling
5efd2a1d3f fix(AppEarTag): 修复耳标添加页面品种和类别显示问题
- 在 etAdd.vue 文件中,将品种和类别的显示逻辑从直接显示值改为通过字典获取标签
- 使用 dict.getLabel 方法确保在非编辑模式下正确显示品种和类别的中文名称
2025-01-08 14:57:03 +08:00
aixianling
d0c447bb41 feat(xumu): 新增死亡审核功能
- 添加死亡审核列表页面,支持筛选、搜索和导出功能
- 实现死亡审核详情页面,包括基础信息、死亡信息和审核信息
- 集成字典加载和用户信息获取
- 优化表格展示和表单验证
2025-01-08 14:51:39 +08:00
aixianling
ebc143a052 refactor(xumu): 修改死亡录入表单字段
- 将 "createTime" 字段重命名为 "deathTime",以更准确地表示死亡日期
- 更新相应的标签、规则和数据显示
2025-01-08 14:51:18 +08:00
aixianling
70b77669ab refactor(xumu): 移除未使用的组件导入
- 删除了 AiEartagPicker 组件的导入语句
- 移除了 components 中的 AiEartagPicker 注册
2025-01-08 14:42:40 +08:00
aixianling
7393bcbc6e refactor(xumu): 优化列表组件的列定义
- 移除了 AppDeathManage 列表中不必要的 prop 属性
- 为 AppOutAudit 列表添加了序号列
- 统一了两个列表中养殖场列的格式
2025-01-08 14:37:45 +08:00
aixianling
2abdab1b20 refactor(xumu): 修正 AppOutAudit 组件的名称
- 将组件名称从 AppSellAudit 修改为 AppOutAudit
- 更新相应的样式类名和模板选择器
2025-01-08 14:33:49 +08:00
aixianling
d1a8800a9b feat(AppBreedArchive): 优化档案查询页面
- 新增免疫记录、治疗记录、淘汰记录、死亡记录、设备检测、贷款信息、保险信息等模块
-优化列表展示方式,使用 tabs进行分类展示
-调整列表列宽和对齐
2025-01-08 10:14:51 +08:00
aixianling
ced505b585 feat(xumu): 新增电子档案功能
- 添加 AppBreedArchive 组件作为电子档案的主入口
- 实现档案列表页面,包括筛选、搜索和导出功能
- 实现档案详细信息页面,展示基础信息和记录
- 集成 vuex 状态管理,支持用户信息和字典数据的全局共享
2025-01-07 18:02:42 +08:00
aixianling
25010984f9 feat(AppOutAudit): 增加字典加载项
- 在 created 钩子中添加了 "outReason" 字典加载项
- 此修改扩展了 AppOutAudit 组件的字典加载功能,支持新的审核原因字典
2025-01-07 17:02:22 +08:00
aixianling
75d5971e42 feat(xumu): 新增淘汰审核功能
- 添加 AppOutAudit组件作为淘汰审核的主入口- 实现 add.vue 页面,用于展示和编辑淘汰审核详情
- 创建 list.vue 页面,用于展示淘汰审核列表
- 在 AppOutManage 中将淘汰日期字段从 createTime改为 outTime
2025-01-07 16:50:48 +08:00
aixianling
f5085b1cb3 style(AiEartagPicker): 调整选择器样式- 为 el-select 添加相对定位,以修复潜在的样式问题- 调整 el-input__prefix 的 z-index,以确保正确的层级关系 2025-01-07 16:29:12 +08:00
aixianling
baa5c3124a refactor(xumu): 优化 AppWeightManage 添加接口调用方式- 修改了 AppWeightManage/add.vue 文件中的 submit 方法
- 将 post 请求的参数传递方式从 body 改为 params
- 这种改动可能会影响后端接口的处理逻辑,需要相应调整
2025-01-07 16:24:00 +08:00
aixianling
d1ae5ff2c7 feat(AppEarTag): 添加养殖场、养殖舍和养殖栏的必填验证
- 在 etAdd.vue 文件中,为养殖场、养殖舍和养殖栏的表单项添加了必填验证规则
- 验证规则仅在 isAdd 变量为真时生效,确保在添加模式下进行验证
- 此修改提高了数据输入的完整性和用户体验
2025-01-07 16:22:31 +08:00
aixianling
962d2a4068 fix(xumu): 修复删除功能使用错误的 HTTP 方法
-将 handleDelete 方法中的 HTTP 请求从 delete 改为 post
-此修改确保了删除操作使用正确的 HTTP 方法,提高了代码的正确性和安全性
2025-01-07 16:20:22 +08:00
aixianling
3adbb68466 refactor(AppDeployCustom): 重构新增页面布局和样式
-优化了表单布局,使用 grid 系统替代原有的 el-row 和 el-col
- 移除了多余的模板代码,简化了结构
- 统一了表单项的样式和间距
- 调整了部分组件的属性,提高了代码可读性
2025-01-07 10:36:29 +08:00
aixianling
7f086f8b83 refactor(xumu): 优化 AppAccountConfigManage 组件中对话框关闭逻辑
- 在对话框关闭事件中添加了 getTableData 方法调用
- 优化了数据重载和状态重置的逻辑
2025-01-07 10:19:26 +08:00
aixianling
95689baff9 feat(AppAccountConfigManage): 优化认证材料对话框样式和功能- 调整对话框宽度为 50vw,使界面更加响应式
- 为删除按钮添加 v-if 指令,仅在没有子节点时显示
- 为删除按钮添加 del 类,以应用特定样式
- 在 utils.js 中添加 $isEmpty函数,用于检查对象是否为空
2025-01-07 10:17:59 +08:00
aixianling
d39650eca5 feat(authList): 添加搜索功能并优化表格数据获取
- 移除了未使用的 AiSelect 组件导入
- 新增了 search 属性的监听器,实现深度监听- 当搜索条件变化时,自动重置页码并重新获取表格数据
2025-01-07 10:05:34 +08:00
aixianling
4f44f50db5 refactor(AppAccountManage): 优化账号删除功能
- 修改 handleDelete 方法,使其接收 row 参数
- 根据账号认证和配置状态,动态显示删除确认提示
- 提升用户体验,减少误操作风险
2025-01-07 10:04:23 +08:00
aixianling
27a2052241 feat(AppAccountManage): 重置密码功能并优化账号管理页面- 在操作列中添加重置密码按钮
- 优化表格列配置,提高可读性
- 调整表单字段名称,提高用户体验
- 移除不必要的密码输入框
2025-01-07 09:55:05 +08:00
aixianling
072390dc13 refactor(ui): 修改导航栏下拉菜单选项文字
- 将"只留当前页"选项修改为"关闭其他",以提升用户体验和可理解性
2025-01-07 09:37:23 +08:00
aixianling
385447d3b4 refactor(AppRoleRightsManager):优化角色标记选择和编辑功能- 在编辑角色时添加 type 参数,确保角色类型正确保存
- 在新增角色页面将角色标记设置为必选项,提高数据完整性- 优化角色保存逻辑,移除冗余的 if 语句
- 重构 back 方法,增加可选参数以提高灵活性
2025-01-07 09:20:48 +08:00
aixianling
532ceeb5e7 feat(AppMenuManager): 优化菜单配置功能
- 修改添加下级菜单的条件,仅当数据没有 component 时显示
- 移除菜单类型选择,简化配置界面
- 调整菜单图标、应用和路径为必填项
- 优化权限码和排序字段的显示逻辑
2025-01-07 09:05:48 +08:00
aixianling
2275590461 feat(scanApps): 增加扫描 core 工程的功能
- 在 start 函数中添加了对 VUE_APP_CORE 环境变量的检查
- 如果 VUE_APP_CORE 存在,将 'packages/core' 添加到扫描范围中
2025-01-07 08:57:23 +08:00
aixianling
d25b082eff fix(AppMenuManager): 修复添加下级菜单显示逻辑
-将添加下级菜单的显示条件从 data.type<2 修改为 data
2025-01-03 18:01:53 +08:00
aixianling
6feaeb22bf feat(x): 新增出栏审核功能
- 添加出栏审核相关的三个子页面:add、list 和 AppSellAudit
- 实现出umu栏审核的添加、列表展示和审批功能
- 集成字典加载、权限控制和用户信息获取
- 优化表格数据加载和搜索过滤
2025-01-03 17:35:41 +08:00
aixianling
dad169496c feat(xumu): 新增出栏申请功能
- 添加 AppSellApply 组件作为出栏申请的主入口
- 实现出栏申请列表页面,包括筛选、导出等功能
- 实现出栏申请详情和编辑页面,包括基础信息、标的信息、解押材料等
- 集成耳标选择器组件,用于选择投保标的
2025-01-03 17:12:03 +08:00
aixianling
3eef1eeb3c feat(x): 新增贷款审核和页面
- 实现贷款申请功能

- 添加贷款审核相关的组件、审批和查看功能
-umu集成养殖场、贷款银行和产品选择
- 支持耳标号扫描和选择
- 添加贷款合同号和资料上传功能- 实现审核状态和结果的展示
2025-01-03 16:35:38 +08:00
aixianling
f108a61293 feat(loan): 适配贷款业务流程
- 修改投保单号为贷款合同号
- 更新相关 API 路径
-调整表格列和搜索条件以适应贷款业务
- 优化耳标号筛选功能
- 更新字典加载项
2025-01-03 14:52:43 +08:00
aixianling
e1231d408c refactor(xumu): 重构理赔审核页面
- 修改页面布局和样式
- 优化理赔材料上传功能
- 添加审核信息相关字段
- 调整投保对象和审核记录的展示方式
- 优化搜索和筛选功能
2025-01-03 11:18:15 +08:00
aixianling
c073c8d0bd feat(xumu): 新增理赔审核功能
- 添加理赔审核页面组件 AppClaimAudit
- 实现理赔申请列表页面,包括搜索、导出和查看功能
- 新增理赔申请详情页面,支持理赔材料上传和提交
- 集成耳标选择器组件,用于选择理赔标的
- 优化页面样式和布局,提升用户体验
2025-01-02 18:00:48 +08:00
aixianling
437ae1425c feat(xumu): 新增理赔申请和贷款申请功能
- 添加理赔申请和贷款申请的路由、页面组件和相关逻辑
- 实现理赔申请和贷款申请的数据获取、表单提交和审核流程
- 优化耳标选择器组件,支持在不同场景下的使用
- 调整图片上传组件,增加只读模式和预览功能
2025-01-02 15:30:44 +08:00
aixianling
b6dcddac6f feat(xumu): 新增投保审批功能
- 添加投保审批相关的页面和组件
- 实现投保审批的新增、编辑和审核功能- 集成投保审批的列表展示和搜索功能
-优化投保审批表单的验证和提交逻辑
2024-12-31 15:18:45 +08:00
aixianling
e283d6a650 refactor(xumu): 移除 AppInsuranceApply 组件中的 AiCard 引用
- 删除了 AiCard 组件的导入语句- 从 components 中移除了 AiCard 的注册

这个改动减少了组件的依赖,如果页面中没有使用 AiCard,这个改动是有益的。如果有使用 AiCard,需要检查并恢复相关功能。
2024-12-31 10:46:01 +08:00
aixianling
230f8c3b80 feat(AppInsuranceApply): 更新投保申请功能
- 修改证件信息上传要求,增加身份证、营业执照等照片- 添加审核信息查看功能,包括审核状态、时间和审核人等信息
- 增加保单订单号和保单资料查看功能
- 优化表单布局,使用 el-form 组件
- 添加保存草稿和保存并提交按钮
- 引入 AiCard组件,提升用户体验
2024-12-31 09:54:14 +08:00
aixianling
382be3d32f feat(AppInsuranceApply): 添加保险申请材料上传功能
- 移除了未使用的组件导入
- 新增了 formImages 数组,定义了需要上传的图片类型和验证规则
- 在证件信息卡片中添加了图片上传功能
- 使用 ai-uploader 组件实现图片上传- 添加了图片预览功能
2024-12-30 18:04:01 +08:00
aixianling
a7c3b22f87 feat(xumu): 新增投保申请功能
- 添加投保申请列表和新增页面
- 实现养殖场、承保公司、保险产品等选择功能
- 添加投保对象选择功能,支持耳标号选择
- 实现投保申请数据的查询、新增、编辑和删除
-优化表格组件,增加隐藏操作列的功能
- 更新耳标号选择组件,支持自定义请求地址
2024-12-30 18:01:13 +08:00
aixianling
3585cceca8 refactor(ui): 优化搜索栏组件的样式和布局
-移除了 AiPullDown 组件的绝对定位样式
- 调整了 AiSearchBar 组件的网格布局和样式
- 优化了展开和收缩功能的实现方式
- 修复了一些潜在的样式冲突和显示问题
2024-12-30 10:29:07 +08:00
aixianling
908e65f136 feat(xumu): 新增死亡登记功能
- 添加死亡登记列表页面和新增/编辑页面
- 实现死亡登记数据的查询、新增、编辑和删除功能
-集成审批流,支持再次提交审批
- 优化搜索功能,支持多条件筛选
2024-12-27 18:06:36 +08:00
aixianling
7828af24fd feat(xumu): 新增淘汰登记功能
- 添加 AppOutManage组件,包含新增和列表页面- 实现淘汰登记的新增、编辑、审核等功能
-集成字典加载、表格展示、搜索筛选等模块
-优化表单验证和图片上传功能
2024-12-27 18:00:52 +08:00
aixianling
185630ad2c refactor(xumu): 移除 AppTreatmentManage 列表页搜索框中不必要的字典属性
- 删除了原场耳标号、生物芯片耳标号和电子耳标号输入框中的 dict 属性
- 优化了代码结构,
2024-12-27 16:49:04 +08:00
aixianling
67c4c8032e style(layout): 调整搜索栏左侧区域高度
- 将 .left 类的 height 属性从3px
- 此调整旨在 32px 修改为 3优化搜索栏的视觉效果和布局
2024-12-27 16:48:33 +08:00
aixianling
39ce5404c1 feat(ui): 优化搜索栏布局和功能
- 为 AiInput 组件添加 placeholder 属性
- 重构 AiSearchBar 组件布局,使用 grid 替代 flex- 调整搜索栏样式
2024-12-27 16:45:43 +08:00
aixianling
9f02c2d011 feat(xumu): 新增治疗登记功能
- 添加治疗登记管理模块,包括新增、编辑和查看治疗记录
- 实现治疗登记列表页面,支持筛选和导出功能
- 优化牲畜选择界面布局
- 初始化治疗登记日期为当前日期
2024-12-27 15:18:47 +08:00
aixianling
9d369e9a0f refactor(xumu): 修改免疫导出表名称
- 将导出表名称从"称重导出表"修改为"免疫登记导出表"
- 优化导出文件命名,使其更加准确地反映内容
2024-12-27 14:31:09 +08:00
aixianling
19a9486f48 feat(xumu): 新增免疫登记功能
- 添加免疫登记管理页面登记的新增、编辑和删除功能
- 集成耳标和相关组件
- 实现免疫号选择器组件
- 优化称重管理功能,修复相关问题
2024-12-27 12:12:02 +08:00
aixianling
ab9ec446c7 fixumu(x导出接口 URL 从 "/api导出接口错误
- 将): 修复称重
2024-12-27 09:49:40 +08:00
aixianling
0d3d8f23bb feat(AppWeightManage): 添加删除功能
- 重构 handleDelete 方法,通过 API 删除数据
- 更新删除按钮点击事件,传入正确的 id 参数
- 删除成功后重新获取数据,保持列表同步
2024-12-26 18:16:00 +08:00
aixianling
971742b392 refactor(xumu): 优化体重录入表单
- 移除冗余的 el-form 标签
- 调整编辑按钮样式
- 优化体重录入对话框布局
- 添加表单验证规则
2024-12-26 18:13:37 +08:00
aixianling
63a425cf77 feat(AppWeightManage): 新增体重录入功能
- 在 AppWeightManage 模块中添加体重录入功能
- 新增表单字段:办理时间、体重(公斤)
- 实现编辑功能,点击编辑按钮弹出对话框,预填当前行数据
- 优化删除按钮,移除冗余的 @click 事件
- 在对话框关闭时重置表单数据
2024-12-26 18:11:19 +08:00
aixianling
e11d504f18 feat(xumu): 新增体重管理功能
- 添加体重管理列表和新增页面
- 实现体重数据的查询、新增和编辑功能- 优化表格展示和搜索过滤
2024-12-26 18:01:44 +08:00
aixianling
0e35945d4a refactor(xumu): 移除未使用的组件导入
- 删除了未使用的 AiDownload 组件导入- 移除了 components 中的 AiDownload 注册
- 优化了代码结构,提高了代码的可读性和维护性
2024-12-26 17:16:19 +08:00
aixianling
0762067bec feat(AppEarTag): 添加耳标登记导出功能
- 在 etList 组件中引入 AiDownload组件
- 在搜索栏中添加导出按钮,实现耳标登记数据导出功能
2024-12-26 17:15:57 +08:00
aixianling
290ecb6823 (xfeatumu): 优化耳标登记功能
- 修改 etAdd组件,增加耳标录入的增删功能
- 更新 etList
- 重构 AiTable 组件组件,使机构选择框只读,优化表格列的渲染逻辑
2024-12-26 11:57:58 +08:00
aixianling
ee15427e88 feat(ear-tag): 新增耳标登记功能
- 添加耳标登记页面,支持新增和编辑功能
- 实现养殖场、养殖舍、养殖栏的级联选择- 添加耳标信息的详细录入,包括生物芯片耳标号、电子耳标号等
-优化列表页面,增加搜索和筛选功能
- 重构表格组件,支持更多列类型和操作
2024-12-25 12:12:47 +08:00
aixianling
06fa7b636e feat(xumu): 新增耳标 组件作为耳标登记的主登记功能
- 添加 AppEarTag入口
- 实现 etAdd组件用于耳标添加和编辑- 创建 etList 组件用于显示耳标列表
- 集成搜索栏、表格和分页组件
- 添加用户认证状态和耳标信息的获取及展示- 实现耳标添加、编辑
2024-12-24 18:01:24 +08:00
aixianling
225c0088e1 feat(ui): 重构高级搜索组件并优化布局
- 重构 AiPullDown 组件,使用 v-model 实现双向绑定
- 优化 AiSearchBar 组件样式和布局
- 调整 AiPage 组件中的滚动条样式
2024-12-24 17:08:43 +08:00
aixianling
ae83152271 refactor(xumu): 重构 AppAuthManage 组件
- 移除 certificates 常量的单独定义
- 将 certificates 数据移至 data 函数内部
-优化代码结构,提高组件的可维护性
2024-12-24 16:15:04 +08:00
aixianling
b1c0beb8f6 feat(AppAuthManage): 新增认证审核功能
- 添加认证审核列表和详细页面组件
- 实现认证材料上传和审核功能
- 优化页面布局和样式
- 增加审核状态和备注说明功能
2024-12-24 16:04:18 +08:00
aixianling
4d96417661 refactor(components): 修改用户中心路由名称
- 将路由名称从 "个人中心" 修改为 "AppUserInfo"
- 保持了代码的逻辑不变,仅更新了路由的名称
2024-12-23 14:39:40 +08:00
aixianling
711db33df3 refactor(home): 重构首页配置和路由处理
- 修改首页路由动态加载逻辑
- 优化滑动导航组件中的菜单处理
- 调整 Vuex store 中的 mods 数据处理方式
- 重构 main.js 中的 HomePage 配置
2024-12-23 14:38:32 +08:00
aixianling
b7c0350134 feat(config): 添加自定义首页配置并优化导航功能
-增加自定义首页配置项,支持设置不同的首页组件
-优化导航功能,添加固定首页到导航栏
- 重构路由生成逻辑,支持自定义签到页和首页
- 更新组件以适应新的导航结构
2024-12-23 14:19:00 +08:00
aixianling
9ff89c19f3 feat(build): 优化路由配置并添加 404 页面
- 在主路由中添加 mainEntry组件作为主入口
- 在主入口下添加所有扫描路由作为子路由
- 添加 404 页面作为未匹配路由的处理- 修改 mainEntry 组件,使用 router-view 替代动态组件
2024-12-23 10:45:38 +08:00
aixianling
4684952973 refactor(router): 优化路由生成逻辑和导航组件
- 修改路由生成逻辑,使用 name 作为 path,解决 path 未定义问题- 更新 sliderNav 组件中的路由跳转逻辑,使用 path 方式跳转
- 优化 url 参数处理,支持查询字符串
-调整路由配置模板,统一格式
2024-12-23 10:20:19 +08:00
aixianling
4ead05b251 refactor(xumu): 调整 API 请求路径和端口- 移除了 axios.js 中对 xumu 环境的特殊处理逻辑
- 更新了 .env.xumu 文件中的 API 地址和端口号
2024-12-23 09:48:13 +08:00
aixianling
cf62d2f508 fix(router): 修复首页未获取用户信息的问题
- 在路由守卫中增加判断,当进入 Home 页面时,如果用户已登录但未获取用户信息,则自动获取用户信息
- 优化了用户体验,确保用户在访问首页时能够及时加载个人相关数据
2024-12-23 09:20:05 +08:00
aixianling
a894db0144 fix(AppSign): 更新 OAuth 令牌请求 URL
- 将请求 URL 从 "/oauth/token" 修改为 "/api/oauth/token"
- 此修改可能是为了适应后端 API路径的更改
2024-12-23 08:46:31 +08:00
aixianling
a748a8b337 refactor(xumu): 统一接口添加 /api 前缀
- 在多个组件中,将请求路径从 "/user/config/page" 修改为 "/api/user/config/page"- 将请求路径从 "/siteUser/querySiteByUserId" 修改为 "/api/siteUser/querySiteByUserId"
- 将请求路径从 "/siteUser/del" 修改为 "/api/siteUser/del"
-将请求路径从 "/siteUser/add" 修改为 "/api/siteUser/add"
- 将请求路径从 "/user/auth/page" 修改为 "/api/user/auth/page"
- 将请求路径从 "/user/auth/update" 修改为 "/api/user/auth/update"
- 将请求路径从 "/user/update-status" 修改为 "/api/user/update-status"

通过添加 /api前缀,统一了接口请求路径的格式,提高了代码的一致性和可维护性。
2024-12-20 16:54:24 +08:00
aixianling
37a6cb3457 build(xumu): 更新 API 服务器地址
- 将 VUE_APP_API 的值从 http://192.168.1.251:19998 修改为 http://192.168.1.87:19998- 此修改更新了与前端应用通信的后端服务器地址
2024-12-20 10:58:02 +08:00
aixianling
045449331f feat(xumu): 完善认证材料页面并优化相关功能
- 新增认证材料页面,支持查看和审核用户提交的认证信息- 优化 axios 配置,修复 URL 替换逻辑
- 更新表格操作按钮,根据认证状态显示不同选项
- 重构页面布局组件,增加返回按钮和内容字符串属性
2024-12-19 18:04:12 +08:00
aixianling
4c72bd2ac9 refactor(xumu): 优化 AppAccountConfigManage 组件中对话框关闭逻辑
- 在关闭对话框按钮的点击事件中添加 getTableData() 方法调用
-这样可以在关闭对话框后立即刷新表格数据,确保界面显示与实际数据保持同步
2024-12-19 16:28:55 +08:00
aixianling
b3942f5822 feat(xumu): 添加认证审核功能
- 新增 AppAuthManage组件作为认证审核的路由组件- 添加 authAdd 和 authList两个子组件分别用于添加认证和认证列表
- 实现了认证列表的展示、搜索和分页功能
- 添加了认证材料的查看和上传功能
- 优化了表单布局,增加了 row 类样式
2024-12-19 16:28:24 +08:00
aixianling
0b6bc910c4 feat(xumu): 添加认证审核功能
- 新增 AppAuthManage组件作为认证审核的路由组件- 添加 authAdd 和 authList两个子组件分别用于添加认证和认证列表
- 实现了认证列表的展示、搜索和分页功能
- 添加了认证材料的查看和上传功能
- 优化了表单布局,增加了 row 类样式
2024-12-18 18:03:35 +08:00
aixianling
a4233d5f2c feat(xumu): 添加数据字典 2024-12-18 15:09:42 +08:00
aixianling
31c874e1ba refactor(core): 重构数据字典组件
- 将 AppDictionary 组件拆分为 dictList 和 DictDetail 两个子组件
-优化数据字典列表的渲染逻辑
2024-12-18 15:08:55 +08:00
aixianling
d40830188d feat(AppSystemAccount): 优化账号管理功能
- 添加禁用/启用账号功能
- 编辑账号信息
- 优化账号列表展示
- 移除不必要的功能
- 调整表单验证规则
2024-12-18 12:01:46 +08:00
aixianling
e789570a1b feat(xumu): 新增应用角色权限管理功能
- 添加 AppRoleRightsManager 组件,实现角色管理、权限分配等功能
- 新增 rightsAdd 组件,用于添加和编辑应用角色
- 新增 rightsGraph 组件,用于展示权限关系图
- 集成 ECharts 实现复杂的权限关系可视化
2024-12-18 10:16:27 +08:00
aixianling
0de94d76ee feat(xumu): 实现畜牧平台登录页面
- 新增登录页面组件 AppSign
- 添加用户名和密码登录功能
- 集成二维码登录
- 优化页面样式和布局
2024-12-17 17:55:58 +08:00
aixianling
536f579523 feat(utils): 重构路由生成逻辑
- 移除 autoRoutes.js 文件,改为使用 apps.js 存储路由信息
- 新增 createRoutes 函数,用于动态生成应用路由
- 更新 build.js,增加路由生成步骤
- 修改 router.js,使用新的路由配置
- 更新 .gitignore,忽略新的 apps.js 文件
2024-12-17 09:34:57 +08:00
aixianling
c8d75ab72a feat(project): 添加 AppSign 组件
- 实现了一个新的登录页面组件,包含左侧背景和右侧登录表单
- 集成了 logo、系统名称、描述等动态内容
- 添加了微信扫码登录功能
- 优化了响应式布局,适配不同屏幕尺寸
2024-12-16 16:58:21 +08:00
aixianling
ad9676c040 style(develop): 格式化 AppDeployCustom 组件代码
- 使用 ESLint 规则格式化了组件代码
- 调整了缩进、空格和换行
- 修复了一些小的语法问题
2024-12-16 16:30:19 +08:00
aixianling
29bd119ff4 refactor(oms): 优化 AppDeployCustom 组件默认值和样式
- 为 form 属性设置默认值,避免 undefined
- 优化输入框和标签的样式
- 调整部分 UI 样式,如换行和间距
2024-12-16 16:17:31 +08:00
aixianling
b07cca9bcf feat(AppDeployCustom): 添加指定登录页配置项
- 在 webConfig.vue 文件中增加"指定登录页"表单项
- 新增 form.signPage 字段用于填写应用的登录页文件名
2024-12-16 15:39:32 +08:00
aixianling
1c2364574d refactor: 删除 webConfig.vue 中的空样式块
- 移除了 webConfig.vue 文件中未使用的空样式块
- 简化了代码结构,提高了代码的可读性和维护性
2024-12-16 15:36:39 +08:00
aixianling
528082fc6b style(xumu): 格式化 AppSystemAccount 组件代码
- 使用 ESLint 规则格式化了代码结构
- 调整了缩进、空格和换行
- 修复了一些小的语法问题
2024-12-16 10:44:19 +08:00
aixianling
823c327894 账号界面 2024-12-12 15:40:27 +08:00
aixianling
39f6275e31 增加畜牧的开发环境 2024-12-11 18:02:24 +08:00
aixianling
44d971998a 优化wxmpConfig组件mod处理逻辑 2024-11-28 10:24:09 +08:00
aixianling
6d7769e61a 优化wxmpConfig组件表单处理及tabBar初始化逻辑 2024-11-28 10:23:15 +08:00
aixianling
afe1df98f3 优化webConfig和wxmpConfig组件的表单处理逻辑 2024-11-28 09:24:32 +08:00
aixianling
97bd799b6d 优化wxmpConfig组件tabBar过滤逻辑 2024-11-27 17:59:55 +08:00
aixianling
3316b73450 优化wxmpConfig组件tabBar配置更新逻辑 2024-11-27 17:55:08 +08:00
aixianling
eee06c837d Merge remote-tracking branch 'origin/devops' into devops 2024-11-27 16:45:17 +08:00
aixianling
c4ae782195 添加appList属性到wxmpConfig组件并传递给add.vue 2024-11-27 16:45:02 +08:00
liuye
2b22db2bf3 审核状态 2024-11-21 10:15:47 +08:00
liuye
30cf69df04 审核 2024-11-18 15:17:23 +08:00
liuye
54352ece58 test 2024-11-18 14:36:04 +08:00
liuye
daffd10bce 邻里互助审核 2024-11-18 10:52:48 +08:00
liuye
a22b574614 积分规则 2024-11-06 09:12:44 +08:00
liuye
e3c79f232d 网格动态 2024-11-05 17:13:45 +08:00
liuye
1ae371cb30 积分规则 2024-11-05 17:05:59 +08:00
aixianling
96d24a8b22 修复默认值给定错误的问题 2024-10-31 11:34:32 +08:00
aixianling
34eb7320fa 给定默认值 2024-10-31 11:30:09 +08:00
aixianling
9fa56ad890 Merge remote-tracking branch 'origin/devops' into devops 2024-10-29 10:56:32 +08:00
aixianling
263dd9ea37 清理favicon.ico 2024-10-29 10:56:10 +08:00
liuye
85de031db2 积分任务 2024-10-29 08:39:15 +08:00
liuye
a6f11e56a7 积分任务 2024-10-24 16:12:41 +08:00
liuye
15f6dc6499 lianhua-名额 2024-10-24 14:04:59 +08:00
aixianling
b64b102682 Merge remote-tracking branch 'origin/build' into devops 2024-10-18 09:31:36 +08:00
aixianling
576553e962 为了保证jenkins的顺利执行 2024-10-17 17:19:49 +08:00
aixianling
ff5e2d4fe2 为了保证jenkins的顺利执行 2024-10-17 15:54:07 +08:00
aixianling
043aaf8399 在仓库制作打包脚本 2024-10-12 16:17:37 +08:00
aixianling
a5e0f3a6c5 优化代码 2024-10-12 14:27:26 +08:00
aixianling
5a2762a7c6 导航条修复一下 2024-10-11 12:04:44 +08:00
aixianling
e2e957880b 针对oms专项进行启动和页面优化 2024-10-11 10:10:06 +08:00
aixianling
104f5d1049 修复接口,模块引用等一些问题 2024-10-10 17:35:04 +08:00
aixianling
b3f11698f2 清理一下工程 2024-10-10 16:29:07 +08:00
aixianling
285e242e9e Merge branch 'build' into devops 2024-10-10 16:21:44 +08:00
aixianling
00b2dbb4a4 持续集成分支 2024-10-10 16:04:11 +08:00
152 changed files with 11063 additions and 958 deletions

View File

@@ -1,2 +0,0 @@
VUE_APP_SCOPE=ai
VUE_APP_API=http://192.168.1.87:9000

View File

@@ -1,5 +0,0 @@
VUE_APP_SCOPE=biaopin
#VUE_APP_API=https://web.fdfengshou.cn/
#VUE_APP_API=http://192.168.1.87:9000/
VUE_APP_API=https://www.wyzzb.com
#VUE_APP_API=http://test87web.cunwuyun.cn/

View File

@@ -1,2 +0,0 @@
VUE_APP_SCOPE=dv
VUE_APP_API=http://192.168.1.87:9000

View File

@@ -1,4 +0,0 @@
VUE_APP_SCOPE=fengdu
#VUE_APP_API=https://web.fdfengshou.cn/
VUE_APP_API=http://192.168.1.87:9000/
#VUE_APP_API=http://test87web.cunwuyun.cn/

5
.env.xumu Normal file
View File

@@ -0,0 +1,5 @@
VUE_APP_SCOPE=xumu
VUE_APP_API=http://192.168.1.87:12413
VUE_APP_IS_SIMPLE_SERVER=1
VUE_APP_PORT=12413
VUE_APP_OMS_ID=2cd70a15-a3cf-4b4d-9a22-0f3b3a888b08 # oms定制方案的ID

4
.gitignore vendored
View File

@@ -27,3 +27,7 @@ yarn-error.log*
/project/*/dist /project/*/dist
/ui/package-lock.json /ui/package-lock.json
/examples/modules.json /examples/modules.json
/examples/router/apps.js
/src/apps/
/src/config.json
/src/utils/apps.js

5
.npmrc
View File

@@ -1,5 +1,4 @@
registry=http://192.168.1.87:4873/
email=aixianling@sinoecare.com email=aixianling@sinoecare.com
always-auth=true
package-lock=false package-lock=false
//192.168.1.87:4873/:_auth="YWRtaW46YWRtaW4xMjM=" registry=http://registry.npmmirror.com
legacy-peer-deps=true

99
bin/build.js Normal file
View File

@@ -0,0 +1,99 @@
const axios = require('axios')
const {fsExtra, copyFiles, findApp, chalkTag, fs} = require("./tools");
const compiler = require('vue-template-compiler')
const getBuildConfig = id => {
axios.post('http://192.168.1.87:12525/node/custom/detail', null, {params: {id}}).then(res => {
if (res?.data) {
const config = res.data.data
fsExtra.outputJson('src/config.json', config.extra)
createPages(config)
}
})
}
const getAppInfo = (file, apps) => {
if (/[\\\/](App[A-Z][^\\\/]+)\.vue$/g.test(file)) {
const name = file.replace(/.+[\\\/](App[^\\\/]+)\.vue$/, '$1'),
source = fs.readFileSync(file).toString(),
parsed = compiler.parseComponent(source),
script = parsed.script?.content || "",
label = script.match(/label:[^,]+/)?.[0]?.replace(/.+["']([^"']+).+/, '$1')
const paths = file.split(/[\\\/]/)
apps.push({
id: file.replace(/\.vue$/, '').replace(/[\\\/]/g, '_'),
label: label || name,
path: `/${file.replace(/\.vue$/, '').replace(/[\\\/]/g, '/')}`,
workspace: paths.at(0),
esm: file.replace(/[\\\/]/g, '/').substring(4),
name
})
}
}
/**
* 根据配置生成应用路由
* @param {Object} config - 配置对象,用于定制化路由生成过程
* @returns {Promise} - 返回一个Promise对象表示路由生成完成
*/
const createRoutes = (config = {}) => {
// 初始化路由数组
const routes = []
// 获取签到页面的路径,如果未指定,则使用默认路径
let signPage = '../views/sign'
let {signPage: sign, homePage: home = "console"} = config.extra || {}
if (config.extra?.signPage) {
signPage = `../apps/custom/${sign}/${sign}`
}
let homePage = `../views/console`
if (config.extra?.homePage) {
homePage = `../apps/custom/${home}/${home}`
}
// 查找并处理所有应用,将它们的信息添加到路由中
return findApp("src/apps", app => getAppInfo(app, routes)).then(() => {
// 生成并输出apps.js文件定义所有应用的路由
fsExtra.outputFile('src/utils/apps.js', `export default [
{path: "/login", name: "登录", component: () => import('${signPage}')},
{path: '/dv', name: '数据大屏入口', component: () => import('../views/dvIndex')},
{path: '/v', name: 'Home', component: () => import('../views/home'), children: [
{path:'/',name:'mainEntry', component:()=>import('../views/mainEntry'),children:[
{name: "${home}", path: "${home}", component: () => import('${homePage}')},
${routes.filter(e => ![sign, home].includes(e.name)).map(e => {
// 解构每个路由的属性,用于生成路由配置
const {name, label, esm} = e
// 生成单个路由配置的字符串表示
return `{name:"${name}",label:"${label}",path:"${name}",component:()=>import("../${esm}")}`
}).join(',\n')},
{path: '*',name: '404',component: ()=>import('../views/building')},
]}
]},
{path: '/', name: "init"},
]`)
// 扫描完毕使用chalkTag标记任务完成
chalkTag.done("扫描完毕")
})
}
const createPages = (config = {}) => {
fsExtra.emptyDir("src/apps", err => {
if (!err) {
const {customPath, appList} = config
const stdApps = {}
appList.filter(e => !/project/.test(e.id))?.forEach(e => {
const paths = e.libPath.split('/').filter(Boolean) || []
paths.pop()
stdApps[paths.join("/")] = 1
})
Promise.all([
copyFiles("src/apps/core", "packages/core"),
copyFiles("src/apps/custom", `project/${customPath}`),
...Object.keys(stdApps).map(e => copyFiles(`src/apps/${e.replace(/^packages[\\\/]/, '')}`, e)),
]).then(() => createRoutes(config)).then(() => fsExtra.ensureFile("src/apps/actions.js"))
}
})
}
const start = () => {
const buildId = process.argv[2] || process.env.VUE_APP_OMS_ID || 'f670cc46-7cf7-4a0f-86ee-3077044c0b17'
getBuildConfig(buildId)
}
start()

View File

@@ -1,33 +0,0 @@
const {chalkTag, findApp, fs, fsExtra} = require("./tools");
const compiler = require('vue-template-compiler')
const getAppInfo = (file, apps) => {
if (/[\\\/](App[A-Z][^\\\/]+)\.vue$/g.test(file)) {
const name = file.replace(/.+[\\\/](App[^\\\/]+)\.vue$/, '$1'),
source = fs.readFileSync(file).toString(),
parsed = compiler.parseComponent(source),
script = parsed.script?.content || "",
label = script.match(/label:[^,]+/)?.[0]?.replace(/.+["']([^"']+).+/, '$1')
const paths = file.split(/[\\\/]/)
apps.push({
id: file.replace(/\.vue$/, '').replace(/[\\\/]/g, '_'),
label: label || name,
path: `/${file.replace(/\.vue$/, '').replace(/[\\\/]/g, '/')}`,
workspace: paths.at(0),
esm: ['.', paths.slice(1)].flat().join("/"),
name
})
}
}
const start = () => {
chalkTag.info("开始扫描库工程...")
const list = []
Promise.all([
findApp('packages', app => getAppInfo(app, list)),
findApp('project', app => getAppInfo(app, list)),
]).then(() => {
fsExtra.outputJson('examples/modules.json', {apps: list})
chalkTag.done("扫描完毕")
})
}
start()

37
bin/scanApps.js Normal file
View File

@@ -0,0 +1,37 @@
const {chalkTag, findApp, fs, fsExtra} = require("./tools");
const compiler = require('vue-template-compiler')
const getAppInfo = (file, apps) => {
if (/[\\\/](App[A-Z][^\\\/]+)\.vue$/g.test(file)) {
const name = file.replace(/.+[\\\/](App[^\\\/]+)\.vue$/, '$1'),
source = fs.readFileSync(file).toString(),
parsed = compiler.parseComponent(source),
script = parsed.script?.content || "",
label = script.match(/label:[^,]+/)?.[0]?.replace(/.+["']([^"']+).+/, '$1')
const paths = file.split(/[\\\/]/)
apps.push({
id: file.replace(/\.vue$/, '').replace(/[\\\/]/g, '_'),
label: label || name,
path: `/${file.replace(/\.vue$/, '').replace(/[\\\/]/g, '/')}`,
workspace: paths.at(0),
esm: file.replace(/[\\\/]/g, '/'),
name
})
}
}
const start = () => {
chalkTag.info("开始扫描库工程...")
const {VUE_APP_SCOPE, VUE_APP_CORE} = process.env
const list = []
let scanScope = ['packages', 'project']
if (VUE_APP_SCOPE) scanScope = [`project/${VUE_APP_SCOPE}`]
if (VUE_APP_CORE) scanScope.push('packages/core')
Promise.all(scanScope.map(e => findApp(e, app => getAppInfo(app, list)))).then(() => {
fsExtra.outputFile('examples/router/apps.js', `export default [${list.map(e => {
const {name, label, path, esm} = e
return `{name:"${name}",label:"${label}",path:"${path}",component:()=>import("@${esm}")}`
}).join(',\n')}]`)
chalkTag.done("扫描完毕")
})
}
start()

View File

@@ -9,13 +9,13 @@
</template> </template>
</header-nav> </header-nav>
<ai-dv-wrapper class="fill" v-if="dvDev"> <ai-dv-wrapper class="fill" v-if="dvDev">
<router-view/> <router-view v-bind="commonAttrs"/>
</ai-dv-wrapper> </ai-dv-wrapper>
<el-row v-else-if="showTools" class="fill mar-t48" type="flex"> <el-row v-else-if="showTools" class="fill mar-t48" type="flex">
<slider-nav/> <slider-nav/>
<main-content class="fill"/> <main-content class="fill" :commonAttrs="commonAttrs"/>
</el-row> </el-row>
<router-view class="fill" v-else/> <router-view class="fill" v-else v-bind="commonAttrs"/>
<div v-if="dialog" class="sign-box"> <div v-if="dialog" class="sign-box">
<ai-sign style="margin: auto" :instance="$request" :action="{login}" <ai-sign style="margin: auto" :instance="$request" :action="{login}"
visible @login="getToken" :showScanLogin="false"/> visible @login="getToken" :showScanLogin="false"/>
@@ -30,7 +30,7 @@ import MainContent from "./components/mainContent";
import HeaderNav from "./components/headerNav"; import HeaderNav from "./components/headerNav";
import {mapActions, mapMutations, mapState} from "vuex"; import {mapActions, mapMutations, mapState} from "vuex";
import Mock from "./components/mock"; import Mock from "./components/mock";
import AiDvWrapper from "@dui/dv/layout/AiDvWrapper/AiDvWrapper.vue"; import AiDvWrapper from "dui/dv/layout/AiDvWrapper/AiDvWrapper.vue";
export default { export default {
name: 'app', name: 'app',
@@ -42,6 +42,13 @@ export default {
/project\/sass/g.test(location.pathname) && (url += "?corpId=ww596787bb70f08288") /project\/sass/g.test(location.pathname) && (url += "?corpId=ww596787bb70f08288")
return url return url
}, },
commonAttrs() {
return {
instance: this.$request,
dict: this.$dict,
permissions: this.$permissions
}
}
}, },
data() { data() {
return { return {
@@ -76,7 +83,8 @@ export default {
} }
}, },
created() { created() {
wx = jWeixin const {jWeixin} = window
window.wx = jWeixin
if (this.user.token) this.getUserInfo().finally(() => { if (this.user.token) this.getUserInfo().finally(() => {
if (/^\/project\/xiushan/.test(location.pathname)) { if (/^\/project\/xiushan/.test(location.pathname)) {
this.getFinanceUser() this.getFinanceUser()

View File

@@ -1,8 +1,8 @@
<template> <template>
<section class="mainContent"> <section class="mainContent">
<ai-nav-tab :routes="apps"/> <ai-nav-tab :routes="$apps"/>
<div class="fill"> <div class="fill">
<router-view/> <router-view v-bind="$attrs.commonAttrs"/>
<ai-empty v-if="isHome">欢迎使用村微产品库</ai-empty> <ai-empty v-if="isHome">欢迎使用村微产品库</ai-empty>
</div> </div>
</section> </section>
@@ -10,13 +10,11 @@
<script> <script>
import AiNavTab from "dui/packages/basic/AiNavTab"; import AiNavTab from "dui/packages/basic/AiNavTab";
import {mapState} from "vuex"
export default { export default {
name: "mainContent", name: "mainContent",
components: {AiNavTab}, components: {AiNavTab},
computed: { computed: {
...mapState(['apps']),
isHome: v => v.$route.path == '/', isHome: v => v.$route.path == '/',
}, },
} }

View File

@@ -39,10 +39,10 @@ export default {
} }
}, },
computed: { computed: {
...mapState(['user', 'apps']), ...mapState(['user']),
navs() { navs() {
let reg = new RegExp(`.*${this.searchApp?.replace(/-/g,'')||''}.*`, 'gi') let reg = new RegExp(`.*${this.searchApp?.replace(/-/g,'')||''}.*`, 'gi')
return (this.apps || []).filter(e => !this.searchApp || reg?.test(e.name) || reg?.test(e.label)).map(e => { return (this.$apps || []).filter(e => !this.searchApp || reg?.test(e.name) || reg?.test(e.label)).map(e => {
if (/\/project\//.test(e.path)) { if (/\/project\//.test(e.path)) {
e.project = process.env.VUE_APP_SCOPE || e.path.replace(/.*project\/([^\/]+)\/.+/, '$1') e.project = process.env.VUE_APP_SCOPE || e.path.replace(/.*project\/([^\/]+)\/.+/, '$1')
} else if (/\/core\//.test(e.path)) { } else if (/\/core\//.test(e.path)) {
@@ -52,9 +52,9 @@ export default {
}) })
}, },
menuPath() { menuPath() {
let paths = [], current = this.apps?.find(e => e.name == this.$route.name) let paths = [], current = this.$apps?.find(e => e.name == this.$route.name)
const findParent = name => { const findParent = name => {
let menu = this.apps?.find(e => e.name == name) let menu = this.$apps?.find(e => e.name == name)
if (menu) { if (menu) {
paths.push(menu.name) paths.push(menu.name)
if (!!menu.parentId) findParent(menu.parentId) if (!!menu.parentId) findParent(menu.parentId)

View File

@@ -1,13 +1,13 @@
import Vue from 'vue'; import Vue from 'vue';
import App from './App.vue'; import App from './App.vue';
import ui from 'element-ui'; import ui from 'element-ui';
import router from './router/router'; import router from './router';
import axios from './router/axios'; import axios from './router/axios';
import utils from './utils'; import utils from './utils';
import dui from 'dui'; import dui from 'dui/packages';
import store from './store'; import store from './store';
import dataV from '@jiaminghi/data-view'; import dataV from '@jiaminghi/data-view';
import dvui from '@dui/dv' import dvui from 'dui/dv'
Vue.use(dataV) Vue.use(dataV)
Vue.use(ui); Vue.use(ui);
@@ -22,6 +22,7 @@ const app = new Vue({
store, store,
render: h => h(App) render: h => h(App)
}); });
let theme = null let theme = null
store.dispatch('getSystem').then(res => { store.dispatch('getSystem').then(res => {
theme = JSON.parse(res?.colorScheme || null) theme = JSON.parse(res?.colorScheme || null)

View File

@@ -1,81 +0,0 @@
import store from "../store";
import {waiting} from "../utils";
import appEntry from "../views/appEntry";
import router from "./router";
export default {
routes: () => store.state.apps,
init() {
//约束正则式
store.commit("cleanApps")
// 自动化本工程应用
waiting.init({innerHTML: '应用加载中..'})
let startTime = new Date().getTime()
/**
* require.context 的路径变量范式只能为静态字符串
*/
switch (process.env.VUE_APP_SCOPE) {
case 'dv':
this.esm = {
packages: require.context('../../packages/bigscreen', true, /\.(\/.+)\/App[A-Z][^\/]+\.vue$/, 'lazy')
}
break
case 'fengdu':
this.esm = {
project: require.context('../../project/fengdu', true, /\.(\/.+)\/App[A-Z][^\/]+\.vue$/, 'lazy')
}
break
case 'ai':
this.esm = {
biaopin: require.context('../../project/biaopin/AppCopilotConfig', true, /\.\/App[A-Z][^\/]+\.vue$/, 'lazy'),
project: require.context('../../project/ai', true, /\.(\/.+)\/App[A-Z][^\/]+\.vue$/, 'lazy')
}
break
case 'oms':
this.esm = {
project: require.context('../../project/oms', true, /\.(\/.+)\/App[A-Z][^\/]+\.vue$/, 'lazy')
}
break
default:
this.esm = {
packages: require.context('../../packages/', true, /\.(\/.+)\/App[A-Z][^\/]+\.vue$/, 'lazy'),
project: require.context('../../project/', true, /\.(\/.+)\/App[A-Z][^\/]+\.vue$/, 'lazy')
}
}
console.log('模块引用用了%s秒', (new Date().getTime() - startTime) / 1000)
startTime = new Date().getTime()
this.loadApps().finally(() => {
console.log('模块加载用了%s秒', (new Date().getTime() - startTime) / 1000)
waiting.close()
})
},
loadMods() {
// return Promise.all(mods.apps.map(e => {
// Vue.component(e.name, this.esm[e.workspace](e.esm))
// const addApp = {...e, component: appEntry}
// waiting.setContent(`加载${e.name}...`)
// //命名规范入口文件必须以App开头
// return store.commit("addApp", addApp)
// }))
},
loadApps() {
//新App的自动化格式
const promise = (mods, base) => Promise.all(mods.keys().map(path => mods(path).then(file => {
if (file.default) {
const {name, label} = file.default
const addApp = {
name: [base, path.replace(/\.\/?(vue)?/g, '')?.split("/")].flat().join("_"),
label: label || name,
path: `/${base}${path.replace(/\.(\/.+\/App.+)\.vue$/, '$1')}`,
component: appEntry,
esm: file.default
}
waiting.setContent(`加载${name}...`)
router.addRoute(addApp)
//命名规范入口文件必须以App开头
return store.commit("addApp", addApp)
} else return 0
}).catch(err => console.log(err))))
return Promise.all(Object.entries(this.esm).map(([root, mods]) => promise(mods, root))).catch(console.error)
}
}

View File

@@ -1,5 +1,5 @@
import {Message} from 'element-ui' import { Message } from 'element-ui'
import instance from '../../ui/lib/js/request' import instance from 'dui/lib/js/request'
let baseURLs = { let baseURLs = {
production: "/", production: "/",
@@ -8,34 +8,10 @@ let baseURLs = {
instance.defaults.baseURL = baseURLs[process.env.NODE_ENV] instance.defaults.baseURL = baseURLs[process.env.NODE_ENV]
instance.interceptors.request.use(config => { instance.interceptors.request.use(config => {
if (config.url.startsWith("/node")) { if (config.url.startsWith("/node")) {
config.baseURL = "/ns" config.url = "/ns" + config.url
} else if (config.url.startsWith("/sse")) {
config.baseURL = "/"
} else if (/\/project\/activeAnalysis/.test(location.pathname)) {
config.baseURL = "/analysis"
} else if (/\/project\/beta/.test(location.pathname)) {
config.baseURL = "/wg"
} else if (/\/project\/sass/.test(location.pathname)) {
config.baseURL = "/saas"
} else if (/\/tianfuxing/.test(location.pathname)) {
config.baseURL = "/tfx"
} else if (/\/qianxinan/.test(location.pathname)) {
// config.baseURL = "/qxn"
} else if (/\/xiushan/.test(location.pathname)) {
config.baseURL = "/xsjr"
} else if (/project\/oms/.test(location.pathname)) {
config.baseURL = "/omsapi"
} else if (/#url-/.test(location.hash)) {
config.baseURL = location.hash.replace(/#url-/, '/')
}
if (["/xsjr", "/tfx", "/omsapi"].includes(config.baseURL)) {
config.url = config.url.replace(/(app|auth|admin|api)\//, "api/")
}
if (['/qxn', '/analysis'].includes(config.baseURL)) {
config.url = config.url.replace(/(app|auth|admin)\//, "api/")
} }
if (process.env.VUE_APP_IS_SIMPLE_SERVER == 1) { if (process.env.VUE_APP_IS_SIMPLE_SERVER == 1) {
config.url = config.url.replace(/(app|auth|admin)\//, "api/") config.url = config.url.replace(/^\/(app|auth|admin)\//, "/api/")
} }
return config return config
}, error => Message.error(error)) }, error => Message.error(error))

View File

@@ -1,13 +1,13 @@
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import autoRoutes from './autoRoutes' import apps from "./apps";
Vue.use(VueRouter) Vue.use(VueRouter)
autoRoutes.init() Vue.prototype.$apps = apps
export default new VueRouter({ export default new VueRouter({
mode: 'history', mode: 'history',
hashbang: false, hashbang: false,
routes: autoRoutes.routes(), routes: apps,
scrollBehavior(to) { scrollBehavior(to) {
if (to.hash) { if (to.hash) {
return { return {

View File

@@ -5,22 +5,8 @@ import * as modules from "dui/lib/js/modules"
import xsActions from "../../project/xiushan/actions" import xsActions from "../../project/xiushan/actions"
Vue.use(Vuex) Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
state: { actions: {...xsActions},
apps: []
},
mutations: {
addApp(state, app) {
state.apps.push(app)
},
cleanApps(state) {
state.apps = []
},
},
actions: {
...xsActions
},
modules, modules,
plugins: [preState()] plugins: [preState()]
}) })

View File

@@ -1,46 +0,0 @@
<template>
<section class="appEntry">
<component v-if="app" :is="app" ref="currentPage" :instance="$request" :dict="$dict" :permissions="$permissions"/>
<ai-empty v-else>无法找到应用文件</ai-empty>
</section>
</template>
<script>
import {mapState} from "vuex";
import Vue from "vue";
export default {
name: "appEntry",
label: "应用库-应用",
computed: {
...mapState(['apps']),
app() {
const app = this.apps.find(e => e.name == this.$route.name)
return app.esm ?? ""
}
},
mounted() {
this.$vm.$on("mock", v => {
if (!!this.$refs.currentPage.$children?.[0]?.form) {
this.$refs.currentPage.$children[0].form = v
this.$refs.currentPage.$children[0].$forceUpdate()
}
})
}
}
</script>
<style lang="scss" scoped>
.appEntry {
width: 100%;
flex: 1;
min-width: 0;
min-height: 0;
height: 100%;
& > * {
height: 100%;
}
}
</style>

View File

@@ -1,47 +1,42 @@
{ {
"name": "dvcp-web-apps", "name": "dvcp-web-apps",
"version": "3.0.0", "version": "4.0.0",
"private": false, "private": false,
"author": "kubbo", "author": "kubbo",
"scripts": { "scripts": {
"dev": "vue-cli-service serve examples/main.js", "dev": "vue-cli-service serve examples/main.js",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"dev:ai": "vue-cli-service serve examples/main.js --mode ai", "oms": "vue-cli-service serve examples/main.js --mode oms",
"dev:oms": "vue-cli-service serve examples/main.js --mode oms", "xumu": "vue-cli-service serve examples/main.js --mode xumu",
"dev:biaopin": "vue-cli-service serve examples/main.js --mode biaopin",
"dev:dv": "vue-cli-service serve examples/main.js --mode dv",
"dev:fengdu": "vue-cli-service serve examples/main.js --mode fengdu",
"lib": "npm publish||(npm unpublish -f&&npm publish)",
"preui": "npm publish -ws||(npm unpublish -f -ws&&npm publish -ws)",
"ui": "npm i dui@latest @dui/dv@latest",
"sync": "node bin/appsSync.js", "sync": "node bin/appsSync.js",
"preview": "vue-cli-service serve" "preview": "node bin/build.js && vue-cli-service serve",
"predev": "node bin/scanApps.js",
"preoms": "dotenv -e .env.oms node bin/scanApps.js",
"prexumu": "dotenv -e .env.xumu node bin/scanApps.js",
"view:xumu": "vue-cli-service serve --mode xumu",
"preview:xumu": "dotenv -e .env.xumu node bin/build.js"
}, },
"workspaces": [
"ui",
"ui/dv"
],
"files": [
"packages",
"project"
],
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@dui/dv": "^1.0.0", "@ckeditor/ckeditor5-vue2": "^3.0.1",
"@jiaminghi/data-view": "^2.10.0", "@jiaminghi/data-view": "^2.10.0",
"@logicflow/core": "^1.2.1", "@logicflow/core": "^1.2.1",
"bin-ace-editor": "^3.2.0", "bin-ace-editor": "^3.2.0",
"crypto-js": "^4.2.0",
"dayjs": "^1.8.35", "dayjs": "^1.8.35",
"dui": "^2.0.0", "echarts": "^5.5.1",
"echarts-wordcloud": "^2.0.0", "echarts-wordcloud": "^2.0.0",
"hash.js": "^1.1.7", "hash.js": "^1.1.7",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"jspdf": "^2.5.2",
"mp4box": "^0.4.1", "mp4box": "^0.4.1",
"print-js": "^1.0.63", "print-js": "^1.0.63",
"serialize-javascript": "^6.0.0", "serialize-javascript": "^6.0.0",
"sortablejs": "^1.12.0", "sortablejs": "^1.12.0",
"vue-carousel": "^0.18.0", "vue-carousel": "^0.18.0",
"vue-cropper": "^0.6.5",
"vue-draggable-resizable": "^2.3.0", "vue-draggable-resizable": "^2.3.0",
"vue-qr": "^4.0.9",
"vue-ruler-tool": "^1.2.4", "vue-ruler-tool": "^1.2.4",
"vue-style-loader": "^4.1.3", "vue-style-loader": "^4.1.3",
"vuedraggable": "^2.24.3" "vuedraggable": "^2.24.3"
@@ -56,6 +51,7 @@
"axios": "^0.19.2", "axios": "^0.19.2",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"core-js": "^2.6.11", "core-js": "^2.6.11",
"dotenv-cli": "^7.4.2",
"element-ui": "^2.15.9", "element-ui": "^2.15.9",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0", "eslint-plugin-vue": "^5.0.0",

View File

@@ -168,7 +168,7 @@ import {monitorTypes} from "../config";
import ConfigItem from "./configItem.vue"; import ConfigItem from "./configItem.vue";
import DatasourcePicker from "./datasourcePicker.vue"; import DatasourcePicker from "./datasourcePicker.vue";
import AiSelect from "dui/packages/basic/AiSelect.vue"; import AiSelect from "dui/packages/basic/AiSelect.vue";
import {DvCompData} from "@dui/dv"; import {DvCompData} from "dui/dv";
export default { export default {
name: 'dataConfig', name: 'dataConfig',

View File

@@ -4,7 +4,7 @@ import ConfigItem from "./configItem";
import ChartPicker from "./chartPicker"; import ChartPicker from "./chartPicker";
import JsonEditor from "./jsonEditor"; import JsonEditor from "./jsonEditor";
import {layers, monitorTypes} from "../config"; import {layers, monitorTypes} from "../config";
import AiDvSummary from "@dui/dv/layout/AiDvSummary/AiDvSummary"; import AiDvSummary from "dui/dv/layout/AiDvSummary/AiDvSummary";
export default { export default {
name: "configExtra", name: "configExtra",

View File

@@ -34,7 +34,7 @@ import 'brace/snippets/json';
import 'brace/theme/github'; import 'brace/theme/github';
import 'brace/theme/monokai'; import 'brace/theme/monokai';
import JsonEditor from "./jsonEditor.vue"; import JsonEditor from "./jsonEditor.vue";
import {DvCompData} from "@dui/dv"; import {DvCompData} from "dui/dv";
export default { export default {
name: "datasourcePicker", name: "datasourcePicker",

View File

@@ -49,6 +49,7 @@
<el-button type="text" @click="toDetail(row.id)">详情</el-button> <el-button type="text" @click="toDetail(row.id)">详情</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button> <el-button type="text" @click="remove(row.id)">删除</el-button>
<el-button type="text" @click="gag(row.createUserId, row.blacklist)">{{ row.blacklist ? '解除禁言' : '禁言' }}</el-button> <el-button type="text" @click="gag(row.createUserId, row.blacklist)">{{ row.blacklist ? '解除禁言' : '禁言' }}</el-button>
<el-button type="text" v-if="row.status<1" @click="admin(row)">审核</el-button>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@@ -77,7 +78,7 @@
}, },
total: 0, total: 0,
colConfigs: [ colConfigs: [
{ prop: 'content', label: '内容', align: 'left' }, { prop: 'content', label: '帖子内容', align: 'left' },
{ prop: 'createUserName', label: '发帖人', align: 'center', width: '120' }, { prop: 'createUserName', label: '发帖人', align: 'center', width: '120' },
{ prop: 'createUserAreaName', label: '所属地区', align: 'center' }, { prop: 'createUserAreaName', label: '所属地区', align: 'center' },
{ prop: 'createTime', label: '创建时间', align: 'center' }, { prop: 'createTime', label: '创建时间', align: 'center' },
@@ -85,6 +86,7 @@
{ prop: 'appreciateCount', label: '点赞数', align: 'center', width: '120' }, { prop: 'appreciateCount', label: '点赞数', align: 'center', width: '120' },
{ prop: 'sharedCount', label: '分享数', align: 'center', width: '120' }, { prop: 'sharedCount', label: '分享数', align: 'center', width: '120' },
{ prop: 'blacklist', label: '状态', align: 'center', format: v => v ? '禁言' : '正常' }, { prop: 'blacklist', label: '状态', align: 'center', format: v => v ? '禁言' : '正常' },
{ prop: 'status', label: '审核状态', align: 'center', width: '120', dict: 'auditStatus' },
{ slot: 'options'}, { slot: 'options'},
], ],
tableData: [], tableData: [],
@@ -100,7 +102,9 @@
created() { created() {
this.search.areaId = this.user.info.areaId this.search.areaId = this.user.info.areaId
this.getList() this.dict.load('auditStatus').then(() => {
this.getList()
})
}, },
methods: { methods: {
@@ -172,6 +176,34 @@
id: id || '' id: id || ''
} }
}) })
},
admin(row) {
console.log(row)
this.$confirm('是否审核通过该条帖子?', {
distinguishCancelAndClose: true,
confirmButtonText: '通过',
closeOnClickModal: true,
cancelButtonText: '拒绝'
}).then((e) => {
this.instance.post(`/app/appneighborhoodassistance/examine?id=${row.id}&pass=1`).then(res => {
if (res.code == 0) {
this.$message.success('审核成功!')
this.search.current = 1
this.getList()
}
})
}).catch((e) => {
if(e == 'cancel') {
this.instance.post(`/app/appneighborhoodassistance/examine?id=${row.id}&pass=0`).then(res => {
if (res.code == 0) {
this.$message.success('审核成功!')
this.search.current = 1
this.getList()
}
})
}
})
} }
} }
} }

View File

@@ -1,151 +1,27 @@
<template> <template>
<section class="AppDictionary"> <section class="AppDictionary">
<ai-list v-if="!showDetail"> <component :is="currentPage" v-bind="$props"/>
<ai-title slot="title" title="数据字典" isShowBottomBorder/>
<template #content>
<ai-search-bar>
<template #left>
<el-button type="primary" size="small" icon="iconfont iconAdd" @click="addDict"
v-if="$permissions('admin_sysdictionary_add')">添加
</el-button>
</template>
<template #right>
<el-input size="small" v-model="search.condition" placeholder="数据项" clearable
@change="page.current=1,getDicts()" prefix-icon="iconfont iconSearch"/>
<el-button type="primary" size="small" icon="iconfont iconSearch"
@click="page.current=1,getDicts()">查询
</el-button>
<el-button size="small" icon="el-icon-refresh-right" @click="resetSearch">重置</el-button>
</template>
</ai-search-bar>
<el-table size="mini" :data="dictList" header-cell-class-name="table-header" tooltip-effect="light"
row-class-name="table-row" cell-class-name="table-cell" @expand-change="getDictInfo">
<el-table-column type="expand">
<el-row slot-scope="{row}" type="flex" align="middle" style="flex-wrap: wrap">
<el-tag v-for="(op,i) in row.detail||[]" :key="i" style="margin: 4px">{{ op.dictValue }}{{ op.dictName }}
{{ op.dictColor ? '| ' + op.dictColor : '' }}
</el-tag>
</el-row>
</el-table-column>
<el-table-column align="center" label="数据项" prop="code"/>
<el-table-column align="center" label="数据项名称" prop="name"/>
<el-table-column align="center" label="操作">
<div slot-scope="{row}">
<el-button type="text" @click="openDetail(row.id)" v-text="'编辑'"
v-if="$permissions('admin_sysdictionary_edit')"/>
<el-button type="text" @click="handleDelete(row.id)" v-text="'删除'"
v-if="$permissions('admin_sysdictionary_del')"/>
</div>
</el-table-column>
<div slot="empty" class="no-data"></div>
</el-table>
<div class="pagination">
<el-pagination background :current-page.sync="page.current" :total="page.total"
layout="total,prev, pager, next,sizes, jumper"
@size-change="handleSizeChange"
:page-size="page.size"
:page-sizes="[10, 20, 50, 100,200]"
@current-change="getDicts"/>
</div>
</template>
</ai-list>
<dict-detail v-else :instance="instance" :permissions="permissions"/>
</section> </section>
</template> </template>
<script> <script>
import DictDetail from "./dictDetail"; import DictDetail from "./dictDetail";
import DictList from "@project/xumu/AppDictionary/dictList.vue";
export default { export default {
name: "AppDictionary", name: "AppDictionary",
components: {DictDetail},
label: "数据字典", label: "数据字典",
props: { props: {
instance: Function, instance: Function,
dict: Object,
permissions: Function permissions: Function
}, },
computed: { computed: {
showDetail() { currentPage() {
return this.$route.hash == "#add" let {hash} = this.$route
return hash == "#add" ? DictDetail : DictList
} }
}, },
data() {
return {
page: {
current: 1,
total: 0,
size: 10
},
search: {
condition: ""
},
dictList: [],
id: ''
}
},
methods: {
resetSearch() {
this.page.current = 1;
this.search.condition = '';
this.getDicts();
},
getDicts() {
this.instance.post("/admin/dictionary/queryDictList", null, {
params: {
...this.page,
name: this.search.condition
}
}).then(res => {
this.dictList = res.data.records.map(e => {
return {...e, detail: []}
})
this.page.total = res.data.total
})
},
addDict() {
this.$router.push({hash: "#add"})
},
handleDelete(id) {
this.$confirm("确定要删除该数据项吗?", {
type: "error"
}).then(() => {
this.instance.post("/admin/dictionary/deleteDict", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getDicts();
this.$message.success("删除成功!")
}
})
}).catch(() => 0)
},
openDetail(id) {
this.$router.push({query: {id}, hash: "#add"})
},
handleSizeChange(val) {
this.page.size = val;
this.getDicts();
},
getDictInfo(row) {
if (row.detail.length) {
row.detail = []
} else {
this.getDict(row.id).then(res => {
if (res && res.data) {
row.detail = res.data.dictionaryDetails || []
}
})
}
},
getDict(dictionaryId) {
return this.instance.post("/admin/dictionary/queryDictDetail", null, {
params: {dictionaryId}
})
},
},
created() {
this.getDicts()
},
} }
</script> </script>

View File

@@ -0,0 +1,142 @@
<script>
const columns = [
{slot: "expand"},
{label: "数据项", prop: "code"},
{label: "数据项名称", prop: "name"},
]
export default {
name: "dictList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
page: {
current: 1,
total: 0,
size: 10
},
search: {
condition: ""
},
dictList: [],
id: ''
}
},
methods: {
resetSearch() {
this.page.current = 1;
this.search.condition = '';
this.getDicts();
},
getDicts() {
this.instance.post("/admin/dictionary/queryDictList", null, {
params: {
...this.page,
name: this.search.condition
}
}).then(res => {
this.dictList = res.data.records.map(e => {
return {...e, detail: []}
})
this.page.total = res.data.total
})
},
addDict() {
this.$router.push({hash: "#add"})
},
handleDelete(id) {
this.$confirm("确定要删除该数据项吗?", {
type: "error"
}).then(() => {
this.instance.post("/admin/dictionary/deleteDict", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getDicts();
this.$message.success("删除成功!")
}
})
}).catch(() => 0)
},
openDetail(id) {
this.$router.push({query: {id}, hash: "#add"})
},
handleSizeChange(val) {
this.page.size = val;
this.getDicts();
},
getDictInfo(row) {
if (row.detail.length) {
row.detail = []
} else {
this.getDict(row.id).then(res => {
if (res && res.data) {
row.detail = res.data.dictionaryDetails || []
}
})
}
},
getDict(dictionaryId) {
return this.instance.post("/admin/dictionary/queryDictDetail", null, {
params: {dictionaryId}
})
},
},
created() {
this.getDicts()
},
}
</script>
<template>
<section class="dictList">
<ai-list>
<ai-title slot="title" title="数据字典" isShowBottomBorder/>
<template #content>
<ai-search-bar>
<template #left>
<el-button type="primary" size="small" icon="iconfont iconAdd" @click="addDict"
v-if="$permissions('admin_sysdictionary_add')">添加
</el-button>
</template>
<template #right>
<el-input size="small" v-model="search.condition" placeholder="数据项" clearable
@change="page.current=1,getDicts()" prefix-icon="iconfont iconSearch"/>
<el-button type="primary" size="small" icon="iconfont iconSearch"
@click="page.current=1,getDicts()">查询
</el-button>
<el-button size="small" icon="el-icon-refresh-right" @click="resetSearch">重置</el-button>
</template>
</ai-search-bar>
<ai-table :tableData="dictList" :colConfigs="columns" :dict="dict" @getList="getDicts"
:total="page.total" :current.sync="page.current" :size.sync="page.size" :page-sizes="[10, 20, 50, 100,200]"
@expand-change="getDictInfo">
<el-table-column slot="expand" type="expand">
<template slot-scope="{row}">
<div class="flex" style="gap:4px">
<el-tag v-for="(op,i) in row.detail||[]" :key="i">{{ [op.dictValue, op.dictName, op.dictColor].filter(Boolean).join("") }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" @click="openDetail(row.id)" v-if="$permissions('admin_sysdictionary_edit')">编辑</el-button>
<el-button type="text" @click="handleDelete(row.id)" v-if="$permissions('admin_sysdictionary_del')">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</section>
</template>
<style scoped lang="scss">
.dictList {
height: 100%;
}
</style>

View File

@@ -40,7 +40,7 @@
<div v-if="node.isLeaf" class="opBtn del" v-text="`删除`" @click="handleDelete(data)"/> <div v-if="node.isLeaf" class="opBtn del" v-text="`删除`" @click="handleDelete(data)"/>
<div v-if="permissions('guide_page_config')&&data.component&&data.type==1" class="opBtn" v-text="`引导页`" <div v-if="permissions('guide_page_config')&&data.component&&data.type==1" class="opBtn" v-text="`引导页`"
@click="$router.push({hash:'#intro',query:{id:data.id}})"/> @click="$router.push({hash:'#intro',query:{id:data.id}})"/>
<div v-if="data.type<2" class="opBtn" v-text="`添加下级`" @click="addMenu(data)"/> <div v-if="!data.component" class="opBtn" v-text="`添加下级`" @click="addMenu(data)"/>
<div class="opBtn" v-text="`编辑`" @click="handleEdit(data)"/> <div class="opBtn" v-text="`编辑`" @click="handleEdit(data)"/>
</el-row> </el-row>
</el-row> </el-row>
@@ -54,29 +54,16 @@
<el-form-item label="菜单名称" prop="name"> <el-form-item label="菜单名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" clearable/> <el-input v-model="form.name" placeholder="请输入" clearable/>
</el-form-item> </el-form-item>
<el-form-item label="菜单类型" prop="type"> <el-form-item label="菜单图标" prop="style">
<ai-select v-model="form.type" clearable :selectList="dict.getDict('menuType')"/> <el-input v-model="form.style" placeholder="请输入" clearable/>
</el-form-item> </el-form-item>
<template v-if="form.type==0"> <el-form-item label="菜单应用" prop="component">
<el-form-item label="菜单图标" prop="style"> <el-input v-model="form.component" placeholder="请输入" clearable/>
<el-input v-model="form.style" placeholder="请输入" clearable/> </el-form-item>
</el-form-item> <el-form-item label="路径(path)" prop="path">
</template> <el-input v-model="form.path" placeholder="请输入" clearable/>
<template v-if="form.type==1"> </el-form-item>
<el-form-item label="菜单图标" prop="style"> <template v-if="form.component">
<el-input v-model="form.style" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="路由名" prop="route">
<span v-text="form.route||'提交保存后会自动生成'"/>
</el-form-item>
<el-form-item label="菜单应用" prop="component">
<el-input v-model="form.component" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="路径(path)" prop="path">
<el-input v-model="form.path" placeholder="请输入" clearable/>
</el-form-item>
</template>
<template v-if="form.type==2">
<el-form-item label="权限码" prop="permission"> <el-form-item label="权限码" prop="permission">
<el-input v-model="form.permission" placeholder="请输入" clearable/> <el-input v-model="form.permission" placeholder="请输入" clearable/>
</el-form-item> </el-form-item>
@@ -84,7 +71,7 @@
<el-form-item label="显示菜单" prop="status"> <el-form-item label="显示菜单" prop="status">
<ai-select v-model="form.status" clearable :selectList="dict.getDict('yesOrNo')"/> <ai-select v-model="form.status" clearable :selectList="dict.getDict('yesOrNo')"/>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type<2" label="排序" prop="showIndex"> <el-form-item label="排序" prop="showIndex">
<el-input v-model="form.showIndex" placeholder="请输入" clearable/> <el-input v-model="form.showIndex" placeholder="请输入" clearable/>
</el-form-item> </el-form-item>
</el-form> </el-form>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="AppGridReview"> <div class="AppFormReview">
<keep-alive :include="['List']"> <keep-alive :include="['List']">
<component ref="component" :is="component" :permissions="permissions " @change="onChange" :params="params" :instance="instance" :dict="dict"></component> <component ref="component" :is="component" :permissions="permissions " @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
</keep-alive> </keep-alive>
@@ -11,7 +11,7 @@
import Detail from './components/Detail' import Detail from './components/Detail'
export default { export default {
name: 'AppGridReview', name: 'AppFormReview',
label: '网格动态', label: '网格动态',
props: { props: {
@@ -56,7 +56,7 @@
</script> </script>
<style lang="scss"> <style lang="scss">
.AppGridReview { .AppFormReview {
height: 100%; height: 100%;
background: #F3F6F9; background: #F3F6F9;
overflow: auto; overflow: auto;

View File

@@ -114,8 +114,8 @@
this.instance.post(`/app/appgirdnews/examine?id=${this.params.id}&pass=${this.form.status}&opinion=${this.form.opinion}`).then(res => { this.instance.post(`/app/appgirdnews/examine?id=${this.params.id}&pass=${this.form.status}&opinion=${this.form.opinion}`).then(res => {
if (res?.code == 0) { if (res?.code == 0) {
this.isShowExamine = false this.isShowExamine = false
this.getDetail()
this.$message.success('审核成功!') this.$message.success('审核成功!')
this.cancel(true)
} }
}) })
} }

View File

@@ -17,8 +17,9 @@
<el-table-column slot="scoringCycle" label="周期范围" align="center"> <el-table-column slot="scoringCycle" label="周期范围" align="center">
<template slot-scope="{ row }"> <template slot-scope="{ row }">
<span v-if="row.parentRuleName == '工单处理'">-</span> <span v-if="row.parentRuleName == '工单处理'">-</span>
<span v-else>{{row.numberLimit.length ? $dict.getLabel("integralRuleScoringCycle", row.scoringCycle) <span v-if="row.parentRuleName != '工单处理' && row.numberLimit">{{row.numberLimit.length ? $dict.getLabel("integralRuleScoringCycle", row.scoringCycle)
: $dict.getLabel("integralRuleScoringCycle", row.scoringCycle) + row.numberLimit + ""}}</span> : $dict.getLabel("integralRuleScoringCycle", row.scoringCycle) + row.numberLimit + ""}}</span>
<span v-if="row.parentRuleName != '工单处理' && !row.numberLimit">{{$dict.getLabel("integralRuleScoringCycle", row.scoringCycle) + "不限次"}}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column slot="integral" label="分值" align="center"> <el-table-column slot="integral" label="分值" align="center">

View File

@@ -10,6 +10,10 @@
<el-input v-model="form.title" placeholder="请输入" show-word-limit maxlength="64"></el-input> <el-input v-model="form.title" placeholder="请输入" show-word-limit maxlength="64"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="名额" prop="quota" style="width: 100%">
<el-input-number v-model="form.quota" :min="1" :max="1000" label="请输入"></el-input-number>
</el-form-item>
<el-form-item label="活动说明" style="width: 100%"> <el-form-item label="活动说明" style="width: 100%">
<el-input type="textarea" :rows="5" v-model="form.detail" placeholder="请输入" show-word-limit maxlength="500"></el-input> <el-input type="textarea" :rows="5" v-model="form.detail" placeholder="请输入" show-word-limit maxlength="500"></el-input>
</el-form-item> </el-form-item>
@@ -124,6 +128,7 @@ export default {
return { return {
form: { form: {
title: '', title: '',
quota: 1,
detail: '', detail: '',
lng: '', lng: '',
lat: '', lat: '',
@@ -144,6 +149,7 @@ export default {
}, },
formRules: { formRules: {
title: [{required: true, message: "请输入活动名称", trigger: "blur"}], title: [{required: true, message: "请输入活动名称", trigger: "blur"}],
quota: [{required: true, message: "请输入名额", trigger: "blur"}],
location: [{required: true, validator: validLocation, trigger: "blur"}], location: [{required: true, validator: validLocation, trigger: "blur"}],
clockRange: [{required: true, message: "请输入打卡范围", trigger: "blur"}], clockRange: [{required: true, message: "请输入打卡范围", trigger: "blur"}],
intoTime: [{required: true, message: "请选择进场打卡时间", trigger: "blur"}], intoTime: [{required: true, message: "请选择进场打卡时间", trigger: "blur"}],
@@ -185,17 +191,17 @@ export default {
watch: { watch: {
'form.intoTime': { 'form.intoTime': {
handler(val) { handler(val) {
if(val) { if (Array.isArray(val) && val.length >= 2) {
this.form.intoBegintime = val[0] this.form.intoBegintime = val[0];
this.form.intoEndtime = val[1] this.form.intoEndtime = val[1];
} }
} }
}, },
'form.exitTime': { 'form.exitTime': {
handler(val) { handler(val) {
if(val) { if (Array.isArray(val) && val.length >= 2) {
this.form.exitBegintime = val[0] this.form.exitBegintime = val[0];
this.form.exitEndtime = val[1] this.form.exitEndtime = val[1];
} }
} }
} }

View File

@@ -8,6 +8,7 @@
<template #content> <template #content>
<ai-wrapper> <ai-wrapper>
<ai-info-item label="活动名称" :value="info.title"></ai-info-item> <ai-info-item label="活动名称" :value="info.title"></ai-info-item>
<ai-info-item label="名额" :value="info.quota"></ai-info-item>
<ai-info-item label="创建人" :value="info.createUserName"></ai-info-item> <ai-info-item label="创建人" :value="info.createUserName"></ai-info-item>
<ai-info-item label="活动说明" isLine :value="info.detail"></ai-info-item> <ai-info-item label="活动说明" isLine :value="info.detail"></ai-info-item>
<ai-info-item label="活动图片" isLine> <ai-info-item label="活动图片" isLine>

View File

@@ -7,29 +7,26 @@
<el-tab-pane label="方案设置"> <el-tab-pane label="方案设置">
<el-form ref="AddForm" :model="form" size="small" label-width="120px" :rules="rules"> <el-form ref="AddForm" :model="form" size="small" label-width="120px" :rules="rules">
<ai-card title="基本信息"> <ai-card title="基本信息">
<template #content> <div class="grid">
<el-form-item label="项目/系统名称" prop="name"> <el-form-item label="项目/系统名称" prop="name" class="row">
<el-input v-model="form.name" placeholder="请输入" clearable/> <ai-input v-model="form.name"/>
</el-form-item> </el-form-item>
<el-row type="flex"> <el-form-item label="系统类型" prop="type">
<div class="fill"> <ai-select v-model="form.type" dict="systemType" @change="form.apps = []"/>
<el-form-item label="系统类型" prop="type"> </el-form-item>
<ai-select v-model="form.type" :selectList="dict.getDict('systemType')" @change="form.apps = [],handleSysTypeChange(form.type)"/> <el-form-item label="库项目根路径" prop="customPath">
</el-form-item> <ai-input v-model="form.customPath"/>
<el-form-item label="更新项目路径" prop="dist"> </el-form-item>
<el-input v-model="form.dist" placeholder="常填写nginx路径,下载包从这里取" clearable/> <el-form-item label="更新项目路径" prop="dist">
</el-form-item> <ai-input v-model="form.dist" placeholder="常填写nginx路径,下载包从这里取"/>
</div> </el-form-item>
<div class="fill mar-l16"> <el-form-item label="版本号" prop="version">
<el-form-item label="库项目根路径" prop="customPath"> <ai-input v-model="form.version"/>
<el-input v-model="form.customPath" placeholder="请输入" clearable/> </el-form-item>
</el-form-item> <el-form-item label="微信机器人" prop="webhook">
<el-form-item label="版本号" prop="version"> <ai-input v-model="form.webhook"/>
<el-input v-model="form.version" placeholder="请输入" clearable/> </el-form-item>
</el-form-item> </div>
</div>
</el-row>
</template>
</ai-card> </ai-card>
<ai-card title="主库应用"> <ai-card title="主库应用">
<template #content> <template #content>
@@ -38,179 +35,15 @@
<ai-empty v-else>请先选择系统类型</ai-empty> <ai-empty v-else>请先选择系统类型</ai-empty>
</template> </template>
</ai-card> </ai-card>
<ai-card title="扩展设置"> <component class="extraConfig" title="扩展设置" :is="extraConfig" v-model="form.extra" :appList="appList"/>
<template #right>
<template v-if="form.type=='web'">
<ai-dialog-btn text="设置系统信息" dialogTitle="系统信息">
<el-form size="small" label-width="140px">
<el-form-item label="系统标题">
<el-input v-model="form.sysInfo.fullTitle" placeholder="请输入..." clearable/>
</el-form-item>
<el-form-item label="favicon">
<el-input v-model="form.sysInfo.favicon" placeholder="请输入..." clearable/>
</el-form-item>
<el-form-item label="logo">
<el-row type="flex">
<el-input v-model="form.sysInfo.logo" placeholder="请输入..." clearable/>
<el-input class="mar-l10" v-model="form.sysInfo.logoText" placeholder="logo文字">
<template #prepend>logo文字</template>
</el-input>
</el-row>
</el-form-item>
<el-form-item label="登录页">
<el-row type="flex" class="mar-b10">
<el-input v-model="form.sysInfo.loginLogo" placeholder="登录页左上角logo,带/代表图片" clearable>
<template #prepend>左页首logo</template>
</el-input>
<el-input class="mar-l10" v-model="form.sysInfo.loginLogoText" placeholder="logo文字" clearable>
<template #prepend>logo文字</template>
</el-input>
</el-row>
<el-row type="flex">
<el-input v-model="form.sysInfo.name" placeholder="左上角标题" clearable>
<template #prepend>左上角标题</template>
</el-input>
<el-input class="mar-l10" v-model="form.sysInfo.title" placeholder="左上角副标题" clearable>
<template #prepend>左上角副标题</template>
</el-input>
</el-row>
<el-input class="mar-t10" type="textarea" rows="5" v-model="form.sysInfo.desc" placeholder="副标题" clearable/>
</el-form-item>
<el-row type="flex">
<div class="fill">
<el-form-item label="版权所有">
<el-input v-model="form.sysInfo.recordDesc" placeholder="请输入..." clearable/>
</el-form-item>
<el-form-item label="备案号">
<el-input v-model="form.sysInfo.recordNo" placeholder="请输入..." clearable/>
</el-form-item>
</div>
<div class="fill">
<el-form-item label="框架版本">
<!--edition 版本标准版standard上架版saas 简易版(不带扫码):simple -->
<el-input v-model="form.sysInfo.edition" placeholder="请输入..." clearable/>
</el-form-item>
<el-form-item label="备案跳转链接">
<el-input v-model="form.sysInfo.recordURL" placeholder="请输入..." clearable/>
</el-form-item>
</div>
</el-row>
<el-form-item label="可信证书">
<el-input type="textarea" v-model="form.sysInfo.ssl" placeholder="请输入可信证书的html代码" clearable rows="5"/>
</el-form-item>
</el-form>
</ai-dialog-btn>
</template>
</template>
<template #content>
<template v-if="form.type=='mp'">
<el-form-item label="小程序AppId">
<el-input v-model="form.appId" clearable placeholder="小程序appId"/>
</el-form-item>
<el-form-item label="半屏小程序">
<el-input v-model="form.embeddedAppIdList" clearable placeholder="如果有多个,请用英文','分割"/>
</el-form-item>
<el-form-item label="接口是否单服务">
<el-checkbox v-model="form.isSingleService"/>
</el-form-item>
<el-form-item label="引导页">
<el-input v-model="form.guide" placeholder="带'/'会被认为是输入的页面路径" clearable/>
</el-form-item>
<ai-title title="底部导航栏">
<el-button type="text" slot="rightBtn" icon="iconfont iconAdd" @click="tabBar.list.push({})">添加</el-button>
</ai-title>
<ai-table :tableData="tabBar.list" :colConfigs="colConfigs" tableSize="mini" :isShowPagination="false" border ref="TabBar">
<el-table-column slot="options" label="操作" width="260" align="center">
<template slot-scope="{row,$index}">
<el-row type="flex" class="tabBarOptions">
<ai-dialog-btn text="更换" dialogTitle="选择应用">
<ai-lib-table :meta="appList" v-model="row.id" @select="v=>handleTabbarChange(row,v)" :isShowPagination="false" v-bind="$props"
:border="false"/>
</ai-dialog-btn>
<ai-dialog-btn text="编辑" dialogTitle="编辑导航栏" width="600px">
<el-form-item label="名称">
<el-input v-model="row.text" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="默认图标">
<el-input v-model="row.iconPath" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="选中图标">
<el-input v-model="row.selectedIconPath" placeholder="请输入" clearable/>
</el-form-item>
</ai-dialog-btn>
<el-button type="text" @click="handleTabbarDelete($index)">删除</el-button>
<el-button type="text" @click="handleTabbarPosition($index,-1)" v-if="$index>0">上移</el-button>
<el-button type="text" @click="handleTabbarPosition($index,1)" v-if="$index<tabBar.list.length-1">下移</el-button>
</el-row>
</template>
</el-table-column>
</ai-table>
</template>
<template v-else-if="form.type=='wxwork'">
<el-row type="flex">
<div class="fill">
<el-form-item label="接口是否单服务">
<el-checkbox v-model="form.isSingleService"/>
</el-form-item>
<el-form-item label="是否启用水印">
<el-checkbox v-model="form.waterMarker"/>
</el-form-item>
<el-form-item label="自定义登录">
<el-checkbox v-model="form.customLogin"/>
</el-form-item>
</div>
<div class="fill">
<el-form-item label="域名根目录(baseURL)">
<el-input v-model="form.base" clearable placeholder="域名根目录"/>
</el-form-item>
<el-form-item label="默认首页">
<el-input v-model="form.homePage" clearable placeholder="填写应用的文件名"/>
</el-form-item>
<el-form-item label="开启百度流量">
<el-checkbox v-model="form.hmt"/>
</el-form-item>
</div>
</el-row>
</template>
<template v-else-if="form.type=='web'">
<el-form-item label="头部导航工具栏">
<el-checkbox v-model="form.downloadCenter">下载中心</el-checkbox>
<el-checkbox v-model="form.dv">数据大屏</el-checkbox>
<el-checkbox v-model="form.showTool">显示/隐藏导航栏</el-checkbox>
<el-checkbox v-model="form.helpDoc">帮助文档</el-checkbox>
<el-checkbox v-model="form.customerService">智能客服</el-checkbox>
<el-checkbox v-model="form.appQRCode">手机APP</el-checkbox>
</el-form-item>
<el-row type="flex">
<div class="fill">
<el-form-item label="接口是否单服务">
<el-checkbox v-model="form.isSingleService"/>
</el-form-item>
<el-form-item label="默认首页">
<el-input v-model="form.homePage" clearable placeholder="填写应用的文件名"/>
</el-form-item>
<el-form-item label="是否加载AI助手">
<el-checkbox v-model="form.copilot"/>
</el-form-item>
</div>
<div class="fill">
<el-form-item label="域名根目录">
<el-input v-model="form.base" clearable placeholder="填写域名根目录(baseURL)"/>
</el-form-item>
<el-form-item label="开启百度流量">
<el-checkbox v-model="form.hmt"/>
</el-form-item>
</div>
</el-row>
</template>
</template>
</ai-card>
</el-form> </el-form>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="方案应用" lazy> <el-tab-pane label="方案应用" lazy>
<ai-lib-table :meta="appList" customData :isShowPagination="false" v-bind="$props" disabled :colConfigs="appListConfigs"> <ai-lib-table :meta="appList" customData :isShowPagination="false" v-bind="$props" disabled
:colConfigs="appListConfigs">
<template slot="options" slot-scope="{row}"> <template slot="options" slot-scope="{row}">
<ai-dialog-btn text="编辑" :customFooter="false" dialogTitle="应用配置" width="500px" @onConfirm="handleAppEdit(row)"> <ai-dialog-btn text="编辑" :customFooter="false" dialogTitle="应用配置" width="500px"
@onConfirm="handleAppEdit(row)">
<el-form size="small" label-width="80px"> <el-form size="small" label-width="80px">
<el-form-item label="应用名称"> <el-form-item label="应用名称">
<el-input v-model="row.label" clearable placeholder="请输入应用名称"/> <el-input v-model="row.label" clearable placeholder="请输入应用名称"/>
@@ -233,6 +66,9 @@
<script> <script>
import {mapActions} from "vuex" import {mapActions} from "vuex"
import AiLibTable from "./AiLibTable"; import AiLibTable from "./AiLibTable";
import webConfig from "./config/webConfig.vue";
import wxcpConfig from "./config/wxcpConfig.vue";
import wxmpConfig from "./config/wxmpConfig.vue";
export default { export default {
name: "add", name: "add",
@@ -256,41 +92,26 @@ export default {
return e return e
}).filter(e => e.project != "core") || [] }).filter(e => e.project != "core") || []
}, },
extraConfig: v => ({
web: webConfig,
wxwork: wxcpConfig,
mp: wxmpConfig
}[v.form.type])
}, },
data() { data() {
return { return {
form: {apps: [], type: null, sysInfo: {}, customPath: ""}, form: {apps: [], type: null, customPath: "", extra: {sysInfo: {}}},
rules: { rules: {
name: {required: true, message: "请输入"}, name: {required: true, message: "请输入"},
type: {required: true, message: "请选择"}, type: {required: true, message: "请选择"},
// customPath: {required: true, message: "请输入"}, // customPath: {required: true, message: "请输入"},
}, },
colConfigs: [
{prop: 'text', label: "名称", width: 120},
{prop: 'pagePath', label: "应用路径"},
{prop: 'iconPath', label: "默认图标"},
{prop: 'selectedIconPath', label: "选中图标"},
],
appListConfigs: [ appListConfigs: [
{prop: 'label', label: "应用名称", render: (h, {row}) => h(row.tabbar ? 'b' : 'p', row.label + ` ${row.tabbar ? '(底部导航栏)' : ''}`)}, {prop: 'label', label: "应用名称", render: (h, {row}) => h(row.tabbar ? 'b' : 'p', row.label + ` ${row.tabbar ? '(底部导航栏)' : ''}`)},
{prop: 'project', label: "项目/框架"}, {prop: 'project', label: "项目/框架"},
{prop: 'category', label: "分类", dict: "appsCategory"}, {prop: 'category', label: "分类", dict: "appsCategory"},
{prop: 'name', label: "模块名"} {prop: 'name', label: "模块名"}
], ],
tabBar: {
color: "#666666",
selectedColor: "#197DF0",
backgroundColor: "#ffffff",
list: [
{pagePath: "pages/AppHome/AppHome", text: "首页", iconPath: "static/TabBar/home.png", selectedIconPath: "static/TabBar/home_selected.png"},
{pagePath: "pages/AppModules/AppModules", text: "应用", iconPath: "static/TabBar/service.png", selectedIconPath: "static/TabBar/service_selected.png"},
{
pagePath: "pages/AppEnteringVillage/AppEnteringVillage", text: "进村",
iconPath: "static/TabBar/custom.png", selectedIconPath: "static/TabBar/custom_selected.png"
},
{pagePath: "pages/AppMine/AppMine", text: "我的", iconPath: "static/TabBar/me.png", selectedIconPath: "static/TabBar/me_selected.png"}
]
}
} }
}, },
methods: { methods: {
@@ -302,7 +123,6 @@ export default {
}).then(res => { }).then(res => {
if (res?.data) { if (res?.data) {
this.form = {...this.form, ...res.data} this.form = {...this.form, ...res.data}
this.handleSysTypeChange(this.form.type, this.form.extra)
} }
}) })
}, },
@@ -313,37 +133,9 @@ export default {
submit() { submit() {
this.$refs.AddForm.validate(v => { this.$refs.AddForm.validate(v => {
if (v) { if (v) {
const { if (this.form.type == 'web') {
tabBar, this.form.extra.sysInfo.desc = JSON.parse(this.form.extra.sysInfo.desc || null)
form: { this.form.extra.sysInfo.ssl = this.form.extra.sysInfo.ssl?.replace(/"/g, "'")
type,
appId,
embeddedAppIdList,
isSingleService,
homePage,
hmt,
dv,
downloadCenter,
showTool,
helpDoc,
customerService,
sysInfo,
appQRCode,
customLogin,
base,
guide,
copilot
}
} = this
if (type == 'mp') {
this.form.extra = {tabBar, appId, embeddedAppIdList, isSingleService, guide}
} else if (type == 'web') {
const {desc} = sysInfo
sysInfo.desc = JSON.parse(desc || null)
sysInfo.ssl = sysInfo.ssl?.replace(/"/g, "'")
this.form.extra = {isSingleService, homePage, hmt, dv, downloadCenter, showTool, helpDoc, customerService, sysInfo, appQRCode, base, copilot}
} else if (type == 'wxwork') {
this.form.extra = {isSingleService, homePage, hmt, customLogin, base}
} }
this.instance.post("/node/custom/addOrUpdate", this.form).then(res => { this.instance.post("/node/custom/addOrUpdate", this.form).then(res => {
if (res?.code == 0) { if (res?.code == 0) {
@@ -354,34 +146,11 @@ export default {
} }
}) })
}, },
handleSysTypeChange(v, data = {}) {
let values = this.$copy(data)
if (v == 'mp') {
if (values?.tabBar) {
this.tabBar = values.tabBar || this.tabBar
delete values.tabBar
}
} else if (v == 'web') {
if (values?.sysInfo?.desc) values.sysInfo.desc = JSON.stringify(values.sysInfo.desc)
}
Object.keys(values).map(e => this.$set(this.form, e, values[e]))
},
handleTabbarChange(row, {name, label}) {
row.text = label
row.pagePath = `pages/${name}/${name}`
},
handleTabbarDelete(i) {
this.tabBar.list?.splice(i, 1)
},
handleAppEdit(row) { handleAppEdit(row) {
const i = this.form.appList.findIndex(e => e.id == row.id) const i = this.form.appList.findIndex(e => e.id == row.id)
this.form.appList.splice(i, 1, row) this.form.appList.splice(i, 1, row)
}, },
handleTabbarPosition(i, offset) {
const row = this.tabBar.list[i]
this.tabBar.list.splice(i, 1, this.tabBar.list[i + offset])
this.tabBar.list.splice(i + offset, 1, row)
}
}, },
created() { created() {
this.getDetail() this.getDetail()
@@ -404,5 +173,16 @@ export default {
margin-left: 0; margin-left: 0;
} }
} }
:deep(.extraConfig) {
.ai-card__body {
display: grid;
grid-template-columns: 1fr 1fr;
.row {
grid-column: span 2;
}
}
}
} }
</style> </style>

View File

@@ -0,0 +1,116 @@
<script>
export default {
name: "webConfig",
model: {
prop: "form",
event: "input"
},
props: {
form: { default: () => ({ sysInfo: {} }) },
title: String
},
watch: {
form: {
handler(v) {
this.$emit("input", v)
},
deep: true
}
}
}
</script>
<template>
<ai-card :title="title" class="webConfig">
<template #right>
<ai-dialog-btn text="设置系统信息" dialogTitle="系统信息">
<el-form size="small" label-width="140px">
<el-form-item label="系统标题">
<el-input v-model="form.sysInfo.fullTitle" placeholder="请输入..." clearable />
</el-form-item>
<el-form-item label="favicon">
<el-input v-model="form.sysInfo.favicon" placeholder="请输入..." clearable />
</el-form-item>
<el-form-item label="logo">
<el-row type="flex">
<el-input v-model="form.sysInfo.logo" placeholder="请输入..." clearable />
<el-input class="mar-l10" v-model="form.sysInfo.logoText" placeholder="logo文字">
<template #prepend>logo文字</template>
</el-input>
</el-row>
</el-form-item>
<el-form-item label="登录页">
<el-row type="flex" class="mar-b10">
<el-input v-model="form.sysInfo.loginLogo" placeholder="登录页左上角logo,带/代表图片" clearable>
<template #prepend>左页首logo</template>
</el-input>
<el-input class="mar-l10" v-model="form.sysInfo.loginLogoText" placeholder="logo文字" clearable>
<template #prepend>logo文字</template>
</el-input>
</el-row>
<el-row type="flex">
<el-input v-model="form.sysInfo.name" placeholder="左上角标题" clearable>
<template #prepend>左上角标题</template>
</el-input>
<el-input class="mar-l10" v-model="form.sysInfo.title" placeholder="左上角副标题" clearable>
<template #prepend>左上角副标题</template>
</el-input>
</el-row>
<el-input class="mar-t10" type="textarea" rows="5" v-model="form.sysInfo.desc" placeholder="副标题"
clearable />
</el-form-item>
<el-row type="flex">
<div class="fill">
<el-form-item label="版权所有">
<el-input v-model="form.sysInfo.recordDesc" placeholder="请输入..." clearable />
</el-form-item>
<el-form-item label="备案号">
<el-input v-model="form.sysInfo.recordNo" placeholder="请输入..." clearable />
</el-form-item>
</div>
<div class="fill">
<el-form-item label="框架版本">
<!--edition 版本标准版standard上架版saas 简易版(不带扫码):simple -->
<el-input v-model="form.sysInfo.edition" placeholder="请输入..." clearable />
</el-form-item>
<el-form-item label="备案跳转链接">
<el-input v-model="form.sysInfo.recordURL" placeholder="请输入..." clearable />
</el-form-item>
</div>
</el-row>
<el-form-item label="可信证书">
<el-input type="textarea" v-model="form.sysInfo.ssl" placeholder="请输入可信证书的html代码" clearable rows="5" />
</el-form-item>
</el-form>
</ai-dialog-btn>
</template>
<template #content>
<el-form-item label="头部导航工具栏" class="row">
<el-checkbox v-model="form.downloadCenter">下载中心</el-checkbox>
<el-checkbox v-model="form.dv">数据大屏</el-checkbox>
<el-checkbox v-model="form.showTool">显示/隐藏导航栏</el-checkbox>
<el-checkbox v-model="form.helpDoc">帮助文档</el-checkbox>
<el-checkbox v-model="form.customerService">智能客服</el-checkbox>
<el-checkbox v-model="form.appQRCode">手机APP</el-checkbox>
</el-form-item>
<el-form-item label="接口是否单服务">
<el-checkbox v-model="form.isSingleService" />
</el-form-item>
<el-form-item label="域名根目录">
<el-input v-model="form.base" clearable placeholder="填写域名根目录(baseURL)" />
</el-form-item>
<el-form-item label="默认首页">
<el-input v-model="form.homePage" clearable placeholder="填写应用的文件名" />
</el-form-item>
<el-form-item label="登录页">
<el-input v-model="form.signPage" clearable placeholder="填写应用的文件名" />
</el-form-item>
<el-form-item label="开启百度流量">
<el-checkbox v-model="form.hmt" />
</el-form-item>
<el-form-item label="是否加载AI助手">
<el-checkbox v-model="form.copilot" />
</el-form-item>
</template>
</ai-card>
</template>

View File

@@ -0,0 +1,51 @@
<script>
export default {
name: "wxcpConfig",
model: {
prop: "form",
event: "input"
},
props: {
form: Object,
title: String
},
watch: {
form: {
handler() {
this.$emit("input", this.form)
},
deep: true
}
}
}
</script>
<template>
<ai-card :title="title" class="wxcpConfig">
<template #content>
<el-form-item label="默认首页">
<el-input v-model="form.homePage" clearable placeholder="填写应用的文件名"/>
</el-form-item>
<el-form-item label="域名根目录(baseURL)">
<el-input v-model="form.base" clearable placeholder="域名根目录"/>
</el-form-item>
<el-form-item label="是否启用水印">
<el-checkbox v-model="form.waterMarker"/>
</el-form-item>
<el-form-item label="接口是否单服务">
<el-checkbox v-model="form.isSingleService"/>
</el-form-item>
<el-form-item label="自定义登录">
<el-checkbox v-model="form.customLogin"/>
</el-form-item>
<el-form-item label="开启百度流量">
<el-checkbox v-model="form.hmt"/>
</el-form-item>
</template>
</ai-card>
</template>
<style scoped lang="scss">
.wxcpConfig {
}
</style>

View File

@@ -0,0 +1,126 @@
<script>
import AiLibTable from "../AiLibTable.vue";
export default {
name: "wxmpConfig",
components: {AiLibTable},
model: {
prop: "form",
event: "input"
},
props: {
form: Object,
title: String,
appList: {default: () => []}
},
watch: {
form: {
handler(v) {
this.$emit("input", v)
},
deep: true
},
tabBar: {
deep: true, handler(v) {
this.$emit("input", {...this.form, tabBar: {...v, list: v.list.filter(e => !!e.pagePath) || []}})
}
}
},
data() {
return {
colConfigs: [
{prop: 'text', label: "名称", width: 120},
{prop: 'pagePath', label: "应用路径"},
{prop: 'iconPath', label: "默认图标"},
{prop: 'selectedIconPath', label: "选中图标"},
],
tabBar: {
color: "#666666",
selectedColor: "#197DF0",
backgroundColor: "#ffffff",
list: [
{pagePath: "pages/AppHome/AppHome", text: "首页", iconPath: "static/TabBar/home.png", selectedIconPath: "static/TabBar/home_selected.png"},
{pagePath: "pages/AppModules/AppModules", text: "应用", iconPath: "static/TabBar/service.png", selectedIconPath: "static/TabBar/service_selected.png"},
{
pagePath: "pages/AppEnteringVillage/AppEnteringVillage", text: "进村",
iconPath: "static/TabBar/custom.png", selectedIconPath: "static/TabBar/custom_selected.png"
},
{pagePath: "pages/AppMine/AppMine", text: "我的", iconPath: "static/TabBar/me.png", selectedIconPath: "static/TabBar/me_selected.png"}
]
}
}
},
methods: {
handleTabbarChange(row, {name, label}) {
row.text = label
row.pagePath = `pages/${name}/${name}`
},
handleTabbarDelete(i) {
this.tabBar.list?.splice(i, 1)
},
handleTabbarPosition(i, offset) {
const row = this.tabBar.list[i]
this.tabBar.list.splice(i, 1, this.tabBar.list[i + offset])
this.tabBar.list.splice(i + offset, 1, row)
}
},
created() {
if (this.form.tabBar?.list?.length > 0) {
this.tabBar = this.form.tabBar
}
}
}
</script>
<template>
<ai-card :title="title" class="wxmpConfig">
<template #content>
<el-form-item label="小程序AppId">
<el-input v-model="form.appId" clearable placeholder="小程序appId"/>
</el-form-item>
<el-form-item label="半屏小程序">
<el-input v-model="form.embeddedAppIdList" clearable placeholder="如果有多个,请用英文','分割"/>
</el-form-item>
<el-form-item label="接口是否单服务">
<el-checkbox v-model="form.isSingleService"/>
</el-form-item>
<el-form-item label="引导页">
<el-input v-model="form.guide" placeholder="带'/'会被认为是输入的页面路径" clearable/>
</el-form-item>
<ai-title class="row" title="底部导航栏">
<el-button type="text" slot="rightBtn" icon="iconfont iconAdd" @click="tabBar.list.push({})">添加</el-button>
</ai-title>
<ai-table class="row" :tableData="tabBar.list" :colConfigs="colConfigs" tableSize="mini" :isShowPagination="false" border ref="TabBar">
<el-table-column slot="options" label="操作" width="260" align="center">
<template slot-scope="{row,$index}">
<el-row type="flex" class="tabBarOptions">
<ai-dialog-btn text="更换" dialogTitle="选择应用">
<ai-lib-table :meta="appList" v-model="row.id" @select="v=>handleTabbarChange(row,v)" :isShowPagination="false" v-bind="$props"
:border="false"/>
</ai-dialog-btn>
<ai-dialog-btn text="编辑" dialogTitle="编辑导航栏" width="600px">
<el-form-item label="名称">
<el-input v-model="row.text" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="默认图标">
<el-input v-model="row.iconPath" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="选中图标">
<el-input v-model="row.selectedIconPath" placeholder="请输入" clearable/>
</el-form-item>
</ai-dialog-btn>
<el-button type="text" @click="handleTabbarDelete($index)">删除</el-button>
<el-button type="text" @click="handleTabbarPosition($index,-1)" v-if="$index>0">上移</el-button>
<el-button type="text" @click="handleTabbarPosition($index,1)" v-if="$index<tabBar.list.length-1">下移</el-button>
</el-row>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
</template>
<style scoped lang="scss">
.wxmpConfig {
}
</style>

View File

@@ -0,0 +1,128 @@
<script>
import {mapState} from "vuex";
export default {
name: "AppAccountConfigManage",
label: "配置管理",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns: [
{label: "序号", type: "index"},
{label: "账号", prop: "userName"},
{label: "姓名", prop: "name"},
{label: "角色", prop: "roleName"},
{label: "所属端", prop: "type", dict: "roleType", width: 120, align: 'center'},
{label: "状态", prop: "configStatus", dict: "configStatus", width: 120, align: 'center'},
],
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {name: ""},
dialog: false,
userId: "",
treeData: []
}
},
computed: {
...mapState(['user'])
},
methods: {
getTableData() {
this.instance.post("/api/user/config/page", null, {
params: {...this.page, ...this.search}
}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
getTreeData() {
const {userId} = this
this.instance.post("/api/siteUser/querySiteByUserId", null, {params: {userId}}).then(res => {
if (res?.data) {
this.treeData = res.data
}
})
},
handleDelete(node) {
this.$confirm("是否要删除该节点?").then(() => {
this.instance.post("/api/siteUser/del", null, {params: {ids: node.id}}).then(res => {
if (res?.code == '0' && res?.data != 1) {
this.$message.success("删除成功!")
this.getTreeData()
} else {
this.$message.error(res.msg)
}
})
})
},
createNode(data) {
this.$prompt("请输入名称").then(({value}) => {
const {userId} = this
this.instance.post("/api/siteUser/add", null, {params: {name: value, parentId: data.id, userId}}).then(res => {
if (res?.code == '0' && res?.data != 1) {
this.$message.success("新增成功!")
this.getTreeData()
} else {
this.$message.error(res.msg)
}
})
})
}
},
created() {
this.dict.load("roleType", "configStatus")
this.getTableData()
}
}
</script>
<template>
<ai-page class="AppAccountConfigManage" :title="$options.label">
<ai-search-bar>
<template #right>
<el-input size="small" placeholder="搜索账号" v-model="search.name" clearable
@change="page.pageNum=1, getTableData()" @getList="getTableData"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" @click="dialog=true,userId=row.id">配置</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog v-model="dialog" title="场地配置" width="50vw" @close="userId='',getTableData()"
@open="getTreeData" customFooter>
<el-button class="mar-b8" type="primary" @click="createNode(treeData)">新增根节点</el-button>
<el-tree :data="treeData" :props="{label:'name'}" default-expand-all>
<template slot-scope="{node,data}">
<div class="flex" style="width: 100%">
<span class="fill" v-text="node.label"/>
<el-button size="mini" type="text" @click="createNode(data)">增加子节点</el-button>
<el-button size="mini" type="text" @click="handleDelete(data)" v-if="$isEmpty(data.children)" class="del">删除</el-button>
</div>
</template>
</el-tree>
<el-button slot="footer" @click="dialog=false,getTableData()">关闭</el-button>
</ai-dialog>
</ai-page>
</template>
<style scoped lang="scss">
.AppAccountConfigManage {
height: 100%;
.el-button .del {
color: #f46;
}
}
</style>

View File

@@ -0,0 +1,263 @@
<template>
<section class="AppAccountManage">
<ai-page title="账号管理">
<ai-area-tree :root-id="rootArea" slot="left" v-model="search.areaId" range="3" @input="page.pageNum=1,getTableData()"/>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="dialog = true">添加</el-button>
<!-- <el-button type="primary" :disabled="!ids.toString()" @click="batchAllot">功能分配</el-button>-->
</template>
<template #right>
<el-input size="small" placeholder="搜索姓名、手机号" v-model="search.condition" clearable
@change="page.pageNum=1, getTableData()"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize"
@getList="getTableData" :col-configs="colConfigs" :dict="dict" @selection-change="v => ids = v.map(e => e.id)">
<el-table-column slot="name" label="姓名" width="180px">
<el-row type="flex" align="middle" slot-scope="{row}">
<el-image class="avatar" :src="row.avatar" :preview-src-list="[row.avatar]">
<el-image slot="error" src="https://cdn.cunwuyun.cn/dvcp/h5/defaultAvatar.png" alt=""/>
</el-image>
<div>{{ row.name }}</div>
</el-row>
</el-table-column>
<el-table-column slot="options" align="center" label="操作" fixed="right" width="220px">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="changeEnable(row)">{{ row.status == 1 ? '禁用' : '启用' }}</el-button>
<el-button type="text" @click="appAllot(row)">编辑</el-button>
<el-button type="text" @click="resetPassword(row.id)">重置密码</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
<!--添加账号功能分配-->
<ai-dialog :title="dialogTitle" :visible.sync="dialog" width="60vw" @open="initDialogData"
@onConfirm="updateAccount" @closed="dialogForm = {}">
<el-form ref="updateAccountForm" :model="dialogForm" :rules="rules" size="small" label-width="120px" class="grid">
<el-form-item required label="行政区划" prop="areaId">
<ai-area-get v-model.trim="dialogForm.areaId" placeholder="请选择" :instance="instance"/>
</el-form-item>
<el-form-item required label="账号" prop="userName">
<el-input v-model.trim="dialogForm.userName" placeholder="请输入..." clearable :maxLength="15"/>
</el-form-item>
<!-- <el-form-item required label="账号密码" prop="password" v-if="!isEdit" :rules="[{ required: true, message: '请输入密码' }]">-->
<!-- <el-input v-model.trim="dialogForm.password" placeholder="请输入密码" clearable :minlength="6"/>-->
<!-- </el-form-item>-->
<el-form-item required label="角色" prop="roleId">
<el-select placeholder="请选择角色" :value="dialogForm.roleId" filterable v-model="dialogForm.roleId" clearable>
<el-option v-for="(op, i) in accountRoles" :key="i" :label="op.name" :value="op.id"/>
</el-select>
</el-form-item>
<el-form-item required label="姓名" prop="name">
<el-input v-model.trim="dialogForm.name" placeholder="请输入..." clearable :maxLength="15"/>
</el-form-item>
<el-form-item label="手机号码" prop="phone">
<el-input v-model.trim="dialogForm.phone" placeholder="请输入..." clearable :maxLength="11"/>
</el-form-item>
<el-form-item label="身份证号" prop="idCard" :rules="[{required:true,message:'请输入身份证号'}]">
<ai-input v-model.trim="dialogForm.idCard" :maxLength="18"/>
</el-form-item>
</el-form>
</ai-dialog>
</section>
</template>
<script>
import {mapState} from "vuex";
export default {
name: "AppAccountManage",
label: "账号管理",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
...mapState(['user']),
cascaderProps() {
return {
value: 'id',
checkStrictly: true,
emitPath: false
}
},
isEdit() {
return !!this.dialogForm.id
},
dialogTitle() {
return this.isEdit ? '编辑账号' : '添加账号'
},
colConfigs() {
return [
// {type: 'selection', align: 'center'},
{label: "账号", prop: "userName"},
{label: "姓名", slot: "name"},
{label: "联系方式", prop: "phone", align: 'center'},
{label: "角色", prop: "roleName", align: 'center'},
{label: "状态", prop: "status", align: 'center', dict: "enable"},
{label: "认证状态", prop: "authStatus", align: 'center', dict: "authStatus"},
{label: "配置状态", prop: "configStatus", align: 'center', dict: "configStatus"},
]
},
rules() {
return {
userName: [{required: true, message: "请输入账号"}],
name: [{required: true, message: "请输入姓名"}],
password: [{required: true, message: '请输入密码'}],
areaId: [{required: true, message: "请选择行政区划"}],
roleId: [{required: true, message: "请选择角色"}],
// phone: [{required: true, message: "请输入手机号码"}]
}
},
rootArea: v => v.user.info.areaId
},
data() {
return {
accountRoles: [],
page: {pageNum: 1, pageSize: 10, total: 0},
dialog: false,
dialogForm: {},
tableData: [],
search: {condition: ""},
ids: [],
form: {
appids: [],
userId: ''
},
}
},
methods: {
getTableData() {
this.instance.post("/admin/user/page", null, {
params: {...this.page, ...this.search}
}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
onConfirm() {
this.$refs.form.validate((valid) => {
if (valid) {
this.instance.post(`/api/sysuserwxmp/addOrUpdate`, {
...this.form
}).then(res => {
if (res.code == 0) {
this.getTableData()
this.$message.success('提交成功!')
this.getList()
}
})
}
})
},
initDialogData() {
//用于优化初始化数据
this.getAccountRoles()
},
getAccountRoles() {
this.accountRoles.length == 0 && this.instance.post("/admin/role/list-all").then(res => {
if (res?.data) {
this.accountRoles = res.data
}
})
},
batchAllot() {
this.dialog = true
this.dialogForm = {areaId: this.user.info.areaId, ids: this.ids}
},
appAllot(row) {
this.dialog = true
this.dialogForm = JSON.parse(JSON.stringify({
...row,
areaId: row.areaId || this.user.info.areaId
}));
},
// 修改
updateAccount() {
this.$refs.updateAccountForm.validate(v => {
if (v) {
this.instance.post("/admin/user/addOrEdit", this.dialogForm).then(res => {
if (res?.code == 0) {
this.dialog = false;
this.$message.success("提交成功")
this.getTableData();
} else {
this.$message.error(res?.msg)
}
})
}
})
},
handleDelete(row) {
const {id: ids, authStatus, configStatus} = row
let text = "是否要删除该账号?"
if (authStatus == 1) {
text = configStatus == 1 ? "该账户已经认证,是否确认删除该账户?" : "该账户已经认证及配置,是否确认删除该账户?"
}
this.$confirm(text).then(() => {
this.instance.post("/admin/user/del", null, {
params: {ids}
}).then(res => {
if (res?.code == 0) {
this.getTableData();
this.$message.success("删除成功!");
}
})
}).catch(() => 0)
},
changeEnable(row) {
const {status, id} = row
this.$confirm(`是否要${status == 1 ? '禁用' : '启用'}该账号?`).then(() => {
this.instance.post("/api/user/update-status", null, {params: {id}}).then(res => {
if (res?.code == 0) {
this.$message.success(`${status == 1 ? '禁用' : '启用'}成功!`)
this.getTableData()
}
})
})
},
resetPassword(id) {
this.$confirm("是否要重置密码?").then(() => {
this.instance.post("/api/user/resetPwd", null, {params: {id}}).then(res => {
if (res?.code == 0) {
this.$message.success("重置成功!")
this.getTableData()
}
})
})
}
},
created() {
this.dict.load("enable", "authStatus", "configStatus")
this.getTableData()
}
}
</script>
<style lang="scss" scoped>
.AppAccountManage {
height: 100%;
:deep(.avatar) {
width: 40px;
height: 40px;
margin-right: 10px;
}
:deep(.el-form) {
.el-cascader,
.el-select {
width: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,43 @@
<script>
import authAdd from "./authAdd.vue";
import authList from "./authList.vue";
export default {
name: "AppAuthManage",
label: "认证审核",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? authAdd : authList
}
},
data(){
return {
certificates:[
{label: "身份证(正面)", prop: "frontCard"},
{label: "身份证(反面)", prop: "reverseCard"},
{label: "营业执照", prop: "businessPic", permit: ["breed"]},
{label: "畜禽经营许可证", prop: "breedPic", permit: ["breed"]},
{label: "动物防疫条件许可证", prop: "prevention", permit: ["breed"]},
{label: "组织机构证明", prop: "orgPic", permit: ["bank", "insurance"]},
]
}
}
}
</script>
<template>
<section class="AppAuthManage">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppAuthManage {
height: 100%;
}
</style>

View File

@@ -0,0 +1,85 @@
<script>
export default {
name: "authAdd",
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {},
}
},
computed: {
isAuditing: v => v.detail.auditStatus == 1
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
this.instance.post("/api/user/auth/page", null, {params: {id}}).then(res => {
if (res?.data?.records) {
const detail = res.data.records[0] || {}
let {picture = "{}"} = detail
picture = JSON.parse(picture)
this.detail = {...detail, ...picture}
}
})
},
getNeedCerts(type) {
return this.$parent.certificates.filter(e => !e.permit || e.permit.includes(type))
},
handleAudit(auditStatus) {
const auditLabels = {
2: "同意通过", 3: "驳回"
}
this.$confirm(`是否要${auditLabels[auditStatus]}认证?`).then(() => {
this.instance.post("/api/user/audit", null, {params:{
id: this.detail.id, auditStatus
}}).then(res => {
if (res?.code == 0) {
this.$confirm("是否要返回列表?","提交成功").then(() => this.back())
}
})
})
}
},
created() {
this.dict.load("auditStatus")
this.getDetail()
}
}
</script>
<template>
<ai-page title="认证材料" class="authAdd" showBack content-string="detail">
<el-form size="small" label-position="top" :model="detail" ref="detail">
<ai-card title="认证材料">
<div class="grid">
<el-form-item v-for="(op,i) in getNeedCerts(detail.type)" :key="i" v-bind="op" :rules="{required:true,message:`请上传${op.label}`,trigger:'change'}">
<el-image :src="detail[op.prop]" :preview-src-list="[detail[op.prop]]"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="备注说明">
<div v-text="detail.remark"/>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isAuditing">
<el-button type="primary" @click="handleAudit(2)">同意</el-button>
<el-button type="danger" @click="handleAudit(3)">拒绝</el-button>
</template>
<el-button @click="back">关闭</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.authAdd {
}
</style>

View File

@@ -0,0 +1,135 @@
<script>
const columns = [
{label: "序号", type: "index"},
{label: "账号", prop: "userName"},
{label: "姓名", prop: "name"},
{label: "角色", prop: "roleName"},
{label: "所属端", prop: "type", dict: "roleType", width: 120, align: 'center'},
{label: "状态", prop: "authStatus", dict: "authStatus", width: 120, align: 'center'},
{label: "审核状态", prop: "auditStatus", dict: "auditStatus", width: 120, align: 'center'},
]
export default {
name: "authList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {name: ""},
dialog: false,
form: {}
}
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/user/auth/page", null, {
params: {...this.page, ...this.search}
}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.authStatus}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
getNeedCerts(type) {
return this.$parent.certificates.filter(e => !e.permit || e.permit.includes(type))
},
handleConfirm() {
this.$refs.form.validate().then(() => {
const {id, remark} = this.form, picture = {}
this.$parent.certificates.forEach(e => {
picture[e.prop] = this.form[e.prop]
})
this.instance.post("/api/user/savePicture", null, {
params: {
id, remark, picture: JSON.stringify(picture)
}
}).then(res => {
if (res?.code == '0' && res?.data != 1) {
this.dialog = false
this.$message.success("提交成功!")
this.getTableData()
}
})
})
},
handleUploadPics(row = {}) {
let {id, type, remark, picture = "{}"} = row
picture = JSON.parse(picture)
this.form = {id, type, remark, ...picture}
this.dialog = true
}
},
created() {
this.dict.load("roleType", "authStatus", "auditStatus")
this.getTableData()
}
}
</script>
<template>
<ai-page class="authList" title="认证审核">
<ai-search-bar>
<template #left>
<ai-select v-model="search.authStatus" dict="authStatus" placeholder="状态"/>
<ai-select v-model="search.auditStatus" dict="auditStatus" placeholder="审核状态"/>
</template>
<template #right>
<el-input size="small" placeholder="搜索账号" v-model="search.name" clearable
@change="page.pageNum=1, getTableData()" @getList="getTableData"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" v-if="'12'.includes(row.permit)"
@click="$router.push({query:{id:row.id},hash:'#add'})">查看
</el-button>
<el-button class="deleteBtn" type="text" v-if="'11'.includes(row.permit)"
@click="$router.push({query:{id:row.id},hash:'#add'})">审核
</el-button>
<el-button type="text" v-if="'00|13'.includes(row.permit)"
@click="handleUploadPics(row)">认证材料
</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog v-model="dialog" title="认证材料" width="60%" @close="form={}" @confirm="handleConfirm">
<el-form class="grid c-3" :model="form" ref="form" label-width="160px">
<el-form-item v-for="(op,i) in getNeedCerts(form.type)" :key="i" v-bind="op" :rules="{required:true,message:`请上传${op.label}`,trigger:'change'}">
<ai-uploader v-model="form[op.prop]" valueIsUrl :limit="1" :instance="instance"/>
</el-form-item>
<el-form-item class="row" label="备注说明" prop="remark">
<el-input type="textarea" :rows="2" v-model="form.remark" placeholder="备注说明具体情况"/>
</el-form-item>
</el-form>
</ai-dialog>
</ai-page>
</template>
<style scoped lang="scss">
.authList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppBreedArchive",
label: "电子档案",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? add : list
}
},
created() {
this.dict.load("archiveStatus", "category", "variety", "insuranceType", "deathReason", "dataSources", "yesOrNo")
}
}
</script>
<template>
<section class="AppBreedArchive">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppBreedArchive {
height: 100%;
}
</style>

View File

@@ -0,0 +1,182 @@
<script>
import {mapState} from "vuex"
const columns = {
weightList: [
{label: "序号", type: "index"},
{label: "重量", prop: "weight"},
{label: "称重时间", prop: "createTime"},
{label: "数据来源", prop: "source", dict: "dataSources"},
{label: "是否变更过", prop: "isUpdate", dict: "yesOrNo"},
],
immunityList: [
{label: "序号", type: "index"},
{label: "疫苗名称", prop: "vaccineName"},
{label: "用药方式", prop: "method"},
{label: "药量(ml)", prop: "dosage"},
{label: "生产厂家", prop: "factory"},
{label: "厂家批号", prop: "batchNumber"},
{label: "免疫时间", prop: "immunityTime"},
{label: "登记时间", prop: "createTime"},
{label: "防疫员", prop: "userName"},
],
treatmentList: [
{label: "序号", type: "index"},
{label: "药品名称", prop: "drugName"},
{label: "药量(ml)", prop: "dosage"},
{label: "生产厂家", prop: "factory"},
{label: "厂家批号", prop: "batchNumber"},
{label: "疾病名称", prop: "diseaseName"},
{label: "症状", prop: "symptom"},
{label: "兽医", prop: "userName"},
{label: "治疗时间", prop: "immunityTime"},
{label: "登记时间", prop: "createTime"},
],
outList: [
{label: "序号", type: "index"},
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category", width: 120},
{label: "品种", prop: "variety", dict: "variety", width: 120},
{label: "日龄(天)", prop: "age", width: 80},
{label: "淘汰时间", prop: "outTime"},
{label: "淘汰原因", prop: "reason"}
],
deathList: [
{label: "序号", type: "index"},
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category", width: 120},
{label: "品种", prop: "variety", dict: "variety", width: 120},
{label: "日龄(天)", prop: "age", width: 80},
{label: "死亡时间", prop: "deathTime"},
{label: "死亡原因", prop: "reason", dict: "deathReason", width: 80},
{label: "登记时间", prop: "createTime"},
{label: "操作人", prop: "userName", width: 100},
],
insuranceList: [
{label: "序号", type: "index"},
{label: "保险类型", prop: "insuranceType", dict: "insuranceType"},
{label: "保单编号", prop: "orderNo"},
]
}
const forms = {
device: [
{label: "当前温度", prop: "temperature"},
{label: "温度状态", prop: "temperatureStatus", dict: "temperatureStatus"},
{label: "运动状态", prop: "sportsSituation", dict: "sportsSituation"},
{label: "在栏状态", prop: "status", dict: "archiveStatus"},
],
loan: [
{label: "贷款合同编号", prop: "contractNo"},
]
}
const navs = [
{label: "体重记录", value: "weightList"},
{label: "免疫记录", value: "immunityList"},
{label: "治疗记录", value: "treatmentList"},
{label: "淘汰记录", value: "outList"},
{label: "死亡记录", value: "deathList"},
{label: "设备检测", value: "device"},
{label: "贷款信息", value: "loan"},
{label: "保险信息", value: "insuranceList"},
]
export default {
name: "baAdd",
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []},
active: "weightList",
columns, navs, forms
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
isAdd: v => !v.$route.query.id,
isEdit: v => v.$route.query.edit == 1,
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/report/getInfo", null, {params: {biochipEarNumber: id}}).then(res => {
if (res?.data) {
const detail = res.data
Object.keys(columns).forEach(key => {
detail[key] = detail[key] || []
})
return this.detail = {...detail}
}
})
},
},
created() {
this.dict.load("auditStatus", "category", "variety")
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="baAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid c-3">
<el-form-item label="养殖户" prop="userName" class="row">
<b v-text="detail.userName"/>
</el-form-item>
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
<el-form-item label="生物芯片耳标号" prop="penId">
<b v-text="detail.biochipEarNumber"/>
</el-form-item>
<el-form-item label="电子耳标号" prop="penId">
<b v-text="detail.electronicEarNumber"/>
</el-form-item>
<el-form-item label="原厂耳标号" prop="penId">
<b v-text="detail.originalEarNumber"/>
</el-form-item>
</div>
</ai-card>
<el-tabs type="border-card" v-model="active">
<el-tab-pane v-for="(nav,i) in navs" :key="i" :label="nav.label" :name="nav.value" lazy>
<template v-if="active==nav.value">
<ai-table v-if="columns[nav.value]" :colConfigs="columns[nav.value]" :table-data="detail[nav.value]" :isShowPagination="!1"/>
<el-form v-if="forms[nav.value]" size="small" class="grid" label-width="120px">
<el-form-item v-for="(item,i) in forms[nav.value]" :key="i" :label="item.label" :prop="item.prop">
<b v-text="dict.getLabel(item.dict||'yesOrNo',detail[item.prop])"/>
</el-form-item>
</el-form>
</template>
</el-tab-pane>
</el-tabs>
</el-form>
<div slot="footer">
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.baAdd {
}
</style>

View File

@@ -0,0 +1,110 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖户", prop: "userName", width: 100},
{label: "养殖场", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`, minWidth: 200},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "电子耳标号", prop: "biochipEarNumber"},
{label: "原厂耳标号", prop: "biochipEarNumber"},
{label: "入栏日期", prop: "createTime", width: 160},
{label: "饲养时长(天)", prop: "days", width: 100, align: 'right', headerAlign: 'center'},
{label: "最新体重(公斤)", prop: "weight", width: 120, align: 'right', headerAlign: 'center'},
// {label: "当前体温", prop: "temperatureStatus", dict: 'temperatureStatus'},
// {label: "运动情况", prop: "sportsSituation", dict: "sportsSituation"},
]
export default {
name: "baList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/report/getArchivePage", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="baList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖户" v-model="search.userId" :instance="instance" :action="`/api/report/getOrgList`" :prop="{label:'name'}" readonly/>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${search.userId||''}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<ai-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber"/>
<ai-input placeholder="电子耳标号" v-model="search.electronicEarNumber"/>
<ai-input placeholder="原厂耳标号" v-model="search.originalEarNumber"/>
<ai-select placeholder="全部状态" v-model="search.status" dict="archiveStatus"/>
<ai-select placeholder="全部类别" v-model="search.category" dict="category"/>
<ai-select placeholder="全部品种" v-model="search.variety" dict="variety"/>
<ai-search label="入栏日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<ai-download :instance="instance" url="/api/report/exportArchive" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.baList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppClaimApply",
label: "理赔申请",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return ["#claim", "#add"].includes(hash) ? add : list
}
},
created() {
this.dict.load("auditStatus", "insureType", "insureStatus", "category", "variety")
},
}
</script>
<template>
<section class="AppClaimApply">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppClaimApply {
height: 100%;
}
</style>

View File

@@ -0,0 +1,147 @@
<script>
import { mapState } from "vuex"
import AiEartagPicker from "@project/xumu/components/AiEartagPicker.vue";
const records = [
{ label: "序号", type: "index" },
{ label: "报案号", prop: "reportNo" },
{ label: "审批状态", prop: "auditStatus", dict: "auditStatus" },
{ label: "审批时间", prop: "auditTime" },
{ label: "审批人", prop: "auditName" },
]
export default {
name: "claimAdd",
components: { AiEartagPicker },
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: { detailList: [], list: [] },
records
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.isClaim ? `新增${appName}` : `${appName}详情`
},
isClaim: v => v.$route.hash == "#claim",
formImages: v => [
{ label: "勘察报告书", prop: "surveyPicture", rules: { required: v.isClaim, message: '请上传 勘察报告书' } },
{ label: "无害化回执单", prop: "receiptPicture", rules: { required: v.isClaim, message: '请上传 无害化回执单' } },
],
columns: v => [
{ label: "序号", type: "index" },
{ label: "生物芯片耳标号", prop: "biochipEarNumber" },
{ label: "身长测量照片", prop: "heightPicture", upload: { instance: v.instance, readonly: !v.isClaim, valueIsUrl: !0, limit: 1 } },
{ label: "电子耳标照片", prop: "earNumberPicture", upload: { instance: v.instance, readonly: !v.isClaim, valueIsUrl: !0, limit: 1 } },
{ label: "防疫耳标照片", prop: "preventionPicture", upload: { instance: v.instance, readonly: !v.isClaim, valueIsUrl: !0, limit: 1 } },
{ label: "无害化处理照片", prop: "harmlessPicture", upload: { instance: v.instance, readonly: !v.isClaim, valueIsUrl: !0, limit: 1 } },
{ label: "报案号", prop: "reportNo", hide: v.isClaim },
].filter(e => !e.hide),
selectedEartags: v => v.detail.list?.length || 0,
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const { id } = this.$route.query
return id && this.instance.post("/api/insurance/claim/apply/getInfo", null, { params: { orderNo: id } }).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.detailList || []
return this.detail = { ...detail }
}
})
},
submit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/insurance/claim/apply/add", { ...this.detail }).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
}
},
created() {
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="claimAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid">
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName" />
</el-form-item>
<el-form-item label="承保公司" prop="companyId">
<b v-text="detail.companyName" />
</el-form-item>
<el-form-item label="投保类型">
<ai-input :value="dict.getLabel('insureType', detail.insureType)" :edit="!1" />
</el-form-item>
<el-form-item label="保险产品" prop="productType">
<b v-text="detail.productType" />
</el-form-item>
<el-form-item label="联系人">
<ai-input v-model="detail.contacts" :edit="!1" />
</el-form-item>
<el-form-item label="联系电话">
<ai-input v-model="detail.phone" :edit="!1" />
</el-form-item>
</div>
</ai-card>
<ai-card title="投保对象">
<template #right v-if="isClaim">
<ai-eartag-picker @select="v => detail.detailList = v.map(e=>({biochipEarNumber:e}))" :instance="instance"
:action="`/api/insurance/claim/apply/getClaimEarNumberList?orderNo=${detail.orderNo}`">
<el-button type="text">选择</el-button>
</ai-eartag-picker>
</template>
<ai-highlight class="mar-b8 font-14" :content="`投保标的共${detail.insureNumber || 0}只,已理赔标的共 @v 只`" color="red"
:value="selectedEartags" />
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1" hideOptions />
</ai-card>
<ai-card title="理赔材料" v-if="isClaim">
<div class="font-12 mar-b8">只能上传JPG/PNG文件且不超过2M一次最多5张</div>
<el-form-item v-for="(img, i) in formImages" :key="i" v-bind="img">
<ai-uploader v-model="detail[img.prop]" :instance="instance" value-is-url :limit="5" />
</el-form-item>
</ai-card>
<ai-card title="理赔记录" v-else>
<ai-table :tableData="detail.list" :colConfigs="records" :isShowPagination="!1" hideOptions />
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isClaim">
<el-button type="primary" @click="submit(1)">提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.claimAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
}
</style>

View File

@@ -0,0 +1,123 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "投保单号", prop: "orderNo"},
{label: "所属养殖户", prop: "applyName"},
{label: "所属养殖场", prop: "farmName"},
{label: "投保数量(头)", prop: "insureNumber", width: 120},
{label: "投保状态", prop: "status", width: 160, dict: "insureStatus"},
{label: "投保类型", prop: "insureType", dict: "insureType"},
{label: "投保时间", prop: "createTime"},
{label: "可理赔数量", prop: "unpaidClaimNumber", width: 120},
{label: "说明", prop: "remarks"},
]
export default {
name: "claimList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/insurance/claim/apply/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: e.remarks == "可申请理赔"}))
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="claimList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-input placeholder="投保订单号" v-model="search.orderNo"/>
<ai-select placeholder="全部投保类型" v-model="search.insureType" dict="insureType"/>
<ai-search label="投保日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="养殖户" v-model="search.applyName"/>
<ai-input placeholder="养殖场" v-model="search.farmName"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<ai-download :instance="instance" url="/api/insurance/claim/apply/export" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="row.permit">
<el-button type="text" @click="dialog=true,$set(form,'id',row.orderNo)">理赔</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.orderNo}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog v-model="dialog" title="理赔须知" @closed="form={}">
<el-form size="small" label-position="top" :mode="form" ref="form">
<el-form-item label="如遇一下情况进行赔付:">
1自然灾害如暴雨洪水台风冰雹雷击暴风雪等导致的肉牛死亡或伤残<br/>
2疾病与疫病:包括但不限于口蹄疫布鲁氏菌病炭疽牛结核病等对肉牛生命安全造成威胁的疾病<br/>
3意外事故:如火灾爆炸触电盗窃走失等<br/>
4强制扑杀:由于政府政策或疫情控制需要对肉牛进行的强制扑杀
</el-form-item>
<el-form-item class="flex center">
<el-checkbox v-model="form.agree">本人阅读并知晓理赔须知承认上传资料的真实性</el-checkbox>
</el-form-item>
</el-form>
<template #foot>
<el-button @click="$router.push({hash:'#claim',query:{id:form.id}})" type="primary" :disabled="!form.agree">符合要求立即申请</el-button>
<el-button @click="dialog=false">取消</el-button>
</template>
</ai-dialog>
</ai-page>
</template>
<style scoped lang="scss">
.claimList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppClaimAudit",
label: "理赔审核",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return ["#audit", "#add"].includes(hash) ? add : list
}
},
created() {
this.dict.load("auditStatus", "insureType", "insureStatus", "category", "variety")
},
}
</script>
<template>
<section class="AppClaimAudit">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppClaimAudit {
height: 100%;
}
</style>

View File

@@ -0,0 +1,160 @@
<script>
import {mapState} from "vuex"
import AiEartagPicker from "@project/xumu/components/AiEartagPicker.vue";
import AiSelect from "dui/packages/basic/AiSelect.vue";
const records = [
{label: "序号", type: "index"},
{label: "报案号", prop: "reportNo"},
{label: "审批状态", prop: "auditStatus", dict: "auditStatus"},
{label: "审批时间", prop: "auditTime"},
{label: "审批人", prop: "auditName"},
]
export default {
name: "claimAdd",
components: {AiSelect, AiEartagPicker},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []},
records
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.isAudit ? `${appName}审批` : `${appName}详情`
},
isAudit: v => v.$route.hash == "#audit",
formImages: v => [
{label: "勘察报告书", prop: "surveyPicture"},
{label: "无害化回执单", prop: "receiptPicture"},
],
columns: v => [
{label: "序号", type: "index"},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "身长测量照片", prop: "heightPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
{label: "电子耳标照片", prop: "earNumberPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
{label: "防疫耳标照片", prop: "preventionPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
{label: "无害化处理照片", prop: "harmlessPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
].filter(e => !e.hide),
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/insurance/claim/apply/getAuditInfo", null, {params: {id}}).then(res => {
if (res?.data) {
const detail = res.data
return this.detail = {...detail}
}
})
},
submit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/insurance/claim/apply/audit", {...this.detail}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
}
},
created() {
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="claimAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid">
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="承保公司" prop="companyId">
<b v-text="detail.companyName"/>
</el-form-item>
<el-form-item label="投保类型">
<ai-input :value="dict.getLabel('insureType',detail.insureType)" :edit="!1"/>
</el-form-item>
<el-form-item label="保险产品" prop="productType">
<b v-text="detail.productType"/>
</el-form-item>
<el-form-item label="联系人">
<ai-input v-model="detail.contacts" :edit="!1"/>
</el-form-item>
<el-form-item label="联系电话">
<ai-input v-model="detail.phone" :edit="!1"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="投保对象">
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1" hideOptions/>
</ai-card>
<ai-card title="理赔材料">
<el-form-item v-for="(img,i) in formImages" :key="i" v-bind="img">
<ai-uploader v-model="detail[img.prop]" value-is-url readonly/>
</el-form-item>
</ai-card>
<ai-card title="审核信息">
<div class="grid">
<template v-if="isAudit">
<el-form-item label="审批状态" prop="auditStatus" :rules="{required:true,message:'请选择审批状态'}">
<ai-select v-model="detail.auditStatus" dict="auditStatus"/>
</el-form-item>
<el-form-item label="理赔资料" class="sc-3">
<ai-uploader v-model="detail.picture" value-is-url :instance="instance" :limit="1"/>
</el-form-item>
<el-form-item label="报案号" prop="reportNo" :rules="{required:true,message:'请输入 报案号'}">
<ai-input v-model="detail.reportNo"/>
</el-form-item>
<el-form-item label="说明">
<ai-input type="textarea" :rows="3" v-model="detail.remarks"/>
</el-form-item>
</template>
<template v-else>
<el-form-item label="审核状态">{{ dict.getLabel('auditStatus', detail.auditStatus) }}</el-form-item>
<el-form-item label="理赔资料" class="sc-3">
<el-image :src="detail.picture" :preview-src-list="[detail.picture]"/>
</el-form-item>
<el-form-item label="审核时间">{{ detail.auditTime }}</el-form-item>
<el-form-item label="审核人">{{ detail.auditName }}</el-form-item>
<el-form-item label="说明">{{ detail.remarks }}</el-form-item>
</template>
</div>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isAudit">
<el-button type="primary" @click="submit">提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.claimAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
}
</style>

View File

@@ -0,0 +1,107 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "报案号", prop: "reportNo"},
{label: "投保单号", prop: "orderNo"},
{label: "所属养殖户", prop: "applyName"},
{label: "投保类型", prop: "insureType", dict: "insureType"},
{label: "理赔数量", prop: "claimNumber"},
{label: "审批状态", prop: "auditStatus", dict: "auditStatus"},
{label: "审批时间", prop: "auditTime"},
{label: "审批人", prop: "auditName"},
]
export default {
name: "claimList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/insurance/claim/apply/getAuditPage", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="claimList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-input placeholder="投保订单号" v-model="search.orderNo"/>
<ai-select placeholder="全部投保类型" v-model="search.insureType" dict="insureType"/>
<ai-select placeholder="全部审批状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-select placeholder="全部投保状态" v-model="search.status" dict="insureStatus"/>
<ai-search label="投保日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="养殖户" v-model="search.applyName"/>
<ai-input placeholder="养殖场" v-model="search.farmName"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<ai-download :instance="instance" url="/api/insurance/claim/apply/exportAudit" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['1'].includes(row.auditStatus)">
<el-button type="text" @click="$router.push({hash:'#audit',query:{id:row.id}})">审核</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.claimList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppDeathAudit",
label: "死亡审核",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return ["#audit", "#add"].includes(hash) ? add : list
}
},
created() {
this.dict.load("auditStatus", "deathReason", "category", "variety")
},
}
</script>
<template>
<section class="AppDeathAudit">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppDeathAudit {
height: 100%;
}
</style>

View File

@@ -0,0 +1,153 @@
<script>
import {mapState} from "vuex"
export default {
name: "deathAdd",
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []}
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.isAudit ? `${appName}审批` : `${appName}详情`
},
isAudit: v => v.$route.hash == "#audit",
formImages: v => [
{label: "身长测量照片", prop: "heightPic"},
{label: "生物芯片照片", prop: "biochipPic"},
{label: "防疫耳标照片", prop: "preventionPic"},
{label: "其他说明照片", prop: "otherPic"},
],
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/breed/death/page", null, {params: {id}}).then(res => {
if (res?.data?.records) {
const detail = res.data.records[0] || {}
if (detail.picture) {
Object.entries(JSON.parse(detail.picture)).forEach(([key, value]) => {
detail[key] = value
})
}
return this.detail = {...detail}
}
})
},
submit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/breed/death/audit", {...this.detail}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
}
},
created() {
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="deathAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid c-4">
<el-form-item label="生物芯片耳标号" class="row">
<b v-text="detail.biochipEarNumber"/>
</el-form-item>
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
<el-form-item label="电子耳标号" prop="electronicEarNumber">
<b v-text="detail.electronicEarNumber"/>
</el-form-item>
<el-form-item label="原厂耳标号" prop="category">
<b v-text="detail.originalEarNumber"/>
</el-form-item>
<el-form-item label="类别" prop="category">
<b v-text="detail.category"/>
</el-form-item>
<el-form-item label="品种" prop="variety">
<b v-text="detail.variety"/>
</el-form-item>
<div class="row flex">
<el-form-item v-for="(img,i) in formImages" :key="i" v-bind="img">
<ai-uploader v-model="detail[img.prop]" value-is-url readonly/>
</el-form-item>
</div>
</div>
</ai-card>
<ai-card title="死亡信息">
<el-form-item label="死亡原因">
<b v-text="dict.getLabel('deathReason',detail.reason)"/>
</el-form-item>
<el-form-item label="死亡日期">
<b v-text="detail.deathTime"/>
</el-form-item>
<el-form-item label="备注">
<b v-text="detail.remark"/>
</el-form-item>
</ai-card>
<ai-card title="审核信息">
<div class="grid">
<template v-if="isAudit">
<el-form-item label="审批状态" prop="auditStatus" :rules="{required:true,message:'请选择审批状态'}">
<ai-select v-model="detail.auditStatus" dict="auditStatus"/>
</el-form-item>
<el-form-item label="意见">
<ai-input type="textarea" :rows="3" v-model="detail.remark"/>
</el-form-item>
</template>
<template v-else>
<el-form-item label="审核状态">{{ dict.getLabel('auditStatus', detail.auditStatus) }}</el-form-item>
<el-form-item label="审核时间">{{ detail.auditTime }}</el-form-item>
<el-form-item label="审核人">{{ detail.auditName }}</el-form-item>
<el-form-item label="意见">{{ detail.remarks }}</el-form-item>
</template>
</div>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isAudit">
<el-button type="primary" @click="submit">提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.deathAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
}
</style>

View File

@@ -0,0 +1,112 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖场", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category", width: 120},
{label: "品种", prop: "variety", dict: "variety", width: 120},
{label: "日龄(天)", prop: "age", width: 80},
{label: "死亡时间", prop: "deathTime"},
{label: "死亡原因", prop: "reason", dict: "deathReason", width: 80},
{label: "登记时间", prop: "createTime"},
{label: "操作人", prop: "userName", width: 100},
{label: "审核状态", prop: "auditStatus", dict: "auditStatus", width: 80},
]
export default {
name: "deathList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/breed/death/getAuditPage", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="deathList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部审核状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="死亡日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.deathBeginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.deathEndDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-search label="登记日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="原场耳标号" v-model="search.originalEarNumber"/>
<ai-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber"/>
<ai-input placeholder="电子耳标号" v-model="search.electronicEarNumber"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<ai-download :instance="instance" url="/api/breed/death/export" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['1'].includes(row.auditStatus)">
<el-button type="text" @click="$router.push({hash:'#audit',query:{id:row.id}})">审核</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.deathList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppDeathManage",
label: "死亡登记",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let { hash } = this.$route
return hash == "#add" ? add : list
}
},
created() {
this.dict.load("yesOrNo", "category", "variety", "deathReason", "auditStatus")
}
}
</script>
<template>
<section class="AppDeathManage">
<component :is="currentPage" v-bind="$props" />
</section>
</template>
<style scoped lang="scss">
.AppDeathManage {
height: 100%;
}
</style>

View File

@@ -0,0 +1,167 @@
<script>
import {mapState} from "vuex"
import AiEartagRemote from "@project/xumu/components/AiEartagRemote.vue";
const formImages = [
{label: "身长测量照片", prop: "heightPic",},
{label: "生物芯片照片", prop: "biochipPic",},
{label: "防疫耳标照片", prop: "preventionPic",},
{label: "其他说明照片", prop: "otherPic",},
]
export default {
name: "deathAdd",
components: {AiEartagRemote},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
formImages,
detail: {},
form: {}
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
isAdd: v => !v.$route.query.id,
isEdit: v => v.$route.query.edit == 1,
isAuthing: v => v.detail.auditStatus == 1,
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail(id) {
return id && this.instance.post("/api/breed/death/page", {biochipEarNumber: id}).then(res => {
if (res?.data?.records) {
const detail = res.data.records[0] || {}
if (detail.picture) {
Object.entries(JSON.parse(detail.picture)).forEach(([key, value]) => {
detail[key] = value
})
}
return this.detail = {...detail}
}
})
},
submit() {
this.$refs.detail.validate().then(() => {
const {biochipEarNumber, id, deathTime, heightPic, biochipPic, preventionPic, otherPic, reason, remarks} = this.detail
this.instance.post("/api/breed/death/addOrEdit", {
biochipEarNumber, id, deathTime, picture: JSON.stringify({heightPic, biochipPic, preventionPic, otherPic}), reason, remarks
}).then(res => {
if (res?.code == 0 && res?.data != 1) {
this.$confirm("是否返回列表页?", "提交成功").then(() => this.back()).catch(() => this.getDetail(biochipEarNumber))
}
})
})
},
handleAudit() {
this.$refs.form.validate().then(() => {
const {id} = this.detail
this.instance.post("/api/breed/death/audit", null, {params: {id, ...this.form}}).then(res => {
if (res?.code == 0) {
this.$message.success("操作成功")
this.back()
}
})
})
},
handlerAutocomplete(value) {
'biochipEarNumber|farmId|houseId|penId|electronicEarNumber|originalEarNumber|category|variety'.split("|").forEach(prop => this.$set(this.detail, prop, value[prop]))
},
},
created() {
this.getDetail(this.$route.query.id)
}
}
</script>
<template>
<ai-page :title="pageTitle" class="deathAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid c-4">
<ai-eartag-remote :instance="instance" @enter="handlerAutocomplete" class="row"/>
<el-form-item label="生物芯片耳标号">
<b v-text="detail.biochipEarNumber"/>
</el-form-item>
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
<el-form-item label="电子耳标号" prop="electronicEarNumber">
<b v-text="detail.electronicEarNumber"/>
</el-form-item>
<el-form-item label="原厂耳标号" prop="originalEarNumber">
<b v-text="detail.originalEarNumber"/>
</el-form-item>
<el-form-item label="类别" prop="category">
<b v-text="detail.category"/>
</el-form-item>
<el-form-item label="品种" prop="variety">
<b v-text="detail.variety"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="上传照片">
<div class="grid c-4">
<el-form-item v-for="(img,i) in formImages" :key="i" v-bind="img">
<ai-uploader v-if="isAdd||isEdit" v-model="detail[img.prop]" :instance="instance" :limit="1" value-is-url/>
<el-image :src="detail[img.prop]" :preview-src-list="[detail[img.prop]]" v-else/>
</el-form-item>
</div>
</ai-card>
<ai-card title="死亡录入">
<div class="grid">
<el-form-item label="死亡日期" prop="deathTime" :rules="[{required:isAdd||isEdit,message:'请选择死亡日期'}]">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-if="isAdd||isEdit" v-model="detail.deathTime"/>
<b v-else v-text="detail.deathTime"/>
</el-form-item>
<el-form-item label="死亡原因" prop="reason" :rules="[{required:isAdd||isEdit,message:'请选择死亡原因'}]">
<ai-select v-if="isAdd||isEdit" v-model="detail.reason" dict="deathReason"/>
<b v-else v-text="dict.getLabel('deathReason',detail.reason)"/>
</el-form-item>
<el-form-item label="备注" prop="remark" class="row">
<ai-input type="textarea" :row="3" v-model="detail.remark" :edit="isAdd||isEdit"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="审批信息" v-if="isAuthing">
<el-form :model="form" size="small" ref="form" label-width="120px">
<el-form-item label="是否同意" prop="auditStatus" :rules="[{required:true,message:'请选择是否同意'}]">
<ai-select v-model="form.auditStatus" :select-list="[{dictValue: 2, dictName: '同意'}, {dictValue: 3, dictName: '不同意'}]"/>
</el-form-item>
<el-form-item label="审批意见" prop="auditReason">
<el-input type="textarea" :row="3" v-model="form.auditReason" clearable placeholder="请输入"/>
</el-form-item>
</el-form>
</ai-card>
</el-form>
<div slot="footer">
<el-button type="primary" @click="submit" v-if="isAdd||isEdit">提交</el-button>
<el-button type="primary" @click="handleAudit" v-if="isAuthing">提交</el-button>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.deathAdd {
.el-date-editor {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,142 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖场",format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category", width: 120},
{label: "品种", prop: "variety", dict: "variety", width: 120},
{label: "日龄(天)", prop: "age", width: 80},
{label: "死亡时间", prop: "deathTime"},
{label: "死亡原因", prop: "reason", dict: "deathReason", width: 80},
{label: "登记时间", prop: "createTime"},
{label: "操作人", prop: "userName", width: 100},
{label: "审核状态", prop: "auditStatus", dict: "auditStatus", width: 80},
]
export default {
name: "deathList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/breed/death/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
handleDelete(id) {
this.$confirm("确定删除该条数据?").then(() => {
this.instance.post("/api/breed/weight/del", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getTableData()
}
})
})
},
handleAuditAgain(id) {
this.$confirm("是否要再次提交审批?").then(() => {
this.instance.post("/api/breed/death/audit", null, {
params: {id, auditStatus: 1}
}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功")
this.getTableData()
}
})
})
}
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="deathList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部审核状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="死亡日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.deathBeginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.deathEndDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-search label="登记日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="原场耳标号" v-model="search.originalEarNumber"/>
<ai-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber"/>
<ai-input placeholder="电子耳标号" v-model="search.electronicEarNumber"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/breed/death/export" :params="{...search,...page}" :fileName="`死亡导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button v-if="row.auditStatus==1" type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber}})">查看</el-button>
<template v-else>
<el-button v-if="row.auditStatus==2" type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber}})">查看</el-button>
<el-button v-if="row.auditStatus==2" type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber,edit:1}})">编辑</el-button>
<el-button v-if="row.auditStatus==3" type="text" @click="handleAuditAgain(row.id)">再次提交</el-button>
<el-button type="text" @click="handleDelete(row.id)">删除</el-button>
</template>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.deathList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,32 @@
<template>
<section class="AppDictionary">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<script>
import DictDetail from "./dictDetail";
import DictList from "@project/xumu/AppDictionary/dictList.vue";
export default {
name: "AppDictionary",
label: "数据字典",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? DictDetail : DictList
}
},
}
</script>
<style lang="scss" scoped>
.AppDictionary {
height: 100%;
}
</style>

View File

@@ -0,0 +1,202 @@
<template>
<section class="dictDetail">
<ai-detail>
<ai-title slot="title" title="字典信息" isShowBottomBorder isShowBack @onBackClick="$router.push({})"/>
<template #content>
<ai-card title="基本信息">
<template #content>
<el-form ref="dictDetailForm" :model="form" :rules="rules" size="small" label-width="110px">
<el-form-item required label="数据项:" prop="code">
<el-input v-model="form.code" style="width: 259px;" clearable
placeholder="请输入..."/>
</el-form-item>
<el-form-item required label="数据项名称:" prop="name">
<el-input v-model="form.name" style="width: 259px;" clearable
placeholder="请输入..."/>
</el-form-item>
</el-form>
</template>
</ai-card>
<ai-card title="数据值" v-if="$route.query.id">
<template #right>
<el-button type="text" icon="iconfont iconAdd"
@click="form.dictionaryDetails.push({name:'',value:'',editable:true})"> 添加
</el-button>
</template>
<template #content>
<el-table border :data="form.dictionaryDetails" header-cell-class-name="table-header"
cell-class-name="table-cell">
<el-table-column align="center" label="值">
<div slot-scope="{row}">
<el-input size="small" v-if="row.editable" v-model="row.value" clearable/>
<span v-else>{{ row.dictValue }}</span>
</div>
</el-table-column>
<el-table-column align="center" label="描述">
<div slot-scope="{row}">
<el-input size="small" v-if="row.editable" v-model="row.name" clearable/>
<span v-else>{{ row.dictName }}</span>
</div>
</el-table-column>
<el-table-column align="center" label="颜色">
<div slot-scope="{row}">
<el-color-picker v-if="row.editable" v-model="row.dictColor" size="medium"></el-color-picker>
<span v-else>{{ row.dictColor || '未设置' }}</span>
</div>
</el-table-column>
<el-table-column align="center" label="操作" width="109px">
<div slot-scope="{row,$index}">
<section v-if="row.editable">
<el-button style="color: #2EA222" type="text" icon="iconfont iconCorrect"
@click="addDict(row)"/>
<el-button style="color: #f46" type="text" icon="iconfont iconClean"
@click="cancelEdit(row,$index)"/>
</section>
<section v-else>
<el-button class="dict-detail-operation" type="text" icon="iconfont iconEdit"
@click="editDetail(row)"/>
<el-button class="dict-detail-operation" type="text" icon="iconfont iconDelete"
@click="delDictValue(row.id)"/>
</section>
</div>
</el-table-column>
</el-table>
</template>
</ai-card>
</template>
<template #footer>
<el-button @click="$router.push({})">返回</el-button>
<el-button type="primary" @click="modifyDict">保存</el-button>
</template>
</ai-detail>
</section>
</template>
<script>
export default {
name: "dictDetail",
props: {
instance: Function,
permissions: Function
},
computed: {
rules() {
return {
code: [
{required: true, message: "请填写数据项"}
],
name: [
{required: true, message: "请填写数据项名称"}
],
// dictionaryDetails: [
// {
// validator: (r, v, cb) => {
// if (v.every(item => item.dictName && item.dictValue)) {
// cb()
// }
// }
// }
// ]
}
}
},
data() {
return {
form: {
code: "",
name: "",
dictionaryDetails: []
},
}
},
created() {
if (this.$route.query.id) this.getDict()
},
methods: {
getDict() {
this.instance.post("/admin/dictionary/queryDictDetail", null, {
params: {dictionaryId: this.$route.query.id}
}).then(res => {
if (res?.data) {
res.data.dictionaryDetails = res.data.dictionaryDetails.map(d => {
return {
...d,
editable: false,
name: "",
value: ""
}
})
this.form = res.data
}
})
},
delDictValue(id) {
this.$confirm("是否要删除该字典值", {
type: 'error'
}).then(() => {
this.instance.post("/admin/dictionary/deletevalue", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.$message.success("删除成功!")
this.getDict()
}
})
}).catch(() => 0)
},
editDetail(row) {
row.editable = true
row.name = row.dictName
row.value = row.dictValue
},
addDict(row) {
row.dictValue = row.value
row.dictName = row.name
row.dictionaryId = this.form.id
this.instance.post("/admin/dictionary/updateDetail", row).then(res => {
row.editable = false
row = res.data.data
this.$message.success("提交成功!")
})
},
cancelEdit(row, index) {
if (row.id) {
row.editable = false
} else {
this.form.dictionaryDetails.splice(index, 1)
}
},
modifyDict() {
this.$refs.dictDetailForm.validate(v => {
if (v) {
this.instance.post("/admin/dictionary/updateDict", this.form).then(res => {
if (res?.code == 0) {
this.$message.success("提交成功!")
this.$router.push({})
}
})
}
})
}
}
}
</script>
<style lang="scss" scoped>
.dictDetail {
height: 100%;
:deep( .el-table__row ){
.el-input__inner {
padding: 0 30px;
border: none;
text-align: center;
background: #ddd;
font-size: 14px;
}
}
}
</style>

View File

@@ -0,0 +1,142 @@
<script>
const columns = [
{slot: "expand"},
{label: "数据项", prop: "code"},
{label: "数据项名称", prop: "name"},
]
export default {
name: "dictList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
page: {
current: 1,
total: 0,
size: 10
},
search: {
condition: ""
},
dictList: [],
id: ''
}
},
methods: {
resetSearch() {
this.page.current = 1;
this.search.condition = '';
this.getDicts();
},
getDicts() {
this.instance.post("/admin/dictionary/queryDictList", null, {
params: {
...this.page,
name: this.search.condition
}
}).then(res => {
this.dictList = res.data.records.map(e => {
return {...e, detail: []}
})
this.page.total = res.data.total
})
},
addDict() {
this.$router.push({hash: "#add"})
},
handleDelete(id) {
this.$confirm("确定要删除该数据项吗?", {
type: "error"
}).then(() => {
this.instance.post("/admin/dictionary/deleteDict", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getDicts();
this.$message.success("删除成功!")
}
})
}).catch(() => 0)
},
openDetail(id) {
this.$router.push({query: {id}, hash: "#add"})
},
handleSizeChange(val) {
this.page.size = val;
this.getDicts();
},
getDictInfo(row) {
if (row.detail.length) {
row.detail = []
} else {
this.getDict(row.id).then(res => {
if (res && res.data) {
row.detail = res.data.dictionaryDetails || []
}
})
}
},
getDict(dictionaryId) {
return this.instance.post("/admin/dictionary/queryDictDetail", null, {
params: {dictionaryId}
})
},
},
created() {
this.getDicts()
},
}
</script>
<template>
<section class="dictList">
<ai-list>
<ai-title slot="title" title="数据字典" isShowBottomBorder/>
<template #content>
<ai-search-bar>
<template #left>
<el-button type="primary" size="small" icon="iconfont iconAdd" @click="addDict"
v-if="$permissions('admin_sysdictionary_add')">添加
</el-button>
</template>
<template #right>
<el-input size="small" v-model="search.condition" placeholder="数据项" clearable
@change="page.current=1,getDicts()" prefix-icon="iconfont iconSearch"/>
<el-button type="primary" size="small" icon="iconfont iconSearch"
@click="page.current=1,getDicts()">查询
</el-button>
<el-button size="small" icon="el-icon-refresh-right" @click="resetSearch">重置</el-button>
</template>
</ai-search-bar>
<ai-table :tableData="dictList" :colConfigs="columns" :dict="dict" @getList="getDicts"
:total="page.total" :current.sync="page.current" :size.sync="page.size" :page-sizes="[10, 20, 50, 100,200]"
@expand-change="getDictInfo">
<el-table-column slot="expand" type="expand">
<template slot-scope="{row}">
<div class="flex" style="gap:4px">
<el-tag v-for="(op,i) in row.detail||[]" :key="i">{{ [op.dictValue, op.dictName, op.dictColor].filter(Boolean).join("") }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" @click="openDetail(row.id)" v-if="$permissions('admin_sysdictionary_edit')">编辑</el-button>
<el-button type="text" @click="handleDelete(row.id)" v-if="$permissions('admin_sysdictionary_del')">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</section>
</template>
<style scoped lang="scss">
.dictList {
height: 100%;
}
</style>

View File

@@ -0,0 +1,36 @@
<script>
import add from "./etAdd.vue";
import list from "./etList.vue";
export default {
name: "AppEarTag",
label: "耳标登记",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? add : list
}
},
data() {
return {
}
}
}
</script>
<template>
<section class="AppEarTag">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppEarTag {
height: 100%;
}
</style>

View File

@@ -0,0 +1,165 @@
<script>
import {mapState} from "vuex"
export default {
name: "etAdd",
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []},
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
isAdd: v => !v.$route.query.id,
isEdit: v => v.$route.query.edit == 1,
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
columns: v => [
{label: "序号", type: "index"},
{label: "生物芯片耳标号", prop: "biochipEarNumber", edit: 1},
{label: "电子耳标号", prop: "electronicEarNumber", edit: 1},
{label: "原厂耳标号", prop: "originalEarNumber", edit: 1},
{label: "戴耳标照片", prop: "picture", upload: {valueIsUrl: !0, instance: v.instance, limit: 1}},
{label: "品种", prop: "variety", select: {dict: "variety"}},
{label: "类别", prop: "category", select: {dict: "category"}},
{label: "日龄/天", prop: "age", num: 1},
{label: "体重/公斤", prop: "weight", num: 1},
]
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/breed/earTag/page", {id}).then(res => {
if (res?.data?.records) {
const detail = res.data.records[0] || {}
return this.detail = {detailList: [], ...detail}
}
})
},
handleDelete(index) {
this.$confirm("确定删除该条数据?").then(() => {
this.detail.detailList.splice(index, 1)
})
},
submit() {
this.$refs.detail.validate().then(() => {
if (this.isEdit) {
const {biochipEarNumber, electronicEarNumber, originalEarNumber, picture, variety, category, age, weight} = this.detail
this.detail.detailList = [{biochipEarNumber, electronicEarNumber, originalEarNumber, picture, variety, category, age, weight}]
}
this.instance.post("/api/breed/earTag/addOrEdit", this.detail).then(res => {
if (res?.code == 0) {
this.$confirm("是否要返回列表?", "提交成功").then(() => this.back())
}
})
})
}
},
created() {
this.dict.load("auditStatus", "category", "variety")
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="etAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid c-3">
<template v-if="isAdd">
<el-form-item label="养殖场" prop="farmId" :rules="{required:isAdd,message:'请选择养殖场'}">
<ai-select v-model="detail.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId" :rules="{required:isAdd,message:'请选择养殖舍'}">
<ai-select v-model="detail.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${detail.farmId||-1}`" :prop="{label:'name'}"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId" :rules="{required:isAdd,message:'请选择养殖栏'}">
<ai-select v-model="detail.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${detail.houseId||-1}`" :prop="{label:'name'}"/>
</el-form-item>
</template>
<template v-else>
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
</template>
</div>
</ai-card>
<ai-card title="耳标录入">
<template v-if="isAdd">
<el-button type="text" slot="right" @click="detail.detailList.push({})">新增</el-button>
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row,$index}">
<div class="table-options">
<el-button type="text" class="deleteBtn" @click="handleDelete($index)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
<div class="grid" v-else>
<el-form-item label="生物芯片耳标号">
<el-input v-if="isEdit" v-model="detail.biochipEarNumber" placeholder="请输入" clearable/>
<b v-else v-text="detail.biochipEarNumber"/>
</el-form-item>
<el-form-item label="电子耳标号" prop="electronicEarNumber">
<el-input v-if="isEdit" v-model="detail.electronicEarNumber" placeholder="请输入" clearable/>
<b v-else v-text="detail.electronicEarNumber"/>
</el-form-item>
<el-form-item label="原厂耳标号" prop="originalEarNumber">
<el-input v-if="isEdit" v-model="detail.originalEarNumber" placeholder="请输入" clearable/>
<b v-else v-text="detail.originalEarNumber"/>
</el-form-item>
<el-form-item label="戴耳标照片" class="row">
<el-image :src="detail.picture" :preview-src-list="[detail.picture]"/>
</el-form-item>
<el-form-item label="品种" prop="variety">
<ai-select v-if="isEdit" v-model="detail.variety" dict="variety"/>
<b v-else v-text="dict.getLabel('variety',detail.variety)"/>
</el-form-item>
<el-form-item label="类别" prop="category">
<ai-select v-if="isEdit" v-model="detail.category" dict="category"/>
<b v-else v-text="dict.getLabel('category',detail.category)"/>
</el-form-item>
<el-form-item label="日龄/天">
<el-input v-if="isEdit" v-model.number="detail.age" placeholder="请输入" clearable/>
<b v-else v-text="detail.age"/>
</el-form-item>
<el-form-item label="体重/公斤">
<el-input v-if="isEdit" v-model.number="detail.weight" placeholder="请输入" clearable/>
<b v-else v-text="detail.weight"/>
</el-form-item>
</div>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isEdit||isAdd">
<el-button type="primary" @click="submit">提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.etAdd {
}
</style>

View File

@@ -0,0 +1,121 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "登记时间", prop: "createTime"},
{label: "日龄/天", prop: "age", width: 120, align: 'right'},
{label: "登记体重", prop: "weight", width: 120, align: 'right'},
{label: "戴标员", prop: "userName", width: 120, align: 'center'},
]
export default {
name: "etList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {}
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/breed/earTag/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
handleDelete(id) {
this.$confirm("确定删除该条数据?").then(() => {
this.instance.post("/api/breed/earTag/del", null, {params: {id}}).then(res => {
if (res?.code == '0' && res?.data != 1) {
this.$message.success("删除成功")
this.getTableData()
}
})
})
}
},
created() {
this.dict.load("auditStatus", "category", "variety")
this.getTableData()
}
}
</script>
<template>
<ai-page class="etList" title="耳标登记">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部机构" v-model="search.userId" :instance="instance" :action="`/api/breed/earTag/getOrgList`" :prop="{label:'name'}" readonly/>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${search.userId||''}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<el-input placeholder="戴标员" v-model="search.userName" dict="authStatus" size="small" clearable/>
<el-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber" dict="authStatus" size="small" clearable/>
<ai-select placeholder="全部类别" v-model="search.category" dict="category"/>
<ai-select placeholder="全部品种" v-model="search.variety" dict="variety"/>
<ai-search label="登记日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-search label="日龄">
<el-input placeholder="最小日龄" v-model="search.beginAge" size="small" clearable/>
<el-input placeholder="最大日龄" v-model="search.endAge" size="small" clearable/>
</ai-search>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/breed/earTag/export" :params="{...search,...page}" :fileName="`耳标登记导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.id,edit:1}})">编辑</el-button>
<el-button type="text" class="deleteBtn" @click="handleDelete(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.etList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppImmunityManage",
label: "免疫登记",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? add : list
}
},
data() {
return {}
}
}
</script>
<template>
<section class="AppImmunityManage">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppImmunityManage {
height: 100%;
}
</style>

View File

@@ -0,0 +1,201 @@
<script>
import {mapState} from "vuex"
import AiEartagPicker from "../components/AiEartagPicker.vue";
const columns = [
{label: "序号", type: "index"},
{label: "疫苗名称", prop: "vaccineName"},
{label: "用药方式", prop: "method"},
{label: "药量(ml)", prop: "dosage"},
{label: "生产厂家", prop: "factory"},
{label: "厂家批号", prop: "batchNumber"},
{label: "免疫时间", prop: "immunityTime"},
{label: "登记时间", prop: "createTime"},
{label: "防疫员", prop: "userName"},
]
export default {
name: "immunityAdd",
components: {AiEartagPicker},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []},
columns,
dialog: false,
form: {}
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
isAdd: v => !v.$route.query.id,
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/breed/immunity/getInfo", null, {params: {biochipEarNumber: id}}).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.immunityList || []
return this.detail = {...detail}
}
})
},
handleDelete(id) {
this.$confirm("确定删除该条数据?").then(() => {
this.instance.post("/api/breed/immunity/del", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getDetail()
}
})
})
},
handleAdd() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/breed/immunity/add", this.detail).then(res => {
if (res?.code == 0) {
this.$message.success("新增成功!")
this.back()
}
})
})
},
submit() {
this.$refs.detail.validate().then(() => {
const action = !this.form.id ? "/api/breed/immunity/add" : "/api/breed/immunity/update"
this.instance.post(action, this.form).then(res => {
if (res?.code == 0) {
this.dialog = false
this.getDetail()
}
})
})
}
},
created() {
this.dict.load("yesOrNo", "category", "variety", "source")
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="immunityAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid c-3" v-if="isAdd">
<el-form-item label="养殖场" prop="farmId" :rules="{required:true,message:'请选择 养殖场'}">
<ai-select v-model="detail.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId" :rules="{required:true,message:'请选择 养殖舍'}">
<ai-select v-model="detail.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${detail.farmId||-1}`" :prop="{label:'name'}"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId" :rules="{required:true,message:'请选择 养殖栏'}">
<ai-select v-model="detail.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${detail.houseId||-1}`" :prop="{label:'name'}"/>
</el-form-item>
<el-form-item label="牲畜" prop="earNumberList" :rules="{required:true,message:'请选择 牲畜'}" class="row">
<ai-eartag-picker v-model="detail.earNumberList" :instance="instance" :penId="detail.penId"/>
</el-form-item>
</div>
<div v-else class="grid c-4">
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
<el-form-item label="生物芯片耳标号">
<b v-text="detail.biochipEarNumber"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="免疫登记">
<template v-if="isAdd">
<div class="grid c-3">
<el-form-item label="免疫日期" prop="immunityTime" :rules="{required:true,message:'请选择 免疫日期'}">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="detail.immunityTime"/>
</el-form-item>
<el-form-item label="疫苗名称" prop="vaccineName" :rules="{required:true,message:'请选择 疫苗名称'}">
<ai-input v-model="detail.vaccineName"/>
</el-form-item>
<el-form-item label="用药方式" prop="method">
<ai-input v-model="detail.method"/>
</el-form-item>
<el-form-item label="生产厂家" prop="factory">
<ai-input v-model="detail.factory"/>
</el-form-item>
<el-form-item label="每头药量" prop="dosage">
<ai-input v-model="detail.dosage"/>
</el-form-item>
<el-form-item label="厂家批号" prop="batchNumber">
<ai-input v-model="detail.batchNumber"/>
</el-form-item>
</div>
</template>
<template v-else>
<el-button type="text" slot="right" @click="dialog=true">新增</el-button>
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row,$index}">
<div class="table-options">
<el-button type="text" @click="dialog=true,form=row">编辑</el-button>
<el-button type="text" class="deleteBtn" @click="handleDelete(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
<ai-dialog v-model="dialog" title="免疫登记" @closed="form={}" @confirm="submit">
<el-form size="small" label-width="120px" class="grid">
<el-form-item label="免疫日期" prop="immunityTime" :rules="{required:true,message:'请选择 免疫日期'}">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="form.immunityTime"/>
</el-form-item>
<el-form-item label="疫苗名称" prop="vaccineName" :rules="{required:true,message:'请选择 疫苗名称'}">
<ai-input v-model="form.vaccineName"/>
</el-form-item>
<el-form-item label="用药方式" prop="method">
<ai-input v-model="form.method"/>
</el-form-item>
<el-form-item label="生产厂家" prop="factory">
<ai-input v-model="form.factory"/>
</el-form-item>
<el-form-item label="每头药量" prop="dosage">
<ai-input v-model="form.dosage"/>
</el-form-item>
<el-form-item label="厂家批号" prop="batchNumber">
<ai-input v-model="form.batchNumber"/>
</el-form-item>
</el-form>
</ai-dialog>
</el-form>
<div slot="footer">
<el-button v-if="isAdd" type="primary" @click="handleAdd">提交</el-button>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.immunityAdd {
.el-date-editor {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,106 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "最近免疫日期", prop: "immunityTime"},
{label: "最近登记时间", prop: "createTime"},
]
export default {
name: "immunityList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/breed/immunity/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
},
created() {
this.dict.load("auditStatus", "category", "variety")
this.getTableData()
}
}
</script>
<template>
<ai-page class="immunityList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<ai-search label="免疫日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.immunityBeginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.immunityEndDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-search label="登记日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<el-input placeholder="原场耳标号" v-model="search.originalEarNumber" dict="authStatus" size="small" clearable/>
<el-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber" dict="authStatus" size="small" clearable/>
<el-input placeholder="电子耳标号" v-model="search.electronicEarNumber" dict="authStatus" size="small" clearable/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/breed/immunity/export" :params="{...search,...page}" :fileName="`免疫登记导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.immunityList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppInsuranceApply",
label: "投保申请",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? add : list
}
},
created() {
this.dict.load("insuranceAuditStatus", "insureType", "insureStatus", "category", "variety")
},
}
</script>
<template>
<section class="AppInsuranceApply">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppInsuranceApply {
height: 100%;
}
</style>

View File

@@ -0,0 +1,177 @@
<script>
import {mapState} from "vuex"
import AiEartagPicker from "@project/xumu/components/AiEartagPicker.vue";
const columns = [
{label: "序号", type: "index"},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category"},
{label: "品种", prop: "variety", dict: "variety"},
]
export default {
name: "iaAdd",
components: {AiEartagPicker},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []},
columns,
companyList: []
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
isAdd: v => !v.$route.query.id,
isEdit: v => v.$route.query.edit == 1,
formImages: v => [
{label: "身份证(正面)", prop: "frontCard", rules: {required: v.isAdd || v.isEdit, message: '请上传 身份证(正面)'}},
{label: "身份证(反面)", prop: "reverseCard", rules: {required: v.isAdd || v.isEdit, message: '请上传 身份证(反面)'}},
{label: "营业执照", prop: "businessPic", rules: {required: v.isAdd || v.isEdit, message: '请上传 营业执照'}},
{label: "畜禽经营许可证", prop: "breedPic", rules: {required: v.isAdd || v.isEdit, message: '请上传 畜禽经营许可证'}},
{label: "动物防疫条件许可证", prop: "prevention", rules: {required: v.isAdd || v.isEdit, message: '请上传 动物防疫条件许可证'}},
]
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/insurance/apply/getInfo", null, {params: {id}}).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.detailList || []
let {farmPicture: picture = "{}"} = detail
picture = JSON.parse(picture)
return this.detail = {...detail, ...picture}
}
})
},
getCompanies() {
this.instance.post("/api/insurance/apply/getCompany").then(res => {
if (res?.data) {
this.companyList = res.data
}
})
},
getProducts(id) {
const item = this.companyList.find(e => e.id == id)
return item?.children || []
},
submit(submitType) {
this.$refs.detail.validate().then(() => {
const earNumberList = this.detail.detailList.map(e => e.biochipEarNumber)
const farmPicture = {}
this.formImages.forEach(e => {
const {prop} = e
const val = this.detail[prop]
if (val) {
farmPicture[prop] = val
}
})
this.detail.farmPicture = JSON.stringify(farmPicture)
this.instance.post("/api/insurance/apply/addOrEdit", {...this.detail, submitType, earNumberList}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
}
},
created() {
this.getCompanies()
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="iaAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid">
<el-form-item label="养殖场" prop="farmId" :rules="{required: isAdd||isEdit,message:'请选择 养殖场'}">
<ai-select v-if="isAdd||isEdit" v-model="detail.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<b v-else v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="承保公司" prop="companyId" :rules="{required: isAdd||isEdit,message:'请选择 承保公司'}">
<ai-select v-if="isAdd||isEdit" v-model="detail.companyId" :select-list="companyList" :prop="{label:'name'}"/>
<b v-else v-text="detail.companyName"/>
</el-form-item>
<el-form-item label="保险产品" prop="productType" :rules="{required: isAdd||isEdit,message:'请选择 保险产品'}">
<ai-select v-if="isAdd||isEdit" v-model="detail.productType" :select-list="getProducts(detail.companyId)" :prop="{label:'name',value:'productType'}"
@select="v=>$set(detail,'insureType',v.children[0].insureType)"/>
<b v-else v-text="dict.getLabel('productType', detail.productType)"/>
</el-form-item>
<el-form-item label="投保类型">
<ai-input :value="dict.getLabel('insureType',detail.insureType)" placeholder="根据保险产品自动带出" :edit="isAdd||isEdit" readonly/>
</el-form-item>
<el-form-item label="联系人">
<ai-input v-model="detail.contacts" :edit="isAdd||isEdit"/>
</el-form-item>
<el-form-item label="联系电话">
<ai-input v-model="detail.phone" :edit="isAdd||isEdit"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="投保对象">
<template #right v-if="isAdd||isEdit">
<ai-eartag-picker @select="v=>detail.detailList=v" :instance="instance" value-key="biochipEarNumber"
:action="`/api/insurance/apply/getEarNumberList?farmId=${detail.farmId}`">
<el-button type="text">选择</el-button>
</ai-eartag-picker>
</template>
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1" hideOptions/>
</ai-card>
<ai-card title="证件信息">
<div class="grid c-5 el-form--label-top">
<el-form-item v-for="(img,i) in formImages" :key="i" v-bind="img">
<ai-uploader v-if="isAdd||isEdit" v-model="detail[img.prop]" :instance="instance" :limit="1" value-is-url/>
<el-image :src="detail[img.prop]" :preview-src-list="[detail[img.prop]]" v-else/>
</el-form-item>
</div>
</ai-card>
<ai-card title="审核信息" v-if="!(isAdd||isEdit)">
<el-form-item label="审核状态">{{ dict.getLabel('insuranceAuditStatus', detail.auditStatus) }}</el-form-item>
<el-form-item label="审核时间">{{ detail.auditTime }}</el-form-item>
<el-form-item label="审核人">{{ detail.auditName }}</el-form-item>
<el-form-item label="保单订单号">{{ detail.orderNo }}</el-form-item>
<el-form-item label="保单资料">
<el-image :src="detail.picture" :preview-src-list="[detail.picture]"/>
</el-form-item>
<el-form-item label="说明">{{ detail.remarks }}</el-form-item>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isAdd||isEdit">
<el-button type="primary" @click="submit(1)">保存草稿</el-button>
<el-button type="primary" @click="submit(2)">保存并提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.iaAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
}
</style>

View File

@@ -0,0 +1,119 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "投保单号", prop: "orderNo"},
{label: "所属养殖场", prop: "farmName"},
{label: "投保类型", prop: "insureType", dict: "insureType"},
{label: "承保公司", prop: "companyName"},
{label: "投保时间", prop: "createTime"},
{label: "投保状态", prop: "status", width: 160, dict: "insureStatus"},
{label: "审核状态", prop: "auditStatus", width: 120, dict: "insuranceAuditStatus"},
{label: "申请人", prop: "applyName", width: 120},
]
export default {
name: "iaList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/insurance/apply/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
handleDelete(id) {
this.$confirm("确定删除该条数据?").then(() => {
this.instance.post("/api/insurance/apply/del", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getTableData()
}
})
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="iaList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<ai-input placeholder="投保订单号" v-model="search.orderNo"/>
<ai-select placeholder="全部投保类型" v-model="search.insureType" dict="insureType"/>
<ai-select placeholder="全部投保状态" v-model="search.status" dict="insureStatus"/>
<ai-select placeholder="全部审批状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="投保日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/insurance/apply/export" :params="{...search,...page}" :fileName="`投保申请导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['00','03'].includes(row.permit)">
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.id,edit:1}})">编辑</el-button>
<el-button type="text" @click="handleDelete(row.id)">删除</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.iaList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppInsuranceAudit",
label: "投保审批",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let { hash } = this.$route
return ["#add", "#audit"].includes(hash) ? add : list
}
},
created() {
this.dict.load("auditStatus", "insureType", "insureStatus", "category", "variety", "productType")
},
}
</script>
<template>
<section class="AppInsuranceAudit">
<component :is="currentPage" v-bind="$props" />
</section>
</template>
<style scoped lang="scss">
.AppInsuranceAudit {
height: 100%;
}
</style>

View File

@@ -0,0 +1,227 @@
<script>
import { mapState } from "vuex"
import AiEartagPicker from "@project/xumu/components/AiEartagPicker.vue";
import AiIdcardRemote from "@project/xumu/components/AiIdcardRemote.vue";
import AiAudit from "../components/AiAudit.vue";
const columns = [
{ label: "序号", type: "index" },
{ label: "生物芯片耳标号", prop: "biochipEarNumber" },
{ label: "类别", prop: "category", dict: "category" },
{ label: "品种", prop: "variety", dict: "variety" },
]
export default {
name: "iaAdd",
components: { AiIdcardRemote, AiEartagPicker, AiAudit },
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: { detailList: [] },
columns,
companyList: []
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
isAdd: v => !v.$route.query.id,
isEdit: v => v.$route.query.edit == 1,
isAudit: v => v.$route.hash == "#audit",
formImages: v => [
{ label: "身份证(正面)", prop: "frontCard", rules: { required: v.isAdd, message: '请上传 身份证(正面)' } },
{ label: "身份证(反面)", prop: "reverseCard", rules: { required: v.isAdd, message: '请上传 身份证(反面)' } },
{ label: "营业执照", prop: "businessPic", rules: { required: v.isAdd, message: '请上传 营业执照' } },
{ label: "畜禽经营许可证", prop: "breedPic", rules: { required: v.isAdd, message: '请上传 畜禽经营许可证' } },
{ label: "动物防疫条件许可证", prop: "prevention", rules: { required: v.isAdd, message: '请上传 动物防疫条件许可证' } },
]
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const { id } = this.$route.query
return id ? this.instance.post("/api/insurance/apply/getInfo", null, { params: { id } }).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.detailList || []
let { farmPicture: picture = "{}" } = detail
picture = JSON.parse(picture)
return this.detail = { ...detail, ...picture }
}
}) : Promise.resolve()
},
getCompanies() {
this.instance.post("/api/insurance/apply/getCompany").then(res => {
if (res?.data) {
this.companyList = res.data
this.$set(this.detail, "companyId", res.data?.[0]?.id)
}
})
},
getProducts(id) {
const item = this.companyList.find(e => e.id == id)
return item?.children || []
},
submit() {
this.$refs.detail.validate().then(() => {
const farmPicture = {}
this.formImages.forEach(e => {
const { prop } = e
const val = this.detail[prop]
if (val) {
farmPicture[prop] = val
}
})
this.detail.farmPicture = JSON.stringify(farmPicture)
this.instance.post("/api/insurance/apply/addOrEditOffline", this.detail).then(res => {
if (res?.code == 0) {
this.$message.success("提交成功!")
this.back()
}
})
})
},
handleAudit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/insurance/apply/audit", this.detail).then(res => {
if (res?.code == 0) {
this.$message.success("提交成功!")
this.back()
}
})
})
},
getRemoteInfo(userId) {
userId && this.instance.post("/api/siteUser/querySiteByUserId", null, {
params: { userId }
}).then(res => {
if (res?.data?.[0]) {
const { id } = res.data[0]
this.$set(this.detail, "farmId", id)
this.$set(this.detail, "targetUser", userId)
}
})
}
},
created() {
this.getDetail().finally(() => this.getCompanies())
}
}
</script>
<template>
<ai-page :title="pageTitle" class="iaAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid">
<el-form-item label="养殖户" class="row">
<ai-idcard-remote :instance="instance" @enter="getRemoteInfo" />
</el-form-item>
<el-form-item label="养殖场" prop="farmId" :rules="{ required: isAdd || isEdit, message: '请选择 养殖场' }">
<ai-select v-if="isAdd || isEdit" v-model="detail.farmId" :instance="instance"
:action="`/api/siteUser/querySiteByUserId?userId=${detail.targetUser}`" :prop="{ label: 'name' }" />
<b v-else v-text="detail.farmName" />
</el-form-item>
<el-form-item label="承保公司" prop="companyId" :rules="{ required: isAdd || isEdit, message: '请选择 承保公司' }">
<ai-select v-if="isAdd || isEdit" v-model="detail.companyId" :select-list="companyList"
:prop="{ label: 'name' }" disabled />
<b v-else v-text="detail.companyName" />
</el-form-item>
<el-form-item label="保险产品" prop="productType" :rules="{ required: isAdd || isEdit, message: '请选择 保险产品' }">
<ai-select v-if="isAdd || isEdit" v-model="detail.productType" :select-list="getProducts(detail.companyId)"
:prop="{ label: 'name', value: 'productType' }"
@select="v => $set(detail, 'insureType', v.children[0].insureType)" />
<b v-else v-text="dict.getLabel('productType', detail.productType)" />
</el-form-item>
<el-form-item label="投保类型">
<ai-input :value="dict.getLabel('insureType', detail.insureType)" placeholder="根据保险产品自动带出"
:edit="isAdd || isEdit" readonly />
</el-form-item>
<el-form-item label="联系人">
<ai-input v-model="detail.contacts" :edit="isAdd || isEdit" />
</el-form-item>
<el-form-item label="联系电话">
<ai-input v-model="detail.phone" :edit="isAdd || isEdit" />
</el-form-item>
</div>
</ai-card>
<ai-card title="投保对象">
<template #right v-if="isAdd">
<ai-eartag-picker @select="v => detail.detailList = v" :instance="instance"
:action="`/api/insurance/apply/getEarNumberList?farmId=${detail.farmId}`">
<el-button type="text">选择</el-button>
</ai-eartag-picker>
</template>
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1" hideOptions />
</ai-card>
<ai-card title="证件信息">
<div class="grid c-5 el-form--label-top">
<el-form-item v-for="(img, i) in formImages" :key="i" v-bind="img">
<ai-uploader v-if="isAdd || isEdit" v-model="detail[img.prop]" :instance="instance" :limit="1"
value-is-url />
<el-image :src="detail[img.prop]" :preview-src-list="[detail[img.prop]]" v-else />
</el-form-item>
</div>
</ai-card>
<ai-card title="审核信息" v-if="!(isAdd || isAudit)">
<el-form-item label="审核状态">{{ dict.getLabel('auditStatus', detail.auditStatus) }}</el-form-item>
<el-form-item label="保单资料" class="sc-4">
<el-image :src="detail.picture" :preview-src-list="[detail.picture]" />
</el-form-item>
<el-form-item label="审核时间">{{ detail.auditTime }}</el-form-item>
<el-form-item label="审核人">{{ detail.auditName }}</el-form-item>
<el-form-item label="保单订单号">{{ detail.orderNo }}</el-form-item>
<el-form-item label="说明">{{ detail.remarks }}</el-form-item>
</ai-card>
<ai-card title="保险资料" v-if="isAudit">
<div class="grid">
<el-form-item label="审批" prop="auditStatus"
:rules="[{ required: true, type: 'enum', enum: [2, 3], message: '请选择是否同意' },]">
<ai-audit @change="v => $set(detail, 'auditStatus', v)" />
</el-form-item>
<el-form-item label="保单资料" class="sc-3">
<ai-uploader v-model="detail.picture" value-is-url :instance="instance" :limit="1" />
</el-form-item>
<el-form-item label="保单订单号" prop="orderNo" :rules="{ required: true, message: '请输入 保单订单号' }"
v-if="detail.auditStatus == 2">
<ai-input v-model="detail.orderNo" />
</el-form-item>
<el-form-item label="说明">
<ai-input type="textarea" :rows="3" v-model="detail.remarks" />
</el-form-item>
</div>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isAdd || isEdit">
<el-button type="primary" @click="submit">提交</el-button>
</template>
<el-button v-if="isAudit" type="primary" @click="handleAudit">提交</el-button>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.iaAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
}
</style>

View File

@@ -0,0 +1,108 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "投保单号", prop: "orderNo"},
{label: "所属养殖场", prop: "farmName"},
{label: "投保类型", prop: "insureType", dict: "insureType"},
{label: "承保公司", prop: "companyName"},
{label: "投保时间", prop: "createTime"},
{label: "投保状态", prop: "status", width: 160, dict: "insureStatus"},
{label: "审核状态", prop: "auditStatus", width: 120, dict: "auditStatus"},
{label: "申请人", prop: "applyName", width: 120},
]
export default {
name: "iaList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/insurance/apply/getAuditPage", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="iaList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-input placeholder="投保订单号" v-model="search.orderNo"/>
<ai-select placeholder="全部投保类型" v-model="search.insureType" dict="insureType"/>
<ai-select placeholder="全部投保状态" v-model="search.status" dict="insureStatus"/>
<ai-select placeholder="全部审批状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="投保日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="养殖户" v-model="search.userName"/>
<ai-input placeholder="养殖场" v-model="search.farmName"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/insurance/apply/exportAudit" :params="{...search,...page}" :fileName="`投保审批导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['01'].includes(row.permit)">
<el-button type="text" @click="$router.push({hash:'#audit',query:{id:row.id}})">审批</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.iaList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppLoanApply",
label: "贷款申请",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? add : list
}
},
created() {
this.dict.load("auditStatus", "loanProduct", "loanStatus", "category", "variety")
},
}
</script>
<template>
<section class="AppLoanApply">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppLoanApply {
height: 100%;
}
</style>

View File

@@ -0,0 +1,187 @@
<script>
import {mapState} from "vuex"
import AiEartagPicker from "@project/xumu/components/AiEartagPicker.vue";
const columns = [
{label: "序号", type: "index"},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category"},
{label: "品种", prop: "variety", dict: "variety"},
]
export default {
name: "loanAdd",
components: {AiEartagPicker},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: [], loanAmount: 0},
columns,
bankList: [],
filterText: ""
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
isAdd: v => !v.$route.query.id,
isEdit: v => v.$route.query.edit == 1,
formImages: v => [
{label: "身份证(正面)", prop: "frontCard", rules: {required: v.isAdd || v.isEdit, message: '请上传 身份证(正面)'}},
{label: "身份证(反面)", prop: "reverseCard", rules: {required: v.isAdd || v.isEdit, message: '请上传 身份证(反面)'}},
{label: "营业执照", prop: "businessPic", rules: {required: v.isAdd || v.isEdit, message: '请上传 营业执照'}},
{label: "畜禽经营许可证", prop: "breedPic", rules: {required: v.isAdd || v.isEdit, message: '请上传 畜禽经营许可证'}},
{label: "动物防疫条件许可证", prop: "prevention", rules: {required: v.isAdd || v.isEdit, message: '请上传 动物防疫条件许可证'}},
],
selectedEartags: v => v.detail.detailList?.length || 0,
tableData: v => v.detail.detailList?.filter(e => e.biochipEarNumber.indexOf(v.filterText) > -1) || [],
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/loan/apply/getInfo", null, {params: {id}}).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.detailList || []
let {farmPicture: picture = "{}"} = detail
picture = JSON.parse(picture)
return this.detail = {...detail, ...picture}
}
})
},
getBanks() {
this.instance.post("/api/loan/apply/getBank").then(res => {
if (res?.data) {
this.bankList = res.data
}
})
},
getProducts(id) {
const item = this.bankList.find(e => e.id == id)
return item?.children || []
},
submit(submitType) {
this.$refs.detail.validate().then(() => {
const earNumberList = this.detail.detailList.map(e => e.biochipEarNumber)
const farmPicture = {}
this.formImages.forEach(e => {
const {prop} = e
const val = this.detail[prop]
if (val) {
farmPicture[prop] = val
}
})
this.detail.farmPicture = JSON.stringify(farmPicture)
this.instance.post("/api/loan/apply/addOrEdit", {...this.detail, submitType}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
}
},
created() {
this.getBanks()
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="loanAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid">
<el-form-item label="养殖场" prop="farmId" :rules="{required: isAdd||isEdit,message:'请选择 养殖场'}">
<ai-select v-if="isAdd||isEdit" v-model="detail.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<b v-else v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="贷款银行" prop="bankId" :rules="{required: isAdd||isEdit,message:'请选择 贷款银行'}">
<ai-select v-if="isAdd||isEdit" v-model="detail.bankId" :select-list="bankList" :prop="{label:'name'}"/>
<b v-else v-text="detail.bankName"/>
</el-form-item>
<el-form-item label="贷款产品" prop="productType" :rules="{required: isAdd||isEdit,message:'请选择 贷款产品'}">
<ai-select v-if="isAdd||isEdit" v-model="detail.productType"
:select-list="getProducts(detail.bankId)" :prop="{label:'name',value:'productType'}"/>
<b v-else v-text="dict.getLabel('loanProduct',detail.productType)"/>
</el-form-item>
<el-form-item label="预期贷款额(万)" prop="loanAmount" :rules="{required: isAdd||isEdit,message:'请输入 预期贷款额'}">
<ai-input v-model.number="detail.loanAmount" :edit="isAdd||isEdit"/>
</el-form-item>
<el-form-item label="联系人" prop="contacts" :rules="{required: isAdd||isEdit,message:'请输入 联系人'}">
<ai-input v-model="detail.contacts" :edit="isAdd||isEdit"/>
</el-form-item>
<el-form-item label="联系电话" prop="phone" :rules="{required: isAdd||isEdit,message:'请输入 联系电话'}">
<ai-input v-model="detail.phone" :edit="isAdd||isEdit"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="质押标的">
<template #right>
<ai-input placeholder="请输入/扫描耳标号" v-model="filterText" class="shrink"/>
<ai-eartag-picker @select="v=>detail.detailList=v" :instance="instance" v-if="isAdd||isEdit" value-key="biochipEarNumber"
:action="`/api/insurance/apply/getEarNumberList?farmId=${detail.farmId}`">
<el-button type="text">选择</el-button>
</ai-eartag-picker>
</template>
<ai-highlight class="mar-b8 font-14" :content="`拟抵押标的选择 @v 只`" color="red" :value="selectedEartags"/>
<ai-table :tableData="tableData" :colConfigs="columns" :isShowPagination="!1" hideOptions/>
</ai-card>
<ai-card title="证件信息">
<div v-if="isAdd||isEdit" class="font-12 color-888">只能上传JPG/PNG文件且不超过2M</div>
<div class="grid c-5 el-form--label-top">
<el-form-item v-for="(img,i) in formImages" :key="i" v-bind="img">
<ai-uploader v-if="isAdd||isEdit" v-model="detail[img.prop]" :instance="instance" :limit="1" value-is-url/>
<el-image :src="detail[img.prop]" :preview-src-list="[detail[img.prop]]" v-else/>
</el-form-item>
</div>
</ai-card>
<ai-card title="审核信息" v-if="!(isAdd||isEdit)">
<el-form-item label="审核状态">{{ dict.getLabel('auditStatus', detail.auditStatus) }}</el-form-item>
<el-form-item label="审核时间">{{ detail.auditTime }}</el-form-item>
<el-form-item label="审核人">{{ detail.auditName }}</el-form-item>
<el-form-item label="贷款合同号">{{ detail.contractNo || "-" }}</el-form-item>
<el-form-item label="贷款资料">
<el-image :src="detail.picture" :preview-src-list="[detail.picture]"/>
</el-form-item>
<el-form-item label="说明">{{ detail.remarks }}</el-form-item>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isAdd||isEdit">
<el-button type="primary" @click="submit(1)">保存草稿</el-button>
<el-button type="primary" @click="submit(2)">保存并提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.loanAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
.shrink {
flex-shrink: 0;
}
}
</style>

View File

@@ -0,0 +1,118 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "贷款合同号", prop: "contractNo"},
{label: "所属养殖户", prop: "applyName"},
{label: "所属养殖场", prop: "farmName"},
{label: "贷款金额(万元)", prop: "loanAmount"},
{label: "贷款状态", prop: "status", width: 160, dict: "loanStatus"},
{label: "审核状态", prop: "auditStatus", width: 120, dict: "auditStatus"},
{label: "贷款时间", prop: "createTime", width: 160},
]
export default {
name: "loanList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/loan/apply/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
handleDelete(id) {
this.$confirm("确定删除该条数据?").then(() => {
this.instance.post("/api/loan/apply/del", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getTableData()
}
})
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="loanList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-input placeholder="贷款合同号" v-model="search.contractNo"/>
<ai-select placeholder="全部贷款状态" v-model="search.status" dict="loanStatus"/>
<ai-select placeholder="全部审批状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="贷款日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="养殖户" v-model="search.applyName"/>
<ai-input placeholder="养殖场" v-model="search.farmName"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/loan/apply/export" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['00','03'].includes(row.permit)">
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.id,edit:1}})">编辑</el-button>
<el-button type="text" @click="handleDelete(row.id)">删除</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.loanList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppLoanAudit",
label: "贷款审核",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return ["#audit", "#add"].includes(hash) ? add : list
}
},
created() {
this.dict.load("auditStatus", "loanProduct", "loanStatus", "category", "variety")
},
}
</script>
<template>
<section class="AppLoanAudit">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppLoanAudit {
height: 100%;
}
</style>

View File

@@ -0,0 +1,248 @@
<script>
import { mapState } from "vuex"
import AiEartagPicker from "@project/xumu/components/AiEartagPicker.vue";
import AiIdcardRemote from "@project/xumu/components/AiIdcardRemote.vue";
import AiAudit from "../components/AiAudit.vue";
const records = [
{ label: "序号", type: "index" },
{ label: "报案号", prop: "reportNo" },
{ label: "审批状态", prop: "auditStatus", dict: "auditStatus" },
{ label: "审批时间", prop: "auditTime" },
{ label: "审批人", prop: "auditName" },
]
export default {
name: "loanAdd",
components: { AiIdcardRemote, AiEartagPicker, AiAudit },
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: { detailList: [] },
records,
filterText: "",
bankList: []
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.isAdd ? `新增${appName}` : v.isAudit ? `${appName}审批` : `${appName}详情`
},
isAdd: v => !v.$route.query.id,
isAudit: v => v.$route.hash == "#audit",
formImages: v => [
{ label: "身份证(正面)", prop: "frontCard", rules: { required: v.isAdd, message: '请上传 身份证(正面)' } },
{ label: "身份证(反面)", prop: "reverseCard", rules: { required: v.isAdd, message: '请上传 身份证(反面)' } },
{ label: "营业执照", prop: "businessPic", rules: { required: v.isAdd, message: '请上传 营业执照' } },
{ label: "畜禽经营许可证", prop: "breedPic", rules: { required: v.isAdd, message: '请上传 畜禽经营许可证' } },
{ label: "动物防疫条件许可证", prop: "prevention", rules: { required: v.isAdd, message: '请上传 动物防疫条件许可证' } },
],
columns: v => [
{ label: "序号", type: "index" },
{ label: "生物芯片耳标号", prop: "biochipEarNumber" },
{ label: "类别", prop: "category", dict: "category" },
{ label: "品种", prop: "variety", dict: "variety" }
].filter(e => !e.hide),
selectedEartags: v => v.detail.detailList?.length || 0,
tableData: v => v.detail.detailList?.filter(e => e.biochipEarNumber.indexOf(v.filterText) > -1) || [],
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const { id } = this.$route.query
return id ? this.instance.post("/api/loan/apply/getInfo", null, { params: { id } }).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.detailList || []
let { farmPicture: picture } = detail
picture = JSON.parse(picture || "{}")
return this.detail = { ...detail, ...picture }
}
}) : Promise.resolve()
},
submit() {
this.$refs.detail.validate().then(() => {
const farmPicture = {}
this.formImages.forEach(e => {
const { prop } = e
const val = this.detail[prop]
if (val) {
farmPicture[prop] = val
}
})
this.detail.farmPicture = JSON.stringify(farmPicture)
this.instance.post("/api/loan/apply/addOrEditOffline", { ...this.detail }).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
},
audit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/loan/apply/audit", { ...this.detail }).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
},
getBanks() {
this.instance.post("/api/loan/apply/getBank").then(res => {
if (res?.data) {
this.bankList = res.data
this.$set(this.detail, "bankId", res.data?.[0]?.id)
}
})
},
getProducts(id) {
const item = this.bankList.find(e => e.id == id)
return item?.children || []
},
getRemoteInfo(userId) {
userId && this.instance.post("/api/siteUser/querySiteByUserId", null, {
params: { userId }
}).then(res => {
if (res?.data) {
this.$set(this.detail, "farmId", res.data?.[0]?.id)
}
})
}
},
created() {
this.getDetail().finally(() => this.getBanks())
}
}
</script>
<template>
<ai-page :title="pageTitle" class="loanAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid">
<el-form-item label="养殖户" class="row">
<ai-idcard-remote :instance="instance" @enter="getRemoteInfo" />
</el-form-item>
<el-form-item label="养殖场" prop="farmId" :rules="{ required: isAdd, message: '请选择 养殖场' }">
<ai-select v-if="isAdd" v-model="detail.farmId" :instance="instance"
:action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{ label: 'name' }" />
<b v-else v-text="detail.farmName" />
</el-form-item>
<el-form-item label="贷款银行" prop="bankId" :rules="{ required: isAdd, message: '请选择 贷款银行' }">
<ai-select v-if="isAdd" v-model="detail.bankId" :select-list="bankList" :prop="{ label: 'name' }"
disabled />
<b v-else v-text="detail.bankName" />
</el-form-item>
<el-form-item label="贷款产品" prop="productType" :rules="{ required: isAdd, message: '请选择 贷款产品' }">
<ai-select v-if="isAdd" v-model="detail.productType" :select-list="getProducts(detail.bankId)"
:prop="{ label: 'name', value: 'productType' }" />
<b v-else v-text="dict.getLabel('loanProduct', detail.productType)" />
</el-form-item>
<el-form-item label="预期贷款额(万)" prop="loanAmount" :rules="{ required: isAdd, message: '请输入 预期贷款额' }">
<ai-input v-model.number="detail.loanAmount" :edit="isAdd" />
</el-form-item>
<el-form-item label="联系人" prop="contacts" :rules="{ required: isAdd, message: '请输入 联系人' }">
<ai-input v-model="detail.contacts" :edit="isAdd" />
</el-form-item>
<el-form-item label="联系电话" prop="phone" :rules="{ required: isAdd, message: '请输入 联系电话' }">
<ai-input v-model="detail.phone" :edit="isAdd" />
</el-form-item>
</div>
</ai-card>
<ai-card title="质押标的">
<template #right>
<ai-input placeholder="请输入/扫描耳标号" v-model="filterText" class="shrink" />
<ai-eartag-picker @select="v => detail.detailList = v" :instance="instance" v-if="isAdd"
:action="`/api/insurance/apply/getEarNumberList?farmId=${detail.farmId}`">
<el-button type="text">选择</el-button>
</ai-eartag-picker>
</template>
<ai-highlight class="mar-b8 font-14" :content="`拟抵押标的选择 @v 只`" color="red" :value="selectedEartags" />
<ai-table :tableData="tableData" :colConfigs="columns" :isShowPagination="!1" hideOptions />
</ai-card>
<ai-card title="证件信息">
<div v-if="isAdd" class="font-12 color-888">只能上传JPG/PNG文件且不超过2M</div>
<div class="grid c-5 el-form--label-top">
<el-form-item v-for="(img, i) in formImages" :key="i" v-bind="img">
<ai-uploader :readonly="!isAdd" v-model="detail[img.prop]" :instance="instance" :limit="1" value-is-url />
</el-form-item>
</div>
</ai-card>
<ai-card title="银行资料" v-if="isAdd">
<div class="grid">
<el-form-item label="贷款合同号" prop="contractNo" :rules="{ required: true, message: '请输入贷款合同号' }">
<ai-input v-model="detail.contractNo" />
</el-form-item>
<el-form-item label="贷款资料" class="sc-2">
<ai-uploader v-model="detail.picture" value-is-url :instance="instance" :limit="1" />
</el-form-item>
<el-form-item label="说明">
<ai-input type="textarea" :rows="3" v-model="detail.remarks" />
</el-form-item>
</div>
</ai-card>
<ai-card title="审核信息" v-if="!isAdd">
<div class="grid">
<template v-if="isAudit">
<el-form-item label="审批状态" prop="auditStatus" :rules="{ required: true, message: '请选择审批状态' }">
<ai-audit @change="v => $set(detail, 'auditStatus', v)" />
</el-form-item>
<el-form-item label="贷款资料" class="sc-3">
<ai-uploader v-model="detail.picture" value-is-url :instance="instance" :limit="1" />
</el-form-item>
<el-form-item label="贷款合同号" prop="contractNo"
:rules="{ required: detail.auditStatus == 2, message: '请输入 报案号' }">
<ai-input v-model="detail.contractNo" />
</el-form-item>
<el-form-item label="说明">
<ai-input type="textarea" :rows="3" v-model="detail.remarks" />
</el-form-item>
</template>
<template v-else>
<el-form-item label="审核状态">{{ dict.getLabel('auditStatus', detail.auditStatus) }}</el-form-item>
<el-form-item label="贷款合同号">{{ detail.contractNo }}</el-form-item>
<el-form-item label="审核时间">{{ detail.auditTime }}</el-form-item>
<el-form-item label="贷款资料" class="sc-3">
<el-image :src="detail.picture" :preview-src-list="[detail.picture]" />
</el-form-item>
<el-form-item label="审核人">{{ detail.auditName }}</el-form-item>
<el-form-item label="说明">{{ detail.remarks }}</el-form-item>
</template>
</div>
</ai-card>
</el-form>
<div slot="footer">
<el-button v-if="isAdd" type="primary" @click="submit">提交</el-button>
<el-button v-if="isAudit" type="primary" @click="audit">提交</el-button>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.loanAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
.shrink {
flex-shrink: 0;
}
}
</style>

View File

@@ -0,0 +1,108 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "贷款合同号", prop: "contractNo"},
{label: "所属养殖户", prop: "applyName"},
{label: "所属养殖场", prop: "farmName"},
{label: "贷款银行", prop: "bankName"},
{label: "贷款产品", prop: "productType", dict: "loanProduct"},
{label: "贷款时间", prop: "createTime", width: 160},
{label: "贷款状态", prop: "status", dict: "loanStatus", width: 100},
{label: "审批状态", prop: "auditStatus", dict: "auditStatus", width: 100},
{label: "审批人", prop: "auditName"},
]
export default {
name: "loanList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/loan/apply/getAuditPage", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="loanList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-input placeholder="贷款合同号" v-model="search.contractNo"/>
<ai-select placeholder="全部贷款状态" v-model="search.status" dict="loanStatus"/>
<ai-select placeholder="全部审批状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="贷款日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="养殖户" v-model="search.applyName"/>
<ai-input placeholder="养殖场" v-model="search.farmName"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/loan/apply/exportAudit" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['1'].includes(row.auditStatus)">
<el-button type="text" @click="$router.push({hash:'#audit',query:{id:row.id}})">审核</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.loanList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppOutAudit",
label: "淘汰审核",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return ["#audit", "#add"].includes(hash) ? add : list
}
},
created() {
this.dict.load("auditStatus","outReason", "category", "variety")
},
}
</script>
<template>
<section class="AppOutAudit">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppOutAudit {
height: 100%;
}
</style>

View File

@@ -0,0 +1,155 @@
<script>
import {mapState} from "vuex"
export default {
name: "oaAdd",
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []}
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.isAudit ? `${appName}审批` : `${appName}详情`
},
isAudit: v => v.$route.hash == "#audit",
formImages: v => [
{label: "身长测量照片", prop: "heightPic"},
{label: "生物芯片照片", prop: "biochipPic"},
{label: "防疫耳标照片", prop: "preventionPic"},
{label: "其他说明照片", prop: "otherPic"},
],
columns: v => [
{label: "序号", type: "index"},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "身长测量照片", prop: "heightPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
{label: "电子耳标照片", prop: "earNumberPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
{label: "防疫耳标照片", prop: "preventionPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
].filter(e => !e.hide),
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/breed/out/getAuditInfo", null, {params: {id}}).then(res => {
if (res?.data) {
const detail = res.data
return this.detail = {...detail}
}
})
},
submit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/breed/out/audit", {...this.detail}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
}
},
created() {
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="oaAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid c-4">
<el-form-item label="生物芯片耳标号" class="row">
<b v-text="detail.biochipEarNumber"/>
</el-form-item>
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
<el-form-item label="电子耳标号" prop="electronicEarNumber">
<b v-text="detail.electronicEarNumber"/>
</el-form-item>
<el-form-item label="原厂耳标号" prop="category">
<b v-text="detail.originalEarNumber"/>
</el-form-item>
<el-form-item label="类别" prop="category">
<b v-text="detail.category"/>
</el-form-item>
<el-form-item label="品种" prop="variety">
<b v-text="detail.variety"/>
</el-form-item>
<div class="row flex">
<el-form-item v-for="(img,i) in formImages" :key="i" v-bind="img">
<ai-uploader v-model="detail[img.prop]" value-is-url readonly/>
</el-form-item>
</div>
</div>
</ai-card>
<ai-card title="淘汰信息">
<el-form-item label="淘汰原因">
<b v-text="dict.getLabel('outReason',detail.reason)"/>
</el-form-item>
<el-form-item label="淘汰时间">
<b v-text="detail.outTime"/>
</el-form-item>
<el-form-item label="备注">
<b v-text="detail.remark"/>
</el-form-item>
</ai-card>
<ai-card title="审核信息">
<div class="grid">
<template v-if="isAudit">
<el-form-item label="审批状态" prop="auditStatus" :rules="{required:true,message:'请选择审批状态'}">
<ai-select v-model="detail.auditStatus" dict="auditStatus"/>
</el-form-item>
<el-form-item label="意见">
<ai-input type="textarea" :rows="3" v-model="detail.remark"/>
</el-form-item>
</template>
<template v-else>
<el-form-item label="审核状态">{{ dict.getLabel('auditStatus', detail.auditStatus) }}</el-form-item>
<el-form-item label="审核时间">{{ detail.auditTime }}</el-form-item>
<el-form-item label="审核人">{{ detail.auditName }}</el-form-item>
<el-form-item label="意见">{{ detail.remarks }}</el-form-item>
</template>
</div>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isAudit">
<el-button type="primary" @click="submit">提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.oaAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
}
</style>

View File

@@ -0,0 +1,112 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category", width: 120},
{label: "品种", prop: "variety", dict: "variety", width: 120},
{label: "日龄(天)", prop: "age", width: 80},
{label: "淘汰时间", prop: "outTime"},
{label: "淘汰原因", prop: "reason", width: 80},
{label: "登记时间", prop: "createTime"},
{label: "操作人", prop: "userName", width: 100},
{label: "审核状态", prop: "auditStatus", dict: "auditStatus", width: 80},
]
export default {
name: "oaList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/breed/out/getAuditPage", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="oaList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部审核状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="淘汰日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.outBeginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.outEndDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-search label="登记日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="原场耳标号" v-model="search.originalEarNumber"/>
<ai-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber"/>
<ai-input placeholder="电子耳标号" v-model="search.electronicEarNumber"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<ai-download :instance="instance" url="/api/breed/out/exportAudit" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['1'].includes(row.auditStatus)">
<el-button type="text" @click="$router.push({hash:'#audit',query:{id:row.id}})">审核</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.oaList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppOutManage",
label: "淘汰登记",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? add : list
}
},
data() {
return {}
}
}
</script>
<template>
<section class="AppOutManage">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppOutManage {
height: 100%;
}
</style>

View File

@@ -0,0 +1,170 @@
<script>
import {mapState} from "vuex"
import AiEartagRemote from "@project/xumu/components/AiEartagRemote.vue";
const formImages = [
{label: "身长测量照片", prop: "heightPic",},
{label: "生物芯片照片", prop: "biochipPic",},
{label: "防疫耳标照片", prop: "preventionPic",},
{label: "其他说明照片", prop: "otherPic",},
]
export default {
name: "outAdd",
components: {AiEartagRemote},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
formImages,
filter: "",
detail: {},
form: {}
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
isAdd: v => !v.$route.query.id,
isEdit: v => v.$route.query.edit == 1,
isAuthing: v => v.detail.auditStatus == 1,
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail(id) {
return id && this.instance.post("/api/breed/out/page", {biochipEarNumber: id}).then(res => {
if (res?.data?.records) {
const detail = res.data.records[0] || {}
if (detail.picture) {
Object.entries(JSON.parse(detail.picture)).forEach(([key, value]) => {
detail[key] = value
})
}
return this.detail = {...detail}
}
})
},
submit() {
this.$refs.detail.validate().then(() => {
const {biochipEarNumber, id, outTime, heightPic, biochipPic, preventionPic, otherPic, reason, remarks} = this.detail
this.instance.post("/api/breed/out/addOrEdit", {
biochipEarNumber, id, outTime, picture: JSON.stringify({heightPic, biochipPic, preventionPic, otherPic}), reason, remarks
}).then(res => {
if (res?.code == 0 && res?.data != 1) {
this.$confirm("是否返回列表页?", "提交成功").then(() => this.back()).catch(() => this.getDetail(biochipEarNumber))
}
})
})
},
handleAudit() {
this.$refs.form.validate().then(() => {
const {id} = this.detail
this.instance.post("/api/breed/out/audit", null, {params: {id, ...this.form}}).then(res => {
if (res?.code == 0) {
this.$message.success("操作成功")
this.back()
}
})
})
},
handlerAutocomplete(value) {
'biochipEarNumber|farmId|houseId|penId|electronicEarNumber|originalEarNumber|category|variety'.split("|").forEach(prop => this.$set(this.detail, prop, value[prop]))
},
},
created() {
this.dict.load("yesOrNo", "category", "variety", "outReason")
this.getDetail(this.$route.query.id)
}
}
</script>
<template>
<ai-page :title="pageTitle" class="outAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid c-4">
<ai-eartag-remote :instance="instance" @enter="handlerAutocomplete" class="row"/>
<el-form-item label="生物芯片耳标号">
<b v-text="detail.biochipEarNumber"/>
</el-form-item>
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
<el-form-item label="电子耳标号" prop="electronicEarNumber">
<b v-text="detail.electronicEarNumber"/>
</el-form-item>
<el-form-item label="原厂耳标号" prop="originalEarNumber">
<b v-text="detail.originalEarNumber"/>
</el-form-item>
<el-form-item label="类别" prop="category">
<b v-text="detail.category"/>
</el-form-item>
<el-form-item label="品种" prop="variety">
<b v-text="detail.variety"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="上传照片">
<div class="grid c-4">
<el-form-item v-for="(img, i) in formImages" :key="i" v-bind="img">
<ai-uploader v-if="isAdd || isEdit" v-model="detail[img.prop]" :instance="instance" :limit="1" value-is-url/>
<el-image :src="detail[img.prop]" :preview-src-list="[detail[img.prop]]" v-else/>
</el-form-item>
</div>
</ai-card>
<ai-card title="淘汰录入">
<div class="grid">
<el-form-item label="淘汰日期" prop="outTime" :rules="[{ required: true, message: '请选择淘汰日期' }]">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-if="isAdd || isEdit" v-model="detail.outTime"/>
<b v-else v-text="detail.outTime"/>
</el-form-item>
<el-form-item label="淘汰原因" prop="reason" :rules="[{ required: true, message: '请选择淘汰原因' }]">
<ai-select v-if="isAdd || isEdit" v-model="detail.reason" dict="outReason"/>
<b v-else v-text="detail.reason"/>
</el-form-item>
<el-form-item label="备注" prop="remark" class="row">
<ai-input type="textarea" :row="3" v-model="detail.remark" :edit="isAdd || isEdit"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="审批信息" v-if="isAuthing">
<el-form :model="form" size="small" ref="form" label-width="120px">
<el-form-item label="是否同意" prop="auditStatus" :rules="[{ required: true, message: '请选择是否同意' }]">
<ai-select v-model="form.auditStatus"
:select-list="[{ dictValue: 2, dictName: '同意' }, { dictValue: 3, dictName: '不同意' }]"/>
</el-form-item>
<el-form-item label="审批意见" prop="auditReason">
<el-input type="textarea" :row="3" v-model="form.auditReason" clearable placeholder="请输入"/>
</el-form-item>
</el-form>
</ai-card>
</el-form>
<div slot="footer">
<el-button type="primary" @click="submit" v-if="isAdd || isEdit">提交</el-button>
<el-button type="primary" @click="handleAudit" v-if="isAuthing">提交</el-button>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.outAdd {
.el-date-editor {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,143 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category", width: 120},
{label: "品种", prop: "variety", dict: "variety", width: 120},
{label: "日龄(天)", prop: "age", width: 80},
{label: "淘汰时间", prop: "outTime"},
{label: "淘汰原因", prop: "reason", width: 80},
{label: "登记时间", prop: "createTime"},
{label: "操作人", prop: "userName", width: 100},
{label: "审核状态", prop: "auditStatus", dict: "auditStatus", width: 80},
]
export default {
name: "outList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/breed/out/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
handleDelete(id) {
this.$confirm("确定删除该条数据?").then(() => {
this.instance.post("/api/breed/weight/del", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getTableData()
}
})
})
},
handleAuditAgain(id) {
this.$confirm("是否要再次提交审批?").then(() => {
this.instance.post("/api/breed/out/audit", null, {
params: {id, auditStatus: 1}
}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功")
this.getTableData()
}
})
})
}
},
created() {
this.dict.load("auditStatus", "category", "variety")
this.getTableData()
}
}
</script>
<template>
<ai-page class="outList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部审核状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="淘汰日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.outBeginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.outEndDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-search label="登记日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="原场耳标号" v-model="search.originalEarNumber"/>
<ai-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber"/>
<ai-input placeholder="电子耳标号" v-model="search.electronicEarNumber"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/breed/out/export" :params="{...search,...page}" :fileName="`淘汰导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button v-if="row.auditStatus==1" type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber}})">查看</el-button>
<template v-else>
<el-button v-if="row.auditStatus==2" type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber}})">查看</el-button>
<el-button v-if="row.auditStatus==2" type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber,edit:1}})">编辑</el-button>
<el-button v-if="row.auditStatus==3" type="text" @click="handleAuditAgain(row.id)">再次提交</el-button>
<el-button type="text" @click="handleDelete(row.id)">删除</el-button>
</template>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.outList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,407 @@
<template>
<section class="AppRoleRightsManager">
<ai-list v-if="!showDetail">
<ai-title slot="title" title="角色管理" isShowBottomBorder/>
<template #content>
<ai-search-bar>
<template #left>
<el-button size="small" type="primary" icon="iconfont iconAdd"
@click="$router.push({hash:'#add'})"
v-if="$permissions('admin_sysapprole_add')">
添加
</el-button>
<el-button
size="small"
icon="iconfont iconDelete"
:disabled="!multipleSelection.length"
class="del-btn-list"
@click="deleteApp('all')"
v-if="$permissions('admin_sysapprole_del')"
>删除
</el-button>
</template>
<template #right>
<el-input
size="small"
v-model="search.roleName"
placeholder="角色名称"
clearable
@change="searchList()"
suffix-icon="iconfont iconSearch"/>
</template>
</ai-search-bar>
<ai-table :tableData="adminList" :colConfigs="colConfigs" :total="total" :current.sync="page.pageNum"
:size.sync="page.pageSize"
@getList="getTableData" :col-configs="colConfigs" :dict="dict"
@selection-change="v=>multipleSelection=v">
<el-table-column label="角色用户" slot="users" align="center">
<template slot-scope="{row}">
<div class="font-12 flex wrap gap-6">
<el-tag size="mini" v-for="(item, i) in row.users" :key="i">{{ item.name }}
<template v-if="item.phone">({{ item.phone }})</template>
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<el-button type="text" @click="beforeCopy(row)" v-if="$permissions('admin_sysapprole_edit')">复制
</el-button>
<el-button type="text" @click="viewApp(row)" v-if="$permissions('admin_sysapprole_detail')">详情
</el-button>
<el-button type="text" @click="openRightsGraph(row)" v-if="$permissions('admin_sysapprole_detail')">关系图
</el-button>
<el-button type="text" @click="toAddAppRole(row)" v-if="$permissions('admin_sysapprole_edit')">编辑
</el-button>
<el-button type="text" @click="deleteApp(row)" v-if="$permissions('admin_sysapprole_del')">删除</el-button>
</template>
</el-table-column>
</ai-table>
<ai-dialog
title="应用角色详情"
:visible.sync="viewShow"
width="600px"
customFooter>
<ai-card title="基本信息">
<template #content>
<ai-wrapper>
<ai-info-item label="应用角色名称" :value="viewInfo.name" isLine/>
</ai-wrapper>
</template>
</ai-card>
<ai-card title="权限信息">
<template #content>
<div style="margin-bottom: 16px" v-text="roleList.map(e => e.name).join('、')"/>
</template>
</ai-card>
<ai-card title="角色账号">
<template #right>
<span style="text-align: right; color: #999">
共<span style="color: #26f" v-text="userList.length"/>个账号
</span>
</template>
<template #content>
<div class="datail-table-body" v-if="userList.length">
<div class="datail-item" v-for="(item, index) in userList" :key="index">
<span class="item-name">{{ item.name }}</span>
<span style="color: #999">{{ item.phone }}</span>
</div>
</div>
</template>
</ai-card>
<template #footer>
<el-button
type="primary"
@click="toAddAppRole(viewInfo)"
v-if="$permissions('admin_sysapprole_edit')"
>编辑角色
</el-button>
</template>
</ai-dialog>
<ai-dialog title="权限关系图" :visible.sync="rightsGraph" class="rightsGraphDialog" customFooter>
<rights-graph :instance="instance" :dict="dict" :app="selectApp"/>
<el-button slot="footer" @click="rightsGraph=false">关闭</el-button>
</ai-dialog>
<!--复制角色-->
<el-dialog
class="editStyle"
:visible.sync="copyDialog"
width="520px"
@close="dataInit()"
title="复制角色">
<el-form :model="form" label-width="80px">
<el-form-item label="角色名" :rules="[{ required: true, message: '', trigger: 'blur' }]">
<el-input
v-model="editName"
placeholder="请输入..."
size="small"
clearable
/>
</el-form-item>
</el-form>
<div slot="footer" style="text-align: center">
<el-button
style="width: 92px"
size="small"
@click="copyDialog = false"
>取消
</el-button
>
<el-button
style="width: 92px"
size="small"
type="primary"
@click="copyFn()"
:disabled="!editName"
>
确认
</el-button>
</div>
</el-dialog>
</template>
</ai-list>
<rights-add v-else :instance="instance" :dict="dict" :permissions="permissions"/>
</section>
</template>
<script>
import RightsAdd from "./rightsAdd";
import RightsGraph from "./rightsGraph";
export default {
name: "AppRoleRightsManager",
components: {RightsGraph, RightsAdd},
label: "角色管理",
provide() {
return {
top: this
}
},
props: {
instance: Function,
dict: Object,
permissions: Function,
actions: {
default: () => ({
list: '/admin/role/page',
apps: '/admin/role/list-all',
delete: '/admin/role/del',
detail: '/admin/role/queryById-checked',
modify: '/admin/role/modify',
})
}
},
computed: {
colConfigs() {
return [
{type: "selection"},
{label: "角色名", prop: "name", width: 140},
{label: "所属端", prop: "type", width: '100px', dict: "roleType"},
{label: "用户数量", prop: "roleCount", align: 'center', width: '80px'},
{slot: "users"},
{slot: "options"}
]
},
showDetail() {
return this.$route.hash == "#add"
}
},
data() {
return {
page: {pageNum: 1, pageSize: 10},
search: {roleName: ''},
adminList: [], //列表数据
total: 0,
multipleSelection: [],
delShow: false,
delParams: "",
delIds: [],
viewShow: false,
viewInfo: {},
roleList: [], //详情权限列表
row: {},
copyDialog: false,
titleDel: "",
form: {},
editName: "",
userList: [],
rightsGraph: false,
selectApp: {}
};
},
created() {
this.getTableData();
this.dict.load('roleType')
},
methods: {
//查询table列表
getTableData() {
this.adminList = [];
this.instance.post(this.actions.list, null, {
params: {...this.page, ...this.search}
}).then(res => {
if (res?.data) {
this.total = res.data.total;
this.adminList = res.data.records;
}
})
},
//查询
searchList() {
this.page.pageNum = 1;
this.getTableData();
},
//添加按钮
toAddAppRole(item) {
this.$router.push({
hash: "#add",
query: {
id: item.id,
name: item.name,
type: item.type,
},
});
},
//删除
deleteApp(e) {
if (e == "all") {
this.multipleSelection.map((item) => {
this.delIds.push(item.id);
});
this.delParams = `ids=${this.delIds}`;
this.titleDel = "确定要执行删除操作吗";
} else {
this.delParams = `ids=${e.id}`;
this.titleDel = "确定需要删除该角色吗";
}
this.$confirm(this.titleDel, {
type: "error",
}).then(() => {
this.instance.post(`${this.actions.delete}?${this.delParams}`).then(res => {
if (res?.msg == "success") {
this.getTableData();
} else {
this.$message.error(res.msg);
}
});
}).catch(() => 0);
},
//查看信息
viewApp(e) {
this.userList = e.users;
this.viewInfo = e;
this.viewShow = true;
this.getRowInfo(this.viewInfo.appId, this.viewInfo.id);
},
//查询 row 信息
getRowInfo(appId, id) {
this.roleList = [];
this.instance.post(`${this.actions.detail}?id=${appId}&roleId=${id}`)
.then(res => {
if (res?.data) {
this.roleList = res.data.filter(e => e.checked)
}
})
},
//复制
beforeCopy(row) {
this.row = row;
this.copyDialog = true;
this.getRowInfo(this.row.appId, this.row.id);
},
//确认复制
copyFn() {
let crr = [];
let appRoleList = this.roleList;
for (let i = 0; i < appRoleList.length; i++) {
if (appRoleList[i].checked) {
crr.push(appRoleList[i].id);
if (appRoleList[i].list.length) {
for (let j = 0; j < appRoleList[i].list.length; j++) {
if (appRoleList[i].list[j].checked) {
crr.push(appRoleList[i].list[j].id);
}
}
}
}
}
this.instance.post(`${this.actions.modify}?menus=${crr}`, null, {
params: {
roleName: this.editName,
appId: this.row.appId,
type: this.row.type
},
})
.then((res) => {
if (res.code == 0) {
this.$message({message: "复制成功", type: "success"});
this.copyDialog = false;
this.searchList()
}
});
},
dataInit() {
this.multipleSelection = [];
this.row = {};
},
openRightsGraph(row) {
this.rightsGraph = true
this.selectApp = row
}
},
}
</script>
<style lang="scss" scoped>
.AppRoleRightsManager {
height: 100%;
:deep( .ai-dialog ) {
.ai-card {
box-shadow: none;
border: 1px solid #eee;
.aibar {
height: 40px;
background: #f3f6f9;
}
.ai-card__body {
padding: 0 16px;
}
}
}
:deep( .rightsGraphDialog ) {
.el-dialog__body {
padding: 0;
}
.ai-dialog__content {
padding-bottom: 0;
}
}
:deep( .datail-table-body ) {
width: 100%;
height: auto;
margin-bottom: 16px;
display: flex;
flex-wrap: wrap;
.datail-item {
flex-shrink: 0;
width: 50%;
height: 24px;
line-height: 24px;
span {
display: inline-block;
font-size: 12px;
}
.item-name {
width: 102px;
padding-left: 16px;
color: #333;
}
}
.datail-item:nth-of-type(2n - 1) {
border-right: 1px solid rgba(208, 212, 220, 1);
width: calc(50% - 1px);
}
}
.padd-l0 {
padding-left: 0 !important;
}
.pad-l16 {
padding-left: 16px;
}
}
</style>

View File

@@ -0,0 +1,193 @@
<template>
<ai-detail class="rightsAdd">
<ai-title :title="addTitle" slot="title" isShowBottomBorder isShowBack @onBackClick="back"/>
<template #content>
<el-form size="small" ref="rightsForm" :model="form" label-width="120px" :rules="rules">
<ai-card title="基本信息">
<template #content>
<div class="grid">
<el-form-item label="应用角色名称" prop="roleName">
<el-input v-model="form.roleName" placeholder="请输入应用角色名称" clearable/>
</el-form-item>
<el-form-item label="角色标记" prop="type" :rules="{required:true,message:'请选择角色标记'}">
<ai-select v-model="form.type" dict="roleType" placeholder="请选择角色标记" clearable/>
</el-form-item>
</div>
</template>
</ai-card>
<ai-card title="权限信息">
<template #content>
<el-form-item label="权限列表" prop="menus">
<div class="roleList">
<el-input v-model="filterText" placeholder="请输入..." clearable suffix-icon="iconfont iconSearch"
@change="$refs.tree.filter(filterText)" :validate-event="false"/>
<div class="tree_list">
<el-tree class="filter-tree" ref="roleTree"
:data="roleList"
show-checkbox
:props="defaultProps"
default-expand-all
:check-strictly="false"
node-key="id"
:default-checked-keys="form.menus"
:filter-node-method="filterNode"
@check="handleMenusSelect"/>
</div>
</div>
</el-form-item>
</template>
</ai-card>
</el-form>
</template>
<template #footer>
<el-button @click="back()">取消</el-button>
<el-button type="primary" @click="confirm">保存</el-button>
</template>
</ai-detail>
</template>
<script>
export default {
name: "rightsAdd",
inject: ['top'],
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
form: {},
id: '',
appList: [],
roleList: [],
defaultProps: {
children: 'list',
label: 'name'
},
treeList: [],
filterText: '',
msgTitle: '添加'
}
},
created() {
if (this.isEdit) {
let {id, name: roleName, type} = this.$route.query
this.form = {menus: [], id, roleName, type}
this.msgTitle = '编辑'
}
this.getPermissions()
},
computed: {
isEdit() {
return this.$route.query.id
},
addTitle() {
return this.isEdit ? '编辑应用角色' : '新增应用角色'
},
rules() {
return {
roleName: {required: true, message: '请输入应用角色名称'},
roleType: {required: true, message: '请选择角色标记'},
menus: {required: true, message: '请选择权限列表内容'},
}
}
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
//应用名称选择 获取权限列表
getId(data) {
if (data.list.length) {
data.list.forEach(item => {
this.getId(item)
})
} else {
if (data.checked) {
this.form.menus?.push(data.id)
}
}
},
getPermissions() {
this.filterText = ''
let {id: roleId} = this.form
this.instance.post(this.top.actions.detail, null, {
params: {roleId}
}).then(res => {
if (res?.data) {
this.roleList = res.data;
if (this.isEdit) {
this.roleList.forEach(e => this.getId(e))
}
this.roleList = this.roleList.filter(item => !(item.component && item.isApp == 0 && item.isMenu == 0))
}
})
},
handleMenusSelect(node, selected) {
this.$set(this.form, 'menus', [...selected?.checkedKeys])
this.$refs.rightsForm.validateField('menus')
},
//保存提交
confirm() {
this.$refs.rightsForm.validate().then(() => {
let menus = [this.$refs.roleTree?.getHalfCheckedKeys(), this.$refs.roleTree?.getCheckedKeys()]?.flat()?.toString()
this.instance.post(this.top.actions.modify, null, {
params: {...this.form, menus}
}).then(res => {
if (res?.msg == "success") {
this.$message.success(`${this.msgTitle}应用角色成功`)
this.back()
this.top.searchList()
}
})
})
},
//取消 返回
back(params = {}) {
this.$router.push(params)
}
},
}
</script>
<style lang="scss" scoped>
.rightsAdd {
width: 100%;
height: 100%;
position: relative;
.el-form-item {
.el-select {
width: 100%;
}
&.is-error {
.roleList {
border-color: #f46;
}
}
}
.roleList {
background-color: #fcfcfc;
border-radius: 2px;
border: solid 1px #d0d4dc;
padding: 8px;
.input {
display: flex;
justify-content: space-between;
padding: 5px;
margin: 0;
}
.tree_list {
padding: 5px;
height: 370px;
overflow: auto;
}
}
}
</style>

View File

@@ -0,0 +1,192 @@
<template>
<section class="rightsGraph">
<div id="RightGraph"/>
</section>
</template>
<script>
;
export default {
name: "rightsGraph",
inject: ['top'],
props: {
instance: Function,
dict: Object,
app: Object
},
computed: {
graphData() {
let data = [...this.users, ...this.nodes].map(e => {
if (e.x) {
return e
} else return {...e, ...this.renderPosition(e)}
})
return [
{
data,
links: this.links,
categories: data.map(e => e.category).flat().map(name => ({name}))
}
]
}
},
data() {
return {
graph: null,
nodes: [],
links: [],
users: []
}
},
watch: {
graphData: {
deep: true, handler() {
this.refreshGraph()
}
}
},
methods: {
initGraph() {
let dom = document.querySelector("#RightGraph")
if (dom) {
this.graph = echarts.init(dom)
this.graph.setOption({
tooltip: {},
series: [
{
type: 'graph',
layout: 'none',
roam: true,
label: {
show: true,
position: 'right',
formatter: '{b}'
},
labelLayout: {
hideOverlap: true,
},
scaleLimit: {
min: 0.4,
max: 4
},
lineStyle: {
color: 'target',
curveness: 0.1
},
emphasis: {
focus: 'adjacency',
lineStyle: {
width: 5
}
}
}
]
})
this.graph.on('click', this.handleNodeClick)
}
},
refreshGraph() {
this.graph?.setOption({
series: this.graphData
})
},
getAppRoles(role) {
if (role) {
this.nodes.push({...role, category: '应用角色', value: "应用角色", symbolSize: 15})
} else this.instance.post(this.top.actions.list, null, {
params: {pageSize: 999}
}).then(res => {
if (res?.data) {
res.data.records.map(e => {
this.getUsers(e.users, e.id)
this.nodes.push({...e, category: '应用角色', value: "应用角色"})
})
}
})
},
getUsers(pending, source) {
pending?.map(e => {
if (!this.users.some(u => u.id == e.phone)) {
this.users.push({id: e.phone, name: e.name, symbolSize: 5, category: '用户', value: "用户"})
}
this.links.push({source, target: e.phone})
})
},
getPermissions({id: roleId, name: category, dataIndex}) {
const addNodes = (list, source, category, pos) => {
list?.map(e => {
let node = {
...e,
symbolSize: 5,
category,
value: e.list?.length > 0 ? "应用" : "权限",
}
node = {...node, ...this.renderPosition(pos || node)}
this.nodes.splice(dataIndex, 0, node)
if (e.checked == 1) {
this.links.push({source, target: e.id})
}
addNodes(e.list, e.id, e.label, node)
})
}
roleId && this.instance.post(this.top.actions.detail, null, {
params: {roleId}
}).then(res => {
if (res?.data) {
addNodes(res.data, roleId, category)
}
})
},
handleNodeClick(v) {
let {data: role, dataIndex} = v
if (!this.nodes.some(e => e.category == role?.name)) {
role && this.getPermissions({...role, dataIndex})
}
},
renderPosition(node) {
node = JSON.parse(JSON.stringify(node))
let pos = {x: 0, y: 0}
if (node?.x) {
pos.x = Math.max(this.graph?.getWidth() / 3 * 2, node.x) + 100 + Math.random() * 50
pos.y = node.y + Math.random() * 100 - 50
} else if (node.value == '应用角色') {
pos.x = this.graph?.getWidth() / 3 - 200 + Math.random() * 200
pos.y = this.graph?.getHeight() / 2 - 100 + Math.random() * 200
} else if (node.value == '应用') {
pos.x = this.graph?.getWidth() / 3 * 2 - 100 + Math.random() * 100
pos.y = Math.random() * this.graph?.getHeight()
} else if (node.value == '用户') {
pos.x = Math.random() * 50
pos.y = Math.random() * this.graph?.getHeight()
} else {
pos.x = this.graph?.getWidth() - 100 + Math.random() * 100
pos.y = Math.random() * this.graph?.getHeight()
}
return pos
}
},
created() {
this.getAppRoles(this.app)
this.getUsers(this.app.users, this.app.id)
this.getPermissions(this.app)
},
mounted() {
this.$nextTick(() => {
this.initGraph()
})
}
}
</script>
<style lang="scss" scoped>
.rightsGraph {
height: 100%;
:deep( #RightGraph ){
width: 100%;
height: 100%;
min-height: 500px;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppSellApply",
label: "出栏申请",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return ["#edit", "#add"].includes(hash) ? add : list
}
},
created() {
this.dict.load("auditStatus", "loanProduct", "loanStatus", "category", "variety")
},
}
</script>
<template>
<section class="AppSellApply">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppSellApply {
height: 100%;
}
</style>

View File

@@ -0,0 +1,145 @@
<script>
import {mapState} from "vuex"
import AiEartagPicker from "@project/xumu/components/AiEartagPicker.vue";
const records = [
{label: "序号", type: "index"},
{label: "解押凭证号", prop: "releaseNo"},
{label: "审批状态", prop: "auditStatus", dict: "auditStatus"},
{label: "审批时间", prop: "auditTime"},
{label: "审批人", prop: "auditName"},
]
export default {
name: "sellAdd",
components: {AiEartagPicker},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []},
records
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.isEdit ? `新增${appName}` : `${appName}详情`
},
isEdit: v => v.$route.hash == "#edit",
formImages: v => [
{label: "合同/协议", prop: "contractPicture", rules: {required: v.isEdit, message: '请上传 合同/协议'}},
],
columns: v => [
{label: "序号", type: "index"},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "身长测量照片", prop: "heightPicture", upload: {instance: v.instance, readonly: !v.isEdit, valueIsUrl: !0, limit: 1}},
{label: "电子耳标照片", prop: "earNumberPicture", upload: {instance: v.instance, readonly: !v.isEdit, valueIsUrl: !0, limit: 1}},
{label: "防疫耳标照片", prop: "preventionPicture", upload: {instance: v.instance, readonly: !v.isEdit, valueIsUrl: !0, limit: 1}},
{label: "解押办结凭证号", prop: "releaseNo", hide: v.isEdit},
].filter(e => !e.hide),
selectedEartags: v => v.detail.list?.length || 0,
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/sell/apply/getInfo", null, {params: {contractNo: id}}).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.detailList || []
detail.list = detail.list || []
return this.detail = {...detail}
}
})
},
submit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/sell/apply/add", {...this.detail}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
}
},
created() {
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="sellAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid">
<el-form-item label="养殖场" prop="farmId" :rules="{message:'请选择 养殖场'}">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="贷款银行" prop="bankId" :rules="{message:'请选择 贷款银行'}">
<b v-text="detail.bankName"/>
</el-form-item>
<el-form-item label="贷款产品" prop="productType" :rules="{message:'请选择 贷款产品'}">
<b v-text="dict.getLabel('loanProduct',detail.productType)"/>
</el-form-item>
<el-form-item label="贷款金额(万)" prop="loanAmount" :rules="{message:'请输入 预期贷款额'}">
<ai-input v-model.number="detail.loanAmount" :edit="!1"/>
</el-form-item>
<el-form-item label="联系人" prop="contacts" :rules="{message:'请输入 联系人'}">
<ai-input v-model="detail.contacts" :edit="!1"/>
</el-form-item>
<el-form-item label="联系电话" prop="phone" :rules="{message:'请输入 联系电话'}">
<ai-input v-model="detail.phone" :edit="!1"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="标的信息">
<template #right v-if="isEdit">
<ai-eartag-picker @select="v=>detail.detailList=v" :instance="instance"
:action="`/api/sell/apply/getClaimEarNumberList?contractNo=${detail.contractNo}`">
<el-button type="text">选择</el-button>
</ai-eartag-picker>
</template>
<ai-highlight class="mar-b8 font-14" :content="`投保标的共${detail.insureNumber||0}只,已理赔标的共 @v 只`" color="red" :value="selectedEartags"/>
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1" hideOptions/>
</ai-card>
<ai-card title="解押材料" v-if="isEdit">
<div class="font-12 mar-b8">只能上传JPG/PNG文件且不超过2M一次最多5张</div>
<el-form-item v-for="(img,i) in formImages" :key="i" v-bind="img">
<ai-uploader v-model="detail[img.prop]" :instance="instance" value-is-url :limit="5"/>
</el-form-item>
</ai-card>
<ai-card title="出栏解押记录" v-else>
<ai-table :tableData="detail.list" :colConfigs="records" :isShowPagination="!1" hideOptions/>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isEdit">
<el-button type="primary" @click="submit(1)">提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.sellAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
}
</style>

View File

@@ -0,0 +1,103 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "贷款合同号", prop: "contractNo"},
{label: "所属养殖户", prop: "applyName"},
{label: "所属养殖场", prop: "farmName"},
{label: "贷款金额(万元)", prop: "loanAmount"},
{label: "贷款状态", prop: "status", dict: "loanStatus"},
{label: "贷款时间", prop: "createTime", width: 160},
{label: "说明", prop: "remarks"},
]
export default {
name: "sellList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/sell/apply/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="sellList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-input placeholder="贷款合同号" v-model="search.contractNo"/>
<ai-search label="贷款日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="养殖户" v-model="search.applyName"/>
<ai-input placeholder="养殖场" v-model="search.farmName"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<ai-download :instance="instance" url="/api/sell/apply/export" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['1'].includes(row.status)">
<el-button type="text" @click="$router.push({hash:'#edit',query:{id:row.contractNo}})">出栏申请</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.contractNo}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.sellList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppSellAudit",
label: "出栏审核",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return ["#audit", "#add"].includes(hash) ? add : list
}
},
created() {
this.dict.load("auditStatus", "loanProduct", "loanStatus", "category", "variety")
},
}
</script>
<template>
<section class="AppSellAudit">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppSellAudit {
height: 100%;
}
</style>

View File

@@ -0,0 +1,149 @@
<script>
import {mapState} from "vuex"
import AiEartagPicker from "@project/xumu/components/AiEartagPicker.vue";
export default {
name: "saAdd",
components: {AiEartagPicker},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []}
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.isAudit ? `${appName}审批` : `${appName}详情`
},
isAudit: v => v.$route.hash == "#audit",
formImages: v => [
{label: "合同/协议", prop: "contractPicture"},
],
columns: v => [
{label: "序号", type: "index"},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "身长测量照片", prop: "heightPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
{label: "电子耳标照片", prop: "earNumberPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
{label: "防疫耳标照片", prop: "preventionPicture", upload: {instance: v.instance, readonly: !0, valueIsUrl: !0, limit: 1}},
].filter(e => !e.hide),
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/sell/apply/getAuditInfo", null, {params: {id}}).then(res => {
if (res?.data) {
const detail = res.data
return this.detail = {...detail}
}
})
},
submit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/sell/apply/audit", {...this.detail}).then(res => {
if (res?.code == '0') {
this.$message.success("提交成功!")
this.back()
}
})
})
}
},
created() {
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="saAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid">
<el-form-item label="养殖场" prop="farmId" :rules="{message:'请选择 养殖场'}">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="贷款银行" prop="bankId" :rules="{message:'请选择 贷款银行'}">
<b v-text="detail.bankName"/>
</el-form-item>
<el-form-item label="贷款产品" prop="productType" :rules="{message:'请选择 贷款产品'}">
<b v-text="dict.getLabel('loanProduct',detail.productType)"/>
</el-form-item>
<el-form-item label="贷款金额(万)" prop="loanAmount" :rules="{message:'请输入 预期贷款额'}">
<ai-input v-model.number="detail.loanAmount" :edit="!1"/>
</el-form-item>
<el-form-item label="联系人" prop="contacts" :rules="{message:'请输入 联系人'}">
<ai-input v-model="detail.contacts" :edit="!1"/>
</el-form-item>
<el-form-item label="联系电话" prop="phone" :rules="{message:'请输入 联系电话'}">
<ai-input v-model="detail.phone" :edit="!1"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="标的信息">
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1" hideOptions/>
</ai-card>
<ai-card title="解押材料">
<el-form-item v-for="(img,i) in formImages" :key="i" v-bind="img">
<ai-uploader v-model="detail[img.prop]" value-is-url readonly/>
</el-form-item>
</ai-card>
<ai-card title="审核信息">
<div class="grid">
<template v-if="isAudit">
<el-form-item label="审批状态" prop="auditStatus" :rules="{required:true,message:'请选择审批状态'}">
<ai-select v-model="detail.auditStatus" dict="auditStatus"/>
</el-form-item>
<el-form-item label="解压资料" class="sc-3">
<ai-uploader v-model="detail.picture" value-is-url :instance="instance" :limit="1"/>
</el-form-item>
<el-form-item label="解押凭证号" prop="reportNo" :rules="{required:true,message:'请输入 解押凭证号'}">
<ai-input v-model="detail.reportNo"/>
</el-form-item>
<el-form-item label="说明">
<ai-input type="textarea" :rows="3" v-model="detail.remarks"/>
</el-form-item>
</template>
<template v-else>
<el-form-item label="审核状态">{{ dict.getLabel('auditStatus', detail.auditStatus) }}</el-form-item>
<el-form-item label="解压资料" class="sc-3">
<el-image :src="detail.picture" :preview-src-list="[detail.picture]"/>
</el-form-item>
<el-form-item label="审核时间">{{ detail.auditTime }}</el-form-item>
<el-form-item label="审核人">{{ detail.auditName }}</el-form-item>
<el-form-item label="说明">{{ detail.remarks }}</el-form-item>
</template>
</div>
</ai-card>
</el-form>
<div slot="footer">
<template v-if="isAudit">
<el-button type="primary" @click="submit">提交</el-button>
</template>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.saAdd {
:deep(.el-form--label-top) {
.el-form-item__label {
width: 100% !important;
}
.el-form-item__content {
margin-left: unset !important;
}
}
}
</style>

View File

@@ -0,0 +1,102 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "解押凭证号", prop: "releaseNo"},
{label: "贷款合同号", prop: "contractNo"},
{label: "所属养殖户", prop: "applyName"},
{label: "解押数量", prop: "sellNumber"},
{label: "审批状态", prop: "auditStatus", dict: "auditStatus"},
{label: "审批时间", prop: "auditTime", width: 160},
{label: "审批人", prop: "auditName"},
]
export default {
name: "saList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/sell/apply/getAuditPage", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.status}` + e.auditStatus}))
this.page.total = res.data.total
}
})
},
},
created() {
this.getTableData()
}
}
</script>
<template>
<ai-page class="saList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-input placeholder="解押凭证号" v-model="search.releaseNo"/>
<ai-input placeholder="贷款合同号" v-model="search.contractNo"/>
<ai-select placeholder="全部审批状态" v-model="search.auditStatus" dict="auditStatus"/>
<ai-search label="投保日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.auditBeginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.auditEndDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-input placeholder="养殖户" v-model="search.applyName"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<ai-download :instance="instance" url="/api/sell/apply/exportAudit" :params="{...search,...page}" :fileName="`${pageTitle}导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<template v-if="['1'].includes(row.auditStatus)">
<el-button type="text" @click="$router.push({hash:'#audit',query:{id:row.id}})">审核</el-button>
</template>
<el-button v-else type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.saList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,231 @@
<template>
<section class="AppSign">
<div class="left signLeftBg">
<el-row type="flex" align="middle">
<img class="AiIcon" v-if="/[\\\/]/.test(logo.icon)" :src="logo.icon" alt=""/>
<ai-icon v-else-if="logo.icon" type="logo" :icon="logo.icon"/>
<div v-if="logo.text" class="logoText mar-l8" v-text="logo.text"/>
</el-row>
<div class="signLeftContent">
<div class="titlePane">
<b v-text="system.name"/>
<div v-text="system.title"/>
</div>
</div>
</div>
<div class="right">
<div class="projectName mar-b48" :title="system.fullTitle">{{ system.fullTitle }}</div>
<el-card class="signBox">
<div class="choosePlatform flex column" v-if="!isAdmin&&!form.type">
<div class="font-20 mar-b40 t-center t-bold">请选择业务端后登陆</div>
<div class="selectPlatform fill">
<div class="flex center pointer" v-for="op in platforms" :key="op.dictValue"
v-text="op.dictName" @click="$set(form,'type',op.dictValue)"/>
</div>
<div class="mar-t32 font-12" style="align-self: flex-end">
未注册用户请扫码添加客服咨询
<i class="iconfont iconEwm" style="font-size: 20px"/>
</div>
</div>
<template v-else>
<div class="font-20 mar-b40 t-center t-bold"><i v-if="!isAdmin" class="el-icon-back" @click="form.type=null"/>账号登录</div>
<el-form :model="form" ref="form" :rules="rules">
<el-form-item prop="username">
<el-input v-model="form.username" placeholder="请输入您的账号"/>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" v-model="form.password" placeholder="请输入您的密码" show-password/>
</el-form-item>
</el-form>
<div class="t-right font-12">忘记密码请联系客服处理</div>
<el-button type="primary" class="login-btn" @click="handleSignIn">登录</el-button>
</template>
</el-card>
<el-row type="flex" align="middle" class="bottomRecord">
<div v-if="system.recordDesc" v-text="system.recordDesc"/>
<el-link v-if="system.recordNo" v-text="system.recordNo" :href="system.recordURL"/>
<div v-if="system.ssl" v-html="system.ssl"/>
</el-row>
</div>
</section>
</template>
<script>
import {mapMutations, mapState} from 'vuex'
const rules = {
username: [{required: true, message: '请输入您的账号', trigger: 'blur'}],
password: [{required: true, message: '请输入您的密码', trigger: 'blur'}]
}
export default {
name: "AppSign",
label: "登录页",
data() {
return {
rules,
form: {}
}
},
computed: {
...mapState(['user', 'sys']),
instance: v => v.$request,
system: v => v.sys?.info || {
fullTitle: '畜牧养殖产业一体化平台'
},
logo: v => !!v.system.loginLogo ? {icon: v.system.loginLogo, text: v.system.loginLogoText} : {icon: v.system.logo, text: v.system.logoText},
isAdmin: v => v.$route.hash == "#sinoecare", //用来判断是否是管理员登录,
dict: v => v.$dict,
platforms: v => v.dict.getDict('roleType').filter(e => !['platform', 'other', 'service'].includes(e.dictValue))
},
created() {
this.dict.load("roleType")
if (this.user.token) {
this.handleGotoHome()
} else {
const {code} = this.$route.query
if (code) {
this.toLogin(code)
}
}
},
methods: {
...mapMutations(['setToken']),
login(data) {
if (data?.access_token) {
this.setToken([data.token_type, data.access_token].join(" "))
this.handleGotoHome()
} else {
this.$message.error(data?.msg || "登录失败!")
}
},
handleGotoHome() {
this.$message.success("登录成功!")
if (this.$route.hash == "#dv") {
this.$router.push({name: "数据大屏入口", hash: "#dv"})
} else {
this.$router.push({name: "Home"})
}
},
handleSignIn() {
this.$refs.form.validate().then(() => {
const password = this.$encryption(this.form.password)
this.form.type = this.form.type || "platform"
this.$request.post("/api/oauth/token", null, {
auth: {username: 'villcloud', password: "villcloud"},
params: {grant_type: 'password', scope: 'server', ...this.form, password}
}).then(data => {
this.login(data)
})
})
}
},
}
</script>
<style lang="scss" scoped>
.AppSign {
display: flex;
box-sizing: border-box;
height: 100%;
.selectPlatform {
width: 100%;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 10px;
& > div {
color: #fff;
background: $primaryBtnColor;
border-radius: 4px;
&:hover {
opacity: 0.8;
}
}
}
.signBox {
width: 500px;
min-height: 300px;
position: relative;
color: $primaryColor;
.choosePlatform {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: inherit;
}
.el-icon-back {
position: absolute;
left: 20px;
top: 25px;
}
.login-btn {
font-size: 16px;
width: 100%;
height: 40px;
line-height: 40px;
margin: 16px auto;
padding: 0;
}
}
.AiIcon {
font-size: 40px;
height: 40px;
}
.logoText {
font-size: 20px;
}
:deep(.left) {
width: 480px;
flex-shrink: 0;
background-size: 100% 100%;
background-repeat: no-repeat;
padding-left: 64px;
padding-top: 40px;
box-sizing: border-box;
color: #fff;
font-size: 16px;
.iconcunwei1 {
font-size: 36px;
}
}
:deep(.right) {
flex: 1;
min-width: 0;
background-color: #F6F8FB;
background-repeat: no-repeat;
background-position: calc(100% - 80px) 0, calc(100% - 40px) 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.bottomRecord {
font-size: 12px;
color: #999;
gap: 16px;
position: fixed;
bottom: 20px;
.el-link {
font-size: inherit;
}
}
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppTreatmentManage",
label: "治疗登记",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? add : list
}
},
data() {
return {}
}
}
</script>
<template>
<section class="AppTreatmentManage">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppTreatmentManage {
height: 100%;
}
</style>

View File

@@ -0,0 +1,186 @@
<script>
import {mapState} from "vuex"
import AiEartagPicker from "../components/AiEartagPicker.vue";
const columns = [
{label: "序号", type: "index"},
{label: "药品名称", prop: "drugName"},
{label: "药量(ml)", prop: "dosage"},
{label: "生产厂家", prop: "factory"},
{label: "厂家批号", prop: "batchNumber"},
{label: "疾病名称", prop: "diseaseName"},
{label: "症状", prop: "symptom"},
{label: "兽医", prop: "userName"},
{label: "治疗时间", prop: "immunityTime"},
{label: "登记时间", prop: "createTime"},
]
const formItems = [
{label: "药品名称", prop: "drugName", rules: {required: true, message: '请选择 药品名称'}},
{label: "每头药量", prop: "dosage"},
{label: "生产厂家", prop: "factory"},
{label: "厂家批号", prop: "batchNumber"},
{label: "疾病名称", prop: "diseaseName"},
{label: "症状", prop: "symptom"},
]
export default {
name: "treatmentAdd",
components: {AiEartagPicker},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []},
columns, formItems,
dialog: false,
form: {}
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
isAdd: v => !v.$route.query.id,
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/breed/treatment/getInfo", null, {params: {biochipEarNumber: id}}).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.treatmentList || []
return this.detail = {...detail}
}
})
},
handleDelete(id) {
this.$confirm("确定删除该条数据?").then(() => {
this.instance.post("/api/breed/treatment/del", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getDetail()
}
})
})
},
handleAdd() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/breed/treatment/add", this.detail).then(res => {
if (res?.code == 0) {
this.$message.success("新增成功!")
this.back()
}
})
})
},
submit() {
this.$refs.detail.validate().then(() => {
this.instance.post("/api/breed/treatment/update", this.form).then(res => {
if (res?.code == 0) {
this.dialog = false
this.getDetail()
}
})
})
}
},
created() {
this.dict.load("yesOrNo")
this.detail.treatmentTime = this.$moment().format("YYYY-MM-DD HH:mm:ss")
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="treatmentAdd" showBack content-string="blank">
<el-form size="small" label-width="120px" :model="detail" ref="detail">
<ai-card title="基础信息">
<div class="grid c-3" v-if="isAdd">
<el-form-item label="养殖场" prop="farmId" :rules="{required:true,message:'请选择 养殖场'}">
<ai-select v-model="detail.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId" :rules="{required:true,message:'请选择 养殖舍'}">
<ai-select v-model="detail.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${detail.farmId||-1}`" :prop="{label:'name'}"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId" :rules="{required:true,message:'请选择 养殖栏'}">
<ai-select v-model="detail.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${detail.houseId||-1}`" :prop="{label:'name'}"/>
</el-form-item>
<el-form-item label="牲畜" prop="earNumberList" :rules="{required:true,message:'请选择 牲畜'}" class="row">
<ai-eartag-picker v-model="detail.earNumberList" :instance="instance" :penId="detail.penId"/>
</el-form-item>
</div>
<div v-else class="grid c-4">
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
<el-form-item label="生物芯片耳标号">
<b v-text="detail.biochipEarNumber"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="治疗登记">
<template v-if="isAdd">
<div class="grid c-3">
<el-form-item label="治疗日期" prop="treatmentTime" :rules="{required:true,message:'请选择 治疗日期'}">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="detail.treatmentTime"/>
</el-form-item>
<el-form-item v-for="(item,i) in formItems" :key="i" v-bind="item">
<ai-input v-model="detail[item.prop]"/>
</el-form-item>
</div>
</template>
<template v-else>
<el-button type="text" slot="right" @click="dialog=true">新增</el-button>
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row,$index}">
<div class="table-options">
<el-button type="text" @click="dialog=true,form=row">编辑</el-button>
<el-button type="text" class="deleteBtn" @click="handleDelete(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-card>
<ai-dialog v-model="dialog" title="治疗登记" @closed="form={}" @confirm="submit">
<el-form size="small" label-width="120px" class="grid">
<el-form-item label="治疗日期" prop="treatmentTime" :rules="{required:true,message:'请选择 治疗日期'}">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="form.treatmentTime"/>
</el-form-item>
<el-form-item v-for="(item,i) in formItems" :key="i" v-bind="item">
<ai-input v-model="detail[item.prop]"/>
</el-form-item>
</el-form>
</ai-dialog>
</el-form>
<div slot="footer">
<el-button v-if="isAdd" type="primary" @click="handleAdd">提交</el-button>
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.treatmentAdd {
.el-date-editor {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,106 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "最近治疗日期", prop: "treatmentTime"},
{label: "最近登记时间", prop: "createTime"},
]
export default {
name: "treatmentList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/breed/treatment/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
},
created() {
this.dict.load("auditStatus", "category", "variety")
this.getTableData()
}
}
</script>
<template>
<ai-page class="treatmentList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<ai-search label="治疗日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.treatmentBeginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.treatmentEndDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<ai-search label="登记日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
<el-input placeholder="原场耳标号" v-model="search.originalEarNumber" size="small" clearable/>
<el-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber" size="small" clearable/>
<el-input placeholder="电子耳标号" v-model="search.electronicEarNumber" size="small" clearable/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
<ai-download :instance="instance" url="/api/breed/treatment/export" :params="{...search,...page}" :fileName="`治疗登记导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.treatmentList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,35 @@
<script>
import add from "./add.vue";
import list from "./list.vue";
export default {
name: "AppWeightManage",
label: "体重管理",
props: {
instance: Function,
dict: Object,
permissions: Function
},
computed: {
currentPage() {
let {hash} = this.$route
return hash == "#add" ? add : list
}
},
created() {
this.dict.load( "yesOrNo", "category", "variety", "dataSources")
},
}
</script>
<template>
<section class="AppWeightManage">
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<style scoped lang="scss">
.AppWeightManage {
height: 100%;
}
</style>

View File

@@ -0,0 +1,144 @@
<script>
import {mapState} from "vuex"
import AiSelect from "dui/packages/basic/AiSelect.vue";
import AiDialog from "dui/packages/basic/AiDialog.vue";
const columns = [
{label: "序号", type: "index"},
{label: "数据来源", prop: "source", dict: "dataSources"},
{label: "重量", prop: "weight"},
{label: "称重时间", prop: "createTime"},
{label: "是否变更过", prop: "isUpdate", dict: "yesOrNo"},
]
export default {
name: "weightAdd",
components: {AiDialog, AiSelect},
props: {
instance: Function,
permissions: Function,
dict: Object
},
data() {
return {
detail: {detailList: []},
columns,
dialog: false,
form: {}
}
},
computed: {
...mapState(["user"]),
userinfo: v => v.user.info || {},
pageTitle: v => {
const appName = v.$parent.menuName || v.$parent.$options.label
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
},
},
methods: {
back(params = {}) {
this.$router.push(params)
},
getDetail() {
const {id} = this.$route.query
return id && this.instance.post("/api/breed/weight/getInfo", null, {params: {biochipEarNumber: id}}).then(res => {
if (res?.data) {
const detail = res.data
detail.detailList = detail.weightList || []
return this.detail = {...detail}
}
})
},
handleDelete(id) {
this.$confirm("确定删除该条数据?").then(() => {
this.instance.post("/api/breed/weight/del", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.getDetail()
}
})
})
},
submit() {
this.$refs.detail.validate().then(() => {
this.form.biochipEarNumber = this.detail.biochipEarNumber
this.instance.post("/api/breed/weight/addOrEdit", null, {params: this.form}).then(res => {
if (res?.code == 0) {
this.dialog = false
this.getDetail()
}
})
})
}
},
created() {
this.getDetail()
}
}
</script>
<template>
<ai-page :title="pageTitle" class="weightAdd" showBack content-string="blank">
<el-form size="small" label-width="120px">
<ai-card title="基础信息">
<div class="grid c-4">
<el-form-item label="生物芯片耳标号">
<b v-text="detail.biochipEarNumber"/>
</el-form-item>
<el-form-item label="养殖场" prop="farmId">
<b v-text="detail.farmName"/>
</el-form-item>
<el-form-item label="养殖舍" prop="houseId">
<b v-text="detail.houseName"/>
</el-form-item>
<el-form-item label="养殖栏" prop="penId">
<b v-text="detail.penName"/>
</el-form-item>
<el-form-item label="类别" prop="penId">
<b v-text="detail.category"/>
</el-form-item>
<el-form-item label="品种" prop="penId">
<b v-text="detail.variety"/>
</el-form-item>
<el-form-item label="最近称重时间">
<b v-text="detail.todayCreateTime||detail.lastCreateTime"/>
</el-form-item>
<el-form-item label="最新体重(公斤)">
<b v-text="detail.todayWeight||detail.lastWeight"/>
</el-form-item>
</div>
</ai-card>
<ai-card title="体重录入">
<el-button type="text" slot="right" @click="dialog=true">新增</el-button>
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row,$index}">
<div class="table-options">
<el-button type="text" @click="dialog=true,form=row">编辑</el-button>
<el-button type="text" class="deleteBtn" @click="handleDelete(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
<ai-dialog v-model="dialog" title="体重录入" @closed="form={}" @confirm="submit" width="50vw">
<el-form size="small" label-width="120px" :model="form" ref="detail" class="grid">
<el-form-item label="办理时间" prop="createTime" :rules="[{required:true,message:'请选择办理时间'}]">
<el-date-picker v-model="form.createTime" :readonly="form.id" value-format="yyyy-MM-dd HH:mm:ss"/>
</el-form-item>
<el-form-item label="体重(公斤)" prop="weight" :rules="[{required:true,message:'请输入体重(公斤)'}]">
<ai-input type="number" v-model="form.weight"/>
</el-form-item>
</el-form>
</ai-dialog>
</el-form>
<div slot="footer">
<el-button @click="back">返回</el-button>
</div>
</ai-page>
</template>
<style scoped lang="scss">
.weightAdd {
}
</style>

View File

@@ -0,0 +1,105 @@
<script>
import {mapState} from "vuex"
const columns = [
{label: "序号", type: "index"},
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
{label: "类别", prop: "category", dict: "category"},
{label: "品种", prop: "variety", dict: "variety"},
{label: "称重日期", prop: "todayCreateTime", width: 160},
{label: "当日体重(公斤)", prop: "todayWeight", width: 120},
{label: "上次称重日期", prop: "lastCreateTime", width: 160},
{label: "上次体重(公斤)", prop: "lastWeight", width: 120},
]
export default {
name: "weightList",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
columns,
tableData: [],
page: {pageNum: 1, pageSize: 10, total: 0},
search: {},
dialog: false,
form: {}
}
},
computed: {
...mapState(['user']),
userinfo: v => v.user.info || {},
pageTitle: v => v.$parent.menuName || v.$parent.$options.label
},
watch: {
search: {
deep: true,
handler() {
this.page.pageNum = 1
this.getTableData()
}
}
},
methods: {
getTableData() {
this.instance.post("/api/breed/weight/page", {...this.page, ...this.search}).then(res => {
if (res?.data) {
this.tableData = res.data?.records
this.page.total = res.data.total
}
})
},
},
created() {
this.dict.load("auditStatus", "category", "variety")
this.getTableData()
}
}
</script>
<template>
<ai-page class="weightList" :title="pageTitle">
<ai-search-bar>
<template #left>
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
<ai-select placeholder="全部类别" v-model="search.category" dict="category"/>
<ai-select placeholder="全部品种" v-model="search.variety" dict="variety"/>
<el-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber" dict="authStatus" size="small" clearable/>
<ai-search label="称重日期">
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.weightBeginDate" type="datetime" placeholder="开始日期" size="small"/>
<el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="search.weightEndDate" type="datetime" placeholder="结束日期" size="small"/>
</ai-search>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<ai-download :instance="instance" url="/api/breed/weight/export" :params="{...search,...page}" :fileName="`称重导出表-${Date.now()}`"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
<el-table-column slot="options" label="操作" fixed="right" align="center">
<template slot-scope="{row}">
<div class="table-options">
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.biochipEarNumber}})">查看</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</ai-page>
</template>
<style scoped lang="scss">
.weightList {
height: 100%;
.deleteBtn {
color: $errorColor;
}
}
</style>

View File

@@ -0,0 +1,19 @@
<script>
const options = [
{ dictValue: 2, dictName: '同意' },
{ dictValue: 3, dictName: '不同意' }
]
export default {
name: "AiAudit",
data() {
return {
options, auditStatus: ""
}
},
}
</script>
<template>
<section class="AiAudit">
<ai-select v-model="auditStatus" :select-list="options" v-bind="$attrs" v-on="$listeners" />
</section>
</template>

Some files were not shown because too many files have changed in this diff Show More