1
This commit is contained in:
42
.env
42
.env
@@ -1,36 +1,16 @@
|
|||||||
# 版本号
|
# 正式环境接口域名 已经改为可配置的,不在此配置
|
||||||
SHOPRO_VERSION=v2.4.1
|
NX_BASE_URL = http://192.168.26.247:9999
|
||||||
|
|
||||||
# 后端接口 - 正式环境(通过 process.env.NODE_ENV 非 development)
|
# 开发环境接口域名
|
||||||
SHOPRO_BASE_URL=http://api-dashboard.yudao.iocoder.cn
|
# NX_DEV_BASE_URL = http://192.168.26.247:9999
|
||||||
|
NX_DEV_BASE_URL = http://192.168.26.105:888/api
|
||||||
# 后端接口 - 测试环境(通过 process.env.NODE_ENV = development)
|
|
||||||
; SHOPRO_DEV_BASE_URL=http://127.0.0.1:48080
|
|
||||||
SHOPRO_DEV_BASE_URL=http://172.16.46.63:30081
|
|
||||||
### SHOPRO_DEV_BASE_URL=http://10.171.1.188:48080
|
|
||||||
### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc
|
|
||||||
|
|
||||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
|
||||||
SHOPRO_UPLOAD_TYPE=server
|
|
||||||
|
|
||||||
# 后端接口前缀(一般不建议调整)
|
|
||||||
SHOPRO_API_PATH=/admin-api
|
|
||||||
|
|
||||||
# 后端 websocket 接口前缀
|
|
||||||
SHOPRO_WEBSOCKET_PATH=/infra/ws
|
|
||||||
|
|
||||||
# 开发环境运行端口
|
# 开发环境运行端口
|
||||||
SHOPRO_DEV_PORT=3000
|
NX_DEV_PORT = 3000
|
||||||
|
|
||||||
|
# 接口地址前缀
|
||||||
|
NX_API_PATH = /api/
|
||||||
|
|
||||||
|
NX_VERSION = v1.0
|
||||||
# 客户端静态资源地址 空=默认使用服务端指定的CDN资源地址前缀 | local=本地 | http(s)://xxx.xxx=自定义静态资源地址前缀
|
# 客户端静态资源地址 空=默认使用服务端指定的CDN资源地址前缀 | local=本地 | http(s)://xxx.xxx=自定义静态资源地址前缀
|
||||||
SHOPRO_STATIC_URL=http://test.yudao.iocoder.cn
|
NX_STATIC_URL = https://file.nx.com
|
||||||
### SHOPRO_STATIC_URL = https://file.sheepjs.com
|
|
||||||
|
|
||||||
# 前端 H5 访问域名
|
|
||||||
SHOPRO_H5_URL=http://127.0.0.1:3000
|
|
||||||
|
|
||||||
# 是否开启直播 1 开启直播 | 0 关闭直播
|
|
||||||
SHOPRO_MPLIVE_ON=0
|
|
||||||
|
|
||||||
# 租户ID 默认 1
|
|
||||||
SHOPRO_TENANT_ID=1
|
|
||||||
|
|||||||
31
.gitignore
vendored
31
.gitignore
vendored
@@ -1,11 +1,20 @@
|
|||||||
unpackage/*
|
# ---> VisualStudioCode
|
||||||
node_modules/*
|
.vscode/*
|
||||||
.idea/*
|
!.vscode/settings.json
|
||||||
deploy.sh
|
!.vscode/tasks.json
|
||||||
.hbuilderx/
|
!.vscode/launch.json
|
||||||
.vscode/
|
!.vscode/extensions.json
|
||||||
**/.DS_Store
|
*.code-workspace
|
||||||
yarn.lock
|
|
||||||
package-lock.json
|
# Local History for Visual Studio Code
|
||||||
*.keystore
|
.history/
|
||||||
pnpm-lock.yaml
|
|
||||||
|
/unpackage/cache
|
||||||
|
/unpackage/debug
|
||||||
|
/unpackage/dist
|
||||||
|
/.hbuilderx
|
||||||
|
/node_modules
|
||||||
|
/unpackage/release
|
||||||
|
/unpackage/resources
|
||||||
|
/.guiplan_cache
|
||||||
|
.idea
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
/unpackage/*
|
|
||||||
/node_modules/**
|
|
||||||
/uni_modules/**
|
|
||||||
/public/*
|
|
||||||
**/*.svg
|
|
||||||
**/*.sh
|
|
||||||
10
.prettierrc
10
.prettierrc
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"printWidth": 100,
|
|
||||||
"semi": true,
|
|
||||||
"vueIndentScriptAndStyle": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"proseWrap": "never",
|
|
||||||
"htmlWhitespaceSensitivity": "strict",
|
|
||||||
"endOfLine": "auto"
|
|
||||||
}
|
|
||||||
68
App.vue
68
App.vue
@@ -1,32 +1,56 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onLaunch, onShow, onError } from '@dcloudio/uni-app';
|
import { onLaunch, onShow, onError } from '@dcloudio/uni-app'
|
||||||
import { ShoproInit } from './sheep';
|
|
||||||
|
|
||||||
onLaunch(() => {
|
import { NxInit } from './nx'
|
||||||
// 隐藏原生导航栏 使用自定义底部导航
|
import $store from '@/nx/store'
|
||||||
uni.hideTabBar({
|
|
||||||
fail: () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 加载Shopro底层依赖
|
|
||||||
ShoproInit();
|
|
||||||
});
|
|
||||||
|
|
||||||
onShow(() => {
|
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
// 获取urlSchemes参数
|
import * as chainway from '@/uni_modules/zzjc-chainway-p100'
|
||||||
const args = plus.runtime.arguments;
|
// #endif
|
||||||
if (args) {
|
onLaunch(async () => {
|
||||||
|
// 加载nx底层依赖
|
||||||
|
await NxInit()
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
let sysInfo = uni.getSystemInfoSync()
|
||||||
|
let brand = sysInfo.brand.toLowerCase()
|
||||||
|
if (brand === 'chainway') {
|
||||||
|
plus.screen.lockOrientation('landscape')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取剪贴板
|
chainway.scanRegister(data => {
|
||||||
uni.getClipboardData({
|
console.log('扫码结果:' + data)
|
||||||
success: (res) => {},
|
$store('biz').scanQRInfo = data
|
||||||
});
|
})
|
||||||
// #endif
|
// #endif
|
||||||
});
|
})
|
||||||
|
|
||||||
|
onError(err => {
|
||||||
|
console.log('AppOnError:', err)
|
||||||
|
})
|
||||||
|
|
||||||
|
onShow(() => {})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '@/sheep/scss/index.scss';
|
@import '@/uview-plus/index.scss';
|
||||||
|
@import '@/nx/scss/index.scss';
|
||||||
|
#u-a-p > div {
|
||||||
|
z-index: 99999 !important;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'zzjc-lcd';
|
||||||
|
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAQQAA0AAAAADNgAAAO6AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCWhEIColEhk4LIAABNgIkAyoEIAWILgdAG1sKAI7RS+dwknLKwRCOFCJ62I82+05UkhDNk0toJKgnoWEW8UYotAuZLN4ogW/0ul/7EucnQS2Yf8HMQ1ALgRPXku5MpmeuEm86VZ/nmOmibc+Hf8ET2dCiAmfjYWSBbhR9kVjhi+cndnx0eOZncg/xTozVeOFDCPDF/mEXfL++dWY6NAG9YBwUglAQDCjQkgf5og3qXj3Gg2SS/GQLBUY0iDe/gHyqjBb1fzrUZzh7nqiQF4VbQAuYZXNVT+q3M9JBNwGFAosSZTT/GYjW9Zq/zZvmNWIZTjuQUSmAbekt0dCKbeefjfoTgPmMaRoFGADMg92QI0BeAloBBSA4FBaLweIweFwrw/Acy7E8z3NiScPiOYkamVPoS1uVTKZzfL6vj5HyZFZq64bllpZPwXRsu1PzqxR5MI0r1ETvndkWy2DFNJGBHfSVPc2AMxCVjboO5ch5uR0SUkEHRAEol8eOumGDytORSX162TbqIaWFybLSpkU7nVAe8DOGLs4CXuz+/t2Hd01+UT5OX6hnfhvOL70IrDPP+jjq5A8XAs9uxsva9uOdTM/dOPNsXeD7PJFxPN4++tPhc3YzsO4FislD9YrTj9cNxlgX8xQS4pVxzepk0GLTJlbyrD+Vo6dCHY+/5XmT8gU+rSjtco+V1VNq5zWu3YVXu6q1bc7ZaoKZ+1XmTk6O8e0BAYFdi1Whfc7ZXwULU71WfZ0H5F89A50oQGBN6M9/IIYgmOvb84QHkDsgC3x0PP9mwoVk1fEJGBCLBxQIAo4BYDlXBMCEcjmCN4vC2YMWLjbQytkPtJETjnZ8CtGBSj06IbfRBdlANypb0cMrN4i91K/ow5QX+nlVBQbxNUscun2fFqRVBByHBISiHxTJCmjpDoMR5B1YfhHAUWIHj0klBD5pgkjINJwwMWERnx1EiboBmVASFPHlDxdK9RNd97OiyIxZpnljRoxapFq3SGVnZZMhQfZTdjbq+eNNbX1vt7GrPwddzOeD20bEMxcbJpt+sG23rS/eA6NLOBY+FLBl1TjNJM0QbG2Na5PaEHCObsiYJVPwjsDyYMRODqhhUpWaMW2RqpFuxJJJA+bhwDnzpSvK9yBWBKTkReh1unkLxupglVUiqy8Ycf5kTGui1Wpz1SyXhcLysktcaNUBKDr4+3WLZmVKksRgBRMZxkzelcAwwJRIMw1Hsj7DMP7a5D9IMAbMRG2a0BU4bp5Oh5u2c083WVio/b4owkSddVAY08AB5DO5cufBkxdvPnz58RcAAAA=')
|
||||||
|
format('woff2');
|
||||||
|
src: url('~@/static/font/zzjc-lcd.ttf') format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
.zzjc-lcd {
|
||||||
|
font-family: 'zzjc-lcd' !important;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
uni-app、ES6、Vue3、Vite、Pinia;
|
||||||
|
|
||||||
|
- 登录,注册
|
||||||
|
- 全局路由守卫(路由配置)
|
||||||
|
- Request封装(请求封装)
|
||||||
|
- api集中管理
|
||||||
|
- flex常用布局css,
|
||||||
|
- utils常用工具函数
|
||||||
|
- 配置pinia(store)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"prompt" : "template"
|
|
||||||
}
|
|
||||||
196
components/da-tree/changelog.md
Normal file
196
components/da-tree/changelog.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# 1.4.2
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增`filterValue`属性,支持通过此关键词来搜索并筛选树结构的内容
|
||||||
|
|
||||||
|
# 1.4.1
|
||||||
|
|
||||||
|
修复
|
||||||
|
|
||||||
|
1. 修复单选 onlyRadioLeaf 时末级节点无法选中的 bug
|
||||||
|
|
||||||
|
# 1.4.0
|
||||||
|
|
||||||
|
版本调整
|
||||||
|
|
||||||
|
建议更新,但需要注意,异步数据的时候,后台需返回 leaf 字段来判断是否末项数据
|
||||||
|
|
||||||
|
1. **调整数据项格式,新增 `leaf` 字段,来判断是否为末节点**
|
||||||
|
2. **调整数据项格式,新增 `sort` 字段,来排序节点位置**
|
||||||
|
3. **注意:异步加载数据,当为末项的时候,需要服务端数据返回 `leaf` 字段**
|
||||||
|
4. 新增 `alwaysFirstLoad` ,即异步数据总会在第一次展开节点时,拉取一次后台数据,来比对是否一致
|
||||||
|
5. 拆分 `field` 属性,**注意: 1.5.0 版本后将移除 `field` 属性**
|
||||||
|
6. 新增 `labelField` 同 `field.label`,指定节点对象中某个属性为**标签**字段,默认`label`
|
||||||
|
7. 新增 `valueField` 同 `field.key`,指定节点对象中某个属性为**值**字段,默认`value`
|
||||||
|
8. 新增 `childrenField` 同 `field.children`,指定节点对象中某个属性为**子树节点**字段,默认`children`
|
||||||
|
9. 新增 `disabledField` 同 `field.disabled`,指定节点对象中某个属性为**禁用**字段,默认`disabled`
|
||||||
|
10. 新增 `appendField` 同 `field.append`,指定节点对象中某个属性为**副标签**字段,默认`append`
|
||||||
|
11. 新增 `leafField` 同 `field.label`,指定节点对象中某个属性为**末级节点**字段,默认`leaf`
|
||||||
|
12. 新增 `sortField` 同 `field.label`,指定节点对象中某个属性为**排序**字段,默认`sort`
|
||||||
|
13. 新增 `isLeafFn` ,用来自定义控制数据项的末项
|
||||||
|
14. 更多的项目示例
|
||||||
|
15. 支持单选取消选中
|
||||||
|
16. 修复节点展开时可能存在的 bug
|
||||||
|
17. 修复节点选择可能存在的 bug
|
||||||
|
18. 调整为子节点默认继承父节点禁用属性
|
||||||
|
19. `setExpandedKeys` 添加参数一为 `all` 即可支持一键展开/收起全部节点
|
||||||
|
20. 其它更多优化
|
||||||
|
|
||||||
|
# 1.3.4
|
||||||
|
|
||||||
|
优化
|
||||||
|
|
||||||
|
1. 优化图标字体命名
|
||||||
|
|
||||||
|
# 1.3.3
|
||||||
|
|
||||||
|
优化
|
||||||
|
|
||||||
|
1. 新增方法调用
|
||||||
|
> - 新增`getUncheckedKeys`,返回未选的 key
|
||||||
|
> - 新增`getUncheckedNodes`,返回未选的节点
|
||||||
|
> - 新增`getUnexpandedKeys`,返回未展开的 key
|
||||||
|
> - 新增`getUnexpandedNodes`,返回未展开的节点
|
||||||
|
2. 优化示例项目
|
||||||
|
|
||||||
|
# 1.3.2
|
||||||
|
|
||||||
|
修复
|
||||||
|
|
||||||
|
1. 修复在 APP 真机环境中的报错
|
||||||
|
|
||||||
|
# 1.3.1
|
||||||
|
|
||||||
|
修复
|
||||||
|
|
||||||
|
1. 修复方法`setExpandedKeys`没联动展开上级父子节点
|
||||||
|
|
||||||
|
# 1.3.0
|
||||||
|
|
||||||
|
优化
|
||||||
|
|
||||||
|
1. `field`新增字段 `append` 用于在标签后面显示小提示
|
||||||
|
2. 新增支持点击标签也能选中节点
|
||||||
|
3. 方法`setExpandedKeys`支持加载动态数据
|
||||||
|
4. 修复父节点禁用,则不能展开及图标展开显示
|
||||||
|
5. 修复动态加载数据时,末级节点的 `children` 为 `null` 时仍显示展开图标
|
||||||
|
|
||||||
|
# 1.2.6
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增支持主题换色
|
||||||
|
2. 支持单选的`onlyRadioLeaf`为`true`时可点父节点展开/收起
|
||||||
|
3. 优化`expandChecked`调整为不展开无子节点的节点
|
||||||
|
|
||||||
|
# 1.2.5
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增 `expandChecked`,控制选择时是否展开当前已选的所有下级节点
|
||||||
|
|
||||||
|
# 1.2.4
|
||||||
|
|
||||||
|
修复
|
||||||
|
|
||||||
|
1. 修复动态数据展开状态异常问题
|
||||||
|
|
||||||
|
# 1.2.3
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增 `checkedDisabled`,是否渲染禁用值
|
||||||
|
2. 新增 `packDisabledkey`,是否返回已禁用并选中的 key
|
||||||
|
3. 修复选择父级时,子级已禁用但仍被选中的问题
|
||||||
|
|
||||||
|
# 1.2.2
|
||||||
|
|
||||||
|
优化
|
||||||
|
|
||||||
|
1. 调整动态数据载入处理方式
|
||||||
|
2. 修复节点数据因动态数据引起的状态异常
|
||||||
|
3. 修复初始节点数据默认选中
|
||||||
|
|
||||||
|
# 1.2.1
|
||||||
|
|
||||||
|
修复
|
||||||
|
|
||||||
|
1. 修复切换`选中状态`被重复选中问题
|
||||||
|
2. 修复动态数据引起的重复选择问题
|
||||||
|
|
||||||
|
# 1.2.0
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增方法调用
|
||||||
|
> - 新增`setCheckedKeys`,方法设置指定 key 的节点选中状态
|
||||||
|
> - 新增`setExpandedKeys`,方法设置指定 key 的节点展开状态
|
||||||
|
2. 修复小程序重复插槽一直刷报错问题
|
||||||
|
3. 优化展开时,会展开子级所以下级节点
|
||||||
|
|
||||||
|
# 1.1.1
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增`data`的`disabled`,支持节点禁用状态
|
||||||
|
2. 新增`field`的`disabled`,可自定`disabled`字段值
|
||||||
|
|
||||||
|
# 1.1.0
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增`loadMode`、`loadApi`,支持展开时加载异步数据
|
||||||
|
2. 新增方法调用
|
||||||
|
> - 新增`getCheckedKeys`,方法返回已选的 key
|
||||||
|
> - 新增`getHalfCheckedKeys`,方法返回半选的 key
|
||||||
|
> - 新增`getExpandedKeys`,方法返回已展开的 key
|
||||||
|
> - 新增`getCheckedNodes`,方法返回已选的节点
|
||||||
|
> - 新增`getHalfCheckedNodes`,方法返回半选的节点
|
||||||
|
> - 新增`getExpandedNodes`,方法返回已展开的节点
|
||||||
|
3. 对代码进行重构,更易于后期拓展
|
||||||
|
4. 此次更新后,页面多个的 DaTee 组件间的数据不再关联
|
||||||
|
|
||||||
|
# 1.0.6
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增`checkStrictly`,多选模式下选中时是否父子不关联
|
||||||
|
|
||||||
|
# 1.0.5
|
||||||
|
|
||||||
|
修复
|
||||||
|
|
||||||
|
1. 修复多选时已选数据重复问题
|
||||||
|
|
||||||
|
# 1.0.4
|
||||||
|
|
||||||
|
修复
|
||||||
|
|
||||||
|
1. 修复 `change` 事件回调数据的问题
|
||||||
|
|
||||||
|
# 1.0.3
|
||||||
|
|
||||||
|
优化
|
||||||
|
|
||||||
|
1. 优化文档及示例说明
|
||||||
|
|
||||||
|
# 1.0.2
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 新增 `onlyRadioLeaf` ,单选时只允许选中末级
|
||||||
|
2. 优化默认展开及默认选择的展开问题
|
||||||
|
|
||||||
|
# 1.0.1
|
||||||
|
|
||||||
|
新增
|
||||||
|
|
||||||
|
1. 支持展开/收起回调事件`@expand`
|
||||||
|
|
||||||
|
# 1.0.0
|
||||||
|
|
||||||
|
初始版本 1.0.0,基于 Vue3 进行开发,支持单选、多选,兼容各大平台
|
||||||
|
|
||||||
|
1. 支持单选
|
||||||
|
2. 支持多选
|
||||||
1181
components/da-tree/index.vue
Normal file
1181
components/da-tree/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
197
components/da-tree/props.ts
Normal file
197
components/da-tree/props.ts
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 树的数据
|
||||||
|
*/
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 主题色
|
||||||
|
*/
|
||||||
|
themeColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#007aff'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否开启多选,默认单选
|
||||||
|
*/
|
||||||
|
showCheckbox: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 默认选中的节点,注意单选时为单个key,多选时为key的数组
|
||||||
|
*/
|
||||||
|
defaultCheckedKeys: {
|
||||||
|
type: [Array, String, Number],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否默认展开全部
|
||||||
|
*/
|
||||||
|
defaultExpandAll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 默认展开的节点
|
||||||
|
*/
|
||||||
|
defaultExpandedKeys: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 筛选关键词
|
||||||
|
*/
|
||||||
|
filterValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否自动展开到选中的节点,默认不展开
|
||||||
|
*/
|
||||||
|
expandChecked: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (旧)字段对应内容,默认为 {label: 'label',key: 'key', children: 'children', disabled: 'disabled', append: 'append'}
|
||||||
|
* 注意:1.5.0版本后不再兼容
|
||||||
|
*/
|
||||||
|
field: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 标签字段(新,拆分了)
|
||||||
|
*/
|
||||||
|
labelField: {
|
||||||
|
type: String,
|
||||||
|
default: 'label'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 值字段(新,拆分了)
|
||||||
|
*/
|
||||||
|
valueField: {
|
||||||
|
type: String,
|
||||||
|
default: 'value'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 下级字段(新,拆分了)
|
||||||
|
*/
|
||||||
|
childrenField: {
|
||||||
|
type: String,
|
||||||
|
default: 'children'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 禁用字段(新,拆分了)
|
||||||
|
*/
|
||||||
|
disabledField: {
|
||||||
|
type: String,
|
||||||
|
default: 'disabled'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 末级节点字段(新,拆分了)
|
||||||
|
*/
|
||||||
|
leafField: {
|
||||||
|
type: String,
|
||||||
|
default: 'leaf'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 副标签字段(新,拆分了)
|
||||||
|
*/
|
||||||
|
appendField: {
|
||||||
|
type: String,
|
||||||
|
default: 'append'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 排序字段(新,拆分了)
|
||||||
|
*/
|
||||||
|
sortField: {
|
||||||
|
type: String,
|
||||||
|
default: 'sort'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Api数据返回后的结果路径,支持嵌套如`data.list`
|
||||||
|
*/
|
||||||
|
resultField: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
isLeafFn: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否显示单选图标,默认显示
|
||||||
|
*/
|
||||||
|
showRadioIcon: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 单选时只允许选中末级,默认可随意选中
|
||||||
|
*/
|
||||||
|
onlyRadioLeaf: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 多选时,是否执行父子不关联的任意勾选,默认父子关联
|
||||||
|
*/
|
||||||
|
checkStrictly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 为 true 时,空的 children 数组会显示展开图标
|
||||||
|
*/
|
||||||
|
loadMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 异步加载接口
|
||||||
|
*/
|
||||||
|
loadApi: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否总在首次的时候加载一下内容,来比对是否一致
|
||||||
|
*/
|
||||||
|
alwaysFirstLoad: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否渲染(操作)禁用值
|
||||||
|
*/
|
||||||
|
checkedDisabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否返回已禁用的但已选中的key
|
||||||
|
*/
|
||||||
|
packDisabledkey: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 选择框的位置,可选 left/right
|
||||||
|
*/
|
||||||
|
checkboxPlacement: {
|
||||||
|
type: String,
|
||||||
|
default: 'left'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 子项缩进距离,默认40,单位rpx
|
||||||
|
*/
|
||||||
|
indent: {
|
||||||
|
type: Number,
|
||||||
|
default: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
310
components/da-tree/readme.md
Normal file
310
components/da-tree/readme.md
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
# da-tree
|
||||||
|
|
||||||
|
一个基于 Vue3 的 tree(树)组件,同时支持主题换色,可能是最适合你的 tree(树)组件
|
||||||
|
|
||||||
|
组件一直在更新,遇到问题可在下方讨论。
|
||||||
|
|
||||||
|
`同时更新 Vue2 版本,在此查看 ===>` **[Vue2 版](https://ext.dcloud.net.cn/plugin?id=12692)**
|
||||||
|
|
||||||
|
### 关于使用
|
||||||
|
|
||||||
|
可在右侧的`使用 HBuilderX 导入插件`或`下载示例项目ZIP`,方便快速上手。
|
||||||
|
|
||||||
|
可通过下方的示例及文档说明,进一步了解使用组件相关细节参数。
|
||||||
|
|
||||||
|
插件地址:https://ext.dcloud.net.cn/plugin?id=12384
|
||||||
|
|
||||||
|
### 组件示例
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
<template>
|
||||||
|
<view>多选</view>
|
||||||
|
<view><button @click="doCheckedTree(['2'],true)">全选</button></view>
|
||||||
|
<view><button @click="doCheckedTree(['2'],false)">取消全选</button></view>
|
||||||
|
<view><button @click="doCheckedTree(['211','222'],true)">选中指定节点</button></view>
|
||||||
|
<view><button @click="doCheckedTree(['211','222'],false)">取消选中指定节点</button></view>
|
||||||
|
<view><button @click="doExpandTree('all',true)">展开全部节点</button></view>
|
||||||
|
<view><button @click="doExpandTree('all',false)">收起全部节点</button></view>
|
||||||
|
<view><button @click="doExpandTree(['22','23'],true)">展开节点</button></view>
|
||||||
|
<view><button @click="doExpandTree(['22','23'],false)">收起节点</button></view>
|
||||||
|
<DaTree
|
||||||
|
ref="DaTreeRef"
|
||||||
|
:data="roomTreeData"
|
||||||
|
labelField="name"
|
||||||
|
valueField="id"
|
||||||
|
defaultExpandAll
|
||||||
|
showCheckbox
|
||||||
|
:defaultCheckedKeys="defaultCheckedKeysValue"
|
||||||
|
@change="handleTreeChange"
|
||||||
|
@expand="handleExpandChange"></DaTree>
|
||||||
|
<view>单选</view>
|
||||||
|
<DaTree
|
||||||
|
:data="roomTreeData"
|
||||||
|
labelField="name"
|
||||||
|
valueField="id"
|
||||||
|
defaultExpandAll
|
||||||
|
:defaultCheckedKeys="defaultCheckedKeysValue2"
|
||||||
|
@change="handleTreeChange"
|
||||||
|
@expand="handleExpandChange"></DaTree>
|
||||||
|
<view>默认展开指定节点</view>
|
||||||
|
<DaTree
|
||||||
|
:data="roomTreeData"
|
||||||
|
labelField="name"
|
||||||
|
valueField="id"
|
||||||
|
showCheckbox
|
||||||
|
:defaultExpandedKeys="defaultExpandKeysValue3"
|
||||||
|
@change="handleTreeChange"
|
||||||
|
@expand="handleExpandChange"></DaTree>
|
||||||
|
<view>异步加载数据</view>
|
||||||
|
<DaTree
|
||||||
|
:data="roomTreeData"
|
||||||
|
labelField="name"
|
||||||
|
valueField="id"
|
||||||
|
showCheckbox
|
||||||
|
loadMode
|
||||||
|
:loadApi="GetApiData"
|
||||||
|
defaultExpandAll
|
||||||
|
@change="handleTreeChange"
|
||||||
|
@expand="handleExpandChange"></DaTree>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { defineComponent, ref } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模拟创建一个接口数据
|
||||||
|
*/
|
||||||
|
function GetApiData(currentNode) {
|
||||||
|
const { key } = currentNode
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// 模拟返回空数据
|
||||||
|
if (key.indexOf('-') > -1) {
|
||||||
|
return resolve(null)
|
||||||
|
// return resolve([])
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve([
|
||||||
|
{
|
||||||
|
id: `${key}-1`,
|
||||||
|
name: `行政部X${key}-1`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: `${key}-2`,
|
||||||
|
name: `财务部X${key}-2`,
|
||||||
|
append: '定义了末项数据',
|
||||||
|
leaf: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: `${key}-3`,
|
||||||
|
name: `资源部X${key}-3`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: `${key}-4`,
|
||||||
|
name: `资源部X${key}-3`,
|
||||||
|
append: '被禁用,无展开图标',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}, 2000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
import DaTree from '@/components/da-tree/index.vue'
|
||||||
|
export default defineComponent({
|
||||||
|
components: { DaTree },
|
||||||
|
setup() {
|
||||||
|
const DaTreeRef = ref()
|
||||||
|
// key的类型必须对应树数据key的类型
|
||||||
|
const defaultCheckedKeysValue = ref(['211', '222'])
|
||||||
|
const defaultCheckedKeysValue2 = ref('222')
|
||||||
|
const defaultExpandKeysValue3 = ref(['212', '231'])
|
||||||
|
const roomTreeData = ref([
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
name: '行政中心',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '21',
|
||||||
|
name: '行政部',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '211',
|
||||||
|
name: '行政一部',
|
||||||
|
children: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '212',
|
||||||
|
name: '行政二部',
|
||||||
|
children: [],
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '22',
|
||||||
|
name: '财务部',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '221',
|
||||||
|
name: '财务一部',
|
||||||
|
children: [],
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '222',
|
||||||
|
name: '财务二部',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '23',
|
||||||
|
name: '人力资源部',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '231',
|
||||||
|
name: '人力一部',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '232',
|
||||||
|
name: '人力二部',
|
||||||
|
append: '更多示例,请下载示例项目查看',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
function doExpandTree(keys, expand) {
|
||||||
|
DaTreeRef.value?.setExpandedKeys(keys, expand)
|
||||||
|
|
||||||
|
const gek = DaTreeRef.value?.getExpandedKeys()
|
||||||
|
console.log('当前已展开的KEY ==>', gek)
|
||||||
|
}
|
||||||
|
function doCheckedTree(keys, checked) {
|
||||||
|
DaTreeRef.value?.setCheckedKeys(keys, checked)
|
||||||
|
|
||||||
|
const gek = DaTreeRef.value?.getCheckedKeys()
|
||||||
|
console.log('当前已选中的KEY ==>', gek)
|
||||||
|
}
|
||||||
|
function handleTreeChange(allSelectedKeys, currentItem) {
|
||||||
|
console.log('handleTreeChange ==>', allSelectedKeys, currentItem)
|
||||||
|
}
|
||||||
|
function handleExpandChange(expand, currentItem) {
|
||||||
|
console.log('handleExpandChange ==>', expand, currentItem)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
DaTreeRef,
|
||||||
|
roomTreeData,
|
||||||
|
defaultCheckedKeysValue,
|
||||||
|
defaultCheckedKeysValue2,
|
||||||
|
defaultExpandKeysValue3,
|
||||||
|
handleTreeChange,
|
||||||
|
handleExpandChange,
|
||||||
|
GetApiData,
|
||||||
|
doExpandTree,
|
||||||
|
doCheckedTree,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
** 更多示例请下载/导入示例项目 ZIP 查看 **
|
||||||
|
|
||||||
|
### 组件参数
|
||||||
|
|
||||||
|
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|
||||||
|
| :------------------ | :------------------------------ | :--------- | :--- | :--------------------------------------------------------------------------- |
|
||||||
|
| data | `Array` | - | 是 | 树的数据 |
|
||||||
|
| themeColor | `String` | `#007aff` | 否 | 主题色,十六进制 |
|
||||||
|
| defaultCheckedKeys | `Array` \| `Number` \| `String` | - | 否 | 默认选中的节点,单选为单个 key,多选为 key 的数组 |
|
||||||
|
| showCheckbox | `Boolean` | `false` | 否 | 是否开启多选,默认单选 |
|
||||||
|
| checkStrictly | `Boolean` | `false` | 否 | 多选时,是否执行父子不关联的任意勾选,默认父子关联 |
|
||||||
|
| showRadioIcon | `Boolean` | `true` | 否 | 是否显示单选图标,默认显示 |
|
||||||
|
| onlyRadioLeaf | `Boolean` | `true` | 否 | 单选时只允许选中末级,默认可随意选中 |
|
||||||
|
| defaultExpandAll | `Boolean` | `false` | 否 | 是否默认展开全部 |
|
||||||
|
| defaultExpandedKeys | `Array` | - | 否 | 默认展开的节点 |
|
||||||
|
| indent | `Number` | `40` | 否 | 子项缩进距离,单位 rpx |
|
||||||
|
| checkboxPlacement | `String` | `left` | 否 | 选择框的位置,可选 left/right |
|
||||||
|
| loadMode | `Boolean` | `false` | 否 | 为 true 时,空的 children 数组会显示展开图标 |
|
||||||
|
| loadApi | `Function` | - | 否 | 选择框的位置,可选 left/right |
|
||||||
|
| checkedDisabled | `Boolean` | `false` | 否 | 是否渲染禁用值,默认不渲染 |
|
||||||
|
| packDisabledkey | `Boolean` | `true` | 否 | 是否返回已禁用的但已选中的 key,默认返回禁用已选值 |
|
||||||
|
| expandChecked | `Boolean` | `false` | 否 | 是否自动展开到选中的节点,默认不展开 |
|
||||||
|
| alwaysFirstLoad | `Boolean` | `false` | 否 | 是否总在首次的时候加载一下内容,默认不加载,否则只有展开末级节点才会加载数据 |
|
||||||
|
| isLeafFn | `Function` | - | 否 | 自定义函数返回来控制数据项的末项 |
|
||||||
|
| field | `Object` | - | 否 | 字段对应内容,格式参考下方(1.5.0 后移除,请用单独的字段匹配) |
|
||||||
|
| labelField | `String` | `label` | 否 | 指定节点对象中某个属性为标签字段,默认`label` |
|
||||||
|
| valueField | `String` | `value` | 否 | 指定节点对象中某个属性为值字段,默认`value` |
|
||||||
|
| childrenField | `String` | `children` | 否 | 指定节点对象中某个属性为子树节点字段,默认`children` |
|
||||||
|
| disabledField | `String` | `disabled` | 否 | 指定节点对象中某个属性为禁用字段,默认`disabled` |
|
||||||
|
| appendField | `String` | `append` | 否 | 指定节点对象中某个属性为副标签字段,默认`append` |
|
||||||
|
| leafField | `String` | `leaf` | 否 | 指定节点对象中某个属性为末级节点字段,默认`leaf` |
|
||||||
|
| sortField | `String` | `sort` | 否 | 指定节点对象中某个属性为排序字段,默认`sort` |
|
||||||
|
| filterValue | `String` | - | 否 | 搜索筛选的关键词,通过输入关键词筛选内容 |
|
||||||
|
|
||||||
|
**field 格式(1.5.0 后移除,请用单独的字段匹配)**
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
label: 'label',
|
||||||
|
key: 'key',
|
||||||
|
children: 'children',
|
||||||
|
disabled: 'disabled',
|
||||||
|
append: 'append'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 组件事件
|
||||||
|
|
||||||
|
| 事件名称 | 回调参数 | 说明 |
|
||||||
|
| :------- | :-------------------------------------- | :-------------- |
|
||||||
|
| change | `(allCheckedKeys, currentItem) => void` | 选中时回调 |
|
||||||
|
| expand | `(expandState, currentItem) => void` | 展开/收起时回调 |
|
||||||
|
|
||||||
|
### 组件方法
|
||||||
|
|
||||||
|
| 方法名称 | 参数 | 说明 |
|
||||||
|
| :------------------ | :--------------- | :------------------------------------------------------------------------------------------------ |
|
||||||
|
| setCheckedKeys | `(keys,checked)` | 设置指定 key 的节点选中/取消选中的状态。注: keys 单选时为 key,多选时为 key 的数组 |
|
||||||
|
| setExpandedKeys | `(keys,expand)` | 设置指定 key 的节点展开/收起的状态,当 keys 为 all 时即代表展开/收起全部。注:keys 为数组或 `all` |
|
||||||
|
| getCheckedKeys | - | 返回已选的 key |
|
||||||
|
| getHalfCheckedKeys | - | 返回半选的 key |
|
||||||
|
| getUncheckedKeys | - | 返回未选的 key |
|
||||||
|
| getCheckedNodes | - | 返回已选的节点 |
|
||||||
|
| getUncheckedNodes | - | 返回未选的节点 |
|
||||||
|
| getHalfCheckedNodes | - | 返回半选的节点 |
|
||||||
|
| getExpandedKeys | - | 返回已展开的 key |
|
||||||
|
| getUnexpandedKeys | - | 返回未展开的 key |
|
||||||
|
| getExpandedNodes | - | 返回已展开的节点 |
|
||||||
|
| getUnexpandedNodes | - | 返回未展开的节点 |
|
||||||
|
|
||||||
|
### 组件版本
|
||||||
|
|
||||||
|
v1.4.2
|
||||||
|
|
||||||
|
### 差异化
|
||||||
|
|
||||||
|
已通过测试
|
||||||
|
|
||||||
|
> - H5 页面
|
||||||
|
> - 微信小程序
|
||||||
|
> - 支付宝、钉钉小程序
|
||||||
|
> - 字节跳动、抖音、今日头条小程序
|
||||||
|
> - 百度小程序
|
||||||
|
> - 飞书小程序
|
||||||
|
> - QQ 小程序
|
||||||
|
> - 京东小程序
|
||||||
|
|
||||||
|
未测试
|
||||||
|
|
||||||
|
> - 快手小程序由于非企业用户暂无演示
|
||||||
|
> - 快应用、360 小程序因 Vue3 支持的原因暂无演示
|
||||||
|
|
||||||
|
### 开发组
|
||||||
|
|
||||||
|
[@CRLANG](https://crlang.com)
|
||||||
150
components/da-tree/utils.ts
Normal file
150
components/da-tree/utils.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/** 未选 */
|
||||||
|
export const unCheckedStatus = 0
|
||||||
|
/** 半选 */
|
||||||
|
export const halfCheckedStatus = 1
|
||||||
|
/** 选中 */
|
||||||
|
export const isCheckedStatus = 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 深拷贝内容
|
||||||
|
* @param originData 拷贝对象
|
||||||
|
* @author crlang(https://crlang.com)
|
||||||
|
*/
|
||||||
|
export function deepClone(originData) {
|
||||||
|
const type = Object.prototype.toString.call(originData)
|
||||||
|
let data
|
||||||
|
if (type === '[object Array]') {
|
||||||
|
data = []
|
||||||
|
for (let i = 0; i < originData.length; i++) {
|
||||||
|
data.push(deepClone(originData[i]))
|
||||||
|
}
|
||||||
|
} else if (type === '[object Object]') {
|
||||||
|
data = {}
|
||||||
|
for (const prop in originData) {
|
||||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
|
if (originData.hasOwnProperty(prop)) { // 非继承属性
|
||||||
|
data[prop] = deepClone(originData[prop])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = originData
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有指定的节点
|
||||||
|
* @param type
|
||||||
|
* @param value
|
||||||
|
* @author crlang(https://crlang.com)
|
||||||
|
*/
|
||||||
|
export function getAllNodes(list, type, value, packDisabledkey = true) {
|
||||||
|
if (!list || list.length === 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = []
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const item = list[i]
|
||||||
|
if (item[type] === value) {
|
||||||
|
if ((packDisabledkey && item.disabled) || !item.disabled) {
|
||||||
|
res.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有指定的key值
|
||||||
|
* @param type
|
||||||
|
* @param value
|
||||||
|
* @author crlang(https://crlang.com)
|
||||||
|
*/
|
||||||
|
export function getAllNodeKeys(list, type, value, packDisabledkey = true) {
|
||||||
|
if (!list || list.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = []
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const item = list[i]
|
||||||
|
if (item[type] === value) {
|
||||||
|
if ((packDisabledkey && item.disabled) || !item.disabled) {
|
||||||
|
res.push(item.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.length ? res : null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误输出
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
*/
|
||||||
|
export function logError(msg, ...args) {
|
||||||
|
console.error(`DaTree: ${msg}`, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toString = Object.prototype.toString
|
||||||
|
|
||||||
|
export function is(val, type) {
|
||||||
|
return toString.call(val) === `[object ${type}]`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否对象(Object)
|
||||||
|
* @param val
|
||||||
|
|
||||||
|
*/
|
||||||
|
export function isObject(val) {
|
||||||
|
return val !== null && is(val, 'Object')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否数字(Number)
|
||||||
|
* @param val
|
||||||
|
|
||||||
|
*/
|
||||||
|
export function isNumber(val) {
|
||||||
|
return is(val, 'Number')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否字符串(String)
|
||||||
|
* @param val
|
||||||
|
|
||||||
|
*/
|
||||||
|
export function isString(val) {
|
||||||
|
return is(val, 'String')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否函数方法(Function)
|
||||||
|
* @param val
|
||||||
|
|
||||||
|
*/
|
||||||
|
export function isFunction(val) {
|
||||||
|
return typeof val === 'function'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否布尔(Boolean)
|
||||||
|
* @param val
|
||||||
|
|
||||||
|
*/
|
||||||
|
export function isBoolean(val) {
|
||||||
|
return is(val, 'Boolean')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否数组(Array)
|
||||||
|
* @param val
|
||||||
|
|
||||||
|
*/
|
||||||
|
export function isArray(val) {
|
||||||
|
return val && Array.isArray(val)
|
||||||
|
}
|
||||||
80
components/my-tabBar/my-tabBar.vue
Normal file
80
components/my-tabBar/my-tabBar.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
//my-tabbar文件
|
||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<u-tabbar
|
||||||
|
:value="currentTab"
|
||||||
|
:fixed="true"
|
||||||
|
:border="false"
|
||||||
|
activeColor="#0055a2"
|
||||||
|
:placeholder="false"
|
||||||
|
@change="changeTabIndex"
|
||||||
|
>
|
||||||
|
<u-tabbar-item
|
||||||
|
v-for="item in switchTabs"
|
||||||
|
:key="item.name"
|
||||||
|
:text="item.text"
|
||||||
|
:icon="item.iconName"
|
||||||
|
></u-tabbar-item>
|
||||||
|
</u-tabbar>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, reactive } from 'vue'
|
||||||
|
|
||||||
|
let props = defineProps({
|
||||||
|
currentTab: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const switchTabs = reactive([
|
||||||
|
{
|
||||||
|
pagePath: '/pages/lims/index/index',
|
||||||
|
iconName: 'home',
|
||||||
|
text: '首页',
|
||||||
|
name: 'home'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: '/pages/me/index',
|
||||||
|
iconName: 'account',
|
||||||
|
text: '我的',
|
||||||
|
name: 'account'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
// const switchTabs = computed(() => {
|
||||||
|
// return [
|
||||||
|
// {
|
||||||
|
// pagePath: '/pages/lims/index/index',
|
||||||
|
// iconName: 'home',
|
||||||
|
// text: '首页',
|
||||||
|
// name: 'home'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// pagePath: '/pages/me/index',
|
||||||
|
// iconName: 'account',
|
||||||
|
// text: '我的',
|
||||||
|
// name: 'account'
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// })
|
||||||
|
|
||||||
|
function changeTabIndex(e) {
|
||||||
|
let pagePath = switchTabs[e].pagePath
|
||||||
|
uni.switchTab({
|
||||||
|
url: pagePath
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
::v-deep .u-tabbar__content {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
padding: 10rpx 0;
|
||||||
|
.u-icon__icon {
|
||||||
|
font-size: 54rpx !important;
|
||||||
|
}
|
||||||
|
.u-tabbar-item__text {
|
||||||
|
font-size: 38rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
67
components/n-scanTemp/n-scanTemp.vue
Normal file
67
components/n-scanTemp/n-scanTemp.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<view class="content">
|
||||||
|
<view class="x-c">
|
||||||
|
<image style="width: 80px; height: 80px" :src="`/static/images/menus/${icon}.png`"></image>
|
||||||
|
</view>
|
||||||
|
<view class="pt50 pb50 fs30">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
</view>
|
||||||
|
<view class="x-c">
|
||||||
|
<u-icon size="150" color="#0055A2" name="scan"></u-icon>
|
||||||
|
</view>
|
||||||
|
<!-- #ifdef H5 -->
|
||||||
|
<up-search
|
||||||
|
shape="square"
|
||||||
|
placeholder="请输入设备id"
|
||||||
|
actionText="查询"
|
||||||
|
:clearabled="false"
|
||||||
|
:showAction="true"
|
||||||
|
@custom="handleInputSearch"
|
||||||
|
></up-search>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
let props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '请扫描设备'
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: 'useRecord'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emits = defineEmits(['deviceId'])
|
||||||
|
const customIconsMap = new Map([
|
||||||
|
['useRecord', ''],
|
||||||
|
['dailyCheck', ''],
|
||||||
|
['maintain', ''],
|
||||||
|
['periodCheck', ''],
|
||||||
|
['calibration', ''],
|
||||||
|
['accept', ''],
|
||||||
|
['transfer', '']
|
||||||
|
])
|
||||||
|
function iconMap(key) {
|
||||||
|
return customIconsMap.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInputSearch(e) {
|
||||||
|
emits('deviceId', e)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@font-face {
|
||||||
|
font-family: 'CustomFont';
|
||||||
|
src: url('@/static/iconfont/iconfont.ttf');
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding-top: 100px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #fff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
136
components/n-upload/n-upload.vue
Normal file
136
components/n-upload/n-upload.vue
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<up-upload
|
||||||
|
v-if="isUpdate"
|
||||||
|
:file-list="fileList"
|
||||||
|
:accept="accept"
|
||||||
|
multiple
|
||||||
|
:size-type="['original', 'compressed']"
|
||||||
|
:max-count="maxCount"
|
||||||
|
:width="width"
|
||||||
|
:height="height"
|
||||||
|
:preview-full-image="true"
|
||||||
|
:disabled="disabled"
|
||||||
|
@delete="deletePic"
|
||||||
|
@after-read="afterRead"
|
||||||
|
>
|
||||||
|
</up-upload>
|
||||||
|
<u-album v-else multiple-size="85" single-size="85" :urls="imgs" row-count="4" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, watch, onMounted } from 'vue'
|
||||||
|
import { getBaseUrl, getImgBaseUrl } from '@/defaultBaseUrl'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: { type: [Array, String], default: '' },
|
||||||
|
isUpdate: { type: Boolean, default: true },
|
||||||
|
disabled: { type: Boolean, default: false },
|
||||||
|
accept: { type: String, default: 'image' },
|
||||||
|
width: { type: String, default: '80px' },
|
||||||
|
height: { type: String, default: '80px' },
|
||||||
|
maxCount: { type: Number, default: 20 },
|
||||||
|
returnAsString: { type: Boolean, default: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const fileList = ref([])
|
||||||
|
const imgs = ref([])
|
||||||
|
const uploadConfig = reactive({
|
||||||
|
baseURL: getBaseUrl(),
|
||||||
|
imgBaseURL: getImgBaseUrl(),
|
||||||
|
header: { 'X-Access-Token': uni.getStorageSync('token') }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 同步外部值到组件
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
newVal => {
|
||||||
|
if (!newVal) return (fileList.value = [])
|
||||||
|
const urls = Array.isArray(newVal) ? newVal : newVal.split(',')
|
||||||
|
imgs.value = urls
|
||||||
|
fileList.value = urls.map(url => ({
|
||||||
|
url: uploadConfig.imgBaseURL + url,
|
||||||
|
status: 'success',
|
||||||
|
message: ''
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const deletePic = event => {
|
||||||
|
fileList.value.splice(event.index, 1)
|
||||||
|
updateParent(fileList.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const afterRead = async event => {
|
||||||
|
const files = [].concat(event.file)
|
||||||
|
const initialLength = fileList.value.length
|
||||||
|
|
||||||
|
// 添加上传状态
|
||||||
|
fileList.value.push(
|
||||||
|
...files.map(file => ({
|
||||||
|
...file,
|
||||||
|
status: 'uploading',
|
||||||
|
message: '上传中'
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const result = await uploadFile(files[i])
|
||||||
|
const index = initialLength + i
|
||||||
|
fileList.value[index] = {
|
||||||
|
...files[i],
|
||||||
|
status: 'success',
|
||||||
|
url: uploadConfig.imgBaseURL + result.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Upload failed:', error)
|
||||||
|
// 回滚失败的上传
|
||||||
|
fileList.value.splice(initialLength, files.length)
|
||||||
|
} finally {
|
||||||
|
updateParent(fileList.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadFile = file => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.uploadFile({
|
||||||
|
url: `${uploadConfig.baseURL}/sys/common/upload`,
|
||||||
|
filePath: file.url,
|
||||||
|
name: 'file',
|
||||||
|
header: uploadConfig.header,
|
||||||
|
formData: { biz: 'lims-device' },
|
||||||
|
success: res => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
resolve(JSON.parse(res.data))
|
||||||
|
} else {
|
||||||
|
reject(new Error(res.data))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: reject
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatValue = fileList => {
|
||||||
|
// 去掉 imgBaseURL 后的部分
|
||||||
|
const baseUrlLength = uploadConfig.imgBaseURL.length
|
||||||
|
return props.returnAsString
|
||||||
|
? fileList.map(item => item.url.slice(baseUrlLength)).join(',')
|
||||||
|
: fileList.map(item => item.url.slice(baseUrlLength))
|
||||||
|
}
|
||||||
|
const updateParent = fileList => {
|
||||||
|
emit('update:modelValue', formatValue(fileList))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.u-upload__wrap__preview) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
557
components/n-verify/n-verify.vue
Normal file
557
components/n-verify/n-verify.vue
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<view v-if="visible" class="s-verify-mask" @touchmove.stop.prevent>
|
||||||
|
<view class="s-verify-wrapper" @tap.stop>
|
||||||
|
<view class="s-verify-header">
|
||||||
|
<text class="s-verify-title">安全验证</text>
|
||||||
|
<view class="s-verify-close" @tap="handleClose">×</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="s-verify-body">
|
||||||
|
<view class="s-verify-image" :style="imageStyle">
|
||||||
|
<image v-if="backgroundImage" class="s-verify-image-bg" :src="backgroundImage" mode="aspectFill" />
|
||||||
|
<image
|
||||||
|
v-if="showBlock"
|
||||||
|
class="s-verify-image-block"
|
||||||
|
:style="blockStyle"
|
||||||
|
:src="blockImage"
|
||||||
|
mode="widthFix"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<view class="s-verify-refresh" @tap="refreshCaptcha">
|
||||||
|
<text>↻</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="loading" class="s-verify-loading">加载中...</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="tipMessage" :class="['s-verify-tip', tipClass]">
|
||||||
|
{{ tipMessage }}
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
class="s-verify-slider"
|
||||||
|
ref="sliderRef"
|
||||||
|
@touchstart.stop.prevent="onDragStart"
|
||||||
|
@touchmove.stop.prevent="onDragMove"
|
||||||
|
@touchend.stop.prevent="onDragEnd"
|
||||||
|
@mousedown.stop="onMouseDown"
|
||||||
|
>
|
||||||
|
<view class="s-verify-slider-track" />
|
||||||
|
<view class="s-verify-slider-fill" :style="sliderFillStyle" />
|
||||||
|
<view
|
||||||
|
class="s-verify-slider-handle"
|
||||||
|
:class="{
|
||||||
|
's-verify-slider-success': success,
|
||||||
|
's-verify-slider-fail': failAnimate
|
||||||
|
}"
|
||||||
|
:style="sliderHandleStyle"
|
||||||
|
>
|
||||||
|
<text class="s-verify-handle-icon">{{ success ? '✔' : '›' }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="s-verify-slider-text">{{ sliderHint }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, nextTick, onBeforeUnmount, reactive, ref } from 'vue'
|
||||||
|
import sysApi from '@/nx/api/sys'
|
||||||
|
import { aesEncrypt } from './utils/aes'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
captchaType: {
|
||||||
|
type: String,
|
||||||
|
default: 'blockPuzzle'
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'pop'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['success', 'error'])
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const success = ref(false)
|
||||||
|
const failAnimate = ref(false)
|
||||||
|
const verifying = ref(false)
|
||||||
|
const tipMessage = ref('')
|
||||||
|
const sliderRef = ref(null)
|
||||||
|
|
||||||
|
const sliderWidth = 310
|
||||||
|
const sliderHeight = 40
|
||||||
|
const blockWidth = Math.floor((sliderWidth * 47) / 310)
|
||||||
|
const imageHeight = 155
|
||||||
|
|
||||||
|
const dragState = reactive({
|
||||||
|
startClientX: 0,
|
||||||
|
baseOffset: 0,
|
||||||
|
isDragging: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const captchaState = reactive({
|
||||||
|
originalImageBase64: '',
|
||||||
|
jigsawImageBase64: '',
|
||||||
|
token: '',
|
||||||
|
secretKey: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleOffset = ref(0)
|
||||||
|
const maxOffset = computed(() => Math.max(sliderWidth - blockWidth, 0))
|
||||||
|
|
||||||
|
const showBlock = computed(() => props.captchaType === 'blockPuzzle' && blockImage.value)
|
||||||
|
const backgroundImage = computed(() =>
|
||||||
|
captchaState.originalImageBase64 ? `data:image/png;base64,${captchaState.originalImageBase64}` : ''
|
||||||
|
)
|
||||||
|
const blockImage = computed(() =>
|
||||||
|
captchaState.jigsawImageBase64 ? `data:image/png;base64,${captchaState.jigsawImageBase64}` : ''
|
||||||
|
)
|
||||||
|
|
||||||
|
const imageStyle = computed(() => ({
|
||||||
|
width: `${sliderWidth}px`,
|
||||||
|
height: `${imageHeight}px`
|
||||||
|
}))
|
||||||
|
|
||||||
|
const blockStyle = computed(() => ({
|
||||||
|
width: `${blockWidth}px`,
|
||||||
|
height: `${imageHeight}px`,
|
||||||
|
transform: `translateX(${handleOffset.value}px)`
|
||||||
|
}))
|
||||||
|
|
||||||
|
const sliderHandleStyle = computed(() => ({
|
||||||
|
width: `${blockWidth}px`,
|
||||||
|
height: `${sliderHeight}px`,
|
||||||
|
transform: `translateX(${handleOffset.value}px)`
|
||||||
|
}))
|
||||||
|
|
||||||
|
const sliderFillStyle = computed(() => ({
|
||||||
|
width: `${handleOffset.value > 0 ? Math.min(handleOffset.value + blockWidth, sliderWidth) : 0}px`
|
||||||
|
}))
|
||||||
|
|
||||||
|
const sliderHint = computed(() => {
|
||||||
|
if (success.value) {
|
||||||
|
return '验证通过'
|
||||||
|
}
|
||||||
|
if (loading.value) {
|
||||||
|
return '验证码加载中'
|
||||||
|
}
|
||||||
|
return '请按住滑块,拖动完成拼图'
|
||||||
|
})
|
||||||
|
|
||||||
|
const tipClass = computed(() => {
|
||||||
|
if (success.value) return 's-verify-tip-success'
|
||||||
|
if (failAnimate.value) return 's-verify-tip-error'
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
|
||||||
|
let closeTimer = null
|
||||||
|
let mouseMoveListener = null
|
||||||
|
let mouseUpListener = null
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
if (visible.value) return
|
||||||
|
visible.value = true
|
||||||
|
resetState()
|
||||||
|
nextTick(() => {
|
||||||
|
loadCaptcha()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
visible.value = false
|
||||||
|
cleanupMouseListeners()
|
||||||
|
clearCloseTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshCaptcha() {
|
||||||
|
if (loading.value) return
|
||||||
|
resetSlider()
|
||||||
|
loadCaptcha()
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetSlider() {
|
||||||
|
handleOffset.value = 0
|
||||||
|
dragState.baseOffset = 0
|
||||||
|
dragState.startClientX = 0
|
||||||
|
dragState.isDragging = false
|
||||||
|
success.value = false
|
||||||
|
failAnimate.value = false
|
||||||
|
tipMessage.value = ''
|
||||||
|
verifying.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetState() {
|
||||||
|
captchaState.originalImageBase64 = ''
|
||||||
|
captchaState.jigsawImageBase64 = ''
|
||||||
|
captchaState.token = ''
|
||||||
|
captchaState.secretKey = ''
|
||||||
|
resetSlider()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCaptcha() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await sysApi.getCaptchaCode({ captchaType: props.captchaType })
|
||||||
|
if (res && (res.repCode === '0000' || res.code === 0)) {
|
||||||
|
const data = res.repData || res.data || {}
|
||||||
|
captchaState.originalImageBase64 = data.originalImageBase64 || ''
|
||||||
|
captchaState.jigsawImageBase64 = data.jigsawImageBase64 || ''
|
||||||
|
captchaState.token = data.token || ''
|
||||||
|
captchaState.secretKey = data.secretKey || ''
|
||||||
|
} else {
|
||||||
|
tipMessage.value = res?.repMsg || res?.msg || '验证码获取失败,请重试'
|
||||||
|
emits('error', res)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('captcha load error', error)
|
||||||
|
tipMessage.value = '验证码加载异常,请稍后再试'
|
||||||
|
emits('error', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDragStart(event) {
|
||||||
|
if (loading.value || success.value) return
|
||||||
|
dragState.isDragging = true
|
||||||
|
dragState.startClientX = getClientX(event)
|
||||||
|
dragState.baseOffset = handleOffset.value
|
||||||
|
failAnimate.value = false
|
||||||
|
tipMessage.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDragMove(event) {
|
||||||
|
if (!dragState.isDragging) return
|
||||||
|
const currentX = getClientX(event)
|
||||||
|
const delta = currentX - dragState.startClientX
|
||||||
|
let next = dragState.baseOffset + delta
|
||||||
|
if (next < 0) next = 0
|
||||||
|
if (next > maxOffset.value) next = maxOffset.value
|
||||||
|
handleOffset.value = next
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDragEnd() {
|
||||||
|
if (!dragState.isDragging) return
|
||||||
|
dragState.isDragging = false
|
||||||
|
submitVerification()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseDown(event) {
|
||||||
|
if (event?.button !== 0) return
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
mouseMoveListener = ev => {
|
||||||
|
onDragMove(ev)
|
||||||
|
ev.preventDefault()
|
||||||
|
}
|
||||||
|
mouseUpListener = ev => {
|
||||||
|
cleanupMouseListeners()
|
||||||
|
onDragEnd(ev)
|
||||||
|
}
|
||||||
|
window.addEventListener('mousemove', mouseMoveListener)
|
||||||
|
window.addEventListener('mouseup', mouseUpListener)
|
||||||
|
}
|
||||||
|
onDragStart(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupMouseListeners() {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
if (mouseMoveListener) {
|
||||||
|
window.removeEventListener('mousemove', mouseMoveListener)
|
||||||
|
mouseMoveListener = null
|
||||||
|
}
|
||||||
|
if (mouseUpListener) {
|
||||||
|
window.removeEventListener('mouseup', mouseUpListener)
|
||||||
|
mouseUpListener = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCloseTimer() {
|
||||||
|
if (closeTimer) {
|
||||||
|
clearTimeout(closeTimer)
|
||||||
|
closeTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitVerification() {
|
||||||
|
if (verifying.value) return
|
||||||
|
verifying.value = true
|
||||||
|
failAnimate.value = false
|
||||||
|
|
||||||
|
const point = {
|
||||||
|
x: Math.round(handleOffset.value),
|
||||||
|
y: 5.0
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = captchaState.secretKey
|
||||||
|
? aesEncrypt(JSON.stringify(point), captchaState.secretKey)
|
||||||
|
: JSON.stringify(point)
|
||||||
|
|
||||||
|
const res = await sysApi.verifyCaptcha({
|
||||||
|
captchaType: props.captchaType,
|
||||||
|
token: captchaState.token,
|
||||||
|
pointJson: payload
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res && (res.repCode === '0000' || res.code === 0)) {
|
||||||
|
success.value = true
|
||||||
|
tipMessage.value = '验证成功'
|
||||||
|
const captchaVerification = captchaState.secretKey
|
||||||
|
? aesEncrypt(`${captchaState.token}---${JSON.stringify(point)}`, captchaState.secretKey)
|
||||||
|
: `${captchaState.token}---${JSON.stringify(point)}`
|
||||||
|
|
||||||
|
emits('success', {
|
||||||
|
captchaVerification,
|
||||||
|
token: captchaState.token,
|
||||||
|
point
|
||||||
|
})
|
||||||
|
|
||||||
|
clearCloseTimer()
|
||||||
|
closeTimer = setTimeout(() => {
|
||||||
|
handleClose()
|
||||||
|
}, 800)
|
||||||
|
} else {
|
||||||
|
handleFail(res?.repMsg || res?.msg || '验证失败,请重试')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('captcha verify error', error)
|
||||||
|
handleFail('网络异常,请重试')
|
||||||
|
} finally {
|
||||||
|
verifying.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFail(message) {
|
||||||
|
failAnimate.value = true
|
||||||
|
tipMessage.value = message
|
||||||
|
emits('error', message)
|
||||||
|
setTimeout(() => {
|
||||||
|
failAnimate.value = false
|
||||||
|
}, 400)
|
||||||
|
setTimeout(() => {
|
||||||
|
refreshCaptcha()
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClientX(event) {
|
||||||
|
if (event?.touches && event.touches.length) {
|
||||||
|
return event.touches[0].clientX
|
||||||
|
}
|
||||||
|
if (event?.changedTouches && event.changedTouches.length) {
|
||||||
|
return event.changedTouches[0].clientX
|
||||||
|
}
|
||||||
|
return event?.clientX || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
cleanupMouseListeners()
|
||||||
|
clearCloseTimer()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show,
|
||||||
|
close: handleClose,
|
||||||
|
refresh: refreshCaptcha
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.s-verify-mask {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-wrapper {
|
||||||
|
width: 340px;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.18);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 1px solid #f2f3f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-close {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #999999;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-body {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-image {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-image-bg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-image-block {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-refresh {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-loading {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(255, 255, 255, 0.65);
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-tip {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 6px 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-tip-success {
|
||||||
|
color: #52c41a;
|
||||||
|
background: rgba(82, 196, 26, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-tip-error {
|
||||||
|
color: #ff4d4f;
|
||||||
|
background: rgba(255, 77, 79, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-slider {
|
||||||
|
position: relative;
|
||||||
|
width: 310px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: #f2f3f5;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-slider-track {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: #f2f3f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-slider-fill {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--ui-BG-Main, #409eff);
|
||||||
|
border-radius: 20px;
|
||||||
|
transition: width 0.05s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-slider-handle {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 6px 14px rgba(25, 87, 170, 0.25);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #1f2d3d;
|
||||||
|
transition: transform 0.05s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-slider-success {
|
||||||
|
background: #52c41a;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-slider-fail {
|
||||||
|
animation: s-verify-shake 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-handle-icon {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-verify-slider-text {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #8c8c8c;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes s-verify-shake {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-4px);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateX(-2px);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: translateX(2px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
58
components/navbar-back/navbar-back.vue
Normal file
58
components/navbar-back/navbar-back.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<up-navbar
|
||||||
|
v-bind="$attrs"
|
||||||
|
:bgColor="bgColor"
|
||||||
|
titleStyle="color:#fff"
|
||||||
|
leftIconColor="#fff"
|
||||||
|
:left-text="leftText"
|
||||||
|
:title="title"
|
||||||
|
placeholder
|
||||||
|
:autoBack="autoBack"
|
||||||
|
:leftIcon="leftIcon"
|
||||||
|
@rightClick="rightClick"
|
||||||
|
@leftClick="leftClick"
|
||||||
|
>
|
||||||
|
<template #right>
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
</up-navbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const emits = defineEmits(['leftClick', 'rightClick'])
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
autoBack: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#fff'
|
||||||
|
},
|
||||||
|
leftIcon: {
|
||||||
|
type: String,
|
||||||
|
default: 'arrow-left'
|
||||||
|
},
|
||||||
|
leftText: {
|
||||||
|
type: String,
|
||||||
|
default: '返回'
|
||||||
|
},
|
||||||
|
bgColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#0055A2'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function rightClick() {
|
||||||
|
emits('rightClick')
|
||||||
|
}
|
||||||
|
function leftClick() {
|
||||||
|
emits('leftClick')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
284
components/sample/auncel-select-popup.vue
Normal file
284
components/sample/auncel-select-popup.vue
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<u-popup :show="showAuncelSelector" closeable @close="close" @open="open" mode="right">
|
||||||
|
<view class="p10">天平选择</view>
|
||||||
|
<scroll-view scroll-y="true" class="content">
|
||||||
|
<u-grid border :col="3" @click="doSelect">
|
||||||
|
<u-grid-item v-for="(auncel, index) in auncelList" :index="index" :key="index">
|
||||||
|
<view class="auncel-item">
|
||||||
|
<view class="auncel-name">
|
||||||
|
{{ auncel.code }}
|
||||||
|
<view style="text-align: center">{{ auncel.controlRealName }}</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="weight">
|
||||||
|
<view
|
||||||
|
:class="
|
||||||
|
auncel.weightStable === 0
|
||||||
|
? 'weight-data-yellow'
|
||||||
|
: auncel.weightStable === 1
|
||||||
|
? 'weight-data'
|
||||||
|
: 'weight-data-warning'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ auncel.weightData }}
|
||||||
|
</view>
|
||||||
|
<view class="weight-unit">{{ auncel.weightUnit }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-grid-item>
|
||||||
|
</u-grid>
|
||||||
|
</scroll-view>
|
||||||
|
</u-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
|
import nx from '@/nx' // 假设你的全局状态/工具挂载在 nx
|
||||||
|
|
||||||
|
// Props & Emits
|
||||||
|
const props = defineProps({
|
||||||
|
showAuncelSelector: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
previousAuncelId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:showAuncelSelector'])
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const auncelList = ref([])
|
||||||
|
|
||||||
|
// 计算属性:获取用户信息
|
||||||
|
const userInfo = computed(() => nx.$store('user').userInfo)
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
const open = () => {
|
||||||
|
getPageData()
|
||||||
|
listenDeviceData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
console.log('auncel-selector close触发')
|
||||||
|
auncelList.value = []
|
||||||
|
uni.$emit('auncelSelector_close')
|
||||||
|
closeDeviceListener()
|
||||||
|
emit('update:showAuncelSelector', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPageData = () => {
|
||||||
|
uni.showLoading({ title: '加载中...' })
|
||||||
|
nx.$api.laboratory
|
||||||
|
.getDeviceLaboratoryListBy({
|
||||||
|
deviceType: 'auncel',
|
||||||
|
pageNo: 0,
|
||||||
|
pageSize: -1,
|
||||||
|
status: '1',
|
||||||
|
isEnable: '1'
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
const dataList = res.records.map(item => ({
|
||||||
|
...item,
|
||||||
|
weightData: '',
|
||||||
|
weightUnit: '',
|
||||||
|
isConnected: 0,
|
||||||
|
weightStable: 0,
|
||||||
|
temperature: 0,
|
||||||
|
humidity: 0,
|
||||||
|
controlRealName: ''
|
||||||
|
}))
|
||||||
|
auncelList.value = dataList
|
||||||
|
uni.hideLoading()
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('加载天平列表失败:', err)
|
||||||
|
uni.hideLoading()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const doSelect = index => {
|
||||||
|
const currentAuncel = auncelList.value[index]
|
||||||
|
|
||||||
|
if (currentAuncel.isConnected !== 1) {
|
||||||
|
uni.showToast({ title: '天平设备尚未连接!', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentAuncel.controlRealName && currentAuncel.controlRealName !== userInfo.value.realname) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `当前天平正被“${currentAuncel.controlRealName}”使用,请选择其他天平!`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let delayFlag = false
|
||||||
|
if (props.previousAuncelId && props.previousAuncelId !== '' && props.previousAuncelId !== currentAuncel.id) {
|
||||||
|
releaseDeviceControl(props.previousAuncelId)
|
||||||
|
delayFlag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const controlDevice = {
|
||||||
|
msgId: currentAuncel.id,
|
||||||
|
cmd: 'controlDevice',
|
||||||
|
clientType: 'caaClient',
|
||||||
|
data: {
|
||||||
|
deviceId: currentAuncel.id,
|
||||||
|
deviceCode: currentAuncel.code,
|
||||||
|
deviceName: currentAuncel.name,
|
||||||
|
isControl: true,
|
||||||
|
controlRealName: userInfo.value.realname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendControl = () => {
|
||||||
|
const controlData = JSON.stringify(controlDevice)
|
||||||
|
nx.$measure.send(controlData)
|
||||||
|
console.log('controlDevice', controlData)
|
||||||
|
uni.$emit('auncelSelector_doSelect', controlDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delayFlag) {
|
||||||
|
setTimeout(sendControl, 300)
|
||||||
|
} else {
|
||||||
|
sendControl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const releaseDeviceControl = deviceId => {
|
||||||
|
if (!deviceId) return
|
||||||
|
const controlDevice = {
|
||||||
|
msgId: deviceId,
|
||||||
|
cmd: 'controlDevice',
|
||||||
|
clientType: 'caaClient',
|
||||||
|
data: {
|
||||||
|
deviceId,
|
||||||
|
isControl: false,
|
||||||
|
controlRealName: userInfo.value.realname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nx.$measure.send(JSON.stringify(controlDevice))
|
||||||
|
}
|
||||||
|
|
||||||
|
const listenDeviceData = () => {
|
||||||
|
uni.$on('deviceData', handleDeviceData)
|
||||||
|
uni.$on('deviceStatus', handleDeviceStatus)
|
||||||
|
uni.$on('connClose', handleConnClose)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeviceData = res => {
|
||||||
|
if (res.deviceType !== 'auncel') return
|
||||||
|
auncelList.value.forEach(item => {
|
||||||
|
if (item.id === res.deviceId) {
|
||||||
|
item.weightData = res.weightData ?? ''
|
||||||
|
item.weightUnit = res.weightUnit ?? ''
|
||||||
|
item.weightStable = res.weightStable ?? 0
|
||||||
|
item.isConnected = 1
|
||||||
|
item.controlRealName = res.controlRealName ?? ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeviceStatus = res => {
|
||||||
|
auncelList.value.forEach(item => {
|
||||||
|
if (item.id === res.deviceId) {
|
||||||
|
item.isConnected = res.connected ?? 0
|
||||||
|
if (res.connected === 0) {
|
||||||
|
item.weightData = '天平断开'
|
||||||
|
item.weightUnit = ''
|
||||||
|
item.weightStable = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConnClose = () => {
|
||||||
|
auncelList.value.forEach(item => {
|
||||||
|
item.weightData = ''
|
||||||
|
item.weightUnit = ''
|
||||||
|
item.weightStable = 0
|
||||||
|
item.controlRealName = ''
|
||||||
|
item.isConnected = 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDeviceListener = () => {
|
||||||
|
uni.$off('deviceData', handleDeviceData)
|
||||||
|
uni.$off('deviceStatus', handleDeviceStatus)
|
||||||
|
uni.$off('connClose', handleConnClose)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生命周期
|
||||||
|
onMounted(() => {
|
||||||
|
// 如果组件在 mounted 时已打开,可考虑自动加载(但通常由父组件控制 show)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
closeDeviceListener()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.content {
|
||||||
|
width: 80vw;
|
||||||
|
height: 90vh;
|
||||||
|
}
|
||||||
|
.auncel-item {
|
||||||
|
height: 180px;
|
||||||
|
width: 180px;
|
||||||
|
background-image: url(/static/images/auncel.png);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.auncel-name {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
.weight {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
font-size: 32px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.weight-data {
|
||||||
|
flex: 1;
|
||||||
|
color: #4cd964;
|
||||||
|
text-align: right;
|
||||||
|
font-family: zzjc-lcd;
|
||||||
|
}
|
||||||
|
.weight-data-yellow {
|
||||||
|
flex: 1;
|
||||||
|
color: #ffff00;
|
||||||
|
text-align: center;
|
||||||
|
font-family: zzjc-lcd;
|
||||||
|
}
|
||||||
|
.weight-data-warning {
|
||||||
|
color: #ff3333;
|
||||||
|
text-align: right;
|
||||||
|
font-family: zzjc-lcd;
|
||||||
|
}
|
||||||
|
.weight-unit {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 24px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.auncel-item {
|
||||||
|
height: 150px;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
.auncel-name {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
152
components/sample/sample-detail-popup.vue
Normal file
152
components/sample/sample-detail-popup.vue
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<!-- 查看样品详情 -->
|
||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<u-popup :show="showPopup" @close="close" @open="open" mode="left">
|
||||||
|
<view class="detail_title">
|
||||||
|
{{ taskDetail.sampleCode }}
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<scroll-view scroll-y style="height: 100vh; width: 30vw">
|
||||||
|
<view style="padding: 10px">
|
||||||
|
<up-collapse :value="getAllIndexes(fieldGroup)" :accordion="false">
|
||||||
|
<up-collapse-item
|
||||||
|
v-for="(fields, groupIndex) in fieldGroup"
|
||||||
|
:title="fields.title"
|
||||||
|
:key="groupIndex"
|
||||||
|
v-if="curParameterKey === '' || curParameterKey === fields.title"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="form-item-my"
|
||||||
|
v-for="(field, fieldIndex) in fields.fields"
|
||||||
|
:key="groupIndex + '-' + fieldIndex"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="label-my"
|
||||||
|
v-html="
|
||||||
|
field.name +
|
||||||
|
(typeof field.unit !== 'undefined' && field.unit !== null ? '(' + field.unit + ')' : '')
|
||||||
|
"
|
||||||
|
></view>
|
||||||
|
<view class="content-my">
|
||||||
|
<view class="content-my-text" v-if="field.type !== 'select'">
|
||||||
|
<text class="content-my-text-value">{{ field.value }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="content-my-text" v-else>
|
||||||
|
<text class="content-my-text-value">{{ field.valueText }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</up-collapse-item>
|
||||||
|
</up-collapse>
|
||||||
|
</view>
|
||||||
|
<view class="p30"></view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import nx from '@/nx'
|
||||||
|
|
||||||
|
// Props
|
||||||
|
const props = defineProps({
|
||||||
|
showPopup: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
detailPopupParam: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
taskDetailId: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Data
|
||||||
|
const curSample = ref({})
|
||||||
|
const curParameterKey = ref('')
|
||||||
|
const fieldGroup = ref([])
|
||||||
|
const taskDetail = ref({})
|
||||||
|
const optionParameterClassify = ref([])
|
||||||
|
const conAssayTaskId = ref('')
|
||||||
|
const busSubCSampleId = ref('')
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
const getAllIndexes = arr => {
|
||||||
|
return arr.map((_, index) => index)
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
uni.$emit('sample-detail-popup_close')
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSampleData = () => {
|
||||||
|
const taskDetailId = props.detailPopupParam.taskDetailId
|
||||||
|
nx.$api.assayTask.queryFieldsByTaskDetail({ taskDetailId }).then(res => {
|
||||||
|
fieldGroup.value = res.result
|
||||||
|
|
||||||
|
const arr = [{ label: '全部', value: '' }]
|
||||||
|
for (const g of fieldGroup.value) {
|
||||||
|
const title = g.title
|
||||||
|
arr.push({
|
||||||
|
label: title,
|
||||||
|
value: title
|
||||||
|
})
|
||||||
|
}
|
||||||
|
optionParameterClassify.value = arr // 字段分类
|
||||||
|
conAssayTaskId.value = res.additionalProperties.conAssayTaskId
|
||||||
|
busSubCSampleId.value = res.additionalProperties.busSubCSampleId
|
||||||
|
taskDetail.value = res.additionalProperties.taskDetail
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
getSampleData()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.form-item-my {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.label-my {
|
||||||
|
width: 180px; /* 标签宽度 */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-my sub {
|
||||||
|
font-size: 0.6em; /* 调整下标字体大小 */
|
||||||
|
vertical-align: sub; /* 调整下标垂直对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-my {
|
||||||
|
flex: 1;
|
||||||
|
padding-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-my-text {
|
||||||
|
height: 35px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.content-my-text-value {
|
||||||
|
color: rgb(48, 49, 51);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail_title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* 水平居中 */
|
||||||
|
align-items: center; /* 垂直居中 */
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
background-color: $uni-color-primary;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
37
components/wf-comment/wf-comment.vue
Normal file
37
components/wf-comment/wf-comment.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<up-row justify="space-between" v-for="item in commentWf">
|
||||||
|
<up-col span="6">
|
||||||
|
<view>
|
||||||
|
{{ item.nodeName }}:<text class="value">{{ item.comment }}</text>
|
||||||
|
</view>
|
||||||
|
</up-col>
|
||||||
|
<up-col span="6">
|
||||||
|
<view class="y-end">
|
||||||
|
<text class="value">{{ item.userName }}</text>
|
||||||
|
<text class="value">{{ item.time }}</text>
|
||||||
|
</view>
|
||||||
|
</up-col>
|
||||||
|
</up-row>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
commentWf: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.u-row {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
color: #666;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
212
components/zzjc-num-keyboard/zzjc-num-keyboard.vue
Normal file
212
components/zzjc-num-keyboard/zzjc-num-keyboard.vue
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<template>
|
||||||
|
<view class="y-f" style="height: 55vh">
|
||||||
|
<view class="weight">
|
||||||
|
<view class="weight-data"> {{ nums }}</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="keyboard-container">
|
||||||
|
<view class="keypad">
|
||||||
|
<view
|
||||||
|
class="oner"
|
||||||
|
:class="item.class"
|
||||||
|
:style="getListItemStyle(index)"
|
||||||
|
v-for="(item, index) in numbers"
|
||||||
|
:key="'num_' + index"
|
||||||
|
@click="changeNums(item, index)"
|
||||||
|
>
|
||||||
|
{{ item.text }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="func-pad">
|
||||||
|
<view @click="jianshao()" class="oner flex1">
|
||||||
|
<u-icon name="arrow-leftward" bold></u-icon>
|
||||||
|
</view>
|
||||||
|
<view @click="setNull()" class="oner flex1 mt10 mb10"> 清空 </view>
|
||||||
|
<view class="oner confirm" @click="ok()"> 确认 </view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'zzjc-num-keyboard',
|
||||||
|
props: {
|
||||||
|
numKeyboardParam: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
} //小数位数,-1为不限制
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nums: '',
|
||||||
|
numbers: [
|
||||||
|
{
|
||||||
|
text: '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '3'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '4'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '5'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '6'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '7'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '8'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '9'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '0',
|
||||||
|
class: 'zero'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '.'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {},
|
||||||
|
methods: {
|
||||||
|
//确认
|
||||||
|
ok() {
|
||||||
|
const val = {
|
||||||
|
val: this.nums
|
||||||
|
}
|
||||||
|
this.nums = ''
|
||||||
|
uni.$emit('keyboardOK', val)
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* 清空
|
||||||
|
* 数字类型改为0,其他类型改为空
|
||||||
|
* */
|
||||||
|
setNull() {
|
||||||
|
this.nums = ''
|
||||||
|
uni.$emit('keyboardOK', null)
|
||||||
|
},
|
||||||
|
clearNum() {
|
||||||
|
this.nums = ''
|
||||||
|
},
|
||||||
|
jianshao() {
|
||||||
|
if (this.nums) {
|
||||||
|
this.nums = this.nums.substring(0, this.nums.length - 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeNums(item, index) {
|
||||||
|
this.sumindex = index
|
||||||
|
if (item.text == '.') {
|
||||||
|
if (this.nums.indexOf('.') != -1 || this.nums.length == 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//检查小数位数
|
||||||
|
let decimal = this.numKeyboardParam.decimal
|
||||||
|
if (decimal == null) decimal = -1
|
||||||
|
if (this.nums.split('.') && this.nums.split('.')[1] && decimal != -1) {
|
||||||
|
if (this.nums.split('.')[1].length >= decimal) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.nums = this.nums + item.text
|
||||||
|
},
|
||||||
|
|
||||||
|
getListItemStyle(index) {
|
||||||
|
return {
|
||||||
|
background: this.numbers[index].background
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.keyboard-container {
|
||||||
|
flex: 5;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: rgba(232, 232, 232, 0.98);
|
||||||
|
}
|
||||||
|
.keypad {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr); /* 默认 3 列 */
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
flex: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oner {
|
||||||
|
width: 95%;
|
||||||
|
height: 55px;
|
||||||
|
font-size: 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 0 占两列 */
|
||||||
|
.zero {
|
||||||
|
width: 98%;
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.func-pad {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding: 10px 10px 10px 0;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.confirm {
|
||||||
|
flex: 2;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #19be6b;
|
||||||
|
}
|
||||||
|
.weight {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #2c405a;
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight-data {
|
||||||
|
color: #4cd964;
|
||||||
|
text-align: right;
|
||||||
|
letter-spacing: 5px;
|
||||||
|
font-size: 50px;
|
||||||
|
font-family: zzjc-lcd;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.weight-data {
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
.oner {
|
||||||
|
height: 33px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.weight-data {
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
34
defaultBaseUrl.js
Normal file
34
defaultBaseUrl.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// 在此不用配置接口前缀
|
||||||
|
const isDev = process.env.NODE_ENV === 'development'
|
||||||
|
const BaseUrl = isDev ? 'http://192.168.26.116:888/admin-api' : 'http://172.33.199.28:8088/api'
|
||||||
|
|
||||||
|
// const BaseUrl = isDev ? 'http://localhost:9999' : ''
|
||||||
|
const upgradeBaseUrl = 'http://172.33.199.28:8088'
|
||||||
|
|
||||||
|
const tenantId = '1'
|
||||||
|
export const clientId = 'lsky_lims'
|
||||||
|
|
||||||
|
function initDefaultBaseUrl() {
|
||||||
|
uni.setStorageSync('base_url', BaseUrl)
|
||||||
|
uni.setStorageSync('tenant_id', tenantId)
|
||||||
|
uni.setStorageSync('upgradeBaseUrl', upgradeBaseUrl)
|
||||||
|
}
|
||||||
|
initDefaultBaseUrl()
|
||||||
|
|
||||||
|
export function getBaseUrl() {
|
||||||
|
return uni.getStorageSync('base_url')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getImgBaseUrl() {
|
||||||
|
return uni.getStorageSync('base_url').replace('/api', '')
|
||||||
|
}
|
||||||
|
export function getTenantId() {
|
||||||
|
return uni.getStorageSync('tenant_id')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUpgradeBaseUrl() {
|
||||||
|
return uni.getStorageSync('upgradeBaseUrl')
|
||||||
|
}
|
||||||
|
export function getWebSocketUrl() {
|
||||||
|
return uni.getStorageSync('base_url').replace('/api', '') + '/ws'
|
||||||
|
}
|
||||||
13421
hybrid/html/build/pdf.js
Normal file
13421
hybrid/html/build/pdf.js
Normal file
File diff suppressed because it is too large
Load Diff
1
hybrid/html/build/pdf.js.map
Normal file
1
hybrid/html/build/pdf.js.map
Normal file
File diff suppressed because one or more lines are too long
45929
hybrid/html/build/pdf.worker.js
vendored
Normal file
45929
hybrid/html/build/pdf.worker.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
hybrid/html/build/pdf.worker.js.map
vendored
Normal file
1
hybrid/html/build/pdf.worker.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
hybrid/html/web/cmaps/78-EUC-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/78-EUC-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/78-EUC-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/78-EUC-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/78-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/78-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/78-RKSJ-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/78-RKSJ-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/78-RKSJ-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/78-RKSJ-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/78-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/78-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/78ms-RKSJ-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/78ms-RKSJ-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/78ms-RKSJ-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/78ms-RKSJ-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/83pv-RKSJ-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/83pv-RKSJ-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/90ms-RKSJ-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/90ms-RKSJ-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/90ms-RKSJ-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/90ms-RKSJ-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/90msp-RKSJ-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/90msp-RKSJ-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/90msp-RKSJ-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/90msp-RKSJ-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/90pv-RKSJ-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/90pv-RKSJ-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/90pv-RKSJ-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/90pv-RKSJ-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Add-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Add-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Add-RKSJ-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Add-RKSJ-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Add-RKSJ-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Add-RKSJ-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Add-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Add-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-CNS1-0.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-CNS1-0.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-CNS1-1.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-CNS1-1.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-CNS1-2.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-CNS1-2.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-CNS1-3.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-CNS1-3.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-CNS1-4.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-CNS1-4.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-CNS1-5.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-CNS1-5.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-CNS1-6.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-CNS1-6.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-CNS1-UCS2.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-CNS1-UCS2.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-GB1-0.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-GB1-0.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-GB1-1.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-GB1-1.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-GB1-2.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-GB1-2.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-GB1-3.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-GB1-3.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-GB1-4.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-GB1-4.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-GB1-5.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-GB1-5.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-GB1-UCS2.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-GB1-UCS2.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Japan1-0.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Japan1-0.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Japan1-1.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Japan1-1.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Japan1-2.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Japan1-2.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Japan1-3.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Japan1-3.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Japan1-4.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Japan1-4.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Japan1-5.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Japan1-5.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Japan1-6.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Japan1-6.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Japan1-UCS2.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Japan1-UCS2.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Korea1-0.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Korea1-0.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Korea1-1.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Korea1-1.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Korea1-2.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Korea1-2.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Adobe-Korea1-UCS2.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Adobe-Korea1-UCS2.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/B5-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/B5-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/B5-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/B5-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/B5pc-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/B5pc-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/B5pc-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/B5pc-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/CNS-EUC-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/CNS-EUC-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/CNS-EUC-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/CNS-EUC-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/CNS1-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/CNS1-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/CNS1-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/CNS1-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/CNS2-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/CNS2-H.bcmap
Normal file
Binary file not shown.
3
hybrid/html/web/cmaps/CNS2-V.bcmap
Normal file
3
hybrid/html/web/cmaps/CNS2-V.bcmap
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<03>RCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSE<53>CNS2-H
|
||||||
BIN
hybrid/html/web/cmaps/ETHK-B5-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/ETHK-B5-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/ETHK-B5-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/ETHK-B5-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/ETen-B5-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/ETen-B5-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/ETen-B5-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/ETen-B5-V.bcmap
Normal file
Binary file not shown.
3
hybrid/html/web/cmaps/ETenms-B5-H.bcmap
Normal file
3
hybrid/html/web/cmaps/ETenms-B5-H.bcmap
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<02>RCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSE<53> ETen-B5-H` ^
|
||||||
BIN
hybrid/html/web/cmaps/ETenms-B5-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/ETenms-B5-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/EUC-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/EUC-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/EUC-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/EUC-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Ext-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Ext-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Ext-RKSJ-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Ext-RKSJ-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Ext-RKSJ-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Ext-RKSJ-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/Ext-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/Ext-V.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/GB-EUC-H.bcmap
Normal file
BIN
hybrid/html/web/cmaps/GB-EUC-H.bcmap
Normal file
Binary file not shown.
BIN
hybrid/html/web/cmaps/GB-EUC-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/GB-EUC-V.bcmap
Normal file
Binary file not shown.
4
hybrid/html/web/cmaps/GB-H.bcmap
Normal file
4
hybrid/html/web/cmaps/GB-H.bcmap
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<02>RCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSE!!<21><>]aX!!]`<60>21<32>> <09>p<0B>z<EFBFBD>$]<5D><06>"R<>d<EFBFBD>-U<>7<EFBFBD>*<17>
|
||||||
|
4<>%<25>+ <20>Z <20>{<7B>/<1F>%<25><<3C>9K<39>b<EFBFBD>1]<5D>.<2E>"<1F><0C>`]<5D>,<2C>"]<5D>
|
||||||
BIN
hybrid/html/web/cmaps/GB-V.bcmap
Normal file
BIN
hybrid/html/web/cmaps/GB-V.bcmap
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user