Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
computedRoomPad
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
邓文彬
computedRoomPad
Commits
c3cdfd16
提交
c3cdfd16
authored
3月 13, 2025
作者:
caodi\cd
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix:保存
上级
07767b16
隐藏空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
1060 行增加
和
395 行删除
+1060
-395
manifest.json
manifest.json
+3
-1
pages.json
pages.json
+78
-82
dialog.vue
pages/listingManagement/dialog.vue
+355
-0
index.vue
pages/listingManagement/index.vue
+522
-309
index.js
request/index.js
+46
-1
index.js
store/index.js
+2
-1
IoReadingAndWriting.js
utils/IoReadingAndWriting.js
+54
-1
没有找到文件。
manifest.json
浏览文件 @
c3cdfd16
...
...
@@ -20,7 +20,9 @@
"delay"
:
0
},
/*
模块配置
*/
"modules"
:
{},
"modules"
:
{
"Camera"
:
{}
},
/*
应用发布信息
*/
"distribute"
:
{
/*
android打包配置
*/
...
...
pages.json
浏览文件 @
c3cdfd16
{
"pages"
:
[
//pages数组中第一项表示应用启动页,参考:https
:
//uniapp.dcloud.io/collocation/pages
{
"path"
:
"pages/login/login"
},
{
"path"
:
"pages/home/home"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
{
//操作日志
"path"
:
"pages/index/operLog"
},
"pages"
:
[
//pages数组中第一项表示应用启动页,参考:https
:
//uniapp.dcloud.io/collocation/pages
{
"path"
:
"pages/login/login"
},
{
"path"
:
"pages/home/home"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
{
//操作日志
"path"
:
"pages/index/operLog"
},
//杭州内网机房巡检
//杭州内网机房巡检
{
"path"
:
"pages/inspection/inspFirst"
},
{
"path"
:
"pages/inspection/inspFirst"
},
{
"path"
:
"pages/report/sampleTable"
},
{
"path"
:
"pages/report/sampleTable"
},
{
//修改密码
"path"
:
"pages/index/editPd"
},
{
"path"
:
"pages/home/home"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
//
机房巡检列表页
{
"path"
:
"pages/inspectionContent/inspectionContent"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
//
井道巡检
{
"path"
:
"pages/shaftInspection/shaftInspection"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
{
//修改密码
"path"
:
"pages/index/editPd"
},
{
"path"
:
"pages/home/home"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
//
新增配置---------
//
机房巡检
{
"path"
:
"pages/inspectionContent/inspectionContent"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
//
井道巡检
{
"path"
:
"pages/shaftInspection/shaftInspection"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
//
机房巡检
{
"path"
:
"pages/inspectionContent/inspectionContentList"
},
//
井道巡检新页面
{
"path"
:
"pages/shaftInspection/shaftInspectionNew"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
},
//
设备上架管理
{
"path"
:
"pages/listingManagement/index"
}
],
"globalStyle"
:
{
"pageOrientation"
:
"landscape"
,
"navigationStyle"
:
"custom"
,
"rpxCalcMaxDeviceWidth"
:
2000
,
"rpxCalcIncludeWidth"
:
750
},
"uniIdRouter"
:
{},
"condition"
:
{
//模式配置,仅开发期间生效
"current"
:
0
,
//当前激活的模式(list
的索引项)
"list"
:
[
{
"name"
:
""
,
//模式名称
"path"
:
""
,
//启动页面,必选
"query"
:
""
//启动参数,在页面的onLoad函数里面得到
}
]
}
}
//
新增配置---------
//
巡检管理
{
"path"
:
"pages/inspectionManagement/index"
},
//
设备上架管理
{
"path"
:
"pages/listingManagement/index"
},
//
井道巡检新页面
{
"path"
:
"pages/shaftInspection/shaftInspectionNew"
,
"style"
:
{
"navigationBarTitleText"
:
""
}
}
],
"globalStyle"
:
{
"pageOrientation"
:
"landscape"
,
"navigationStyle"
:
"custom"
,
"rpxCalcBaseDeviceWidth"
:
1280
,
//
设计稿基准宽度
"rpxCalcMaxDeviceWidth"
:
2560
,
//
设备最大宽度
"rpxCalcIncludeWidth"
:
1024
//
包含宽度
},
"uniIdRouter"
:
{},
"condition"
:
{
//模式配置,仅开发期间生效
"current"
:
0
,
//当前激活的模式(list
的索引项)
"list"
:
[{
"name"
:
""
,
//模式名称
"path"
:
""
,
//启动页面,必选
"query"
:
""
//启动参数,在页面的onLoad函数里面得到
}]
}
}
\ No newline at end of file
pages/listingManagement/dialog.vue
0 → 100644
浏览文件 @
c3cdfd16
<
template
>
<view
class=
"synchronous-dialog"
>
<view
class=
"synchronous-content"
>
<view
class=
"row-item"
>
<text
class=
"title"
>
待打包数据
</text>
</view>
<view
class=
"row-item count-num"
>
<text
class=
"num"
>
{{
list
.
length
}}
</text>
<text>
条
</text>
</view>
<view
class=
"operating-instructions"
>
<view
class=
"title"
>
操作说明:
</view>
<view
class=
"instructions-item"
>
1、在PAD端,点击“数据打包”,做好同步准备。
</view>
<view
class=
"instructions-item"
>
2、完成打包后,将PAD直联PC机,等待PC机自动导入需同步记录。
</view>
<view
class=
"instructions-item"
>
3、PC端自动导入完成后,请点击“同步数据”,同步成功后,列表自动刷新展示同步数据;且PAD同步的数据不支持修改。
</view>
</view>
<view
class=
"row-item bottom-row"
>
<button
class=
"button"
:loading=
"loading"
@
click=
"clickHandle"
>
数据打包
</button>
</view>
<!-- 关闭按钮 -->
<div
class=
"close-button"
>
<text
class=
"iconfont icon-a-bianzu16beifen"
@
click=
"close"
></text>
</div>
</view>
</view>
</
template
>
<
script
>
import
{
SYNCHRONIZE_DATA_PAD
,
checkAndCreateDirectory
,
createFileWithPlusIO
,
setSm2
,
USER_FILE_NAME
,
getUserList
,
}
from
"@/utils/systemCofig"
;
import
{
copyDirectory
,
deleteAllFilesInDirectory
,
addLog
,
getLogContent
,
LOG_TYPE_ENUM
,
writeDeviceData
,
}
from
"@/utils/IoReadingAndWriting.js"
;
import
moment
from
"moment"
;
import
{
Base64
}
from
"js-base64"
;
export
default
{
props
:
{
list
:
{
type
:
Array
,
default
:
()
=>
{
return
[];
},
},
photos
:
{
type
:
Array
,
default
:
()
=>
{
return
[];
},
},
},
components
:
{},
data
()
{
return
{
loading
:
false
,
};
},
mounted
()
{},
watch
:
{
list
(
newData
)
{
const
temp
=
[];
newData
.
forEach
((
item
)
=>
{
temp
.
push
(...(
item
.
list
||
[]));
});
},
},
methods
:
{
close
()
{
this
.
$emit
(
"close"
);
},
getDetails
()
{
this
.
$emit
(
"getDetails"
);
},
/**
* 1.生成两个文件. 机房文件 和 井道文件
* 2. 更新巡检数据状态 synchronization 置为 1
* 3. 写入数据
* 读取上一次打包的文件, 复制到 [ history ] 文件夹中
*/
clickHandle
()
{
if
(
this
.
loading
)
return
;
const
directoryPath
=
`
${
SYNCHRONIZE_DATA_PAD
}
/发送数据`
;
const
targetDirectoryPath
=
`
${
SYNCHRONIZE_DATA_PAD
}
/history/listingManagement`
;
checkAndCreateDirectory
(
directoryPath
).
then
(()
=>
{
copyDirectory
(
directoryPath
,
targetDirectoryPath
)
.
then
(()
=>
{
return
deleteAllFilesInDirectory
(
directoryPath
);
})
.
then
(()
=>
{
this
.
coverData
();
})
.
catch
((
error
)
=>
{
uni
.
showToast
({
title
:
error
,
icon
:
"none"
,
duration
:
1000
,
});
});
});
},
// 处理数据
coverData
()
{
const
userName
=
this
.
$store
.
state
.
now_user
.
user
;
let
syncedData
=
this
.
list
;
let
timeStr
=
moment
().
format
(
"yyyy_MM_DD_hh_mm_ss"
);
syncedData
.
forEach
((
item
)
=>
{
item
.
synchronization
=
true
;
item
.
selected
=
false
;
item
.
status
=
"已同步"
;
});
console
.
log
(
"syncedData"
,
syncedData
);
this
.
loading
=
true
;
let
SBSJ_DATA_FILE_NAME
=
`
${
userName
}
_SBSJ_
${
timeStr
}
.txt`
;
const
tmepList
=
this
.
packedData
(
syncedData
,
SBSJ_DATA_FILE_NAME
);
let
arr
=
this
.
photos
.
map
((
item
)
=>
{
// 查找 B 数组中是否有对应的对象
const
updatedItem
=
syncedData
.
find
((
bItem
)
=>
bItem
.
id
===
item
.
id
);
// 如果有,则返回 B 数组中的对象,否则返回原对象
return
updatedItem
?
updatedItem
:
item
;
});
writeDeviceData
(
tmepList
,
userName
)
.
then
((
res
)
=>
{
console
.
log
(
"tmepList"
,
tmepList
);
this
.
$store
.
commit
(
"SET_DEVICEDATA"
,
arr
);
console
.
log
(
"arr"
,
arr
)
setTimeout
(()
=>
{
uni
.
showToast
({
title
:
"打包成功"
,
icon
:
"none"
,
duration
:
2000
,
});
this
.
close
();
this
.
loading
=
false
;
let
length
=
tmepList
.
length
;
// 生成日志
const
logContent
=
getLogContent
(
LOG_TYPE_ENUM
.
sys
,
`
${
length
}
张照同步`
,
"同步"
);
const
log_list
=
this
.
$store
.
state
.
log_list
;
log_list
.
push
(
logContent
);
this
.
$store
.
commit
(
"SET_LOG_LIST"
,
log_list
);
addLog
(
log_list
).
then
(()
=>
{});
// 更新同步时间
this
.
updateSysTime
();
this
.
getDetails
();
},
2
*
1000
);
})
.
catch
((
error
)
=>
{
this
.
loading
=
false
;
uni
.
showToast
({
title
:
error
,
icon
:
"none"
,
duration
:
2000
,
});
})
.
catch
(()
=>
{
setTimeout
(()
=>
{
uni
.
showToast
({
title
:
"打包失败"
,
icon
:
"none"
,
duration
:
2000
,
});
this
.
loading
=
false
;
},
2
*
1000
);
});
},
// 打包文件
packedData
(
content
,
fileName
)
{
const
fileContent
=
setSm2
(
content
);
return
createFileWithPlusIO
(
`
${
SYNCHRONIZE_DATA_PAD
}
/发送数据`
,
fileName
,
fileContent
);
},
// 更新最近一次同步时间
updateSysTime
()
{
getUserList
().
then
((
personList
)
=>
{
const
now_user
=
this
.
$store
.
state
.
now_user
;
const
key
=
personList
.
findIndex
(
(
item
)
=>
item
.
userId
==
now_user
.
userId
);
// 更新用户同步时间
const
userInfo
=
personList
[
key
];
const
LastSynchronizationTime
=
moment
().
format
(
"yyyy-MM-DD HH:mm"
);
personList
[
key
].
LastSynchronizationTime
=
LastSynchronizationTime
;
userInfo
.
LastSynchronizationTime
=
LastSynchronizationTime
;
this
.
$store
.
commit
(
"SET_USER"
,
userInfo
);
uni
.
setStorageSync
(
"last_time"
,
userInfo
.
LastSynchronizationTime
||
""
);
// 更新用户数据
const
fileContent
=
JSON
.
stringify
(
Base64
.
encode
(
JSON
.
stringify
(
personList
))
);
uni
.
setStorage
({
key
:
"user_data"
,
data
:
JSON
.
stringify
(
personList
),
fail
:
(
error
)
=>
{
console
.
log
(
"APP.vue 存储数据失败"
,
error
);
},
});
createFileWithPlusIO
(
SYNCHRONIZE_DATA_PAD
,
USER_FILE_NAME
,
fileContent
)
.
then
(()
=>
{
console
.
log
(
"---用户数据更新成功"
);
})
.
catch
((
error
)
=>
{
console
.
log
(
"---用户数据更新失败"
,
error
);
});
});
},
},
};
</
script
>
<
style
scoped
lang=
"less"
>
.synchronous-dialog {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
.synchronous-content {
padding: 3% 20px 32px 24px;
width: 400px;
height: 60%;
box-sizing: border-box;
background-image: linear-gradient(
-6deg,
#f9ffe7 0%,
#ffffff 12%,
#fcfeff 73%,
#ccf1ff 100%
);
border: 0.4px solid rgba(224, 224, 224, 1);
border-radius: 12px;
position: relative;
.row-item {
display: flex;
align-items: center;
justify-content: center;
}
.title {
font-family: PingFangSC-Medium;
font-size: 18px;
color: #000000;
text-align: center;
line-height: 26px;
font-weight: 500;
}
.count-num {
margin: 5% 0 5% 0;
align-items: flex-end;
.num {
display: inline-block;
font-family: AlibabaPuHuiTi_2_65_Medium;
font-size: 50px;
color: #3774f6;
line-height: 44px;
font-weight: 500;
}
}
.operating-instructions {
margin-bottom: 8%;
.title {
font-size: 13px;
color: #4a4a4a;
line-height: 24px;
font-weight: 600;
text-align: left;
}
.instructions-item {
font-size: 12px;
color: #7c7c7c;
line-height: 22px;
font-weight: 400;
}
}
// 打包按钮
.bottom-row {
position: absolute;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
.button {
display: flex;
align-items: center;
justify-content: center;
background-image: linear-gradient(180deg, #3773f6 0%, #2c57f6 99%);
box-shadow: 0px 10px 24px 0px rgba(51, 104, 246, 0.24);
border-radius: 27px;
width: 160px;
height: 40px;
color: #fff;
}
}
// 关闭按钮
.close-button {
position: absolute;
bottom: -40px;
left: 50%;
transform: translateX(-50%);
.iconfont {
color: #fff;
font-size: 24px;
}
}
}
}
</
style
>
pages/listingManagement/index.vue
浏览文件 @
c3cdfd16
<
template
>
<view
class=
"container"
>
<uni-nav-bar
:fixed=
"true"
background-color=
"rgba(214, 240, 255, 0.0)"
status-bar
rightWidth=
"300"
>
<block
slot=
"left"
>
<view
class=
"uni-nav-bar-text"
@
click=
"back"
>
<text
class=
"iconfont icon-Arrow-Left"
></text>
</view>
</block>
<block
slot=
"right"
class=
"nav-right"
>
<view
class=
"header-buttons"
>
<button
class=
"button"
@
click=
"lookLog"
>
机房巡检
</button>
</view>
</block>
</uni-nav-bar>
<!-- 第一个模块:标题和按钮 -->
<view
class=
"header"
>
<view
class=
"title"
>
<view
class=
"blue-line"
></view>
<text>
设备上架管理
</text>
</view>
<view
class=
"buttons"
>
<button
class=
"btn"
@
click=
"syncData"
>
数据同步
</button>
<button
class=
"btn"
@
click=
"takePhoto"
>
设备上架拍照
</button>
</view>
</view>
<view
class=
"container"
>
<uni-nav-bar
:fixed=
"true"
background-color=
"rgba(214, 240, 255, 0.0)"
status-bar
rightWidth=
"300"
>
<block
slot=
"left"
>
<view
class=
"uni-nav-bar-text"
@
click=
"back"
>
<text
class=
"iconfont icon-Arrow-Left"
></text>
</view>
</block>
<block
slot=
"right"
class=
"nav-right"
>
<view
class=
"header-buttons"
>
<button
class=
"button"
>
机房
</button>
</view>
</block>
</uni-nav-bar>
<!-- 第二个模块:选中条数显示 -->
<view
class=
"selected-count"
>
选中:
{{
selectedCount
}}
条
</view>
<!-- 第二个模块:选中条数显示 -->
<view
class=
"selected-count"
>
已选择
{{
selectedCount
}}
张图片
</view>
<!-- 第三个模块:照片显示模块 -->
<view
class=
"photo-list"
>
<view
v-for=
"(group, date) in photoGroups"
:key=
"date"
class=
"photo-group"
>
<view
class=
"group-header"
>
<text>
{{
date
}}
</text>
<button
class=
"select-all"
@
click=
"toggleSelectAll(date)"
>
全选
</button>
</view>
<view
class=
"card-list"
>
<view
v-for=
"photo in group"
:key=
"photo.id"
class=
"card"
@
click=
"previewPhoto(photo)"
>
<image
:src=
"photo.url"
mode=
"widthFix"
class=
"photo"
></image>
<view
class=
"check-icon"
@
click
.
stop=
"toggleSelect(photo)"
>
<text
v-if=
"photo.selected"
>
✔
</text>
</view>
<view
class=
"delete-icon"
@
click
.
stop=
"deletePhoto(photo)"
>
×
</view>
<view
class=
"photo-info"
>
<text>
编号:
{{
photo
.
id
}}
</text>
<text>
时间:
{{
photo
.
time
}}
</text>
<text>
状态:
{{
photo
.
synced
?
'已同步'
:
'待同步'
}}
</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 第三个模块:照片显示模块 -->
<view
class=
"photo-list"
>
<view
v-for=
"(group, date) in photoGroups"
:key=
"date"
class=
"photo-group"
>
<view
class=
"group-header"
>
<view
class=
"circle-button"
:class=
"
{ active: filteredData(selectedPhotos, group, date) }"
@click.stop="toggleSelectAll(date)"
>
<text
class=
"inner-circle"
v-if=
"filteredData(selectedPhotos, group, date)"
></text>
</view>
<text
class=
"date"
>
{{
date
}}
</text>
</view>
<view
class=
"card-list"
>
<view
v-for=
"photo in group"
:key=
"photo.id"
class=
"card"
@
click=
"previewPhoto(photo)"
>
<image
:src=
"photo.url"
mode=
"scaleToFill"
class=
"photo"
></image>
<view
class=
"check-icon"
@
click
.
stop=
"toggleSelect(photo)"
>
<text
v-if=
"photo.selected"
>
√
</text>
</view>
<view
class=
"delete-icon"
@
click
.
stop=
"deletePhoto(photo)"
>
×
</view>
<view
class=
"type"
>
{{
photo
.
synchronization
?
"已同步"
:
"待同步"
}}
</view>
<view
class=
"photo-info"
>
<text
class=
"no"
>
照片编号:
{{
photo
.
id
}}
</text>
</view>
<view
v-if=
"false"
class=
"photo-info"
>
<text>
编号:
{{
photo
.
id
}}
</text>
<text>
时间:
{{
photo
.
time
}}
</text>
<text
>
状态:
{{
photo
.
synchronization
?
"已同步"
:
"待同步"
}}
</text
>
</view>
</view>
</view>
</view>
</view>
<view
v-if=
"selectedPhotos && selectedPhotos.length"
class=
"inspection-button"
@
click=
"openDialog(true)"
>
数据同步
</view
>
<view
v-else
class=
"inspection-button"
@
click=
"takePhoto"
>
设备拍照
</view>
<!-- 打包弹窗 -->
<Dialog
v-show=
"isDialog && selectedPhotos.length"
ref=
"Dialog"
:list=
"selectedPhotos"
:photos=
"photos"
@
close=
"openDialog(false)"
@
getDetails=
"getDetails"
></Dialog>
</view>
</
template
>
<
script
>
import
moment
from
"moment"
;
export
default
{
data
()
{
return
{
photos
:
[],
// 所有照片
selectedPhotos
:
[],
// 选中的照片
syncedPhotos
:
[]
// 已同步的照片
};
},
computed
:
{
// 按日期分组照片
photoGroups
()
{
const
groups
=
{};
this
.
photos
.
forEach
(
photo
=>
{
const
date
=
photo
.
time
.
split
(
' '
)[
0
];
if
(
!
groups
[
date
])
{
groups
[
date
]
=
[];
}
groups
[
date
].
push
(
photo
);
});
return
groups
;
},
// 选中的照片数量
selectedCount
()
{
return
this
.
selectedPhotos
.
length
;
}
},
methods
:
{
back
()
{
uni
.
navigateBack
();
},
// 拍照
takePhoto
()
{
uni
.
chooseImage
({
count
:
1
,
success
:
(
res
)
=>
{
console
.
log
(
111
,
res
)
const
tempFilePaths
=
res
.
tempFilePaths
;
const
newPhoto
=
{
id
:
this
.
getFileName
(
tempFilePaths
[
0
]),
url
:
tempFilePaths
[
0
],
time
:
moment
(
new
Date
()).
format
(
"yyyy-MM-DD HH:mm"
),
selected
:
false
,
synced
:
false
};
this
.
photos
.
unshift
(
newPhoto
);
}
});
},
// 从路径中提取文件名(去掉路径和扩展名)
getFileName
(
filePath
)
{
// 获取路径中的文件名部分(包括扩展名)
const
fileNameWithExt
=
filePath
.
split
(
'/'
).
pop
();
// 去掉扩展名
const
fileName
=
fileNameWithExt
.
split
(
'.'
).
shift
();
return
fileName
;
},
// 切换选中状态
toggleSelect
(
photo
)
{
photo
.
selected
=
!
photo
.
selected
;
if
(
photo
.
selected
)
{
this
.
selectedPhotos
.
push
(
photo
);
}
else
{
this
.
selectedPhotos
=
this
.
selectedPhotos
.
filter
(
p
=>
p
.
id
!==
photo
.
id
);
}
},
// 全选/取消全选
toggleSelectAll
(
date
)
{
const
group
=
this
.
photoGroups
[
date
];
const
allSelected
=
group
.
every
(
photo
=>
photo
.
selected
);
group
.
forEach
(
photo
=>
{
photo
.
selected
=
!
allSelected
;
if
(
photo
.
selected
)
{
this
.
selectedPhotos
.
push
(
photo
);
}
else
{
this
.
selectedPhotos
=
this
.
selectedPhotos
.
filter
(
p
=>
p
.
id
!==
photo
.
id
);
}
});
},
// 删除照片
deletePhoto
(
photo
)
{
this
.
photos
=
this
.
photos
.
filter
(
p
=>
p
.
id
!==
photo
.
id
);
this
.
selectedPhotos
=
this
.
selectedPhotos
.
filter
(
p
=>
p
.
id
!==
photo
.
id
);
},
// 预览照片
previewPhoto
(
photo
)
{
uni
.
previewImage
({
urls
:
this
.
photos
.
map
(
p
=>
p
.
url
),
current
:
photo
.
url
});
},
// 数据同步
syncData
()
{
const
syncedData
=
this
.
selectedPhotos
.
map
(
photo
=>
({
id
:
photo
.
id
,
time
:
photo
.
time
,
status
:
photo
.
synced
?
'已同步'
:
'待同步'
}));
// 存储到本地
uni
.
setStorageSync
(
'syncedPhotos'
,
syncedData
);
// 更新同步状态
this
.
selectedPhotos
.
forEach
(
photo
=>
{
photo
.
synced
=
true
;
});
this
.
selectedPhotos
=
[];
}
},
mounted
()
{
// 从本地存储加载已同步的照片
const
syncedPhotos
=
uni
.
getStorageSync
(
'syncedPhotos'
)
||
[];
this
.
syncedPhotos
=
syncedPhotos
;
// 更新照片的同步状态
this
.
photos
.
forEach
(
photo
=>
{
if
(
this
.
syncedPhotos
.
some
(
p
=>
p
.
id
===
photo
.
id
))
{
photo
.
synced
=
true
;
}
});
}
};
import
moment
from
"moment"
;
import
Dialog
from
"./dialog.vue"
;
import
{
addLog
,
getLogContent
,
LOG_TYPE_ENUM
,
writeDeviceData
,
}
from
"@/utils/IoReadingAndWriting.js"
;
import
{
getDeviceDataDetails
}
from
"@/request/index.js"
;
export
default
{
components
:
{
Dialog
,
},
data
()
{
return
{
isDialog
:
false
,
//
photos
:
[],
// 所有照片
selectedPhotos
:
[],
// 选中的照片
syncedPhotos
:
[],
// 已同步的照片
};
},
computed
:
{
// 按日期分组照片
photoGroups
()
{
const
groups
=
{};
this
.
photos
.
forEach
((
photo
)
=>
{
const
date
=
photo
.
date
;
if
(
!
groups
[
date
])
{
groups
[
date
]
=
[];
}
groups
[
date
].
push
(
photo
);
});
return
groups
;
},
// 选中的照片数量
selectedCount
()
{
return
this
.
selectedPhotos
.
length
;
},
userInfo
()
{
return
this
.
$store
.
state
.
now_user
||
{};
},
},
onLoad
(
options
)
{
this
.
getDetails
();
},
mounted
()
{
const
systemInfo
=
uni
.
getSystemInfoSync
();
console
.
log
(
"设备逻辑宽度"
,
systemInfo
.
windowWidth
);
// 设备逻辑宽度
console
.
log
(
"设备像素比"
,
systemInfo
.
pixelRatio
);
// 设备像素比
},
methods
:
{
back
()
{
uni
.
navigateBack
();
},
// 回显数据
getDetails
()
{
uni
.
showLoading
();
getDeviceDataDetails
()
.
then
((
res
)
=>
{
this
.
photos
=
res
;
console
.
log
(
"this.photos"
,
this
.
photos
);
uni
.
hideLoading
();
})
.
catch
((
error
)
=>
{
uni
.
showToast
({
title
:
error
.
msg
,
icon
:
"none"
,
duration
:
1000
,
});
uni
.
hideLoading
();
});
},
// 拍照
takePhoto
()
{
uni
.
chooseImage
({
count
:
1
,
sourceType
:
[
"camera"
],
// 可以从相机拍摄
success
:
async
(
res
)
=>
{
console
.
log
(
111
,
res
);
const
tempFilePath
=
res
.
tempFilePaths
[
0
];
// 存储照片到本地
uni
.
saveImageToPhotosAlbum
({
filePath
:
tempFilePath
,
success
:
function
()
{
console
.
log
(
"图片已成功保存到相册"
);
},
fail
:
function
(
err
)
{
console
.
error
(
"保存图片到相册失败:"
,
err
);
},
});
const
base64
=
await
this
.
convertFileToBase64
(
tempFilePath
);
// 数据合成
const
newPhoto
=
{
id
:
this
.
getFileName
(
tempFilePath
),
url
:
base64
,
date
:
moment
(
new
Date
()).
format
(
"yyyy-MM-DD"
),
time
:
moment
(
new
Date
()).
format
(
"yyyy-MM-DD HH:mm"
),
selected
:
false
,
synchronization
:
false
,
};
console
.
log
(
"newPhoto"
,
newPhoto
);
this
.
photos
.
unshift
(
newPhoto
);
this
.
addPhotos
(
this
.
photos
,
`照片编号(
${
newPhoto
.
id
}
)`
,
"设备上架"
);
},
});
},
// 转化为base64
convertFileToBase64
(
filePath
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
plus
.
io
.
resolveLocalFileSystemURL
(
filePath
,
function
(
entry
)
{
entry
.
file
(
function
(
file
)
{
const
reader
=
new
plus
.
io
.
FileReader
();
reader
.
onloadend
=
function
(
evt
)
{
const
base64
=
evt
.
target
.
result
;
// 获取 Base64 数据
resolve
(
base64
);
// 返回 Base64 数据
};
reader
.
readAsDataURL
(
file
);
// 读取文件并转换为 Base64
},
function
(
error
)
{
reject
(
"获取文件对象失败:"
+
error
.
message
);
}
);
},
function
(
error
)
{
reject
(
"解析文件路径失败:"
+
error
.
message
);
}
);
});
},
// 照片数据处理
addPhotos
(
photos
,
title
,
type
)
{
// 传入数据 文案 操作类型
// let deviceData = this.$store.state.deviceData; //获取全部数据
// deviceData = photos;
// 写入设备上架文件
this
.
$store
.
commit
(
"SET_DEVICEDATA"
,
photos
);
writeDeviceData
(
photos
,
this
.
userInfo
.
user
);
// 日志
let
logContent
=
getLogContent
(
LOG_TYPE_ENUM
.
edit
,
title
,
type
);
const
log_list
=
this
.
$store
.
state
.
log_list
;
logContent
.
inspectionType
=
"3"
;
log_list
.
push
(
logContent
);
this
.
$store
.
commit
(
"SET_LOG_LIST"
,
log_list
);
addLog
(
log_list
).
then
((
res
)
=>
{
console
.
log
(
"日志文件写入成功"
);
});
// 日志
console
.
log
(
"photos"
,
photos
,
logContent
);
},
// 从路径中提取文件名(去掉路径和扩展名)
getFileName
(
filePath
)
{
// 获取路径中的文件名部分(包括扩展名)
const
fileNameWithExt
=
filePath
.
split
(
"/"
).
pop
();
// 去掉扩展名
const
fileName
=
fileNameWithExt
.
split
(
"."
).
shift
();
return
fileName
;
},
// 根据当前行的日期获取照片集合
filteredData
(
data
,
group
,
targetDate
)
{
let
filteredData
=
data
&&
data
.
filter
((
item
)
=>
item
.
date
===
targetDate
);
return
filteredData
.
length
==
group
.
length
;
},
// 切换选中状态
toggleSelect
(
photo
)
{
photo
.
selected
=
!
photo
.
selected
;
if
(
photo
.
selected
)
{
this
.
selectedPhotos
.
push
(
photo
);
}
else
{
this
.
selectedPhotos
=
this
.
selectedPhotos
.
filter
(
(
p
)
=>
p
.
id
!==
photo
.
id
);
}
},
// 全选/取消全选
toggleSelectAll
(
date
)
{
const
group
=
this
.
photoGroups
[
date
];
const
allSelected
=
group
.
every
((
photo
)
=>
photo
.
selected
);
group
.
forEach
((
photo
)
=>
{
photo
.
selected
=
!
allSelected
;
if
(
photo
.
selected
)
{
this
.
selectedPhotos
.
push
(
photo
);
}
else
{
this
.
selectedPhotos
=
this
.
selectedPhotos
.
filter
(
(
p
)
=>
p
.
id
!==
photo
.
id
);
}
});
console
.
log
(
this
.
selectedPhotos
);
},
// 删除照片
deletePhoto
(
photo
)
{
this
.
photos
=
this
.
photos
.
filter
((
p
)
=>
p
.
id
!==
photo
.
id
);
this
.
selectedPhotos
=
this
.
selectedPhotos
.
filter
(
(
p
)
=>
p
.
id
!==
photo
.
id
);
this
.
addPhotos
(
this
.
photos
,
"删除照片"
,
"删除"
);
},
// 预览照片
previewPhoto
(
photo
)
{
uni
.
previewImage
({
urls
:
this
.
photos
.
map
((
p
)
=>
p
.
url
),
current
:
photo
.
url
,
});
},
openDialog
(
show
)
{
this
.
isDialog
=
show
;
if
(
!
show
)
{
this
.
getDetails
();
}
},
// 数据同步
syncData
()
{
const
syncedData
=
this
.
selectedPhotos
.
map
((
photo
)
=>
({
id
:
photo
.
id
,
time
:
photo
.
time
,
synchronization
:
true
,
status
:
photo
.
synchronization
?
"已同步"
:
"待同步"
,
}));
// 存储到本地
uni
.
setStorageSync
(
"syncedPhotos"
,
syncedData
);
// 更新同步状态
this
.
selectedPhotos
.
forEach
((
photo
)
=>
{
photo
.
synchronization
=
true
;
});
this
.
selectedPhotos
=
[];
},
},
};
</
script
>
<
style
lang=
"less"
scoped
>
/* 导航栏样式--- */
.uni-nav-bar-text {
height: 36px;
width: 36px;
background: #ffffff;
border: 0.4px solid rgba(224, 224, 224, 1);
border-radius: 18px;
border-radius: 50%;
color: #333;
text-align: center;
.iconfont {
font-size: 20px;
line-height: 36px;
}
}
.nav-right {
width: 240px;
}
/* 导航栏样式--- */
.uni-nav-bar-text {
height: 36px;
width: 36px;
background: #ffffff;
border: 0.4px solid rgba(224, 224, 224, 1);
border-radius: 18px;
border-radius: 50%;
color: #333;
text-align: center;
.header-buttons {
display: flex;
align-items: center;
margin-left: auto; // 将按钮组推到最右侧
.iconfont {
font-size: 20px;
line-height: 36px;
}
}
.button {
width: 112px;
height: 36px;
background: #FFFFFF;
border-radius: 18px;
margin-left: 16px;
font-family: PingFangSC-Regular;
font-size: 16px;
color: #000000;
line-height: 36px;
font-weight: 400;
}
}
.nav-right {
width: 240px;
}
/* 导航栏样式--- */
.header-buttons {
display: flex;
align-items: center;
margin-left: auto; // 将按钮组推到最右侧
.container {
background-image: linear-gradient(115deg, #E8F0FB 0%, #E1EBFA 100%);
padding: 0 32px;
height: 100vh;
.button {
width: 112px;
height: 36px;
background: #ffffff;
border-radius: 18px;
margin-left: 16px;
font-family: PingFangSC-Regular;
font-size: 16px;
color: #000000;
line-height: 36px;
font-weight: 400;
}
}
}
/* 导航栏样式--- */
.head
er {
display: flex
;
justify-content: space-between
;
align-items: center
;
margin-bottom: 16px;
.contain
er {
background-image: linear-gradient(115deg, #e8f0fb 0%, #e1ebfa 100%)
;
// padding: 0 32px
;
height: 100vh
;
}
.title {
display: flex;
align-items: center;
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.blue-line {
width: 4px;
height: 20px;
background-color: blue;
margin-right: 8px;
}
}
.title {
display: flex;
align-items: center;
.buttons {
display: flex;
.blue-line {
width: 4px;
height: 20px;
background-color: blue;
margin-right: 8px;
}
}
.btn {
width: 112px;
height: 36px;
border-radius: 18px;
margin-left: 8px;
}
}
}
.buttons {
display: flex;
.selected-count {
background-color: #f0f0f0;
padding: 8px;
border-radius: 8px;
margin-bottom: 16px;
}
.btn {
width: 112px;
height: 36px;
border-radius: 18px;
margin-left: 8px;
}
}
}
.photo-list {
.photo-group {
margin-bottom: 16px;
.selected-count {
background-color: #f0f0f0;
padding: 8px;
border-radius: 8px;
margin-bottom: 16px;
}
.group-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
.photo-list {
padding: 24px 0;
.photo-group {
margin-bottom: 16px;
.select-all {
font-size: 14px;
color: blue;
}
}
.group-header {
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 8px;
padding: 0 32px;
.date {
font-size: 26px;
color: #000000;
line-height: 18px;
font-weight: 500;
margin-left: 8px;
}
}
.card-list {
display: flex;
flex-wrap: wrap;
gap: 16
px;
.card-list {
display: flex;
flex-wrap: wrap;
gap: 8
px;
.card {
width: calc(20% - 13px);
position: relative;
.card {
width: 210px;
height: 236px;
margin-bottom: 22px;
position: relative;
.photo {
width: 100%;
height: auto;
border-radius: 8px;
}
.photo {
width: 210px;
height: 210px;
}
.check-icon {
position: absolute;
top: 8px;
righ
t: 8px;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
}
.check-icon {
position: absolute;
top: 8px;
lef
t: 8px;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
}
.delete-icon {
position: absolute;
bottom: 8px;
left: 8px;
background-color: rgba(255, 0, 0, 0.8);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
color: white;
}
.delete-icon {
position: absolute;
bottom: 8px;
left: 8px;
background-color: rgba(255, 0, 0, 0.8);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
color: white;
}
.type {
width: 52px;
padding: 0 8px;
text-align: center;
background: rgba(0, 0, 0, 0.5);
border-radius: 11px;
font-size: 12px;
color: #ffffff;
text-align: left;
line-height: 20px;
font-weight: 400;
position: absolute;
right: 13.5px;
top: 8px;
}
.photo-info {
text-align: center;
.photo-info {
margin-top: 8px;
text-align: center;
text {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #000000;
line-height: 22px;
font-weight: 400;
}
}
}
}
}
}
// 按钮
.inspection-button {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
bottom: 60px;
width: 280px;
height: 48px;
left: 50%;
transform: translateX(-50%);
background-image: linear-gradient(180deg, #3773f6 0%, #2c57f6 99%);
box-shadow: 0px 10px 24px 0px rgba(51, 104, 246, 0.24);
border-radius: 27px;
font-family: PingFangSC-Regular;
font-size: 20px;
line-height: 48px;
color: #ffffff;
text-align: center;
font-weight: 400;
}
text {
display: blo980782ck;
font-size: 12px;
}
}
}
}
}
}
</
style
>
\ No newline at end of file
/* 圆形按钮样式 */
.circle-button {
width: 18px;
height: 18px;
border-radius: 50%;
border: 1.14px solid rgba(199, 199, 199, 1);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
/* 选中状态样式 */
&.active {
border-color: #007aff;
background-color: #3774f6;
}
/* 内部小圆点 */
.inner-circle {
width: 18px;
height: 18px;
border-radius: 50%;
margin-right: 8px;
}
}
</
style
>
request/index.js
浏览文件 @
c3cdfd16
...
...
@@ -2,7 +2,8 @@ import store from "../store/index";
import
{
readLogData
,
readInspectionData
,
readDarfData
readDarfData
,
readDeviceData
}
from
"@/utils/IoReadingAndWriting.js"
;
import
lodash
from
"lodash"
;
...
...
@@ -98,4 +99,47 @@ export const getDarft = () => {
reject
(
error
)
})
})
}
// 获取设备上架所有图片信息
export
const
getAllDeviceData
=
()
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
console
.
log
(
store
.
state
.
deviceData
.
length
)
if
(
store
.
state
.
deviceData
.
length
){
const
tempRes
=
lodash
.
orderBy
(
store
.
state
.
deviceData
,
"creatTime"
,
[
'desc'
]);
const
result
=
lodash
.
orderBy
(
tempRes
,
"synchronization"
,
[
'asc'
]);
resolve
(
result
)
return
;
}
readDeviceData
()
.
then
((
res
)
=>
{
const
tempRes
=
lodash
.
orderBy
(
res
,
"creatTime"
,
[
'desc'
]);
const
result
=
lodash
.
orderBy
(
tempRes
,
"synchronization"
,
[
'asc'
]);
store
.
commit
(
"SET_DEVICEDATA"
,
result
);
resolve
(
result
)
})
.
catch
((
error
)
=>
{
reject
(
error
)
})
})
}
/**
*
* 获取设备上架数据详情
* @returns
*/
export
const
getDeviceDataDetails
=
()
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
getAllDeviceData
()
.
then
((
res
)
=>
{
const
detailsInfo
=
res
resolve
(
detailsInfo
)
})
.
catch
((
error
)
=>
{
reject
(
error
)
})
})
}
\ No newline at end of file
store/index.js
浏览文件 @
c3cdfd16
...
...
@@ -72,7 +72,8 @@ const store = new Vuex.Store({
// 保存 日志文件
state
.
log_list
=
payload
;
},
SET_DEVICEDATA
(
state
,
payload
)
{
//保存上次上报时间
SET_DEVICEDATA
(
state
,
payload
)
{
//保存设备上架数据
state
.
deviceData
=
payload
},
DEL_DEVICEDATA
(
state
,
index
)
{
...
...
utils/IoReadingAndWriting.js
浏览文件 @
c3cdfd16
...
...
@@ -23,10 +23,11 @@ import { Base64 } from "js-base64";
export
const
LOG_TYPE_ENUM
=
{
login
:
"登录"
,
add
:
"新增成功"
,
photo
:
"拍照"
,
edit
:
"编辑成功"
,
darf
:
"草稿"
,
delete
:
"删除"
,
sys
:
"同步
数据
"
,
sys
:
"同步"
,
updatePassword
:
"修改密码"
,
};
...
...
@@ -192,6 +193,58 @@ export const readInspectionData = () => {
.
catch
((
error
)
=>
reject
(
error
));
});
};
/**
* // 写入【设备上架】文件
* @param {*} content 写入内容
* @param {*} isEncryption 是否加密
* @returns
*/
export
const
writeDeviceData
=
(
content
,
userName
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
let
fileName
=
`list.txt`
;
const
fileContent
=
Base64
.
encode
(
JSON
.
stringify
(
content
));
createFileWithPlusIO
(
`
${
SYNCHRONIZE_DATA_PAD
}
/设备上架/
${
userName
}
`
,
fileName
,
fileContent
).
then
((
res
)
=>
{
// store 更新 设备上架. 缓存数据置为空,让接口重新读取文件数据
store
.
commit
(
"SET_DEVICEDATA"
,
[]);
resolve
();
});
});
};
// 读取【设备上架】文件
export
const
readDeviceData
=
()
=>
{
const
now_user
=
store
.
state
.
now_user
;
console
.
log
(
"now_user"
,
now_user
)
const
isAdmin
=
now_user
.
isAdmin
;
// const directoryPath = isAdmin
// ? `${SYNCHRONIZE_DATA_PAD}/设备上架`
// : `${SYNCHRONIZE_DATA_PAD}/设备上架/${now_user.user}`;
const
directoryPath
=
`
${
SYNCHRONIZE_DATA_PAD
}
/设备上架/
${
now_user
.
user
}
`
;
console
.
log
(
"directoryPath"
,
directoryPath
)
return
new
Promise
((
resolve
,
reject
)
=>
{
readFilesInDirectory
(
directoryPath
)
.
then
((
res
)
=>
{
console
.
log
(
"directoryPathres"
,
res
)
const
temp
=
res
.
map
((
element
)
=>
{
return
JSON
.
parse
(
Base64
.
decode
(
element
));
});
resolve
(
lodash
.
flattenDeep
(
temp
));
})
.
catch
((
error
)
=>
reject
(
error
));
});
};
// 复制文件夹中的所有数据到另一个文件夹
/**
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论