Commit 4ef34e45 authored by 李苏's avatar 李苏 💬

1.erpapp的样式变更

首页样式调整
登录样式调整
个人调整
表单调整
流程调整
其他样式调整
parent a7a0833f
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
"version": "0.0",
"configurations": [{
"app-plus" :
{
"launchtype" : "local"
},
"default" :
{
"launchtype" : "local"
},
"h5" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
onLaunch: function() { onLaunch: function() {
uni.getSystemInfo({ uni.getSystemInfo({
success: function(e) { success: function(e) {
// #ifdef APP-PLUS // #ifdef APP-PLUS
// 检测升级 // 检测升级
appUpdate() appUpdate()
...@@ -155,6 +154,36 @@ ...@@ -155,6 +154,36 @@
@import "plugin/colorui/main.css"; @import "plugin/colorui/main.css";
@import "plugin/colorui/icon.css"; @import "plugin/colorui/icon.css";
@import "plugin/colorui/animation.css"; @import "plugin/colorui/animation.css";
.lccontent {
padding: 60rpx;
padding-left:20%;
.row {
padding: 24rpx 0;
}
}
.bg-blue{
background-color: #0081FF!important;
color: #FFFFFF!important;
}
.newts{
text-align: center;font-size: 16px!important;
}
.newcont{
text-align: center;font-size: 15px!important;
}
.wid100{
width: 100%!important;
}
.tkqx{
width: 50%;background-color: #FFFFFF!important;color: #666;font-size: 16px!important;font-weight: 800;
}
.tkqd{
width: 50%;background-color: #FFFFFF!important;color: #0081FF;font-size: 16px!important;font-weight: 800;
}
.cuIcon-close{
display: none;
}
.nav-list { .nav-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
......
File added
File added
File added
File added
File added
...@@ -9,20 +9,16 @@ const router = new Router({ ...@@ -9,20 +9,16 @@ const router = new Router({
encodeURI:true, encodeURI:true,
routes: [...modules]//路由表 routes: [...modules]//路由表
}); });
console.log(router)
const whiteList = ['/pages/login/login'] const whiteList = ['/pages/login/login']
//全局路由前置守卫 //全局路由前置守卫
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
console.log("to")
console.log(to)
console.log("from")
console.log(from) console.log(from)
console.log("next")
console.log(next)
let token=uni.getStorageSync("ACCESS_TOKEN"); let token=uni.getStorageSync("ACCESS_TOKEN");
console.log(token)
if(token){ if(token){
next() next()
}else if(to.name=="fwqsz"){
next()
}else{ }else{
if (whiteList.indexOf(to.path) !== -1) { if (whiteList.indexOf(to.path) !== -1) {
next() next()
......
...@@ -6,6 +6,13 @@ const routes = [ ...@@ -6,6 +6,13 @@ const routes = [
title: '登录', title: '登录',
}, },
}, },
{
path: "/pages/login/fwqsz",
name: 'fwqsz',
meta: {
title: '服务器设置',
},
},
{ {
//注意:path必须跟pages.json中的地址对应,最前面别忘了加'/'哦 //注意:path必须跟pages.json中的地址对应,最前面别忘了加'/'哦
path: '/pages/index/index', path: '/pages/index/index',
...@@ -30,6 +37,13 @@ const routes = [ ...@@ -30,6 +37,13 @@ const routes = [
title: '个人中心', title: '个人中心',
}, },
}, },
{
path: '/pages/user/setting',
name: 'setting',
meta: {
title: '用户设置',
},
},
{ {
path: '/pages/user/userdetail', path: '/pages/user/userdetail',
name: 'userdetail', name: 'userdetail',
......
let BASE_URL = '' let BASE_URL = ''
let fwq="" let fwq=""
let dkh=""
// 修改 // 修改
try { try {
fwq = uni.getStorageSync('fwq'); fwq = uni.getStorageSync('fwq');
dkh = uni.getStorageSync('dkh');
console.log(fwq)
console.log(dkh)
} catch (e) { } catch (e) {
// error // error
} }
let newUrl = 'http://'+fwq+'/'+dkh let newUrl = 'http://'+fwq
console.log(BASE_URL) console.log(BASE_URL)
if (process.env.NODE_ENV == 'development') { if (process.env.NODE_ENV == 'development') {
BASE_URL = newUrl BASE_URL = newUrl
......
...@@ -180,29 +180,29 @@ export const us = { ...@@ -180,29 +180,29 @@ export const us = {
system:[ system:[
{ {
title:"采购计划审批", title:"采购计划审批",
icon:icon_prefix+"kaoqing.png", icon:icon_prefix+"ztnew.png",
description:"采购计划审批", description:"采购计划审批",
useCount:1000, useCount:1000,
page:'cgjh' page:'cgjh'
}, },
{ {
title:"领料单审批", title:"领料单审批",
icon:icon_prefix+"qiandao.png", icon:icon_prefix+"renwu.png",
description:"签到", description:"领料单审批",
useCount:1000, useCount:1000,
page:'lldsp' page:'lldsp'
}, },
{ {
title:"采购订单审批", title:"采购订单审批",
icon:icon_prefix+"qingjia2.png", icon:icon_prefix+"hetong.png",
description:"请假", description:"采购订单审批",
useCount:1000, useCount:1000,
page:'cgddsp' page:'cgddsp'
}, },
{ {
title:"销售订单审批", title:"销售订单审批",
icon:icon_prefix+"waichu.png", icon:icon_prefix+"liucheng.png",
description:"外出", description:"销售订单审批",
useCount:1000, useCount:1000,
page:'xsddsp' page:'xsddsp'
}, },
......
<template>
<view class="sidebar">
<view class="sidebar_tab">
<view class="sidebar_ico">
</view>
<view class="sidebar_title">
{{title}}
</view>
</view>
<slot></slot>
</view>
</template>
<script>
export default {
name: 'm-sidebar',
props:{
title: String,
},
data() {
return{}
}
}
</script>
<style lang="scss" scoped>
.sidebar_tab{
display: flex;
align-items: center;
.sidebar_ico{
border-width: 0px;
width: 2px;
height: 18px;
background: inherit;
background-color: rgba(19, 98, 253, 1);
border: none;
border-radius: 2px;
box-shadow: none;
}
.sidebar_title{
margin-left: 20rpx;
font-weight: bold;
font-size: 32rpx;
}
}
</style>
<template>
<view class="steps">
<view class="date">
<view class="">
<text>{{item[date] | formatTime('yyyy-MM-dd')}}</text>
<slot name='dateTop'></slot>
</view>
<!-- <view class="">
<text>{{item[date] | formatTime('hh:mm:ss')}}</text>
<slot name='dateBot'></slot>
</view> -->
</view>
<view class="tail" :class="{'active-tail':activity === index}"></view>
<view class="node" :class="{'active-node':activity === index}"></view>
<view class="wrapper">
<view class="">
<text>{{item[wrapperStatus]}}</text>
<slot name='status'></slot>
</view>
<view class="wrapperTitle">
<text>{{wrapperTitle}}</text>
<slot name='content'></slot>
</view>
</view>
</view>
</template>
<script>
/*
activity: 当前进度条状态
wrapperStatus:流程状态对应字段
wrapperTitle:详情对应字段
index:进度条
date:时间
*/
export default {
name: 'm-steps',
data() {
return {
}
},
filters: {
filterzt:function(a){
const ztList = {"P": "计划", "F":"确认","C":"完成","I":"开始","S":"审批"};
return ztList[a]
},
jqfilters:function(a){
const ztList = {"BJ": "病假", "SJ":"事假","JB":"加班","TS":"调休","QT":"其他"};
return ztList[a]
},
ztTrans: function(a) {
const ztList = {0: "创建", 1:"提交",2:"退回",3:"撤回",4:"转交",5:"调度",8:"待处理"};
return ztList[a]
},
formatTime: function(time) {
if(time!=null&&time!="")
{
var date = new Date(time);
return date.Format("MM-dd hh:mm")
}else{
return "";
}
},
formatDate:function(time){
if(time!=null&&time!="")
{
var date = new Date(time);
return date.Format("yyyy-MM-dd")
}else{
return "";
}
}
},
props: {
item: {
type: Object,
},
activity: {
type: Number,
default: 0
},
wrapperStatus: {
type: String,
default: 'status'
},
wrapperTitle: {
type: String,
default: 'content'
},
date: {
type: String,
default: 'date'
},
index: {
type: Number,
default: 0
}
}
}
</script>
<style lang="scss" scoped>
.steps {
position: relative;
padding-bottom: 40rpx;
&:last-child {
.tail {
display: none;
}
}
.date {
position: absolute;
top: -6rpx;
color: #AAAAAA;
text-align: center;
font-size: 30rpx;
}
.active-tail {
border-left: 2px solid rgb(11, 189, 135) !important
}
.active-node {
background-color: rgb(11, 189, 135) !important;
}
.tail {
position: absolute;
left: 200rpx;
height: 100%;
border-left: 4rpx solid rgb(11, 189, 135)
}
.node {
left: 190rpx;
width: 24rpx;
height: 24rpx;
position: absolute;
background-color: rgb(11, 189, 135);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.wrapper {
position: relative;
padding-left: 240rpx;
top: -6rpx;
.wrapperTitle{
color: #AAAAAA;
}
}
}
</style>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title class="title">[文件管理器]</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<style type="text/css">
.content {background: transparent;}
.fixed {position: fixed;bottom: 0;left: 0;right: 0;width: 100%;}
.content .mask {top: 0;background: rgba(0,0,0,.4);z-index: 90;}
.content .file-content {z-index: 91;height: 60px;background: #fff;text-align: center;}
.btn {position: relative;}
.btn .file {position: absolute;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;height: 60px;width: 100%;opacity: 0;}
.btn-bg {margin-top: 10px;background: #0066CC;color: #fff;width: 80%;height: 40px;border: 0;border-radius: 5px;}
.tis {top: 0;z-index: 95;display: none;justify-content: center;align-items: center;}
.tis .tis-content {background: #fff;width: 60%;border-radius: 10px;padding: 20px 0;}
.tis .tis-content img {width: 50px;height: 50px;}
.tis-progress {margin: 10px 0;color: #999;}
.cancel-btn {margin-top: 30px;height: 30px;line-height: 1;padding: 0 2em;background: #e3e3e3;color: #898989;border: 0;border-radius: 5px;}
</style>
</head>
<body>
<div class="content">
<div class="fixed mask"></div>
<div align="center" class="fixed tis">
<div class="tis-content">
<div class="logo"></div>
<div class="tis-progress">
努力上传中..
</div>
<div class="cancel">
<button type="button" class="cancel-btn">取消上传</button>
</div>
</div>
</div>
<div class="fixed file-content">
<div class="btn">
<button type="button" class="btn-bg">打开文件管理器</button>
<input class="file" type="file" />
</div>
</div>
</div>
<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
<script src="js/h5-uploader.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
let mask = document.querySelector(".mask");
let fileDom = document.querySelector(".file");
let tis = document.querySelector(".tis");
let logoDom = document.querySelector(".logo");
let progress = document.querySelector(".tis-progress");
let cancel = document.querySelector(".cancel-btn");
let createUpload = (file,{ url, key='file', header = {},data = {},addName,addSize}) => {
console.log(`
上传地址:${url}\n
上传附件:${file.name} 附件大小:${file.size}\n
请求头:${JSON.stringify(header)}\n
业务参数:${JSON.stringify(data)}
`);
if (!url) {return;}
tis.style.display = 'flex';
let formData = new FormData();
for (let keys in data) {formData.append(keys, data[keys]);}
if (addName) {formData.append(addName, file.name);console.log('补充参数:',JSON.stringify({addName:file.name}));}
if (addSize) {formData.append(addSize, file.size);console.log('补充参数:',JSON.stringify({addSize:file.size}));}
formData.append(key, file);
let xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
for (let keys in header) {
xhr.setRequestHeader(keys, header[keys]);
}
xhr.upload.addEventListener("progress", function(event) {
if(event.lengthComputable){
let percent = Math.ceil(event.loaded * 100 / event.total) + "%";
progress.innerText = `努力上传中..${percent}`;
}
}, false);
xhr.ontimeout = function(){
// xhr请求超时事件处理
progress.innerText = '请求超时';
setTimeout(()=>{
tis.style.display = 'none';
plus.webview.currentWebview().close();
},1000);
};
xhr.onreadystatechange = (ev) => {
if(xhr.readyState == 4) {
console.log('status:'+xhr.status);
if (xhr.status == 200) {
progress.innerText = '上传成功';
console.log('服务端返回数据:'+xhr.responseText);
location.href = `callback?fileName=${escape(file.name)}&size=${file.size}&str=${escape(xhr.responseText)}`;
}
else {
progress.innerText = '上传失败了';
if (xhr.status == 0) {
console.log('请检查请求头Content-Type与服务端是否匹配,并确认服务端已正确开启跨域');
}
}
setTimeout(()=>{
tis.style.display = 'none';
plus.webview.currentWebview().close();
},1000);
}
};
xhr.send(formData);
cancel.addEventListener("click", ()=>{
xhr.abort();
plus.webview.currentWebview().close();
});
}
mask.addEventListener("click", () => {
plus.webview.currentWebview().close();
});
document.addEventListener('UniAppJSBridgeReady', () => {
let {url,key,header,formData,logo,maxSize,addName,addSize} = plus.webview.currentWebview();
if (logo) {
let img = document.createElement('img');
img.src = logo;
logoDom.appendChild(img);
}
fileDom.value = '';
fileDom.addEventListener('change', (event) => {
let file = fileDom.files[0];
// 默认限制文件小于10M,可自行修改
if(file.size > (1024*1024 * Math.abs(maxSize))) {
plus.nativeUI.toast(`单个文件请勿超过${maxSize}M,请重新上传`);
return;
}
createUpload(file,{ url, key,header,addName,addSize,formData});
}, false);
});
\ No newline at end of file
{ {
"name" : "格物OA", "name" : "格物ERP",
"appid" : "__UNI__1E6036D", "appid" : "__UNI__1E6036D",
"description" : "", "description" : "",
"versionName" : "1.0.0", "versionName" : "1.0.0",
...@@ -8,8 +8,7 @@ ...@@ -8,8 +8,7 @@
"app-plus" : { "app-plus" : {
/* 5+App特有相关 */ /* 5+App特有相关 */
"modules" : { "modules" : {
"Maps" : {}, "Maps" : {}
"Push" : {}
}, },
/* 模块配置 */ /* 模块配置 */
"distribute" : { "distribute" : {
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
"path": "pages/basics/layout", "path": "pages/basics/layout",
"style": {} "style": {}
}, },
{
"path": "pages/index/index",
"style": {}
},
{ {
"path": "pages/basics/background", "path": "pages/basics/background",
"style": {} "style": {}
...@@ -256,6 +260,14 @@ ...@@ -256,6 +260,14 @@
{ {
"path": "pages/xsddsp/allcklc", "path": "pages/xsddsp/allcklc",
"style": {} "style": {}
},
{
"path": "pages/login/fwqsz",
"style": {}
},
{
"path": "pages/user/setting",
"style": {}
} }
], ],
"globalStyle": { "globalStyle": {
......
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
<view>备注:<text>{{item.bz||"未备注"}}</text></view> <view>备注:<text>{{item.bz||"未备注"}}</text></view>
</view> </view>
<view class="flex justify-between" style="padding-top: 10px;margin-bottom: 0px;"> <view class="flex justify-between" style="padding-top: 10px;margin-bottom: 0px;">
<view >状态: <view style="font-weight: 600;">状态:
<text style="color:#FBBD08;"> <text style="color:#FBBD08;">
{{item.zt|filterzt}} {{item.zt|filterzt}}
</text> </text>
...@@ -97,19 +97,15 @@ ...@@ -97,19 +97,15 @@
<!-- 转交确认模态框 --> <!-- 转交确认模态框 -->
<view class="cu-modal" :class="modalName=='zj'?'show':''"> <view class="cu-modal" :class="modalName=='zj'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts">提示
<view class="content">提示</view>
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否转交选中单号请求? 是否转交选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="tkqx cu-btn " @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrzj">确定</button> <button class="tkqd cu-btn " @tap="qrzj">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -117,19 +113,16 @@ ...@@ -117,19 +113,16 @@
<!-- 退回确认模态框 --> <!-- 退回确认模态框 -->
<view class="cu-modal" :class="modalName=='th'?'show':''"> <view class="cu-modal" :class="modalName=='th'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts" style="" >
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否退回选中单号请求? 是否退回选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrth">确定</button> <button class="cu-btn tkqd" @tap="qrth">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -137,19 +130,16 @@ ...@@ -137,19 +130,16 @@
<!-- 撤回确认模态框 --> <!-- 撤回确认模态框 -->
<view class="cu-modal" :class="modalName=='ch'?'show':''"> <view class="cu-modal" :class="modalName=='ch'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts" style="" >
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否撤回选中单号请求? 是否撤回选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white" >
<view class="action"> <view class="action flex justify-around wid100" >
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrch">确定</button> <button class="cu-btn tkqd" @tap="qrch">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -157,19 +147,16 @@ ...@@ -157,19 +147,16 @@
<!-- 提交确认模态框 --> <!-- 提交确认模态框 -->
<view class="cu-modal" :class="modalName=='tj'?'show':''"> <view class="cu-modal" :class="modalName=='tj'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts">
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否提交选中单号请求? 是否提交选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrtj">确定</button> <button class="cu-btn tkqd" @tap="qrtj">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -215,7 +202,7 @@ ...@@ -215,7 +202,7 @@
</view> </view>
<!-- 事务处理 --> <!-- 事务处理 -->
<!-- 转交模态框 --> <!-- 转交模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='zjmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='zjmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body" > <view class="example-body" >
...@@ -269,7 +256,7 @@ ...@@ -269,7 +256,7 @@
</view> </view>
</view> </view>
<!-- 提交模态框 --> <!-- 提交模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='tjmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='tjmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body" > <view class="example-body" >
...@@ -323,7 +310,7 @@ ...@@ -323,7 +310,7 @@
</view> </view>
</view> </view>
<!-- 撤回模态框 --> <!-- 撤回模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='chmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='chmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body" > <view class="example-body" >
...@@ -377,7 +364,7 @@ ...@@ -377,7 +364,7 @@
</view> </view>
</view> </view>
<!-- 撤回模态框 --> <!-- 撤回模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='thmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='thmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body" > <view class="example-body" >
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
<view>生成方式:<text>{{transScfs(item.djly)}}</text></view> <view>生成方式:<text>{{transScfs(item.djly)}}</text></view>
</view> </view>
<view class="flex justify-between" style="padding-top: 10px;margin-bottom: 0px;"> <view class="flex justify-between" style="padding-top: 10px;margin-bottom: 0px;">
<view >状态: <view style="font-weight: 600;" >状态:
<text style="color:#FBBD08;"> <text style="color:#FBBD08;">
{{item.zt|filterzt}} {{item.zt|filterzt}}
</text> </text>
...@@ -62,19 +62,15 @@ ...@@ -62,19 +62,15 @@
<!-- 转交确认模态框 --> <!-- 转交确认模态框 -->
<view class="cu-modal" :class="modalName=='zj'?'show':''"> <view class="cu-modal" :class="modalName=='zj'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts">提示
<view class="content">提示</view>
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否转交选中单号请求? 是否转交选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="tkqx cu-btn " @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrzj">确定</button> <button class="tkqd cu-btn " @tap="qrzj">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -82,19 +78,16 @@ ...@@ -82,19 +78,16 @@
<!-- 退回确认模态框 --> <!-- 退回确认模态框 -->
<view class="cu-modal" :class="modalName=='th'?'show':''"> <view class="cu-modal" :class="modalName=='th'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts" style="" >
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否退回选中单号请求? 是否退回选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrth">确定</button> <button class="cu-btn tkqd" @tap="qrth">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -102,19 +95,16 @@ ...@@ -102,19 +95,16 @@
<!-- 撤回确认模态框 --> <!-- 撤回确认模态框 -->
<view class="cu-modal" :class="modalName=='ch'?'show':''"> <view class="cu-modal" :class="modalName=='ch'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts" style="" >
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否撤回选中单号请求? 是否撤回选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white" >
<view class="action"> <view class="action flex justify-around wid100" >
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrch">确定</button> <button class="cu-btn tkqd" @tap="qrch">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -122,19 +112,16 @@ ...@@ -122,19 +112,16 @@
<!-- 提交确认模态框 --> <!-- 提交确认模态框 -->
<view class="cu-modal" :class="modalName=='tj'?'show':''"> <view class="cu-modal" :class="modalName=='tj'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts">
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否提交选中单号请求? 是否提交选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrtj">确定</button> <button class="cu-btn tkqd" @tap="qrtj">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -180,7 +167,7 @@ ...@@ -180,7 +167,7 @@
</view> </view>
<!-- 事务处理 --> <!-- 事务处理 -->
<!-- 转交模态框 --> <!-- 转交模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='zjmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='zjmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body" > <view class="example-body" >
...@@ -234,7 +221,7 @@ ...@@ -234,7 +221,7 @@
</view> </view>
</view> </view>
<!-- 提交模态框 --> <!-- 提交模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='tjmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='tjmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body" > <view class="example-body" >
...@@ -288,7 +275,7 @@ ...@@ -288,7 +275,7 @@
</view> </view>
</view> </view>
<!-- 撤回模态框 --> <!-- 撤回模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='chmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='chmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body" > <view class="example-body" >
...@@ -342,7 +329,7 @@ ...@@ -342,7 +329,7 @@
</view> </view>
</view> </view>
<!-- 撤回模态框 --> <!-- 撤回模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='thmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='thmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body" > <view class="example-body" >
......
...@@ -92,17 +92,14 @@ ...@@ -92,17 +92,14 @@
</view> </view>
<view class="cu-form-group padding-bottom "> <view class="cu-form-group padding-bottom ">
<view > <view >
<view class="cu-timeline " v-for="(item,index) in lc" :key="index"> <view class="lccontent">
<view class="cu-time" style="width: 80px;">{{item.nodeName}}</view> <mSidebar title="进度详情">
<view class="cu-item text-gray"> <view class="row">
<view class="bg-blue content" v-if="item.nodeName!='结束'" > <m-steps :wrapperTitle='(item.actionUserName+getAction(item.action))||""' v-for="(item, index) in lc" :item='item' :key="index" :index='index' :activity= 'activity' date='actionTime' >
<text></text> {{item.actionTime|formatTime(item.actionTime)}}{{item.actionUserName}}{{item.action|ztTrans(item.action)}} <text slot="status">{{item.nodeName}}</text>
</m-steps>
</view> </view>
<view class="bg-blue content" v-if="item.nodeName=='结束'"> </mSidebar>
<text></text> {{'结束'}}
</view>
</view>
</view> </view>
</view> </view>
</view> </view>
...@@ -188,6 +185,10 @@ ...@@ -188,6 +185,10 @@
}, },
}, },
methods: { methods: {
getAction: function(a) {
const ztList = {0: "创建", 1:"提交",2:"退回",3:"撤回",4:"转交",5:"调度",8:"待处理"};
return ztList[a]
},
// 流程 // 流程
apigetlc(){ apigetlc(){
let url="wf/query/wflog/"+this.cgjh.wfinstid let url="wf/query/wflog/"+this.cgjh.wfinstid
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
<view>需求日期:<text>{{item.xqrq|formatDate}}</text></view> <view>需求日期:<text>{{item.xqrq|formatDate}}</text></view>
</view> </view>
<view class="flex justify-between" style="padding-top: 10px;margin-bottom: 0px;"> <view class="flex justify-between" style="padding-top: 10px;margin-bottom: 0px;">
<view >状态: <view style="font-weight: 600;">状态:
<text style="color:#FBBD08;"> <text style="color:#FBBD08;">
{{item.zt|filterzt}} {{item.zt|filterzt}}
</text> </text>
...@@ -66,19 +66,15 @@ ...@@ -66,19 +66,15 @@
<!-- 转交确认模态框 --> <!-- 转交确认模态框 -->
<view class="cu-modal" :class="modalName=='zj'?'show':''"> <view class="cu-modal" :class="modalName=='zj'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts">提示
<view class="content">提示</view>
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否转交选中单号请求? 是否转交选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="tkqx cu-btn " @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrzj">确定</button> <button class="tkqd cu-btn " @tap="qrzj">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -86,19 +82,16 @@ ...@@ -86,19 +82,16 @@
<!-- 退回确认模态框 --> <!-- 退回确认模态框 -->
<view class="cu-modal" :class="modalName=='th'?'show':''"> <view class="cu-modal" :class="modalName=='th'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts" style="" >
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否退回选中单号请求? 是否退回选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrth">确定</button> <button class="cu-btn tkqd" @tap="qrth">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -106,19 +99,16 @@ ...@@ -106,19 +99,16 @@
<!-- 撤回确认模态框 --> <!-- 撤回确认模态框 -->
<view class="cu-modal" :class="modalName=='ch'?'show':''"> <view class="cu-modal" :class="modalName=='ch'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts" style="" >
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否撤回选中单号请求? 是否撤回选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white" >
<view class="action"> <view class="action flex justify-around wid100" >
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrch">确定</button> <button class="cu-btn tkqd" @tap="qrch">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -126,19 +116,16 @@ ...@@ -126,19 +116,16 @@
<!-- 提交确认模态框 --> <!-- 提交确认模态框 -->
<view class="cu-modal" :class="modalName=='tj'?'show':''"> <view class="cu-modal" :class="modalName=='tj'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts">
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view> </view>
<view class="padding-xl bg-white"> <view class="padding-xl bg-white newcont">
是否提交选中单号请求? 是否提交选中单号请求?
</view> </view>
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-end">
<view class="action"> <view class="action wid100">
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrtj">确定</button> <button class="cu-btn tkqd" @tap="qrtj">确定</button>
</view> </view>
</view> </view>
</view> </view>
...@@ -1025,7 +1012,7 @@ ...@@ -1025,7 +1012,7 @@
* { * {
font-size: 14px !important; font-size: 14px !important;
} }
.uni-card__head-padding { .uni-card__head-padding {
background-color: #007AFF !important; background-color: #007AFF !important;
color: #FFFFFF; color: #FFFFFF;
......
...@@ -89,7 +89,7 @@ ...@@ -89,7 +89,7 @@
</view> </view>
<view class="cu-form-group padding-bottom "> <view class="cu-form-group padding-bottom ">
<view > <view >
<view class="cu-timeline " v-for="(item,index) in lc" :key="index"> <!-- <view class="cu-timeline " v-for="(item,index) in lc" :key="index">
<view class="cu-time" style="width: 80px;">{{item.nodeName}}</view> <view class="cu-time" style="width: 80px;">{{item.nodeName}}</view>
<view class="cu-item text-gray"> <view class="cu-item text-gray">
<view class="bg-blue content" v-if="item.nodeName!='结束'"> <view class="bg-blue content" v-if="item.nodeName!='结束'">
...@@ -99,6 +99,15 @@ ...@@ -99,6 +99,15 @@
<text></text> {{'结束'}} <text></text> {{'结束'}}
</view> </view>
</view> </view>
</view> -->
<view class="lccontent">
<mSidebar title="进度详情">
<view class="row">
<m-steps :wrapperTitle='(item.actionUserName+getAction(item.action))||""' v-for="(item, index) in lc" :item='item' :key="index" :index='index' :activity= 'activity' date='actionTime' >
<text slot="status">{{item.nodeName}}</text>
</m-steps>
</view>
</mSidebar>
</view> </view>
</view> </view>
</view> </view>
...@@ -184,6 +193,10 @@ ...@@ -184,6 +193,10 @@
}, },
}, },
methods: { methods: {
getAction: function(a) {
const ztList = {0: "创建", 1:"提交",2:"退回",3:"撤回",4:"转交",5:"调度",8:"待处理"};
return ztList[a]
},
// 流程 // 流程
apigetlc(){ apigetlc(){
let url="wf/query/wflog/"+this.cgjh.wfinstid let url="wf/query/wflog/"+this.cgjh.wfinstid
......
<template>
<view class="bg-white">
<cu-custom bgColor="bg-white" isBack="true">
<slot slot="backText">返回</slot>
</cu-custom>
<view>
<view class="cu-form-group " >
<view class="title">当前地址:</view>
<input disabled="" placeholder="" name="input" >
{{fwq||'未设置'}}
</input>
</view>
<view class="cu-form-group " v-for="(item,index) in fwqlist" :key="index">
<view class="title">地址{{index+1}}:</view>
<input placeholder="" v-model="item.fwq" name="input" >
<view class="flex" style="width: 90px;justify-content: space-around;">
<view style="color: red;" @tap="delfwq(item,index)">删除</view>
<!-- <view style="color: #007AFF;">编辑</view> -->
<view style="color:#39B54A;" @tap="select(item)">选择</view>
</view>
</input>
</view>
<view class="padding">
<button class="cu-btn block line-orange lg margin " @tap="adddz()">
<text class="cuIcon-add"></text> 新增地址</button>
</view>
</view>
</view>
</template>
<script>
import http from '@/common/service/service.js';
export default {
data:function(){
return{
fwq:"",
fwqlist:[],
infor:{userName:"",password:""}
}
},
methods:{
delfwq(item,index){
this.fwqlist.splice(index,1);
},
adddz(){
this.fwqlist.push({fwq:""})
},
select(item){
let newfwqlist=[]
this.fwqlist.forEach(e=>{
if(e.fwq!=""){
newfwqlist.push(e)
}
})
uni.setStorage({
key: 'fwqlist',
data: newfwqlist,
success: function () {
}
});
uni.setStorage({
key: 'fwq',
data: item.fwq,
success: function () {
}
});
if(item.fwq){
http.config.baseUrl='http://'+item.fwq
this.$Router.push({
path: '/pages/login/login', query:{
infor:this.infor
},
})
}else{
this.$tip.toast('请填入地址!')
}
}
},
onLoad(d) {
let infor=JSON.parse(d.query).infor
this.infor=infor
let that =this
// 获取本地服务器列表
uni.getStorage({
key:'fwqlist',
success:function(e){
that.fwqlist=e.data
},
fail() {
console.log("获取失败,请手动输入")
}
})
uni.getStorage({
key:'fwq',
success:function(e){
console.log("获取服务器")
console.log(e)
that.fwq=e.data
},
fail() {
console.log("获取失败,请手动输入")
}
})
}
}
</script>
<style>
</style>
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<scroll-view scroll-y class="page"> <scroll-view scroll-y class="page">
<view class="text-center" :style="[{animation: 'show ' + 0.4+ 's 1'}]"> <view class="text-center" :style="[{animation: 'show ' + 0.4+ 's 1'}]">
<image src="http://119.3.92.249:18080/dmg/images/logo.ico" mode='aspectFit' class="zai-logo "></image> <image src="http://119.3.92.249:18080/dmg/images/logo.ico" mode='aspectFit' class="zai-logo "></image>
<view class="zai-title text-shadow" style="color: #FFFFFF;">GAVEL ERP </view> <view class="zai-title text-shadow" style="margin-top: -20px;color: #FFFFFF;font-size:25px;">格物软件</view>
</view> </view>
<view class="box padding-lr-xl login-paddingtop" :style="[{animation: 'show ' + 0.6+ 's 1'}]"> <view class="box padding-lr-xl login-paddingtop" :style="[{animation: 'show ' + 0.6+ 's 1'}]">
<block v-if="loginWay==1"> <block v-if="loginWay==1">
...@@ -18,16 +18,19 @@ ...@@ -18,16 +18,19 @@
<text :class="[showPassword ? 'cuIcon-attention' : 'cuIcon-attentionforbid']" @click="changePassword"></text> <text :class="[showPassword ? 'cuIcon-attention' : 'cuIcon-attentionforbid']" @click="changePassword"></text>
</view> </view>
</view> </view>
<view @click="gofwqsz" class="padding text-right" style="color: #EEEEEE;">
服务器设置
</view>
<!-- 端口号 dkh该为项目后缀 --> <!-- 端口号 dkh该为项目后缀 -->
<view class="cu-form-group margin-top shadow-warp" :class="[shape=='round'?'round':'']"> <!-- <view class="cu-form-group margin-top shadow-warp" :class="[shape=='round'?'round':'']">
<view class="title"><text class="cuIcon-settings margin-right-xs"></text>服务器:</view> <view class="title"><text class="cuIcon-settings margin-right-xs"></text>服务器:</view>
<input placeholder="请输入服务器地址及端口号" name="input" v-model="fwq" ></input> <input placeholder="请输入服务器地址及端口号" name="input" v-model="fwq" ></input>
</view> </view>
<view class="cu-form-group margin-top shadow-warp" :class="[shape=='round'?'round':'']"> <view class="cu-form-group margin-top shadow-warp" :class="[shape=='round'?'round':'']">
<view class="title"><text class="cuIcon-settings margin-right-xs"></text>服务名:</view> <view class="title"><text class="cuIcon-settings margin-right-xs"></text>服务名:</view>
<input placeholder="请输入服务名" name="input" v-model="dkh"></input> <input placeholder="请输入服务名" name="input" v-model="dkh"></input>
</view> </view> -->
<view class="padding text-center margin-top"> <view class="padding text-center ">
<button style="width: 100%;" class="cu-btn bg-blue lg margin-right shadow" :loading="loading" :class="[shape=='round'?'round':'']" <button style="width: 100%;" class="cu-btn bg-blue lg margin-right shadow" :loading="loading" :class="[shape=='round'?'round':'']"
@tap="onLogin"><text space="emsp">{{loading ? "登录中...":" 登录 "}}</text> @tap="onLogin"><text space="emsp">{{loading ? "登录中...":" 登录 "}}</text>
</button> </button>
...@@ -112,7 +115,13 @@ ...@@ -112,7 +115,13 @@
} }
}; };
}, },
onLoad:function(){
onLoad:function(da){
if(da&&da.query){
let infor=JSON.parse(da.query).infor
this.userName=infor.userName
this.password=infor.password
}
var that=this var that=this
uni.getStorage({ uni.getStorage({
key:'userName', key:'userName',
...@@ -132,24 +141,24 @@ ...@@ -132,24 +141,24 @@
console.log("获取失败,请手动输入") console.log("获取失败,请手动输入")
} }
}) })
uni.getStorage({ // uni.getStorage({
key:'fwq', // key:'fwq',
success:function(e){ // success:function(e){
that.fwq=e.data // that.fwq=e.data
}, // },
fail() { // fail() {
console.log("获取失败,请手动输入") // console.log("获取失败,请手动输入")
} // }
}) // })
uni.getStorage({ // uni.getStorage({
key:'dkh', // key:'dkh',
success:function(e){ // success:function(e){
that.dkh=e.data // that.dkh=e.data
}, // },
fail() { // fail() {
console.log("获取失败,请手动输入") // console.log("获取失败,请手动输入")
} // }
}) // })
// #ifdef APP-PLUS // #ifdef APP-PLUS
var that=this var that=this
plus.runtime.getProperty( plus.runtime.appid, function ( wgtinfo ) { plus.runtime.getProperty( plus.runtime.appid, function ( wgtinfo ) {
...@@ -177,9 +186,24 @@ ...@@ -177,9 +186,24 @@
}, },
methods: { methods: {
...mapActions([ "mLogin","PhoneLogin","ThirdLogin" ]), ...mapActions([ "mLogin","PhoneLogin","ThirdLogin" ]),
gofwqsz(){
this.$Router.push({
path: '/pages/login/fwqsz', query:{
infor:{
userName:this.userName,
password:this.password
}
},
})
},
onLogin: function (){ onLogin: function (){
http.config.baseUrl='http://'+this.fwq+'/'+this.dkh // http.config.baseUrl='http://'+this.fwq+'/'+this.dkh
let _this=this; let _this=this;
if(http.config.baseUrl=="http://"){
this.$tip.toast('请先设置服务器');
return;
}
if(!this.userName || this.userName.length==0){ if(!this.userName || this.userName.length==0){
this.$tip.toast('请填写用户名'); this.$tip.toast('请填写用户名');
return; return;
...@@ -239,20 +263,6 @@ ...@@ -239,20 +263,6 @@
console.log("用户名获取成功") console.log("用户名获取成功")
} }
}); });
uni.setStorage({
key: 'fwq',
data: _this.fwq,
success: function () {
console.log("服务器获取成功")
}
});
uni.setStorage({
key: 'dkh',
data: _this.dkh,
success: function () {
console.log("端口号获取成功")
}
});
//ACCESS_TOKEN //ACCESS_TOKEN
this.$tip.success('登录成功!') this.$tip.success('登录成功!')
this.$Router.replaceAll({name:'index'}) this.$Router.replaceAll({name:'index'})
......
<template> <template>
<view> <view style="height: 100vh;background-color:#FFFFFF">
<scroll-view scroll-y class="page"> <scroll-view scroll-y class="page">
<!-- 头部logo--> <!-- 头部logo-->
<view class="UCenter-bg"> <view class="userinforbg" style="height: 150px;background-color: white;">
<image :src="personalList.avatar" class="png round animation-slide-right margin-bottom-sm" mode="scaleToFill" :style="[{animationDelay: '0.1s'}]"></image> <view class="flex" style="padding-top: 50px;padding-left: 50px;">
<image src="https://static.jeecg.com/upload/test/wave_1595818053612.gif" mode="scaleToFill" class="gif-wave"></image> <image style="border-radius: 50%;height: 70px;width: 70px;" src="../../static/tx.jpg"></image>
<view style="height: 70px;line-height: 70px;padding-left: 20px;font-size: 18px!important;font-weight: 550;">
{{userName}}
</view>
</view> </view>
<view class="padding flex text-center text-grey bg-white shadow-warp">
<view class="flex flex-sub flex-direction solid-right animation-slide-top" :style="[{animationDelay: '0.2s'}]">
<view class="text-xl text-orange">{{personalList.username}}</view>
<view class="margin-top-sm"><text class="cuIcon-people"></text> 用户</view>
</view>
<view class="flex flex-sub flex-direction animation-slide-top" :style="[{animationDelay: '0.2s'}]">
<view class="text-xl text-green">{{personalList.post?personalList.post:'员工'}}</view>
<view class="margin-top-sm"><text class="cuIcon-news"></text> 职务</view>
</view>
</view> </view>
<!-- 列表list--> <view class=" flex bg-white cu-list menu card-menu margin-top-xl margin-bottom-xl shadow-lg radius">
<view class="cu-list menu card-menu margin-top-xl margin-bottom-xl shadow-lg radius"> <view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.1s'}]">
<!-- <view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.1s'}]"> <view class="content" >
<view class="content" > <text class="cuIcon-favorfill text-yellow"></text>
<text class="cuIcon-favorfill text-yellow"></text> <text class="text-grey">收藏</text>
<text class="text-grey">收藏</text> </view>
</view> </view>
</view> <!-- <view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.2s'}]">
<view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.2s'}]"> <view class="content">
<view class="content"> <text class="cuIcon-redpacket_fill text-red"></text>
<text class="cuIcon-redpacket_fill text-red"></text> <text class="text-grey">红包</text>
<text class="text-grey">红包</text> </view>
</view> </view>
</view> <view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.3s'}]" @tap="scan">
<view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.3s'}]" @tap="scan"> <view class="content">
<view class="content"> <text class="cuIcon-scan text-red"></text>
<text class="cuIcon-scan text-red"></text> <text class="text-grey">扫码</text>
<text class="text-grey">扫码</text> </view>
</view> </view>
</view> <navigator class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.4s'}]" url="/pages/user/location" hover-class="none">
<navigator class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.4s'}]" url="/pages/user/location" hover-class="none"> <view class="content" >
<view class="content" > <text class="cuIcon-location text-cyan"></text>
<text class="cuIcon-location text-cyan"></text> <text class="text-grey">定位</text>
<text class="text-grey">定位</text> </view>
</view> </navigator> -->
</navigator> <navigator class="cu-item arrow animation-slide-bottom" url="/pages/user/setting" :style="[{animationDelay: '0.6s'}]" >
<navigator class="cu-item arrow animation-slide-bottom" url="/pages/user/userdetail" :style="[{animationDelay: '0.6s'}]"> <view class="content">
<view class="content"> <text class="cuIcon-settingsfill text-cyan"></text>
<text class="cuIcon-settingsfill text-cyan"></text> <text class="text-grey">设置</text>
<text class="text-grey">设置</text> </view>
</view> </navigator>
</navigator> --> <navigator style="" class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.7s'}]" url="/pages/login/login" hover-class="none">
<view class="content" @click="clearToken()">
<text class="cuIcon-exit text-cyan"></text>
<text class="text-grey">退出</text>
</view>
</navigator>
</view>
<!-- 列表list-->
<!-- <view class="cu-list menu card-menu margin-top-xl margin-bottom-xl shadow-lg radius">
<navigator class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.7s'}]" url="/pages/login/login" hover-class="none"> <navigator class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.7s'}]" url="/pages/login/login" hover-class="none">
<view class="content" @click="clearToken()"> <view class="content" @click="clearToken()">
<text class="cuIcon-exit text-cyan"></text> <text class="cuIcon-exit text-cyan"></text>
<text class="text-grey">退出</text> <text class="text-grey">退出</text>
</view> </view>
</navigator> </navigator>
</view> </view> -->
<view class="cu-tabbar-height"></view> <view class="cu-tabbar-height"></view>
</scroll-view> </scroll-view>
</view> </view>
...@@ -86,12 +88,27 @@ ...@@ -86,12 +88,27 @@
cur: { cur: {
immediate: true, immediate: true,
handler() { handler() {
let that=this
uni.getStorage({
key:'userName',
success:function(e){
that.userName=e.data
},
fail() {
console.log("获取失败,请手动输入")
}
})
console.log('watch',this.cur) console.log('watch',this.cur)
this.userId=this.$store.getters.userid; this.userId=this.$store.getters.userid;
this.load() this.load()
}, },
}, },
}, },
data:function(){
return{
userName:"",
}
},
methods: { methods: {
clearToken(){ clearToken(){
uni.removeStorage({key:"ACCESS_TOKEN"}) uni.removeStorage({key:"ACCESS_TOKEN"})
...@@ -172,7 +189,9 @@ ...@@ -172,7 +189,9 @@
font-weight: 300; font-weight: 300;
text-shadow: 0 0 3px rgba(0, 0, 0, 0.3); text-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
} }
.userinforbg{
background:linear-gradient(to top,pink,#4a86e8);
}
.UCenter-bg text { .UCenter-bg text {
opacity: 0.8; opacity: 0.8;
} }
......
<template>
<view class="bg-white" style="height: 100vh;">
<cu-custom bgColor="bg-white" isBack="true">
<slot slot="backText">返回</slot>
</cu-custom>
<view>
<view class=" flex bg-white cu-list menu card-menu margin-top-xl margin-bottom-xl shadow-lg radius">
<view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.1s'}]">
<view class="content flex justify-between" @tap="clearCache" >
<view>
<text class="cuIcon-calendar text-yellow"></text>
<text class="text-grey">清除缓存</text>
</view>
<view>
<text class="text-grey">{{fileSizeString}}</text>
</view>
</view>
</view>
<view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.1s'}]">
<view class="content flex justify-between" >
<view>
<text class="cuIcon-expressman text-yellow"></text>
<text class="text-grey">关于我们</text>
</view>
<view>
<text class="text-grey"></text>
</view>
</view>
</view>
<view class="cu-item arrow animation-slide-bottom" :style="[{animationDelay: '0.1s'}]" >
<view class="content flex justify-between" >
<view>
<text class="cuIcon-vip text-yellow"></text>
<text class="text-grey">当前版本</text>
</view>
<view>
<text class="text-grey">v1.0.0</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import http from '@/common/service/service.js';
export default {
data:function(){
return{
fileSizeString:"0kb"
}
},
watch: {
cur: {
immediate: true,
handler() {
this.formatSize()
},
},
},
methods:{
// 清除缓存
clearCache() {
let that = this;
let os = plus.os.name;
if (os == 'Android') {
let main = plus.android.runtimeMainActivity();
let sdRoot = main.getCacheDir();
let files = plus.android.invoke(sdRoot, "listFiles");
let len = files.length;
for (let i = 0; i < len; i++) {
let filePath = '' + files[i]; // 没有找到合适的方法获取路径,这样写可以转成文件路径
plus.io.resolveLocalFileSystemURL(filePath, function(entry) {
if (entry.isDirectory) {
entry.removeRecursively(function(entry) { //递归删除其下的所有文件及子目录
uni.showToast({
title: '缓存清理完成',
duration: 2000
});
that.formatSize(); // 重新计算缓存
}, function(e) {
console.log(e.message)
});
} else {
entry.remove();
}
}, function(e) {
console.log('文件路径读取失败')
});
}
} else { // ios
plus.cache.clear(function() {
uni.showToast({
title: '缓存清理完成',
duration: 2000
});
that.formatSize();
});
}},
// 计算缓存
formatSize() {
let that = this;
plus.cache.calculate(function(size) {
let sizeCache = parseInt(size);
if (sizeCache == 0) {
that.fileSizeString = "0B";
} else if (sizeCache < 1024) {
that.fileSizeString = sizeCache + "B";
} else if (sizeCache < 1048576) {
that.fileSizeString = (sizeCache / 1024).toFixed(2) + "KB";
} else if (sizeCache < 1073741824) {
that.fileSizeString = (sizeCache / 1048576).toFixed(2) + "MB";
} else {
that.fileSizeString = (sizeCache / 1073741824).toFixed(2) + "GB";
}
});
}
},
onLoad(d) {
}
}
</script>
<style>
</style>
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<view class="cu-form-group" style="border-top: 0.5px solid #EEEEEE;"> <view class="cu-form-group" style="border-top: 0.5px solid #EEEEEE;">
<view class="title">流程</view> <view class="title">流程</view>
</view> </view>
<view class="cu-form-group padding-bottom "> <!-- <view class="cu-form-group padding-bottom ">
<view > <view >
<view class="cu-timeline " v-for="(item,index) in lc" :key="index"> <view class="cu-timeline " v-for="(item,index) in lc" :key="index">
<view class="cu-time" style="width: 80px;">{{item.nodeName}}</view> <view class="cu-time" style="width: 80px;">{{item.nodeName}}</view>
...@@ -22,8 +22,17 @@ ...@@ -22,8 +22,17 @@
</view> </view>
</view> </view>
</view> </view>
</view> -->
</view> </view>
</view> </view>
<view class="lccontent">
<mSidebar title="进度详情">
<view class="row">
<m-steps :wrapperTitle='(item.actionUserName+getAction(item.action))||""' v-for="(item, index) in lc" :item='item' :key="index" :index='index' :activity= 'activity' date='actionTime' >
<text slot="status">{{item.nodeName}}</text>
</m-steps>
</view>
</mSidebar>
</view> </view>
</view> </view>
...@@ -81,6 +90,15 @@ ...@@ -81,6 +90,15 @@
}, },
data() { data() {
return { return {
activity: 0,
wrapRecordList: [
{updateTime: '11-19 17:04:36',progressStatus: '4', content: '已到达目的地,本次服务结束!'},
{updateTime: '11-19 17:04:36',progressStatus: '3',content: '终于到家啦,回去睡觉!'},
{updateTime: '11-19 17:04:36',progressStatus: '2',content: '还有一半路程马上结束啦,换人来开!'},
{updateTime: '11-19 17:04:36',progressStatus: '1',content: '准备出发啦!一路顺风!'},
{updateTime: '11-19 17:04:36',progressStatus: '0',content: '开始啦'},
],
progressStatus: ['开始','准备出发','到达服务区', '到达目的地', '已送达'], // 0 开始, 1 准备出发 2 到达中转站 3 送往目的地 4 已送达
lc:[], lc:[],
wllist:[], wllist:[],
value:['key1','key2'], value:['key1','key2'],
...@@ -105,6 +123,10 @@ ...@@ -105,6 +123,10 @@
}, },
}, },
methods: { methods: {
getAction: function(a) {
const ztList = {0: "创建", 1:"提交",2:"退回",3:"撤回",4:"转交",5:"调度",8:"待处理"};
return ztList[a]
},
// 流程 // 流程
apigetlc(){ apigetlc(){
let url="wf/query/wflog/"+this.cgjh.wfinstid let url="wf/query/wflog/"+this.cgjh.wfinstid
...@@ -311,5 +333,6 @@ ...@@ -311,5 +333,6 @@
border-radius: 10px; border-radius: 10px;
margin: 60px auto; margin: 60px auto;
} }
</style> </style>
...@@ -61,12 +61,12 @@ ...@@ -61,12 +61,12 @@
</view> </view>
<view class=""> <view class="">
<view> <view>
订单日期:{{item.ywrq|formatDate}} 订单日期{{item.ywrq|formatDate}}
</view> </view>
<view>备注:<text>{{item.bz||"未设定"}}</text></view> <view>备注:<text>{{item.bz||"未设定"}}</text></view>
</view> </view>
<view class="flex justify-between" style="padding-top: 10px;margin-bottom: 0px;"> <view class="flex justify-between" style="padding-top: 10px;margin-bottom: 0px;">
<view>状态: <view style="font-weight: 600;">状态:
<text style="color:#FBBD08;"> <text style="color:#FBBD08;">
{{item.zt|filterzt}} {{item.zt|filterzt}}
</text> </text>
...@@ -84,85 +84,72 @@ ...@@ -84,85 +84,72 @@
</view> </view>
</view> </view>
<!-- 转交确认模态框 --> <!-- 转交确认模态框 -->
<view class="cu-modal" :class="modalName=='zj'?'show':''"> <view class="cu-modal" :class="modalName=='zj'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts">提示
<view class="content">提示</view> </view>
<view class="action" @tap="hideModal"> <view class="padding-xl bg-white newcont">
<text class="cuIcon-close text-red"></text> 是否转交选中单号请求?
</view> </view>
</view> <view class="cu-bar bg-white justify-end">
<view class="padding-xl bg-white"> <view class="action wid100">
是否转交选中单号请求? <button class="tkqx cu-btn " @tap="hideModal">取消</button>
</view> <button class="tkqd cu-btn " @tap="qrzj">确定</button>
<view class="cu-bar bg-white justify-end"> </view>
<view class="action"> </view>
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button>
<button class="cu-btn bg-green margin-left" @tap="qrzj">确定</button>
</view> </view>
</view> </view>
</view>
</view>
<!-- 退回确认模态框 --> <!-- 退回确认模态框 -->
<view class="cu-modal" :class="modalName=='th'?'show':''"> <view class="cu-modal" :class="modalName=='th'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts" style="" >
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal"> </view>
<text class="cuIcon-close text-red"></text> <view class="padding-xl bg-white newcont">
</view> 是否退回选中单号请求?
</view> </view>
<view class="padding-xl bg-white"> <view class="cu-bar bg-white justify-end">
是否退回选中单号请求? <view class="action wid100">
</view> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<view class="cu-bar bg-white justify-end"> <button class="cu-btn tkqd" @tap="qrth">确定</button>
<view class="action"> </view>
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> </view>
<button class="cu-btn bg-green margin-left" @tap="qrth">确定</button>
</view> </view>
</view> </view>
</view>
</view>
<!-- 撤回确认模态框 --> <!-- 撤回确认模态框 -->
<view class="cu-modal" :class="modalName=='ch'?'show':''"> <view class="cu-modal" :class="modalName=='ch'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts" style="" >
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal"> </view>
<text class="cuIcon-close text-red"></text> <view class="padding-xl bg-white newcont">
</view> 是否撤回选中单号请求?
</view> </view>
<view class="padding-xl bg-white"> <view class="cu-bar bg-white" >
是否撤回选中单号请求? <view class="action flex justify-around wid100" >
</view> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<view class="cu-bar bg-white justify-end"> <button class="cu-btn tkqd" @tap="qrch">确定</button>
<view class="action"> </view>
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> </view>
<button class="cu-btn bg-green margin-left" @tap="qrch">确定</button>
</view> </view>
</view> </view>
</view>
</view>
<!-- 提交确认模态框 --> <!-- 提交确认模态框 -->
<view class="cu-modal" :class="modalName=='tj'?'show':''"> <view class="cu-modal" :class="modalName=='tj'?'show':''">
<view class="cu-dialog"> <view class="cu-dialog">
<view class="cu-bar bg-white justify-end"> <view class="cu-bar bg-white justify-center newts">
<view class="content">提示</view> 提示
<view class="action" @tap="hideModal"> </view>
<text class="cuIcon-close text-red"></text> <view class="padding-xl bg-white newcont">
</view> 是否提交选中单号请求?
</view> </view>
<view class="padding-xl bg-white"> <view class="cu-bar bg-white justify-end">
是否提交选中单号请求? <view class="action wid100">
</view> <button class="cu-btn tkqx" @tap="hideModal">取消</button>
<view class="cu-bar bg-white justify-end"> <button class="cu-btn tkqd" @tap="qrtj">确定</button>
<view class="action"> </view>
<button class="cu-btn line-green text-green" @tap="hideModal">取消</button> </view>
<button class="cu-btn bg-green margin-left" @tap="qrtj">确定</button>
</view> </view>
</view> </view>
</view>
</view>
<!-- 悬浮按钮 --> <!-- 悬浮按钮 -->
<uni-fab :pattern="pattern" :content="content" @trigger="trigger"></uni-fab> <uni-fab :pattern="pattern" :content="content" @trigger="trigger"></uni-fab>
<!-- 筛选弹框 --> <!-- 筛选弹框 -->
...@@ -206,7 +193,7 @@ ...@@ -206,7 +193,7 @@
</view> </view>
<!-- 事务处理 --> <!-- 事务处理 -->
<!-- 转交模态框 --> <!-- 转交模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='zjmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='zjmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body"> <view class="example-body">
...@@ -260,7 +247,7 @@ ...@@ -260,7 +247,7 @@
</view> </view>
</view> </view>
<!-- 提交模态框 --> <!-- 提交模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='tjmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='tjmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body"> <view class="example-body">
...@@ -314,7 +301,7 @@ ...@@ -314,7 +301,7 @@
</view> </view>
</view> </view>
<!-- 撤回模态框 --> <!-- 撤回模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='chmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='chmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body"> <view class="example-body">
...@@ -368,7 +355,7 @@ ...@@ -368,7 +355,7 @@
</view> </view>
</view> </view>
<!-- 撤回模态框 --> <!-- 撤回模态框 -->
<view class="cu-modal drawer-modal justify-end" :class="modalName=='thmodal'?'show':''" @tap="hideModal"> <view class="cu-modal bottom-modal justify-end" :class="modalName=='thmodal'?'show':''" @tap="hideModal">
<view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop="" <view class="cu-dialog basis-lg" style="overflow: auto;" @tap.stop=""
:style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]"> :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="example-body"> <view class="example-body">
......
<template>
<div class="content">
<div @click="onClose" class="fixed mask"></div>
<div v-if="showTip" align="center" class="fixed tis">
<div class="tis-content">
<div>
<img :src="logo" >
</div>
<div class="tis-progress">
努力上传中
<text v-if="progress<100">..{{progress}}%</text>
</div>
<div class="cancel">
<button @click="onAbort" type="button" class="cancel-btn">取消上传</button>
</div>
</div>
</div>
<div class="fixed file-content">
<view ref="input" class="btn">
<button type="button" class="btn-bg">打开文件管理器</button>
</view>
</div>
</div>
</template>
<script>
export default {
props: {
logo: {
type: String,
default: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F00%2F00%2F07%2F155788a6d8a5c42.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1619847627&t=2da40b583002205c204d980b54b35040'
},
progress: {
type: [Number,String],
default: 0
},
showTip: {
type: Boolean,
default: false
}
},
data() {
return {
hFile: '',
}
},
mounted() {
this.hFile = document.createElement('input')
this.hFile.type = 'file';
this.hFile.value = '';
this.hFile.style.position = 'absolute';
this.hFile.style.zIndex = 93;
this.hFile.style.left = 0;
this.hFile.style.right = 0;
this.hFile.style.top = 0;
this.hFile.style.bottom = 0;
this.hFile.style.height = '60px';
this.hFile.style.width = '100%';
this.hFile.style.opacity = 0;
this.$refs.input.$el.appendChild(this.hFile);
},
methods: {
onAbort() {
this.$emit('abort',{});
},
onClose() {
this.$emit('close',{});
}
}
}
</script>
<style scoped>
.content {background: transparent;}
.fixed {position: fixed;bottom: 0;left: 0;right: 0;width: 100%;}
.content .mask {top: 0;background: rgba(0,0,0,.4);z-index: 90;}
.content .file-content {z-index: 91;height: 60px;background: #fff;text-align: center;}
.btn {position: relative;}
.btn .file {position: absolute;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;height: 60px;width: 100%;opacity: 0;}
.btn-bg {margin-top: 10px;background: #0066CC;color: #fff;width: 80%;height: 40px;line-height: 40px;border: 0;border-radius: 5px;}
.tis {top: 0;z-index: 95;display: flex;justify-content: center;align-items: center;}
.tis .tis-content {background: #fff;width: 60%;border-radius: 10px;padding: 20px 0;}
.tis .tis-content img {width: 50px;height: 50px;}
.tis-progress {margin: 10px 0;color: #999;}
.cancel-btn {margin-top: 30px;height: 30px;width: 60%;line-height: 30px;font-size: 14px;padding: 0 2em;background: #e3e3e3;color: #898989;border: 0!important;border-radius: 5px;}
.cancel-btn:after {height: 0!important;border: 0!important;}
</style>
<template>
<view>
<view class='t-toptips' :style="{top: top,background: cubgColor}" :class="[show?'t-top-show':'']">
<view v-if="loading" class="flex flex-sub">
<view class="flex flex-sub">
<view class="cu-progress flex-sub round striped active sm">
<view :style="{ background: color,width: value + '%'}"></view>
</view>
<text class="margin-left">{{value}}%</text>
</view>
<view @click="downOnAbort" v-if="value<100" class="close">取消</view>
</view>
<block v-else>{{msg}}</block>
</view>
<!-- #ifdef H5 -->
<h5-file
v-if="showH5"
ref="h5File"
:logo="logo"
:showTip="showTip"
:progress="value"
@abort="onAbort"
@close="onCloseH5"
></h5-file>
<!-- #endif -->
</view>
</template>
<script>
import h5File from './h5-file.vue';
export default {
components: {h5File},
props: {
logo: {
type: String,
default: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F00%2F00%2F07%2F155788a6d8a5c42.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1619847627&t=2da40b583002205c204d980b54b35040'
},
top: {
type: String,
default: 'auto'
},
bgColor: {
type: String,
default: 'rgba(49, 126, 243, 0.5)',
},
color: {
type: String,
default: '#55aa00',
}
},
data() {
this.uploadTask = null;
this.downloadTask = null;
return {
cubgColor: '',
loading: false,
value: 5,
show: false,
msg: '执行完毕~',
showH5: false,
showTip: false,
}
},
methods: {
toast(title = '',{ duration = 2000, icon = 'none'} = {}) {
uni.showToast({title,duration,icon});
},
getRequest(url) {
let theRequest = new Object();
let index = url.indexOf("?");
if (index != -1) {
let str = url.substring(index+1);
let strs = str.split("&");
for(let i = 0; i < strs.length; i ++) {
theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
}
}
return theRequest;
},
/*
上传说明:
currentWebview: 当前窗口webview对象
url:上传接口地址
name:上传文件key值
header: 上传接口请求头
...:body内其他参数
*/
appChooseFile({currentWebview,url,name = 'file',header,addName = '',addSize = '',maxSize = 10,...formData} = {}) {
// #ifdef APP-PLUS
let wvPath = '/uni_modules/l-file/hybrid/html/index.html';
let wv = plus.webview.create("",wvPath,{
'uni-app': 'none',
top: 0,
height: '100%',
background: 'transparent'
},{
url,
header,
addName,
addSize,
maxSize,
formData,
key: name,
logo: this.logo
});
wv.loadURL(wvPath);
currentWebview.append(wv);
wv.overrideUrlLoading({mode:'reject'},(e)=>{
let {fileName,size,str} = this.getRequest(e.url);
fileName = unescape(fileName);
str = unescape(str);
return this.handleBack(fileName,str,size);
});
// #endif
},
wxChooseFile({maxSize = 10,...param}) {
wx.chooseMessageFile({
count: 1,
type: 'file',
success: ({tempFiles}) => {
this.setdefUI();
let file = tempFiles[0];
if(file.size > (1024*1024 * Math.abs(maxSize))) {
uni.showToast({
title:`单个文件请勿超过${maxSize}M,请重新上传`,
icon: 'none'
});
return this.errorHandler('文件选择失败',this.upErr);
}
this.handleWXUpload(param,file);
},
fail: () => this.errorHandler('文件选择失败',this.upErr)
})
},
h5ChooseFile({maxSize = 10,...param} = {}) {
this.showH5 = true;
this.value = 0;
this.$nextTick(()=>{
this.$refs.h5File.hFile.onchange = (event) => {
let file = event.target.files[0];
if(file.size > (1024*1024 * Math.abs(maxSize))) {
uni.showToast({
title:`单个文件请勿超过${maxSize}M,请重新上传`,
icon: 'none'
});
return;
}
this.handleH5Upload(param, file);
}
})
},
handleH5Upload({url,name = 'file',header,addName,addSize,...data} = {},tempFile) {
let fileName = tempFile.name;
let formData = new FormData();
for (let keys in data) {formData.append(keys, data[keys]);}
if (addName) {formData.append(addName, fileName);}
if (addSize) {formData.append(addSize, tempFile.size);}
formData.append(name, tempFile);
this.uploadTask = new XMLHttpRequest();
this.uploadTask.open("POST", url, true);
for (let keys in header) {this.uploadTask.setRequestHeader(keys, header[keys]);}
this.uploadTask.ontimeout = () => {
setTimeout(()=>{
this.showTip = false;
return this.errorHandler('请求超时',this.upErr);
},1000);
};
this.uploadTask.upload.addEventListener("progress",(event) => {
if(event.lengthComputable){
this.value = Math.ceil(event.loaded * 100 / event.total);
if (this.value > 100) {this.value=100;}
this.$forceUpdate();
}
}, false);
this.uploadTask.onreadystatechange = (ev) => {
if(this.uploadTask.readyState == 4) {
console.log('status:'+this.uploadTask.status);
if (this.uploadTask.status == 200) {
return this.handleBack(fileName,this.uploadTask.responseText,tempFile.size);
}
else {
this.showTip = false;
if (this.uploadTask.status == 0) {
console.log('请检查请求头Content-Type与服务端是否匹配,并确认服务端已正确开启跨域');
}
return this.errorHandler('文件上传失败',this.upErr);
}
}
};
this.showTip = true;
this.uploadTask.send(formData);
},
handleWXUpload({url,name = 'file',header,addName,addSize,...formData} = {},tempFile) {
let opt = {url,name,formData,header,filePath:tempFile.path};
if (addName) {opt.formData[addName] = tempFile.name;}
if (addSize) {opt.formData[addSize] = tempFile.size;}
let fileName = tempFile.name;
opt['fail'] = (e) => {
this.showTip = false;
return this.errorHandler('文件上传失败',this.upErr)
};
opt['success'] = (res) => {
if (res.statusCode==200) {
let data = JSON.parse(res.data);
//可自行添加后台返回状态验证
return this.onCommit(this.$emit('up-success',{fileName,size:tempFile.size,data}));
}
return this.errorHandler('文件上传失败',this.upErr);
};
this.showTip = true;
this.uploadTask = uni.uploadFile(opt);
this.uploadTask&&this.uploadTask.onProgressUpdate(({progress = 0}) => {
if (progress <= 100) {
this.value = progress;
this.$forceUpdate();
}
});
},
onCloseH5() {
this.showH5 = false;
},
onAbort() {
this.uploadTask&&this.uploadTask.abort();
this.showTip = false;
},
downOnAbort() {
this.downloadTask&&this.downloadTask.abort();
this.onCommit(false,'已取消');
},
// app+h5返回内容,此处按实际项目修改
handleBack(fileName,str = '{}',size) {
console.log('可根据需求自行修改emit内容,服务端返回:'+ str);
try{
str = JSON.parse(str);
}catch(e){
str = {id:str};
}
return this.onCommit(this.$emit('up-success',{statusCode: 200,fileName,size,...str}));
},
/*
上传
*/
upload(param = {}) {
if (!param.url) {this.toast('上传地址不正确');return;}
if (this.loading) {this.toast('还有个文件玩命处理中,请稍候..');return;}
// #ifdef APP-PLUS
return this.appChooseFile(param);
// #endif
// #ifdef MP-WEIXIN
return this.wxChooseFile(param);
// #endif
// #ifdef H5
this.h5ChooseFile(param);
// #endif
},
/*
打开文件
*/
open(filePath) {
let system = uni.getSystemInfoSync().platform;
if(system == 'ios'){filePath = encodeURI(filePath);}
uni.openDocument({
filePath,
success: (res) => {console.log('打开文档成功');}
});
},
/*
APP自定义保存
*/
plusSaveFile({url,customName='',opt}) {
return new Promise((resolve,reject)=>{
// 可自行修改参数
// 参数api: http://www.html5plus.org/doc/zh_cn/downloader.html#plus.downloader.DownloadOptions
let downloadOptions = {
method: "GET",
timeout: 120,
retryInterval: 10,
filename:'file://storage/emulated/0/lFile/' + customName
};
downloadOptions = {...downloadOptions,...opt};
this.downloadTask = plus.downloader.createDownload(url, downloadOptions,(d, status)=>{
// 下载完成
if(status == 200){
let tempFilePath = d.filename;
this.value = 100;
this.onCommit(resolve(tempFilePath))
} else {
this.errorHandler('下载失败',reject)
}
this.downloadTask = null;
});
this.downloadTask.addEventListener('statechanged',({downloadedSize=0,state=0,totalSize=0}={})=>{
if (state===3) {
let total = totalSize>0?totalSize:fileSize;
let progressVal = Math.ceil(downloadedSize / total* 100);
this.value = progressVal>100?100:progressVal;
this.$forceUpdate()
}
},false);
this.downloadTask.start();
});
},
/*
下载
type: temporary=返回临时地址,save=长期保存到本地
*/
download({url,type = 'temporary',customName = '',...opt}) {
if (this.loading) {
this.toast('还有个文件玩命处理中,请稍候..');
return;
}
this.setdefUI();
// #ifdef APP-PLUS
if (type == 'save') {
return this.plusSaveFile({url,customName,opt});
}
// #endif
return new Promise((resolve,reject)=>{
url = encodeURI(url);
this.downloadTask = uni.downloadFile({
url,
success: ({statusCode,tempFilePath}) => {
if (statusCode === 200) {
// #ifndef H5
if (type == 'save') {
uni.saveFile({
tempFilePath,
success:({savedFilePath}) => this.onCommit(resolve(savedFilePath)),
fail: () => this.errorHandler('下载失败',reject)
});
}
else {
this.onCommit(resolve(tempFilePath))
}
// #endif
// #ifdef H5
this.onCommit(resolve(tempFilePath))
// #endif
}
},
fail: () => this.errorHandler('下载失败',reject)
});
this.downloadTask.onProgressUpdate(({progress = 0}) => {
if (progress <= 100) {
this.value = progress;
this.$forceUpdate();
}
});
})
},
onCommit(resolve,msg = '执行完毕~') {
this.msg = msg;
this.loading = false;
this.showTip = false;
this.cubgColor = 'rgba(57, 181, 74, 0.5)';
this.uploadTask = null;
this.downloadTask = null;
setTimeout(()=>{this.show = false;this.showH5 = false;},1500);
return resolve;
},
setdefUI() {
this.cubgColor = this.bgColor;
this.value = 0;
this.loading = true;
this.show = true;
},
upErr(errText) {
this.$emit('up-error',errText);
},
errorHandler(errText,reject) {
this.msg = errText;
this.loading = false;
this.cubgColor = 'rgba(229, 77, 66, 0.5)';
this.uploadTask = null;
this.downloadTask = null;
setTimeout(()=>{this.show = false;},1500);
return reject(errText);
}
}
}
</script>
<style scoped>
.t-toptips {
width: 100%;
padding: 18upx 30upx;
box-sizing: border-box;
position: fixed;
z-index: 90;
color: #fff;
font-size: 30upx;
left: 0;
display: flex;
align-items: center;
justify-content: center;
word-break: break-all;
opacity: 0;
transform: translateZ(0) translateY(-100%);
transition: all 0.3s ease-in-out;
}
.close {
width: 3em;
text-align: right;
}
.t-top-show {
transform: translateZ(0) translateY(0);
opacity: 1;
}
.flex {
display: flex;
align-items: center;
}
.flex-sub {
flex: 1;
}
.round {
border-radius: 5000upx;
}
/* ==================
进度条
==================== */
.cu-progress {
overflow: hidden;
height: 28upx;
background-color: #ebeef5;
display: inline-flex;
align-items: center;
width: 100%;
}
.cu-progress+view,
.cu-progress+text {
line-height: 1;
}
.cu-progress.xs {
height: 10upx;
}
.cu-progress.sm {
height: 20upx;
}
.cu-progress view {
width: 0;
height: 100%;
align-items: center;
display: flex;
justify-items: flex-end;
justify-content: space-around;
font-size: 20upx;
color: #ffffff;
transition: width 0.6s ease;
}
.cu-progress text {
align-items: center;
display: flex;
font-size: 20upx;
color: #333333;
text-indent: 10upx;
}
.cu-progress.text-progress {
padding-right: 60upx;
}
.cu-progress.striped view {
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-size: 72upx 72upx;
}
.cu-progress.active view {
animation: progress-stripes 2s linear infinite;
}
@keyframes progress-stripes {
from {
background-position: 72upx 0;
}
to {
background-position: 0 0;
}
}
</style>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title class="title">[文件管理器]</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<style type="text/css">
.content {background: transparent;}
.fixed {position: fixed;bottom: 0;left: 0;right: 0;width: 100%;}
.content .mask {top: 0;background: rgba(0,0,0,.4);z-index: 90;}
.content .file-content {z-index: 91;height: 60px;background: #fff;text-align: center;}
.btn {position: relative;}
.btn .file {position: absolute;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;height: 60px;width: 100%;opacity: 0;}
.btn-bg {margin-top: 10px;background: #0066CC;color: #fff;width: 80%;height: 40px;border: 0;border-radius: 5px;}
.tis {top: 0;z-index: 95;display: none;justify-content: center;align-items: center;}
.tis .tis-content {background: #fff;width: 60%;border-radius: 10px;padding: 20px 0;}
.tis .tis-content img {width: 50px;height: 50px;}
.tis-progress {margin: 10px 0;color: #999;}
.cancel-btn {margin-top: 30px;height: 30px;line-height: 1;padding: 0 2em;background: #e3e3e3;color: #898989;border: 0;border-radius: 5px;}
</style>
</head>
<body>
<div class="content">
<div class="fixed mask"></div>
<div align="center" class="fixed tis">
<div class="tis-content">
<div class="logo"></div>
<div class="tis-progress">
努力上传中..
</div>
<div class="cancel">
<button type="button" class="cancel-btn">取消上传</button>
</div>
</div>
</div>
<div class="fixed file-content">
<div class="btn">
<button type="button" class="btn-bg">打开文件管理器</button>
<input class="file" type="file" />
</div>
</div>
</div>
<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
<script src="js/h5-uploader.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
let mask = document.querySelector(".mask");
let fileDom = document.querySelector(".file");
let tis = document.querySelector(".tis");
let logoDom = document.querySelector(".logo");
let progress = document.querySelector(".tis-progress");
let cancel = document.querySelector(".cancel-btn");
let createUpload = (file,{ url, key='file', header = {},data = {},addName,addSize}) => {
console.log(`
上传地址:${url}\n
上传附件:${file.name} 附件大小:${file.size}\n
请求头:${JSON.stringify(header)}\n
业务参数:${JSON.stringify(data)}
`);
if (!url) {return;}
tis.style.display = 'flex';
let formData = new FormData();
for (let keys in data) {formData.append(keys, data[keys]);}
if (addName) {formData.append(addName, file.name);console.log('补充参数:',JSON.stringify({addName:file.name}));}
if (addSize) {formData.append(addSize, file.size);console.log('补充参数:',JSON.stringify({addSize:file.size}));}
formData.append(key, file);
let xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
for (let keys in header) {
xhr.setRequestHeader(keys, header[keys]);
}
xhr.upload.addEventListener("progress", function(event) {
if(event.lengthComputable){
let percent = Math.ceil(event.loaded * 100 / event.total) + "%";
progress.innerText = `努力上传中..${percent}`;
}
}, false);
xhr.ontimeout = function(){
// xhr请求超时事件处理
progress.innerText = '请求超时';
setTimeout(()=>{
tis.style.display = 'none';
plus.webview.currentWebview().close();
},1000);
};
xhr.onreadystatechange = (ev) => {
if(xhr.readyState == 4) {
console.log('status:'+xhr.status);
if (xhr.status == 200) {
progress.innerText = '上传成功';
console.log('服务端返回数据:'+xhr.responseText);
location.href = `callback?fileName=${escape(file.name)}&size=${file.size}&str=${escape(xhr.responseText)}`;
}
else {
progress.innerText = '上传失败了';
if (xhr.status == 0) {
console.log('请检查请求头Content-Type与服务端是否匹配,并确认服务端已正确开启跨域');
}
}
setTimeout(()=>{
tis.style.display = 'none';
plus.webview.currentWebview().close();
},1000);
}
};
xhr.send(formData);
cancel.addEventListener("click", ()=>{
xhr.abort();
plus.webview.currentWebview().close();
});
}
mask.addEventListener("click", () => {
plus.webview.currentWebview().close();
});
document.addEventListener('UniAppJSBridgeReady', () => {
let {url,key,header,formData,logo,maxSize,addName,addSize} = plus.webview.currentWebview();
if (logo) {
let img = document.createElement('img');
img.src = logo;
logoDom.appendChild(img);
}
fileDom.value = '';
fileDom.addEventListener('change', (event) => {
let file = fileDom.files[0];
// 默认限制文件小于10M,可自行修改
if(file.size > (1024*1024 * Math.abs(maxSize))) {
plus.nativeUI.toast(`单个文件请勿超过${maxSize}M,请重新上传`);
return;
}
createUpload(file,{ url, key,header,addName,addSize,formData});
}, false);
});
\ No newline at end of file
{
"id": "l-file",
"displayName": "附件选择上传下载APP-H5-小程序",
"version": "1.2.6",
"description": "附件选择上传下载APP-H5-小程序",
"keywords": [
"附件",
"上传",
"下载",
"预览"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.1"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "u"
}
}
}
}
}
\ No newline at end of file
<template>
<view class="uni-collapse-item">
<!-- onClick(!isOpen) -->
<view @click="onClick(!isOpen)" class="uni-collapse-item__title"
:class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}">
<view class="uni-collapse-item__title-wrap">
<slot name="title">
<view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}">
<image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" />
<text class="uni-collapse-item__title-text">{{ title }}</text>
</view>
</slot>
</view>
<view
v-if="showArrow"
:class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }"
class="uni-collapse-item__title-arrow">
<uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="arrowdown" />
</view>
</view>
<view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}"
:style="{height: (isOpen?height:0) +'px'}">
<view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content"
:class="{open:isheight,'uni-collapse-item--border':border&&isOpen}">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
/**
* CollapseItem 折叠面板子组件
* @description 折叠面板子组件
* @property {String} title 标题文字
* @property {String} thumb 标题左侧缩略图
* @property {String} name 唯一标志符
* @property {Boolean} open = [true|false] 是否展开组件
* @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线
* @property {Boolean} border = [true|false] 是否显示分隔线
* @property {Boolean} disabled = [true|false] 是否展开面板
* @property {Boolean} showAnimation = [true|false] 开启动画
* @property {Boolean} showArrow = [true|false] 是否显示右侧箭头
*/
export default {
name: 'uniCollapseItem',
props: {
// 列表标题
title: {
type: String,
default: ''
},
name: {
type: [Number, String],
default: ''
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// #ifdef APP-PLUS
// 是否显示动画,app 端默认不开启动画,卡顿严重
showAnimation: {
type: Boolean,
default: false
},
// #endif
// #ifndef APP-PLUS
// 是否显示动画
showAnimation: {
type: Boolean,
default: true
},
// #endif
// 是否展开
open: {
type: Boolean,
default: false
},
// 缩略图
thumb: {
type: String,
default: ''
},
// 标题分隔线显示类型
titleBorder: {
type: String,
default: 'auto'
},
border: {
type: Boolean,
default: true
},
showArrow:{
type: Boolean,
default: true
}
},
data() {
// TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug
const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
isOpen: false,
isheight: null,
height: 0,
elId,
nameSync: 0
}
},
watch: {
open(val) {
this.isOpen = val
this.onClick(val,'init')
}
},
updated(e) {
this.$nextTick(()=> {
this.init(true)
})
},
created(){
this.collapse = this.getCollapse()
this.oldHeight = 0
},
// #ifndef VUE3
// TODO vue2
destroyed() {
if (this.__isUnmounted) return
this.uninstall()
},
// #endif
// #ifdef VUE3
// TODO vue3
unmounted() {
this.__isUnmounted = true
this.uninstall()
},
// #endif
mounted() {
if (!this.collapse) return
if (this.name !== '') {
this.nameSync = this.name
} else {
this.nameSync = this.collapse.childrens.length + ''
}
if (this.collapse.names.indexOf(this.nameSync) === -1) {
this.collapse.names.push(this.nameSync)
} else {
console.warn(`name 值 ${this.nameSync} 重复`);
}
if (this.collapse.childrens.indexOf(this) === -1) {
this.collapse.childrens.push(this)
}
this.init()
},
methods: {
init(type) {
// #ifndef APP-NVUE
this.getCollapseHeight(type)
// #endif
// #ifdef APP-NVUE
this.getNvueHwight(type)
// #endif
},
uninstall() {
if (this.collapse) {
this.collapse.childrens.forEach((item, index) => {
if (item === this) {
this.collapse.childrens.splice(index, 1)
}
})
this.collapse.names.forEach((item, index) => {
if (item === this.nameSync) {
this.collapse.names.splice(index, 1)
}
})
}
},
onClick(isOpen,type) {
if (this.disabled) return
this.isOpen = isOpen
if (this.isOpen && this.collapse) {
this.collapse.setAccordion(this)
}
// if(type !== 'init'){
// this.collapse.onChange(isOpen,this)
// }
},
getCollapseHeight(type, index = 0) {
const views = uni.createSelectorQuery().in(this)
views
.select(`#${this.elId}`)
.fields({
size: true
}, data => {
// TODO 百度中可能获取不到节点信息 ,需要循环获取
if (index >= 10) return
if (!data) {
index++
this.getCollapseHeight(false, index)
return
}
// #ifdef APP-NVUE
this.height = data.height + 1
// #endif
// #ifndef APP-NVUE
this.height = data.height
// #endif
this.isheight = true
if (type) return
this.onClick(this.open,'init')
})
.exec()
},
getNvueHwight(type) {
const result = dom.getComponentRect(this.$refs['collapse--hook'], option => {
if (option && option.result && option.size) {
// #ifdef APP-NVUE
this.height = option.size.height + 1
// #endif
// #ifndef APP-NVUE
this.height = option.size.height
// #endif
this.isheight = true
if (type) return
this.onClick(this.open,'init')
}
})
},
/**
* 获取父元素实例
*/
getCollapse(name = 'uniCollapse') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
}
}
}
</script>
<style lang="scss" scoped>
.uni-collapse-item {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
&__title {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
transition: border-bottom-color .3s;
// transition-property: border-bottom-color;
// transition-duration: 5s;
&-wrap {
width: 100%;
flex: 1;
}
&-box {
padding: 0 15px;
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 48px;
line-height: 48px;
background-color: #fff;
color: #303133;
font-size: 13px;
font-weight: 500;
/* #ifdef H5 */
cursor: pointer;
outline: none;
/* #endif */
&.is-disabled {
.uni-collapse-item__title-text {
color: $uni-text-color-disable;
}
}
}
&.uni-collapse-item-border {
border-bottom: 1px solid #ebeef5;
}
&.is-open {
border-bottom-color: transparent;
}
&-img {
height: $uni-img-size-base;
width: $uni-img-size-base;
margin-right: 10px;
}
&-text {
flex: 1;
font-size: $uni-font-size-base;
/* #ifndef APP-NVUE */
white-space: nowrap;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
overflow: hidden;
text-overflow: ellipsis;
}
&-arrow {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
margin-right: 10px;
transform: rotate(0deg);
&-active {
transform: rotate(180deg);
}
}
}
&__wrap {
/* #ifndef APP-NVUE */
will-change: height;
box-sizing: border-box;
/* #endif */
background-color: #fff;
overflow: hidden;
position: relative;
height: 0;
&.is--transition {
// transition: all 0.3s;
transition-property: height, border-bottom-width;
transition-duration: 0.3s;
/* #ifndef APP-NVUE */
will-change: height;
/* #endif */
}
&-content {
position: absolute;
font-size: 13px;
color: #303133;
// transition: height 0.3s;
border-bottom-color: transparent;
border-bottom-style: solid;
border-bottom-width: 0;
&.uni-collapse-item--border {
border-bottom-width: 1px;
border-bottom-color: red;
border-bottom-color: #ebeef5;
}
&.open {
position: relative;
}
}
}
&--animation {
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
}
}
</style>
<template>
<view class="uni-collapse">
<slot />
</view>
</template>
<script>
/**
* Collapse 折叠面板
* @description 展示可以折叠 / 展开的内容区域
* @tutorial https://ext.dcloud.net.cn/plugin?id=23
* @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式,参数类型为string,否则为array)
* @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果
* @event {Function} change 切换面板时触发,如果是手风琴模式,返回类型为string,否则为array
*/
export default {
name: 'uniCollapse',
emits:['change','activeItem','input','update:modelValue'],
props: {
value: {
type: [String, Array],
default: ''
},
modelValue: {
type: [String, Array],
default: ''
},
accordion: {
// 是否开启手风琴效果
type: [Boolean, String],
default: false
},
},
data() {
return {}
},
computed: {
// TODO 兼容 vue2 和 vue3
dataValue() {
let value = (typeof this.value === 'string' && this.value === '') ||
(Array.isArray(this.value) && this.value.length === 0)
let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') ||
(Array.isArray(this.modelValue) && this.modelValue.length === 0)
if (value) {
return this.modelValue
}
if (modelValue) {
return this.value
}
return this.value
}
},
watch: {
dataValue(val) {
this.setOpen(val)
}
},
created() {
this.childrens = []
this.names = []
},
mounted() {
this.setOpen(this.dataValue)
},
methods: {
setOpen(val) {
let str = typeof val === 'string'
let arr = Array.isArray(val)
this.childrens.forEach((vm, index) => {
if (str) {
if (val === vm.nameSync) {
if (!this.accordion) {
console.warn('accordion 属性为 false ,v-model 类型应该为 array')
return
}
vm.isOpen = true
}
}
if (arr) {
val.forEach(v => {
if (v === vm.nameSync) {
if (this.accordion) {
console.warn('accordion 属性为 true ,v-model 类型应该为 string')
return
}
vm.isOpen = true
}
})
}
})
this.emit(val)
},
setAccordion(self) {
if (!this.accordion) return
this.childrens.forEach((vm, index) => {
if (self !== vm) {
vm.isOpen = false
}
})
},
resize() {
this.childrens.forEach((vm, index) => {
// #ifndef APP-NVUE
vm.getCollapseHeight()
// #endif
// #ifdef APP-NVUE
vm.getNvueHwight()
// #endif
})
},
onChange(isOpen, self) {
let activeItem = []
if (this.accordion) {
activeItem = isOpen ? self.nameSync : ''
} else {
this.childrens.forEach((vm, index) => {
if (vm.isOpen) {
activeItem.push(vm.nameSync)
}
})
}
this.$emit('change', activeItem)
this.emit(activeItem)
},
emit(val){
this.$emit('input', val)
this.$emit('update:modelValue', val)
}
}
}
</script>
<style lang="scss" scoped>
.uni-collapse {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
flex-direction: column;
background-color: $uni-bg-color;
}
</style>
'use strict';
const ERR_MSG_OK = 'chooseAndUploadFile:ok';
const ERR_MSG_FAIL = 'chooseAndUploadFile:fail';
function chooseImage(opts) {
const {
count,
sizeType = ['original', 'compressed'],
sourceType = ['album', 'camera'],
extension
} = opts
return new Promise((resolve, reject) => {
uni.chooseImage({
count,
sizeType,
sourceType,
extension,
success(res) {
resolve(normalizeChooseAndUploadFileRes(res, 'image'));
},
fail(res) {
reject({
errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
});
},
});
});
}
function chooseVideo(opts) {
const {
camera,
compressed,
maxDuration,
sourceType = ['album', 'camera'],
extension
} = opts;
return new Promise((resolve, reject) => {
uni.chooseVideo({
camera,
compressed,
maxDuration,
sourceType,
extension,
success(res) {
const {
tempFilePath,
duration,
size,
height,
width
} = res;
resolve(normalizeChooseAndUploadFileRes({
errMsg: 'chooseVideo:ok',
tempFilePaths: [tempFilePath],
tempFiles: [
{
name: (res.tempFile && res.tempFile.name) || '',
path: tempFilePath,
size,
type: (res.tempFile && res.tempFile.type) || '',
width,
height,
duration,
fileType: 'video',
cloudPath: '',
}, ],
}, 'video'));
},
fail(res) {
reject({
errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
});
},
});
});
}
function chooseAll(opts) {
const {
count,
extension
} = opts;
return new Promise((resolve, reject) => {
let chooseFile = uni.chooseFile;
if (typeof wx !== 'undefined' &&
typeof wx.chooseMessageFile === 'function') {
chooseFile = wx.chooseMessageFile;
}
if (typeof chooseFile !== 'function') {
return reject({
errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。',
});
}
chooseFile({
type: 'all',
count,
extension,
success(res) {
resolve(normalizeChooseAndUploadFileRes(res));
},
fail(res) {
reject({
errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL),
});
},
});
});
}
function normalizeChooseAndUploadFileRes(res, fileType) {
res.tempFiles.forEach((item, index) => {
if (!item.name) {
item.name = item.path.substring(item.path.lastIndexOf('/') + 1);
}
if (fileType) {
item.fileType = fileType;
}
item.cloudPath =
Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.'));
});
if (!res.tempFilePaths) {
res.tempFilePaths = res.tempFiles.map((file) => file.path);
}
return res;
}
function uploadCloudFiles(files, max = 5, onUploadProgress) {
files = JSON.parse(JSON.stringify(files))
const len = files.length
let count = 0
let self = this
return new Promise(resolve => {
while (count < max) {
next()
}
function next() {
let cur = count++
if (cur >= len) {
!files.find(item => !item.url && !item.errMsg) && resolve(files)
return
}
const fileItem = files[cur]
const index = self.files.findIndex(v => v.uuid === fileItem.uuid)
fileItem.url = ''
delete fileItem.errMsg
uniCloud
.uploadFile({
filePath: fileItem.path,
cloudPath: fileItem.cloudPath,
fileType: fileItem.fileType,
onUploadProgress: res => {
res.index = index
onUploadProgress && onUploadProgress(res)
}
})
.then(res => {
fileItem.url = res.fileID
fileItem.index = index
if (cur < len) {
next()
}
})
.catch(res => {
fileItem.errMsg = res.errMsg || res.message
fileItem.index = index
if (cur < len) {
next()
}
})
}
})
}
function uploadFiles(choosePromise, {
onChooseFile,
onUploadProgress
}) {
return choosePromise
.then((res) => {
if (onChooseFile) {
const customChooseRes = onChooseFile(res);
if (typeof customChooseRes !== 'undefined') {
return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ?
res : chooseRes);
}
}
return res;
})
.then((res) => {
if (res === false) {
return {
errMsg: ERR_MSG_OK,
tempFilePaths: [],
tempFiles: [],
};
}
return res
})
}
function chooseAndUploadFile(opts = {
type: 'all'
}) {
if (opts.type === 'image') {
return uploadFiles(chooseImage(opts), opts);
}
else if (opts.type === 'video') {
return uploadFiles(chooseVideo(opts), opts);
}
return uploadFiles(chooseAll(opts), opts);
}
export {
chooseAndUploadFile,
uploadCloudFiles
};
<template>
<view class="uni-file-picker">
<view v-if="title" class="uni-file-picker__header">
<text class="file-title">{{ title }}</text>
<text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
</view>
<upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly"
:image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview"
:delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
<slot>
<view class="is-add">
<view class="icon-add"></view>
<view class="icon-add rotate"></view>
</view>
</slot>
</upload-image>
<upload-file v-if="fileMediatype !== 'image' || showType !== 'grid'" :readonly="readonly"
:list-styles="listStyles" :files-list="filesList" :showType="showType" :delIcon="delIcon"
@uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
<slot><button type="primary" size="mini">选择文件</button></slot>
</upload-file>
</view>
</template>
<script>
import {
chooseAndUploadFile,
uploadCloudFiles
} from './choose-and-upload-file.js'
import {
get_file_ext,
get_extname,
get_files_and_is_max,
get_file_info,
get_file_data
} from './utils.js'
import uploadImage from './upload-image.vue'
import uploadFile from './upload-file.vue'
let fileInput = null
/**
* FilePicker 文件选择上传
* @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
* @tutorial https://ext.dcloud.net.cn/plugin?id=4079
* @property {Object|Array} value 组件数据,通常用来回显 ,类型由return-type属性决定
* @property {Boolean} disabled = [true|false] 组件禁用
* @value true 禁用
* @value false 取消禁用
* @property {Boolean} readonly = [true|false] 组件只读,不可选择,不显示进度,不显示删除按钮
* @value true 只读
* @value false 取消只读
* @property {String} return-type = [array|object] 限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖
* @value array 规定 value 属性的类型为数组
* @value object 规定 value 属性的类型为对象
* @property {Boolean} disable-preview = [true|false] 禁用图片预览,仅 mode:grid 时生效
* @value true 禁用图片预览
* @value false 取消禁用图片预览
* @property {Boolean} del-icon = [true|false] 是否显示删除按钮
* @value true 显示删除按钮
* @value false 不显示删除按钮
* @property {Boolean} auto-upload = [true|false] 是否自动上传,值为true则只触发@select,可自行上传
* @value true 自动上传
* @value false 取消自动上传
* @property {Number|String} limit 最大选择个数 ,h5 会自动忽略多选的部分
* @property {String} title 组件标题,右侧显示上传计数
* @property {String} mode = [list|grid] 选择文件后的文件列表样式
* @value list 列表显示
* @value grid 宫格显示
* @property {String} file-mediatype = [image|video|all] 选择文件类型
* @value image 只选择图片
* @value video 只选择视频
* @value all 选择所有文件
* @property {Array} file-extname 选择文件后缀,根据 file-mediatype 属性而不同
* @property {Object} list-style mode:list 时的样式
* @property {Object} image-styles 选择文件后缀,根据 file-mediatype 属性而不同
* @event {Function} select 选择文件后触发
* @event {Function} progress 文件上传时触发
* @event {Function} success 上传成功触发
* @event {Function} fail 上传失败触发
* @event {Function} delete 文件从列表移除时触发
*/
export default {
name: 'uniFilePicker',
components: {
uploadImage,
uploadFile
},
emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'],
props: {
// #ifdef VUE3
modelValue: {
type: [Array, Object],
default () {
return []
}
},
// #endif
// #ifndef VUE3
value: {
type: [Array, Object],
default () {
return []
}
},
// #endif
disabled: {
type: Boolean,
default: false
},
disablePreview: {
type: Boolean,
default: false
},
delIcon: {
type: Boolean,
default: true
},
// 自动上传
autoUpload: {
type: Boolean,
default: true
},
// 最大选择个数 ,h5只能限制单选或是多选
limit: {
type: [Number, String],
default: 9
},
// 列表样式 grid | list | list-card
mode: {
type: String,
default: 'grid'
},
// 选择文件类型 image/video/all
fileMediatype: {
type: String,
default: 'image'
},
// 文件类型筛选
fileExtname: {
type: [Array, String],
default () {
return []
}
},
title: {
type: String,
default: ''
},
listStyles: {
type: Object,
default () {
return {
// 是否显示边框
border: true,
// 是否显示分隔线
dividline: true,
// 线条样式
borderStyle: {}
}
}
},
imageStyles: {
type: Object,
default () {
return {
width: 'auto',
height: 'auto'
}
}
},
readonly: {
type: Boolean,
default: false
},
returnType: {
type: String,
default: 'array'
},
sizeType: {
type: Array,
default () {
return ['original', 'compressed']
}
}
},
data() {
return {
files: [],
localValue: []
}
},
watch: {
// #ifndef VUE3
value: {
handler(newVal, oldVal) {
this.setValue(newVal, oldVal)
},
immediate: true
},
// #endif
// #ifdef VUE3
modelValue: {
handler(newVal, oldVal) {
this.setValue(newVal, oldVal)
},
immediate: true
},
// #endif
},
computed: {
filesList() {
let files = []
this.files.forEach(v => {
files.push(v)
})
return files
},
showType() {
if (this.fileMediatype === 'image') {
return this.mode
}
return 'list'
},
limitLength() {
if (this.returnType === 'object') {
return 1
}
if (!this.limit) {
return 1
}
if (this.limit >= 9) {
return 9
}
return this.limit
}
},
created() {
// TODO 兼容不开通服务空间的情况
if (!(uniCloud.config && uniCloud.config.provider)) {
this.noSpace = true
uniCloud.chooseAndUploadFile = chooseAndUploadFile
}
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
if (this.form && this.formItem) {
if (this.formItem.name) {
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
}
}
},
methods: {
/**
* 公开用户使用,清空文件
* @param {Object} index
*/
clearFiles(index) {
if (index !== 0 && !index) {
this.files = []
this.$nextTick(() => {
this.setEmit()
})
} else {
this.files.splice(index, 1)
}
this.$nextTick(() => {
this.setEmit()
})
},
/**
* 公开用户使用,继续上传
*/
upload() {
let files = []
this.files.forEach((v, index) => {
if (v.status === 'ready' || v.status === 'error') {
files.push(Object.assign({}, v))
}
})
this.uploadFiles(files)
},
async setValue(newVal, oldVal) {
const newData = async (v) => {
const reg = /cloud:\/\/([\w.]+\/?)\S*/
let url = ''
if(v.fileID){
url = v.fileID
}else{
url = v.url
}
if (reg.test(url)) {
v.fileID = url
v.url = await this.getTempFileURL(url)
}
v.path = v.url
return v
}
if (this.returnType === 'object') {
if (newVal) {
await newData(newVal)
} else {
newVal = {}
}
} else {
if (!newVal) newVal = []
for(let i =0 ;i < newVal.length ;i++){
let v = newVal[i]
await newData(v)
}
}
this.localValue = newVal
if (this.form && this.formItem &&!this.is_reset) {
this.is_reset = false
this.formItem.setValue(this.localValue)
}
let filesData = Object.keys(newVal).length > 0 ? newVal : [];
this.files = [].concat(filesData)
},
/**
* 选择文件
*/
choose() {
if (this.disabled) return
if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType ===
'array') {
uni.showToast({
title: `您最多选择 ${this.limitLength} 个文件`,
icon: 'none'
})
return
}
this.chooseFiles()
},
/**
* 选择文件并上传
*/
chooseFiles() {
const _extname = get_extname(this.fileExtname)
// 获取后缀
uniCloud
.chooseAndUploadFile({
type: this.fileMediatype,
compressed: false,
sizeType: this.sizeType,
// TODO 如果为空,video 有问题
extension: _extname.length > 0 ? _extname : undefined,
count: this.limitLength - this.files.length, //默认9
onChooseFile: this.chooseFileCallback,
onUploadProgress: progressEvent => {
this.setProgress(progressEvent, progressEvent.index)
}
})
.then(result => {
this.setSuccessAndError(result.tempFiles)
})
.catch(err => {
console.log('选择失败', err)
})
},
/**
* 选择文件回调
* @param {Object} res
*/
async chooseFileCallback(res) {
const _extname = get_extname(this.fileExtname)
const is_one = (Number(this.limitLength) === 1 &&
this.disablePreview &&
!this.disabled) ||
this.returnType === 'object'
// 如果这有一个文件 ,需要清空本地缓存数据
if (is_one) {
this.files = []
}
let {
filePaths,
files
} = get_files_and_is_max(res, _extname)
if (!(_extname && _extname.length > 0)) {
filePaths = res.tempFilePaths
files = res.tempFiles
}
let currentData = []
for (let i = 0; i < files.length; i++) {
if (this.limitLength - this.files.length <= 0) break
files[i].uuid = Date.now()
let filedata = await get_file_data(files[i], this.fileMediatype)
filedata.progress = 0
filedata.status = 'ready'
this.files.push(filedata)
currentData.push({
...filedata,
file: files[i]
})
}
this.$emit('select', {
tempFiles: currentData,
tempFilePaths: filePaths
})
res.tempFiles = files
// 停止自动上传
if (!this.autoUpload || this.noSpace) {
res.tempFiles = []
}
},
/**
* 批传
* @param {Object} e
*/
uploadFiles(files) {
files = [].concat(files)
uploadCloudFiles.call(this, files, 5, res => {
this.setProgress(res, res.index, true)
})
.then(result => {
this.setSuccessAndError(result)
})
.catch(err => {
console.log(err)
})
},
/**
* 成功或失败
*/
async setSuccessAndError(res, fn) {
let successData = []
let errorData = []
let tempFilePath = []
let errorTempFilePath = []
for (let i = 0; i < res.length; i++) {
const item = res[i]
const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index
if (index === -1 || !this.files) break
if (item.errMsg === 'request:fail') {
this.files[index].url = item.path
this.files[index].status = 'error'
this.files[index].errMsg = item.errMsg
// this.files[index].progress = -1
errorData.push(this.files[index])
errorTempFilePath.push(this.files[index].url)
} else {
this.files[index].errMsg = ''
this.files[index].fileID = item.url
const reg = /cloud:\/\/([\w.]+\/?)\S*/
if (reg.test(item.url)) {
this.files[index].url = await this.getTempFileURL(item.url)
}else{
this.files[index].url = item.url
}
this.files[index].status = 'success'
this.files[index].progress += 1
successData.push(this.files[index])
tempFilePath.push(this.files[index].fileID)
}
}
if (successData.length > 0) {
this.setEmit()
// 状态改变返回
this.$emit('success', {
tempFiles: this.backObject(successData),
tempFilePaths: tempFilePath
})
}
if (errorData.length > 0) {
this.$emit('fail', {
tempFiles: this.backObject(errorData),
tempFilePaths: errorTempFilePath
})
}
},
/**
* 获取进度
* @param {Object} progressEvent
* @param {Object} index
* @param {Object} type
*/
setProgress(progressEvent, index, type) {
const fileLenth = this.files.length
const percentNum = (index / fileLenth) * 100
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
let idx = index
if (!type) {
idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid)
}
if (idx === -1 || !this.files[idx]) return
// fix by mehaotian 100 就会消失,-1 是为了让进度条消失
this.files[idx].progress = percentCompleted - 1
// 上传中
this.$emit('progress', {
index: idx,
progress: parseInt(percentCompleted),
tempFile: this.files[idx]
})
},
/**
* 删除文件
* @param {Object} index
*/
delFile(index) {
this.$emit('delete', {
tempFile: this.files[index],
tempFilePath: this.files[index].url
})
this.files.splice(index, 1)
this.$nextTick(() => {
this.setEmit()
})
},
/**
* 获取文件名和后缀
* @param {Object} name
*/
getFileExt(name) {
const last_len = name.lastIndexOf('.')
const len = name.length
return {
name: name.substring(0, last_len),
ext: name.substring(last_len + 1, len)
}
},
/**
* 处理返回事件
*/
setEmit() {
let data = []
if (this.returnType === 'object') {
data = this.backObject(this.files)[0]
this.localValue = data?data:null
} else {
data = this.backObject(this.files)
if (!this.localValue) {
this.localValue = []
}
this.localValue = [...data]
}
// #ifdef VUE3
this.$emit('update:modelValue', this.localValue)
// #endif
// #ifndef VUE3
this.$emit('input', this.localValue)
// #endif
},
/**
* 处理返回参数
* @param {Object} files
*/
backObject(files) {
let newFilesData = []
files.forEach(v => {
newFilesData.push({
extname: v.extname,
fileType: v.fileType,
image: v.image,
name: v.name,
path: v.path,
size: v.size,
fileID:v.fileID,
url: v.url
})
})
return newFilesData
},
async getTempFileURL(fileList) {
fileList = {
fileList: [].concat(fileList)
}
const urls = await uniCloud.getTempFileURL(fileList)
return urls.fileList[0].tempFileURL || ''
},
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
}
}
}
</script>
<style>
.uni-file-picker {
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
/* #endif */
}
.uni-file-picker__header {
padding-top: 5px;
padding-bottom: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: space-between;
}
.file-title {
font-size: 14px;
color: #333;
}
.file-count {
font-size: 14px;
color: #999;
}
.is-add {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.icon-add {
width: 50px;
height: 5px;
background-color: #f1f1f1;
border-radius: 2px;
}
.rotate {
position: absolute;
transform: rotate(90deg);
}
</style>
<template>
<view class="uni-file-picker__files">
<view v-if="!readonly" class="files-button" @click="choose">
<slot></slot>
</view>
<!-- :class="{'is-text-box':showType === 'list'}" -->
<view v-if="list.length > 0" class="uni-file-picker__lists is-text-box" :style="borderStyle">
<!-- ,'is-list-card':showType === 'list-card' -->
<view class="uni-file-picker__lists-box" v-for="(item ,index) in list" :key="index" :class="{
'files-border':index !== 0 && styles.dividline}"
:style="index !== 0 && styles.dividline &&borderLineStyle">
<view class="uni-file-picker__item">
<!-- :class="{'is-text-image':showType === 'list'}" -->
<!-- <view class="files__image is-text-image">
<image class="header-image" :src="item.logo" mode="aspectFit"></image>
</view> -->
<view class="files__name">{{item.name}}</view>
<view v-if="delIcon&&!readonly" class="icon-del-box icon-files" @click="delFile(index)">
<view class="icon-del icon-files"></view>
<view class="icon-del rotate"></view>
</view>
</view>
<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
:backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
</view>
<view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
点击重试
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: "uploadFile",
emits:['uploadFiles','choose','delFile'],
props: {
filesList: {
type: Array,
default () {
return []
}
},
delIcon: {
type: Boolean,
default: true
},
limit: {
type: [Number, String],
default: 9
},
showType: {
type: String,
default: ''
},
listStyles: {
type: Object,
default () {
return {
// 是否显示边框
border: true,
// 是否显示分隔线
dividline: true,
// 线条样式
borderStyle: {}
}
}
},
readonly:{
type:Boolean,
default:false
}
},
computed: {
list() {
let files = []
this.filesList.forEach(v => {
files.push(v)
})
return files
},
styles() {
let styles = {
border: true,
dividline: true,
'border-style': {}
}
return Object.assign(styles, this.listStyles)
},
borderStyle() {
let {
borderStyle,
border
} = this.styles
let obj = {}
if (!border) {
obj.border = 'none'
} else {
let width = (borderStyle && borderStyle.width) || 1
width = this.value2px(width)
let radius = (borderStyle && borderStyle.radius) || 5
radius = this.value2px(radius)
obj = {
'border-width': width,
'border-style': (borderStyle && borderStyle.style) || 'solid',
'border-color': (borderStyle && borderStyle.color) || '#eee',
'border-radius': radius
}
}
let classles = ''
for (let i in obj) {
classles += `${i}:${obj[i]};`
}
return classles
},
borderLineStyle() {
let obj = {}
let {
borderStyle
} = this.styles
if (borderStyle && borderStyle.color) {
obj['border-color'] = borderStyle.color
}
if (borderStyle && borderStyle.width) {
let width = borderStyle && borderStyle.width || 1
let style = borderStyle && borderStyle.style || 0
if (typeof width === 'number') {
width += 'px'
} else {
width = width.indexOf('px') ? width : width + 'px'
}
obj['border-width'] = width
if (typeof style === 'number') {
style += 'px'
} else {
style = style.indexOf('px') ? style : style + 'px'
}
obj['border-top-style'] = style
}
let classles = ''
for (let i in obj) {
classles += `${i}:${obj[i]};`
}
return classles
}
},
methods: {
uploadFiles(item, index) {
this.$emit("uploadFiles", {
item,
index
})
},
choose() {
this.$emit("choose")
},
delFile(index) {
this.$emit('delFile', index)
},
value2px(value) {
if (typeof value === 'number') {
value += 'px'
} else {
value = value.indexOf('px') !== -1 ? value : value + 'px'
}
return value
}
}
}
</script>
<style lang="scss">
.uni-file-picker__files {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: flex-start;
}
.files-button {
// border: 1px red solid;
}
.uni-file-picker__lists {
position: relative;
margin-top: 5px;
overflow: hidden;
}
.file-picker__mask {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
right: 0;
top: 0;
bottom: 0;
left: 0;
color: #fff;
font-size: 14px;
background-color: rgba(0, 0, 0, 0.4);
}
.uni-file-picker__lists-box {
position: relative;
}
.uni-file-picker__item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
padding: 8px 10px;
padding-right: 5px;
padding-left: 10px;
}
.files-border {
border-top: 1px #eee solid;
}
.files__name {
flex: 1;
font-size: 14px;
color: #666;
margin-right: 25px;
/* #ifndef APP-NVUE */
word-break: break-all;
word-wrap: break-word;
/* #endif */
}
.icon-files {
/* #ifndef APP-NVUE */
position: static;
background-color: initial;
/* #endif */
}
// .icon-files .icon-del {
// background-color: #333;
// width: 12px;
// height: 1px;
// }
.is-list-card {
border: 1px #eee solid;
margin-bottom: 5px;
border-radius: 5px;
box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.1);
padding: 5px;
}
.files__image {
width: 40px;
height: 40px;
margin-right: 10px;
}
.header-image {
width: 100%;
height: 100%;
}
.is-text-box {
border: 1px #eee solid;
border-radius: 5px;
}
.is-text-image {
width: 25px;
height: 25px;
margin-left: 5px;
}
.rotate {
position: absolute;
transform: rotate(90deg);
}
.icon-del-box {
/* #ifndef APP-NVUE */
display: flex;
margin: auto 0;
/* #endif */
align-items: center;
justify-content: center;
position: absolute;
top: 0px;
bottom: 0;
right: 5px;
height: 26px;
width: 26px;
// border-radius: 50%;
// background-color: rgba(0, 0, 0, 0.5);
z-index: 2;
transform: rotate(-45deg);
}
.icon-del {
width: 15px;
height: 1px;
background-color: #333;
// border-radius: 1px;
}
/* #ifdef H5 */
@media all and (min-width: 768px) {
.uni-file-picker__files {
max-width: 375px;
}
}
/* #endif */
</style>
<template>
<view class="uni-file-picker__container">
<view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle">
<view class="file-picker__box-content" :style="borderStyle">
<image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image>
<view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)">
<view class="icon-del"></view>
<view class="icon-del rotate"></view>
</view>
<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
:backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
</view>
<view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
点击重试
</view>
</view>
</view>
<view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle">
<view class="file-picker__box-content is-add" :style="borderStyle" @click="choose">
<slot>
<view class="icon-add"></view>
<view class="icon-add rotate"></view>
</slot>
</view>
</view>
</view>
</template>
<script>
export default {
name: "uploadImage",
emits:['uploadFiles','choose','delFile'],
props: {
filesList: {
type: Array,
default () {
return []
}
},
disabled:{
type: Boolean,
default: false
},
disablePreview: {
type: Boolean,
default: false
},
limit: {
type: [Number, String],
default: 9
},
imageStyles: {
type: Object,
default () {
return {
width: 'auto',
height: 'auto',
border: {}
}
}
},
delIcon: {
type: Boolean,
default: true
},
readonly:{
type:Boolean,
default:false
}
},
computed: {
styles() {
let styles = {
width: 'auto',
height: 'auto',
border: {}
}
return Object.assign(styles, this.imageStyles)
},
boxStyle() {
const {
width = 'auto',
height = 'auto'
} = this.styles
let obj = {}
if (height === 'auto') {
if (width !== 'auto') {
obj.height = this.value2px(width)
obj['padding-top'] = 0
} else {
obj.height = 0
}
} else {
obj.height = this.value2px(height)
obj['padding-top'] = 0
}
if (width === 'auto') {
if (height !== 'auto') {
obj.width = this.value2px(height)
} else {
obj.width = '33.3%'
}
} else {
obj.width = this.value2px(width)
}
let classles = ''
for(let i in obj){
classles+= `${i}:${obj[i]};`
}
return classles
},
borderStyle() {
let {
border
} = this.styles
let obj = {}
if (typeof border === 'boolean') {
obj.border = border ? '1px #eee solid' : 'none'
} else {
let width = (border && border.width) || 1
width = this.value2px(width)
let radius = (border && border.radius) || 5
radius = this.value2px(radius)
obj = {
'border-width': width,
'border-style': (border && border.style) || 'solid',
'border-color': (border && border.color) || '#eee',
'border-radius': radius
}
}
let classles = ''
for(let i in obj){
classles+= `${i}:${obj[i]};`
}
return classles
}
},
methods: {
uploadFiles(item, index) {
this.$emit("uploadFiles", item)
},
choose() {
this.$emit("choose")
},
delFile(index) {
this.$emit('delFile', index)
},
prviewImage(img, index) {
let urls = []
if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){
this.$emit("choose")
}
if(this.disablePreview) return
this.filesList.forEach(i => {
urls.push(i.url)
})
uni.previewImage({
urls: urls,
current: index
});
},
value2px(value) {
if (typeof value === 'number') {
value += 'px'
} else {
if (value.indexOf('%') === -1) {
value = value.indexOf('px') !== -1 ? value : value + 'px'
}
}
return value
}
}
}
</script>
<style lang="scss">
.uni-file-picker__container {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-wrap: wrap;
margin: -5px;
}
.file-picker__box {
position: relative;
// flex: 0 0 33.3%;
width: 33.3%;
height: 0;
padding-top: 33.33%;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.file-picker__box-content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: 5px;
border: 1px #eee solid;
border-radius: 8px;
overflow: hidden;
}
.file-picker__progress {
position: absolute;
bottom: 0;
left: 0;
right: 0;
/* border: 1px red solid; */
z-index: 2;
}
.file-picker__progress-item {
width: 100%;
}
.file-picker__mask {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
right: 0;
top: 0;
bottom: 0;
left: 0;
color: #fff;
font-size: 12px;
background-color: rgba(0, 0, 0, 0.4);
}
.file-image {
width: 100%;
height: 100%;
}
.is-add {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.icon-add {
width: 50px;
height: 5px;
background-color: #f1f1f1;
border-radius: 2px;
}
.rotate {
position: absolute;
transform: rotate(90deg);
}
.icon-del-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
position: absolute;
top: 5px;
right: 5px;
height: 26px;
width: 26px;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 2;
transform: rotate(-45deg);
}
.icon-del {
width: 15px;
height: 2px;
background-color: #fff;
border-radius: 2px;
}
</style>
/**
* 获取文件名和后缀
* @param {String} name
*/
export const get_file_ext = (name) => {
const last_len = name.lastIndexOf('.')
const len = name.length
return {
name: name.substring(0, last_len),
ext: name.substring(last_len + 1, len)
}
}
/**
* 获取扩展名
* @param {Array} fileExtname
*/
export const get_extname = (fileExtname) => {
if (!Array.isArray(fileExtname)) {
let extname = fileExtname.replace(/(\[|\])/g, '')
return extname.split(',')
} else {
return fileExtname
}
return []
}
/**
* 获取文件和检测是否可选
*/
export const get_files_and_is_max = (res, _extname) => {
let filePaths = []
let files = []
if(!_extname || _extname.length === 0){
return {
filePaths,
files
}
}
res.tempFiles.forEach(v => {
let fileFullName = get_file_ext(v.name)
const extname = fileFullName.ext.toLowerCase()
if (_extname.indexOf(extname) !== -1) {
files.push(v)
filePaths.push(v.path)
}
})
if (files.length !== res.tempFiles.length) {
uni.showToast({
title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`,
icon: 'none',
duration: 5000
})
}
return {
filePaths,
files
}
}
/**
* 获取图片信息
* @param {Object} filepath
*/
export const get_file_info = (filepath) => {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: filepath,
success(res) {
resolve(res)
},
fail(err) {
reject(err)
}
})
})
}
/**
* 获取封装数据
*/
export const get_file_data = async (files, type = 'image') => {
// 最终需要上传数据库的数据
let fileFullName = get_file_ext(files.name)
const extname = fileFullName.ext.toLowerCase()
let filedata = {
name: files.name,
uuid: files.uuid,
extname: extname || '',
cloudPath: files.cloudPath,
fileType: files.fileType,
url: files.path || files.path,
size: files.size, //单位是字节
image: {},
path: files.path,
video: {}
}
if (type === 'image') {
const imageinfo = await get_file_info(files.path)
delete filedata.video
filedata.image.width = imageinfo.width
filedata.image.height = imageinfo.height
filedata.image.location = imageinfo.path
} else {
delete filedata.image
}
return filedata
}
export default {
"pulldown": "\ue588",
"refreshempty": "\ue461",
"back": "\ue471",
"forward": "\ue470",
"more": "\ue507",
"more-filled": "\ue537",
"scan": "\ue612",
"qq": "\ue264",
"weibo": "\ue260",
"weixin": "\ue261",
"pengyouquan": "\ue262",
"loop": "\ue565",
"refresh": "\ue407",
"refresh-filled": "\ue437",
"arrowthindown": "\ue585",
"arrowthinleft": "\ue586",
"arrowthinright": "\ue587",
"arrowthinup": "\ue584",
"undo-filled": "\ue7d6",
"undo": "\ue406",
"redo": "\ue405",
"redo-filled": "\ue7d9",
"bars": "\ue563",
"chatboxes": "\ue203",
"camera": "\ue301",
"chatboxes-filled": "\ue233",
"camera-filled": "\ue7ef",
"cart-filled": "\ue7f4",
"cart": "\ue7f5",
"checkbox-filled": "\ue442",
"checkbox": "\ue7fa",
"arrowleft": "\ue582",
"arrowdown": "\ue581",
"arrowright": "\ue583",
"smallcircle-filled": "\ue801",
"arrowup": "\ue580",
"circle": "\ue411",
"eye-filled": "\ue568",
"eye-slash-filled": "\ue822",
"eye-slash": "\ue823",
"eye": "\ue824",
"flag-filled": "\ue825",
"flag": "\ue508",
"gear-filled": "\ue532",
"reload": "\ue462",
"gear": "\ue502",
"hand-thumbsdown-filled": "\ue83b",
"hand-thumbsdown": "\ue83c",
"hand-thumbsup-filled": "\ue83d",
"heart-filled": "\ue83e",
"hand-thumbsup": "\ue83f",
"heart": "\ue840",
"home": "\ue500",
"info": "\ue504",
"home-filled": "\ue530",
"info-filled": "\ue534",
"circle-filled": "\ue441",
"chat-filled": "\ue847",
"chat": "\ue263",
"mail-open-filled": "\ue84d",
"email-filled": "\ue231",
"mail-open": "\ue84e",
"email": "\ue201",
"checkmarkempty": "\ue472",
"list": "\ue562",
"locked-filled": "\ue856",
"locked": "\ue506",
"map-filled": "\ue85c",
"map-pin": "\ue85e",
"map-pin-ellipse": "\ue864",
"map": "\ue364",
"minus-filled": "\ue440",
"mic-filled": "\ue332",
"minus": "\ue410",
"micoff": "\ue360",
"mic": "\ue302",
"clear": "\ue434",
"smallcircle": "\ue868",
"close": "\ue404",
"closeempty": "\ue460",
"paperclip": "\ue567",
"paperplane": "\ue503",
"paperplane-filled": "\ue86e",
"person-filled": "\ue131",
"contact-filled": "\ue130",
"person": "\ue101",
"contact": "\ue100",
"images-filled": "\ue87a",
"phone": "\ue200",
"images": "\ue87b",
"image": "\ue363",
"image-filled": "\ue877",
"location-filled": "\ue333",
"location": "\ue303",
"plus-filled": "\ue439",
"plus": "\ue409",
"plusempty": "\ue468",
"help-filled": "\ue535",
"help": "\ue505",
"navigate-filled": "\ue884",
"navigate": "\ue501",
"mic-slash-filled": "\ue892",
"search": "\ue466",
"settings": "\ue560",
"sound": "\ue590",
"sound-filled": "\ue8a1",
"spinner-cycle": "\ue465",
"download-filled": "\ue8a4",
"personadd-filled": "\ue132",
"videocam-filled": "\ue8af",
"personadd": "\ue102",
"upload": "\ue402",
"upload-filled": "\ue8b1",
"starhalf": "\ue463",
"star-filled": "\ue438",
"star": "\ue408",
"trash": "\ue401",
"phone-filled": "\ue230",
"compose": "\ue400",
"videocam": "\ue300",
"trash-filled": "\ue8dc",
"download": "\ue403",
"chatbubble-filled": "\ue232",
"chatbubble": "\ue202",
"cloud-download": "\ue8e4",
"cloud-upload-filled": "\ue8e5",
"cloud-upload": "\ue8e6",
"cloud-download-filled": "\ue8e9",
"headphones":"\ue8bf",
"shop":"\ue609"
}
<template>
<text :style="{ color: color, 'font-size': size + 'px' }" class="uni-icons" :class="[fontFamily,fontFamily?type:'']" @click="_onClick">{{fontFamily ? '' : icons[type]}}</text>
</template>
<script>
import icons from './icons.js';
// #ifdef APP-NVUE
var domModule = weex.requireModule('dom');
domModule.addRule('fontFace', {
'fontFamily': "uniicons",
'src': "url('data:font/truetype;charset=utf-8;base64,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AXcBeAF5AXoBewF8AX0BfgF/AYABgQGCAYMHdW5pMDAwMAdjb250YWN0BnBlcnNvbglwZXJzb25hZGQNY29udGFjdGZpbGxlZAxwZXJzb25maWxsZWQPcGVyc29uYWRkZmlsbGVkBXBob25lBWVtYWlsCmNoYXRidWJibGUJY2hhdGJveGVzC3Bob25lZmlsbGVkC2VtYWlsZmlsbGVkEGNoYXRidWJibGVmaWxsZWQPY2hhdGJveGVzZmlsbGVkBXdlaWJvBndlaXhpbgtwZW5neW91cXVhbgRjaGF0AnFxCHZpZGVvY2FtBmNhbWVyYQNtaWMIbG9jYXRpb24JbWljZmlsbGVkDmxvY2F0aW9uZmlsbGVkBm1pY29mZgVpbWFnZQNtYXAHY29tcG9zZQV0cmFzaAZ1cGxvYWQIZG93bmxvYWQFY2xvc2UEcmVkbwR1bmRvB3JlZnJlc2gEc3Rhcgt3aGl0ZWNpcmNsZQVjbGVhcg1yZWZyZXNoZmlsbGVkCnN0YXJmaWxsZWQKcGx1c2ZpbGxlZAttaW51c2ZpbGxlZAxjaXJjbGVmaWxsZWQOY2hlY2tib3hmaWxsZWQKY2xvc2VlbXB0eQxyZWZyZXNoZW1wdHkGcmVsb2FkCHN0YXJoYWxmDHNwaW5uZXJjeWNsZQZzZWFyY2gJcGx1c2VtcHR5B2ZvcndhcmQEYmFjaw5jaGVja21hcmtlbXB0eQRob21lCG5hdmlnYXRlBGdlYXIKcGFwZXJwbGFuZQRpbmZvBGhlbHAGbG9ja2VkBG1vcmUEZmxhZwpob21lZmlsbGVkCmdlYXJmaWxsZWQKaW5mb2ZpbGxlZApoZWxwZmlsbGVkCm1vcmVmaWxsZWQIc2V0dGluZ3MEbGlzdARiYXJzBGxvb3AJcGFwZXJjbGlwCWV5ZWZpbGxlZAx1cHdhcmRzYXJyb3cOZG93bndhcmRzYXJyb3cObGVmdHdhcmRzYXJyb3cPcmlnaHR3YXJkc2Fycm93C2Fycm93dGhpbnVwDWFycm93dGhpbmRvd24NYXJyb3d0aGlubGVmdA5hcnJvd3RoaW5yaWdodAhwdWxsZG93bgVzb3VuZARzaG9wBHNjYW4KdW5kb2ZpbGxlZApyZWRvZmlsbGVkDGNhbWVyYWZpbGxlZApjYXJ0ZmlsbGVkBGNhcnQIY2hlY2tib3gRc21hbGxjaXJjbGVmaWxsZWQOZXllc2xhc2hmaWxsZWQIZXllc2xhc2gDZXllCmZsYWdmaWxsZWQVaGFuZHRodW1ic2Rvd25fZmlsbGVkDmhhbmR0aHVtYnNkb3duEmhhbmR0aHVtYnN1cGZpbGxlZAtoZWFydGZpbGxlZAxoYW5kdGh1bWJzdXAOYmxhY2toZWFydHN1aXQKY2hhdGZpbGxlZA5tYWlsb3BlbmZpbGxlZAhtYWlsb3Blbgxsb2NrZWRmaWxsZWQJbWFwZmlsbGVkBm1hcHBpbg1tYXBwaW5lbGxpcHNlC3NtYWxsY2lyY2xlEHBhcGVycGxhbmVmaWxsZWQLaW1hZ2VmaWxsZWQMaW1hZ2VzZmlsbGVkBmltYWdlcw5uYXZpZ2F0ZWZpbGxlZA5taWNzbGFzaGZpbGxlZAtzb3VuZGZpbGxlZA5kb3dubG9hZGZpbGxlZA52aWRlb2NhbWZpbGxlZAx1cGxvYWRmaWxsZWQKaGVhZHBob25lcwt0cmFzaGZpbGxlZA1jbG91ZGRvd25sb2FkEWNsb3VkdXBsb2FkZmlsbGVkC2Nsb3VkdXBsb2FkE2Nsb3VkZG93bmxvYWRmaWxsZWQHdW5pMDAwOQAAAAAB//8AAgABAAAADAAAABYAAAACAAEAAQCFAAEABAAAAAIAAAAAAAAAAQAAAADVpCcIAAAAANoxE3MAAAAA2jSpUA==')"
});
// #endif
/**
* Icons 图标
* @description 用于展示 icons 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: 'UniIcons',
emits:['click'],
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
fontFamily:{
type: String,
default: ''
}
},
data() {
return {
icons: icons
}
},
methods: {
_onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
/* #ifndef APP-NVUE */
@font-face {
font-family: uniicons;
src: url('./uni.ttf') format('truetype');
}
/* #endif */
.uni-icons {
font-family: uniicons;
text-decoration: none;
text-align: center;
}
</style>
{
"uni-search-bar.cancel": "cancel",
"uni-search-bar.placeholder": "Search enter content"
}
\ No newline at end of file
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}
{
"uni-search-bar.cancel": "cancel",
"uni-search-bar.placeholder": "请输入搜索内容"
}
{
"uni-search-bar.cancel": "cancel",
"uni-search-bar.placeholder": "請輸入搜索內容"
}
<template>
<view class="uni-searchbar">
<view :style="{borderRadius:radius+'px',backgroundColor: bgColor}" class="uni-searchbar__box" @click="searchClick">
<view class="uni-searchbar__box-icon-search">
<slot name="searchIcon">
<uni-icons color="#999999" size="18" type="search" />
</slot>
</view>
<input v-if="show || searchVal" :focus="showSync" :placeholder="placeholderText" :maxlength="maxlength" class="uni-searchbar__box-search-input"
confirm-type="search" type="text" v-model="searchVal" @confirm="confirm" @blur="blur" @focus="emitFocus" />
<text v-else class="uni-searchbar__text-placeholder">{{ placeholder }}</text>
<view v-if="show && (clearButton==='always'||clearButton==='auto'&&searchVal!=='')" class="uni-searchbar__box-icon-clear"
@click="clear">
<slot name="clearIcon">
<uni-icons color="#c0c4cc" size="15" type="clear" />
</slot>
</view>
</view>
<text @click="cancel" class="uni-searchbar__cancel" v-if="cancelButton ==='always' || show && cancelButton ==='auto'">{{cancelTextI18n}}</text>
</view>
</template>
<script>
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const { t } = initVueI18n(messages)
/**
* SearchBar 搜索栏
* @description 评分组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=866
* @property {Number} radius 搜索栏圆角
* @property {Number} maxlength 输入最大长度
* @property {String} placeholder 搜索栏Placeholder
* @property {String} clearButton = [always|auto|none] 是否显示清除按钮
* @value always 一直显示
* @value auto 输入框不为空时显示
* @value none 一直不显示
* @property {String} cancelButton = [always|auto|none] 是否显示取消按钮
* @value always 一直显示
* @value auto 输入框不为空时显示
* @value none 一直不显示
* @property {String} cancelText 取消按钮的文字
* @property {String} bgColor 输入框背景颜色
* @property {Boolean} focus 是否自动聚焦
* @event {Function} confirm uniSearchBar 的输入框 confirm 事件,返回参数为uniSearchBar的value,e={value:Number}
* @event {Function} input uniSearchBar 的 value 改变时触发事件,返回参数为uniSearchBar的value,e=value
* @event {Function} cancel 点击取消按钮时触发事件,返回参数为uniSearchBar的value,e={value:Number}
* @event {Function} clear 点击清除按钮时触发事件,返回参数为uniSearchBar的value,e={value:Number}
* @event {Function} blur input失去焦点时触发事件,返回参数为uniSearchBar的value,e={value:Number}
*/
export default {
name: "UniSearchBar",
emits:['input','update:modelValue','clear','cancel','confirm','blur','focus'],
props: {
placeholder: {
type: String,
default: ""
},
radius: {
type: [Number, String],
default: 5
},
clearButton: {
type: String,
default: "auto"
},
cancelButton: {
type: String,
default: "auto"
},
cancelText: {
type: String,
default: '取消'
},
bgColor: {
type: String,
default: "#F8F8F8"
},
maxlength: {
type: [Number, String],
default: 100
},
value: {
type: [Number, String],
default: ""
},
modelValue: {
type: [Number, String],
default: ""
},
focus: {
type: Boolean,
default: false
}
},
data() {
return {
show: false,
showSync: false,
searchVal: ''
}
},
computed:{
cancelTextI18n() {
return this.cancelText || t("uni-search-bar.cancel")
},
placeholderText() {
return this.placeholder || t("uni-search-bar.placeholder")
}
},
watch: {
// #ifndef VUE3
value: {
immediate: true,
handler(newVal) {
this.searchVal = newVal
if (newVal) {
this.show = true
}
}
},
// #endif
// #ifdef VUE3
modelValue: {
immediate: true,
handler(newVal) {
this.searchVal = newVal
if (newVal) {
this.show = true
}
}
},
// #endif
focus: {
immediate: true,
handler(newVal) {
if (newVal) {
this.show = true;
this.$nextTick(() => {
this.showSync = true
})
}
}
},
searchVal(newVal, oldVal) {
// #ifndef VUE3
this.$emit("input", newVal)
// #endif
// #ifdef VUE3
this.$emit("update:modelValue", newVal)
// #endif
}
},
methods: {
searchClick() {
if (this.show) {
return
}
this.show = true;
this.$nextTick(() => {
this.showSync = true
})
},
clear() {
this.$emit("clear", {
value: this.searchVal
})
this.searchVal = ""
},
cancel() {
this.$emit("cancel", {
value: this.searchVal
});
this.searchVal = ""
this.show = false
this.showSync = false
// #ifndef APP-PLUS
uni.hideKeyboard()
// #endif
// #ifdef APP-PLUS
plus.key.hideSoftKeybord()
// #endif
},
confirm() {
// #ifndef APP-PLUS
uni.hideKeyboard();
// #endif
// #ifdef APP-PLUS
plus.key.hideSoftKeybord()
// #endif
this.$emit("confirm", {
value: this.searchVal
})
},
blur() {
// #ifndef APP-PLUS
uni.hideKeyboard();
// #endif
// #ifdef APP-PLUS
plus.key.hideSoftKeybord()
// #endif
this.$emit("blur", {
value: this.searchVal
})
},
emitFocus(e) {
this.$emit("focus", e.detail)
}
}
};
</script>
<style lang="scss" scoped>
$uni-searchbar-height: 32px;
.uni-searchbar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
position: relative;
padding: $uni-spacing-col-base;
// background-color: $uni-bg-color;
}
.uni-searchbar__box {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
overflow: hidden;
position: relative;
flex: 1;
justify-content: center;
flex-direction: row;
align-items: center;
height: $uni-searchbar-height;
padding: 5px 8px 5px 0px;
border-width: 0.5px;
border-style: solid;
border-color: $uni-border-color;
}
.uni-searchbar__box-icon-search {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
// width: 32px;
padding: 0 8px;
justify-content: center;
align-items: center;
color: $uni-text-color-placeholder;
}
.uni-searchbar__box-search-input {
flex: 1;
font-size: $uni-font-size-sm;
color: $uni-text-color;
}
.uni-searchbar__box-icon-clear {
align-items: center;
line-height: 24px;
padding-left: 8px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-searchbar__text-placeholder {
font-size: $uni-font-size-sm;
color: $uni-text-color-placeholder;
margin-left: 5px;
}
.uni-searchbar__cancel {
padding-left: 10px;
line-height: $uni-searchbar-height;
font-size: 12px;
color: $uni-text-color;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment