代码更新

This commit is contained in:
jia 2023-11-09 09:44:44 +08:00
parent 2c47202245
commit 35db378ced
154 changed files with 24722 additions and 8832 deletions

View File

@ -25,7 +25,10 @@
@import "static/css/base.css";
@import "static/css/style.scss";
/deep/.u-action-sheet__item-wrap {
overflow: auto;
max-height: 50vh;
}
@import url('static/iconfont/iconfont.css')
</style>

View File

@ -12,7 +12,7 @@ export const getSalaryDeatilsApi = () => oahttp.get('/user/info')
//获取部门
export const getDepartmentApi = () => oahttp.get('/index/get_department_tree')
//获取部门人员
export const getDepartmentPersonApi = (data) => oahttp.get('/index/get_employee', data)
export const getDepartmentPersonApi = (data) => oahttp.get('/common/get_employee', data)
//新建任务
export const addNewTaskApi = (data) => oahttp.post('/task/add', data)
//获取文档列表
@ -74,8 +74,7 @@ export const comanyerlist = (data) => oahttp.get('/user_user/index',data)
//获取企业员工详情
export const comanyerview= (data) => oahttp.get('/user_user/view',data)
//编辑员工状态
//新增编辑员工
export const addcomanyer= (data) => oahttp.post('/user_user/add',data)
@ -171,7 +170,7 @@ export const appexpenseindex = (data) => oahttp.get('/finance_expense/index',dat
//获取我处理的报销记录
export const apexpenselist = (data) => oahttp.get('/finance_expense/list',data)
//获取抄送的报销记录
export const appexpensecopy = (data) => oahttp.get('/oa_approve/copy',data)
export const appexpensecopy = (data) => oahttp.get('/finance_expense/copy',data)
//报销类型
export const appexpensecate = (data) => oahttp.get('/common/get_expense_cate',data)
@ -230,18 +229,30 @@ export const tasksubjec = (data) => oahttp.get('/common/get_task_list',data)
//获取发票开票
export const invoicecheckedlist = (data) => oahttp.get('/finance_invoice/checkedlist',data)
//发票开票/修改
export const financeopen = (data) => oahttp.get('/finance_invoice/open',data)
export const financeopen = (data) => oahttp.post('/finance_invoice/open',data)
//发票作废
export const tovoid = (data) => oahttp.post('/finance_invoice/tovoid',data)
//发票反作废
export const novoid = (data) => oahttp.get('/finance_invoice/novoid',data)
export const novoid = (data) => oahttp.post('/finance_invoice/novoid',data)
//获取发票到账信息
export const incomeflow = (data) => oahttp.get('/finance_income/index',data)
//获取发票到账详情
export const incomeview = (data) => oahttp.get('/finance_income/view',data)
//新增发票到账
export const addincome = (data) => oahttp.post('/finance_income/add',data)
//删除发票到账
export const deleteincome = (data) => oahttp.post('/finance_income/delete',data)
// 获取未读消息数
export const msgcount = (data) => oahttp.get('/common/msg_count',data)
export const deletemessage = (data) => oahttp.post('/message_index/delete',data)

View File

@ -36,7 +36,7 @@ function oaUploads(src, type) {
// console.log('上传', type === 'img' ? '图片' : '视频', '', src)
let a = uni.uploadFile({
url: HTTP_REQUEST_URL_THREE + '/api/home_index/upload',
url: HTTP_REQUEST_URL_THREE + '/api/common/upload',
filePath: src,
name: 'file',
header: header,

View File

@ -24,10 +24,7 @@
"path": "pages/oaLogin/editpwd",
"style": {
"navigationBarTitleText": "修改密码",
"app-plus": {
"titleNView": false,
"bounce": "none"
},
"navigationBarBackgroundColor": "#3274F9",
"navigationBarTextStyle": "white"
}
@ -62,8 +59,9 @@
{
"path": "pages/examine/index",
"style": {
"navigationBarTitleText": "审批"
"navigationBarTitleText": "审批",
"navigationBarBackgroundColor": "#3274F9",
"navigationBarTextStyle": "white"
}
},
{
@ -77,15 +75,18 @@
{
"path": "pages/leaveapplication/index",
"style": {
"navigationBarTitleText": "申请"
"navigationBarTitleText": "申请",
"navigationBarBackgroundColor": "#3274F9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/resources/index",
"style": {
"navigationBarTitleText": "人力资源"
"navigationBarTitleText": "人力资源",
"navigationBarBackgroundColor": "#3274F9",
"navigationBarTextStyle": "white"
}
},
@ -194,6 +195,7 @@
"path": "pages/addquest/index",
"style": {
"navigationBarTitleText": "新增任务",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#3274F9"
}
@ -211,6 +213,7 @@
"path": "pages/reimbursementrecord/index",
"style": {
"navigationBarTitleText": "报销打款",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#3274F9"
}
@ -384,7 +387,7 @@
{
"path": "pages/addfinance/index",
"style": {
"navigationBarTitleText": "编辑到账",
"navigationBarTitleText": "新增到账",
"navigationBarBackgroundColor": "#3274F9",
"navigationBarTextStyle": "white"
}
@ -404,8 +407,15 @@
"navigationBarBackgroundColor": "#3274F9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/waitlist/index",
"style": {
"navigationBarTitleText": "待办列表",
"navigationBarBackgroundColor": "#3274F9",
"navigationBarTextStyle": "white"
}
}
],
"globalStyle": {

View File

@ -10,7 +10,7 @@
<view class="acea-row row-middle">
<text class="item-name">发票id:</text>
<input placeholder="请输入发票id" type="text" readonly v-model="merchantData.id">
<input placeholder="请输入发票id" type="text" readonly disabled v-model="typename" @click="show4=true">
</view>
</view>
@ -26,7 +26,7 @@
<view class="acea-row row-middle">
<text class="item-name">备注:</text>
<input placeholder="请输入备注" type="text" readonly v-model="merchantData.remarks">
<input placeholder="请输入备注" type="text" readonly v-model="remarks">
</view>
</view>
@ -51,7 +51,9 @@
</view>
</form>
<u-action-sheet :show="show3" :actions="actions3" title="请选择任务性质" @close="show3 = false" @select="Select3">
<u-action-sheet :show="show3" :actions="actions3" title="请选择发票类型" @close="show3 = false" @select="Select3">
</u-action-sheet>
<u-action-sheet :show="show4" :actions="actions4" title="请选择发票列表" @close="show4 = false" @select="Select4">
</u-action-sheet>
<u-datetime-picker :show="endmonth" mode="date" closeOnClickOverlay @confirm="end_monthConfirm"
@cancel="end_monthClose" @close="end_monthClose"></u-datetime-picker>
@ -67,8 +69,9 @@
<script>
import {
userpositiongroup,
financeopen
addincome,
invoicecheckedlist
} from '@/api/oa.js'
import {
Toast
@ -84,10 +87,11 @@
name: "",
merchantData: {
},
show3: false,
show4: false,
amount: '',
remarks:'',
actions3: [{
name: '部分到账',
id: 1
@ -100,23 +104,35 @@
}],
uidtype: 1,
actions4: [],
typename:''
};
},
onLoad(option) {
this.type = option.type
if (this.type == 1) {
// this.buname=
}
this.datalist()
},
methods: {
async datalist() {
let res = await invoicecheckedlist({
page: 1,
limit: 1000
});
console.log(res)
const detailTypeOptions = res.data.data.map((step, index) => {
return {
name: step.name +','+ step.amount,
id: step.id,
};
});
// console.log(detailTypeOptions)
this.actions4=detailTypeOptions
},
//
end_monthClose() {
this.endmonth = false
@ -140,18 +156,26 @@
this.uidtype = e.id
this.name = e.name
},
Select4(e) {
// console.log(e)
this.merchantData.inid = e.id
this.typename = e.name
},
validateForm: function() {
this.merchantData.amount = []
this.merchantData.remarks=[]
this.merchantData.amount.push(this.amount)
this.merchantData.remarks.push(this.remarks)
let that = this,
value = that.merchantData;
// console.log(this.merchantData)
if (!value.enter_type) return Toast(
'请选择到账类型'
);
if (!value.id) return Toast('请输入发票 id');
if (!value.inid) return Toast('请输入发票 id');
if (!value.enter_time) return Toast('请输入到账时间');
if (!value.amount.length>0) return Toast('请输入金额');
if (!value.amount.length > 0) return Toast('请输入金额');
that.validate = true;
return true;
},
@ -161,7 +185,7 @@
let that = this
console.log(that.validate)
if (this.validateForm() && this.validate) {
const res = await financeopen(this.merchantData)
const res = await addincome(this.merchantData)
Toast(res.msg);
if (res.code == 0) {
uni.navigateBack({

View File

@ -176,25 +176,33 @@
</u-picker>
<u-datetime-picker :show="porject1month" mode="date" closeOnClickOverlay @confirm="porject1_monthConfirm"
<!-- <u-datetime-picker :show="porject1month" mode="date" closeOnClickOverlay @confirm="porject1_monthConfirm"
@cancel="porject1_monthClose" @close="porject1_monthClose"></u-datetime-picker>
<u-datetime-picker :show="porject2month" mode="date" closeOnClickOverlay @confirm="porject2_monthConfirm"
@cancel="porject2_monthClose" @close="porject2_monthClose"></u-datetime-picker>
<u-datetime-picker :show="endmonth" mode="date" closeOnClickOverlay @confirm="end_monthConfirm"
@cancel="end_monthClose" @close="end_monthClose"></u-datetime-picker>
<u-datetime-picker :show="startmonth" mode="date" closeOnClickOverlay @confirm="start_monthConfirm"
@cancel="start_monthClose" @close="start_monthClose"></u-datetime-picker>
<view class="content-bottom"> -->
<u-calendar :show="porject1month" mode='single' @confirm="porject1_monthConfirm"
@close="porject1_monthClose"></u-calendar>
<u-calendar :show="porject2month" mode='single' @confirm="porject2_monthConfirm"></u-calendar>
<u-calendar :show="endmonth" mode='single' @confirm="end_monthConfirm" @close="end_monthClose"></u-calendar>
<u-calendar :show="startmonth" mode='single' @confirm="start_monthConfirm"
@close="start_monthClose"></u-calendar>
<view class="content-bottom">
<view class="content-bottom-two" @click="submit">
立即提交
</view>
</view>
</view>
</view>
</view>
</template>
@ -272,8 +280,7 @@
companyrole: '',
start_time: '',
end_time: ''
}
, //
}, //
{
name: '验收阶段',
companyrole: '',
@ -506,10 +513,8 @@
}
})
let dat = await getemployee({
did: codelist[1][0].id
did: codelist[0][0].id
})
codelist[2] = dat.data.map((item) => {
@ -655,7 +660,8 @@
porject1_monthConfirm(e) {
this.porject1month = false
this.phases[this.num].start_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
// this.phases[this.num].start_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
this.phases[this.num].start_time = e[0]
this.$nextTick(() => {
this.validatePhaseTime(this.num)
})
@ -664,7 +670,8 @@
},
porject2_monthConfirm(e) {
this.porject2month = false
this.phases[this.num].end_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
// this.phases[this.num].end_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
this.phases[this.num].end_time = e[0]
this.$nextTick(() => {
this.validatePhaseTime(this.num)
})
@ -687,8 +694,8 @@
},
end_monthConfirm(e) {
this.endmonth = false
this.merchantData.end_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
// this.merchantData.end_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
this.merchantData.end_time = e[0]
},
//
start_monthClose() {
@ -697,7 +704,8 @@
},
start_monthConfirm(e) {
this.startmonth = false
this.merchantData.start_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
// this.merchantData.start_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
this.merchantData.start_time = e[0]
},
validatePhaseTime(index) {
@ -707,7 +715,7 @@
Toast('项目时间范围不合法')
}
const phase = this.phases[index]
console.log(phase.start_time, this.merchantData.start_time)
// console.log(phase.start_time, this.merchantData.start_time)
//
if (new Date(phase.start_time) < new Date(this.merchantData.start_time) || new Date(
phase.end_time) >

View File

@ -25,14 +25,15 @@
closeOnClickOverlay @close="flag1 = false" @change="columnCode" @confirm="confirm"
keyName="name" :defaultIndex='defaultIndex'>
</u-picker>
<!-- 协作人 -->
<u-picker :show="flag2" :columns="columns3" ref="uPicker5" @cancel="cancel"
<u-picker :show="flag2" :columns="columns4" ref="uPicker5" @cancel="cancel"
@close="flag2 = false" @confirm="confirm1" @change="columnCode1" keyName="name"
closeOnClickOverlay :defaultIndex='defaultIndex1'>
</u-picker>
<!-- 验收人 -->
<u-picker :show="flag3" :columns="columns3" ref="uPicker6" @cancel="cancel"
<u-picker :show="flag3" :columns="columns5" ref="uPicker6" @cancel="cancel"
@close="flag3 = false" @confirm="confirm" @change="columnCode2" keyName="name"
closeOnClickOverlay :defaultIndex='defaultIndex2'>
</u-picker>
@ -56,8 +57,8 @@
</u-form-item>
<!-- 月份 -->
<u-form-item label="预估工时:" prop="userInfo.plan_hours" borderBottom
@click="endmonth = true; hideKeyboard()" ref="item5">
<u--input v-model="model1.userInfo.plan_hours" disabled disabledColor="#ffffff"
ref="item5">
<u--input v-model="model1.userInfo.plan_hours" disabledColor="#ffffff"
placeholder="请选择" border="none"></u--input>
</u-form-item>
<!-- 关联项目 -->
@ -110,9 +111,11 @@
@confirm="start_timeConfirm" @cancel="start_timeClose"
@close="start_timeClose"></u-datetime-picker> -->
<!-- 结束日期 -->
<u-datetime-picker :show="endtime" :value="end_time" mode="datetime" closeOnClickOverlay
<!-- <u-datetime-picker :show="endtime" :value="end_time" mode="datetime" closeOnClickOverlay
@confirm="end_timeConfirm" @cancel="end_timeClose"
@close="end_timeClose"></u-datetime-picker>
@close="end_timeClose"></u-datetime-picker> -->
<u-calendar :show="endtime"
color="#f56c6c" mode='single' @close="end_timeClose" @confirm="end_timeConfirm" @cancel="end_timeClose"></u-calendar>
<!-- 结束月份 -->
<u-datetime-picker :show="endmonth" :value="plan_hours" mode="date" closeOnClickOverlay
@confirm="end_monthConfirm" @cancel="end_monthClose"
@ -157,6 +160,8 @@
defaultIndex1: [0, 0, 0],
defaultIndex2: [0, 0, 0],
columns3: [],
columns4: [],
columns5: [],
director_name: '', //
assist_admin_names: '', //
priority_status: '', //
@ -496,8 +501,9 @@
},
end_timeConfirm(e) {
this.endtime = false
this.model1.userInfo.end_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.$refs.form1.validateField('userInfo.end_time')
console.log(e)
this.model1.userInfo.end_time = e[0]
// this.$refs.form1.validateField('userInfo.end_time')
},
//
end_monthClose() {

View File

@ -1,6 +1,6 @@
<template>
<view class="application">
<block v-for="item in appDataList" :key="item.title">
<block v-for="(item,i) in appDataList" :key="i">
<view class="apply_line">
<view class="title flex_a_c">
<view class="text flex_a_c">
@ -71,9 +71,20 @@
},
lineCentClick(item) {
if(item.id=='9998'){
uni.navigateTo({
url: '/pages/resources/index'
})
}else if(item.id=='9999'){
uni.navigateTo({
url: '/pages/finance/index'
})
}else{
uni.navigateTo({
url: '/pages/leaveapplication/index?type='+item.id
})
}
},

View File

@ -23,11 +23,12 @@
</view>
<view class="one-titleb"
:style="{'color':item.status==1?'#34A853':item.status==2?'#F9AA32':item.status==3?'#F02828':''}">
<text v-if='item.status==1'>已通过</text>
<text v-if='item.status==2'>待审批</text>
<text v-if='item.status==3'>审批拒绝</text>
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
</view>
</view>
<view class="middle-one-time">类型{{item.flow_type}}</view>
@ -84,23 +85,7 @@
],
list2: [],
num: 0,
btnList: [{
id: '1',
name: '编辑',
width: '100rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}, {
id: '2',
name: '删除',
height: '100rpx',
width: '100rpx',
bgColor: '#ed656d',
color: '#FFFFFF',
fontSize: '28rpx'
}],
btnList: [],
loadConfig: {
page: 1,
limit: 5,
@ -248,8 +233,15 @@
})
if (res.code == 0) {
this.loadConfig.status = "loading"
if (this.num == 0) {
this.initList()
} else if (this.num == 1) {
this.initList1()
} else {
this.initList2()
}
}
Toast(res.msg);
@ -262,16 +254,16 @@
})
},
tian(item) {
console.log(item)
// console.log(item)
if (this.num == 1) {
uni.navigateTo({
url: '/pages/leave_request/index?type=' + item.id
})
}else if (this.num == 2){
} else if (this.num == 2) {
uni.navigateTo({
url: '/pages/leave_request/detail?type=' + item.id +'&num=0'
url: '/pages/leave_request/detail?type=' + item.id + '&num=0'
})
}else{
} else {
uni.navigateTo({
url: '/pages/leave_request/detail?type=' + item.id
})

View File

@ -7,7 +7,7 @@
<view class="middle-three">
<view class="middle-threeb" v-for="(items,j) in list" :key='j' @click="deleteDepartment(items.id)">
<view class="middle-threeb" v-for="(items,j) in list" :key='j'>
<liu-swipe-action :index="j" @clickItem="clickItem" :btnList='btnList'>
<view class="middle-threeb-title">
<view class="title">
@ -22,30 +22,19 @@
<view class="middle-threeb-a">
<view class="threeb-a-b">
<view class="name">开票人<text>{{items.open_name}}</text></view>
<view class="name">开票银行<text>{{items.invoice_account}}</text></view>
<view class="name">开票金额()<text>{{items.amount}}</text></view>
<!-- <view class="achor">{{items.name2}}</view> -->
<!-- <view class="middle-edit" @click="editpart(items)">
编辑
</view>
<view class="middle-delete" @click="clickItem(items)">
删除
</view> -->
</view>
</view>
<!-- <view class="middle-threeb-b">
{{items.name}}
</view> -->
</view>
</liu-swipe-action>
</view>
</view>
</view>
</view>
@ -98,7 +87,9 @@
<script>
import {
incomeflow,
deleteincomeflow
deleteincome,
incomeview,
invoicecheckedlist,
} from '@/api/oa.js'
import {
HTTP_REQUEST_URL
@ -112,14 +103,6 @@
return {
show: false,
btnList: [{
id: '1',
name: '编辑',
width: '100rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}, {
id: '2',
name: '删除',
height: '100rpx',
@ -163,13 +146,14 @@
uni.stopPullDownRefresh()
},
methods: {
async initList() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list = [];
await this.getDocumentList();
},
//
//
async getDocumentList() {
if (this.loadConfig.status == "nomore") return;
this.loadConfig.status = "loading"
@ -197,15 +181,21 @@
})
},
//
clickItem(data) {
async clickItem(data) {
// console.log(e)
if (data.id == 2) {
let res = await incomeview({
id: this.list[data.index].id
})
let data1=res.data
uni.showModal({
content: '确定要删除吗',
success: (e) => {
if (e.confirm) {
this.deleteDepartment(this.list[data.index].id)
this.deleteDepartment(this.list[data.index].id,data1.income[0].inid)
}
}
})
@ -216,10 +206,11 @@
},
//
async deleteDepartment(id) {
async deleteDepartment(id,inid) {
const res = await deleteincomeflow({
id: id
const res = await deleteincome({
id: id,
inid:inid
})
if (res.code == 0) {
@ -499,15 +490,15 @@
background: #3274F9;
border-radius: 60rpx 60rpx;
z-index: 99999;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
display: flex;
margin: 0 auto;
margin-top: 25rpx;
position: fixed;
left: 50%;
margin-left: -193rpx;
bottom: 100rpx;
.middle-two-img {
width: 23rpx;

View File

@ -7,8 +7,10 @@
<view class="middle-three">
<view class="middle-threeb" v-for="(items,j) in list" :key='j' @click="deleteDepartment(items.id)">
<liu-swipe-action :index="j" @clickItem="clickItem" :btnList='btnList'>
<view class="middle-threeb" v-for="(items,j) in list" :key='j'>
<liu-swipe-action :index="j" @clickItem="clickItem" :btnList='btnList1'
v-if="items.check_status==2">
<view class="middle-threeb-title">
<view class="title">
{{items.name}}
@ -20,29 +22,98 @@
<view class="middle-threeba">
<view class="middle-threeb-a">
<!-- check_status 状态0待审核,1审核中,2审核通过,3审核不通过,4已撤销,5已开具,10已作废 -->
<view class="threeb-a-b">
<view class="name">开票人<text>{{items.open_name}}</text></view>
<view class="name">所属部门<text>{{items.department_name}}</text></view>
<view class="name">开票金额()<text>{{items.amount}}</text></view>
<view class="name">
开票类型<text>{{items.invoice_type==1?'增值税专用发票':items.invoice_type==2?'普通发票':'专用发票'}}</text>
</view>
<!-- <view class="achor">{{items.name2}}</view> -->
<!-- <view class="middle-edit" @click="editpart(items)">
编辑
</view>
<view class="middle-delete" @click="clickItem(items)">
删除
</view> -->
</view>
</view>
</view>
</liu-swipe-action>
<liu-swipe-action :index="j" @clickItem="clickItem" :btnList='btnList'
v-if="items.check_status==5">
<view class="middle-threeb-title">
<view class="title">
{{items.name}}
</view>
<view class="title-status">
{{items.create_time}}
</view>
</view>
<view class="middle-threeba">
<view class="middle-threeb-a">
<!-- check_status 状态0待审核,1审核中,2审核通过,3审核不通过,4已撤销,5已开具,10已作废 -->
<view class="threeb-a-b">
<view class="name">开票人<text>{{items.open_name}}</text></view>
<view class="name">所属部门<text>{{items.department_name}}</text></view>
<view class="name">开票金额()<text>{{items.amount}}</text></view>
<view class="name">
开票类型<text>{{items.invoice_type==1?'增值税专用发票':items.invoice_type==2?'普通发票':'专用发票'}}</text>
</view>
<!-- <view class="achor">{{items.name2}}</view> -->
<view class="middle-edit" @click="editpart(items)">
<!-- <view class="middle-edit" @click="editpart(items)">
编辑
</view>
<view class="middle-delete" @click="clickItem(items)">
删除
</view>
</view>
</view>
<!-- <view class="middle-threeb-b">
{{items.name}}
</view> -->
</view>
</view>
</view>
</liu-swipe-action>
<liu-swipe-action :index="j" @clickItem="clickItem" :btnList='btnList2'
v-if="items.check_status==10">
<view class="middle-threeb-title">
<view class="title">
{{items.name}}
</view>
<view class="title-status">
{{items.create_time}}
</view>
</view>
<view class="middle-threeba">
<view class="middle-threeb-a">
<!-- check_status 状态0待审核,1审核中,2审核通过,3审核不通过,4已撤销,5已开具,10已作废 -->
<view class="threeb-a-b">
<view class="name">开票人<text>{{items.open_name}}</text></view>
<view class="name">所属部门<text>{{items.department_name}}</text></view>
<view class="name">开票金额()<text>{{items.amount}}</text></view>
<view class="name">
开票类型<text>{{items.invoice_type==1?'增值税专用发票':items.invoice_type==2?'普通发票':'专用发票'}}</text>
</view>
<!-- <view class="achor">{{items.name2}}</view> -->
<!-- <view class="middle-edit" @click="editpart(items)">
编辑
</view>
<view class="middle-delete" @click="clickItem(items)">
删除
</view> -->
</view>
</view>
</view>
</liu-swipe-action>
</view>
@ -81,12 +152,7 @@
</view>
</view>
</view>
<view class="content-middle-two">
<view class="middle-two-img">
<image src="@/static/images/ja2.png" mode="aspectFit"></image>
</view>
<view class="" @click="adddepart">新建任务</view>
</view>
<u-empty v-if="loadConfig.status=='nomore'&& list.length==0" text="没有信息"
icon="/static/empty/data.png"></u-empty>
<u-loadmore v-else :status="loadConfig.status" :loading-text="loadConfig.loadingText"
@ -98,7 +164,8 @@
<script>
import {
invoicecheckedlist,
tovoid
tovoid,
novoid
} from '@/api/oa.js'
import {
HTTP_REQUEST_URL
@ -128,6 +195,32 @@
color: '#FFFFFF',
fontSize: '28rpx'
}],
btnList2: [{
id: '1',
name: '编辑',
width: '100rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}, {
id: '4',
name: '反作废',
height: '100rpx',
width: '100rpx',
bgColor: '#ed656d',
color: '#FFFFFF',
fontSize: '28rpx'
}],
btnList1: [{
id: '3',
name: '开票',
width: '100rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}],
loadConfig: {
page: 1,
limit: 5,
@ -198,11 +291,10 @@
},
//
clickItem(data) {
// console.log(e)
console.log(data)
if (data.id == 2) {
uni.showModal({
content: '确定要删除吗',
content: '确定要作废发票吗',
success: (e) => {
if (e.confirm) {
this.deleteDepartment(this.list[data.index].id)
@ -210,12 +302,25 @@
}
})
} else if (data.id == 4) {
uni.showModal({
content: '确定要反作废发票吗',
success: (e) => {
if (e.confirm) {
this.deleteDepartment1(this.list[data.index].id)
}
}
})
} else if (data.id == 3) {
this.editpart(this.list[data.index], '3')
} else {
this.editpart(this.list[data.index])
this.editpart(this.list[data.index], '1')
}
},
//
//
async deleteDepartment(id) {
const res = await tovoid({
@ -229,15 +334,31 @@
}
Toast(res.msg);
},
//
async deleteDepartment1(id) {
const res = await novoid({
id: id
})
if (res.code == 0) {
this.initList()
}
Toast(res.msg);
},
taskDetail(item) {
},
//
editpart(item) {
editpart(item, type) {
uni.navigateTo({
url: '/pages/addfinanceincome/index??type=' + 2 + "&data=" + encodeURIComponent(JSON.stringify(
url: '/pages/addfinanceincome/index??type=' + type + "&data=" + encodeURIComponent(JSON
.stringify(
item))
})
},

View File

@ -30,8 +30,8 @@
</view>
<view class="work">
<view class="wr_item">
<view class="wr_t_left">(技术部):</view>
<view class="wr_t_right">技术专业</view>
<view class="wr_t_left">{{ oaUserInfo.department_name}}</view>
<!-- <view class="wr_t_right">技术专业</view> -->
</view>
</view>
</view>
@ -48,8 +48,7 @@
</view>
</view>
<view class="content-banner">
<view class="content-banner-title" style="display: flex;justify-content: space-between;">
<view class="content-banner-title" style="display: flex;justify-content: space-between;" @click="daiban">
<view class="">
待办事项
</view>
@ -58,21 +57,24 @@
<text class="iconfont icon-xiangyou" style="margin-top: 5rpx;margin-right: 20rpx;"></text>
</view>
</view>
<view class="content-banner-detail" v-for="(item,k) in list2" :key='k' v-if="isshow1">
<view class="">
<view class="content-banner-detail" v-for="(item,k) in list2" :key='k' @click="spclickItem(item)">
<view class="banner-detail">
<view class="banner-detail-title">
提醒
</view>
<view class="banner-detail-detail">
{{item.title}}
你有一个{{item.flow_type}}审批
</view>
</view>
<view class="iconfont icon-xiangyou" style="margin-top: 5rpx;">
</view>
</view>
</view>
<!-- <view class="bootom-top-three" v-else>
<view class="bootom-top-three" v-else>
<scroll-view scroll-x="true" scroll-left="0" class="top-three">
<view class="top_users-header">
<view class="">
@ -92,7 +94,7 @@
</view>
<view class="top_users-header">
<view class="">
{{list6.approve}}
{{list6.invoice}}
</view>
<view class="title3">
待审发票
@ -100,14 +102,14 @@
</view>
<view class="top_users-header">
<view class="">
{{list6.approve}}
{{list6.task}}
</view>
<view class="title4">
回款
完成任务
</view>
</view>
</scroll-view>
</view>
</view> -->
</view>
<view class="content-sort">
<view class="content-sort-con" v-for="(item,l) in list3" :key="l" @click="appcenter(item,l)">
@ -149,7 +151,7 @@
<view class="bootom-top-one">
我的任务
</view>
<view class="bootom-top-two">
<view class="bootom-top-two" @click="geng">
<text>更多</text>
<text class="iconfont icon-xiangyou" style="font-size: 25rpx;"></text>
</view>
@ -232,7 +234,7 @@
img: require('@/static/images/s4.png')
}, {
name: '请假申请',
name: '外出申请',
img: require('@/static/images/s3.png')
}, {
@ -244,8 +246,8 @@
img: require('@/static/images/s8.png')
}, {
name: '用章申请',
img: require('@/static/images/s7.png')
name: '发票申请',
img: require('@/static/images/fa.png')
}, {
name: '报销申请',
@ -256,32 +258,11 @@
img: require('@/static/images/s1.png')
}],
list4: [{
name: '紧急',
con: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
status: '紧急紧',
con1: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
con2: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
}, {
name: '紧急',
con: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
status: '紧急紧',
con1: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
con2: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
}, {
name: '紧急',
con: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
status: '紧急紧',
con1: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
con2: '紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急紧急',
}],
list4: [],
HTTP_IMG_URL: '',
list5: [],
list6: {},
isshow1:true
isshow1: true
}
},
computed: {
@ -323,8 +304,13 @@
},
async getDtlist1() {
let res = await tasksubjec()
this.list2 = res.data.data
this.list2 = res.data
},
spclickItem(item){
console.log(item)
uni.navigateTo({
url:'/pages/leave_request/index?type='+item.id
})
},
//
async getDocumentList2() {
@ -338,10 +324,23 @@
this.list4 = res.data.data
},
//
daiban(){
uni.navigateTo({
url:'/pages/waitlist/index'
})
},
//
geng() {
uni.switchTab({
url: '/pages/oaTask/oaTask'
})
},
//
async getList() {
const res = await getdatatotal()
this.list = res.data
},
//
@ -382,7 +381,7 @@
break;
case "task":
uni.switchTab({
url: '/oaTask/oaTask'
url: '/pages/oaTask/oaTask'
})
break;
case "article":
@ -401,45 +400,46 @@
},
//
gongao1() {
this.isshow1=!this.isshow1
this.isshow1 = !this.isshow1
},
//
appcenter(item, i) {
switch (i) {
case 0:
uni.navigateTo({
url: '/pages/views/com_approve'
url: '/pages/leaveapplication/index?type=1'
})
break;
case 1:
uni.navigateTo({
url: '/pages/appcenter/index'
url: '/pages/leaveapplication/index?type=2'
})
break;
case 2:
uni.navigateTo({
url: '/pages/appcenter/index'
url: '/pages/leaveapplication/index?type=3'
})
break;
case 3:
uni.navigateTo({
url: '/pages/appcenter/index'
url: '/pages/leaveapplication/index?type=15'
})
break;
case 4:
uni.navigateTo({
url: '/pages/appcenter/index'
url: '/pages/leaveapplication/index?type=7'
})
break;
case 5:
uni.navigateTo({
url: '/pages/appcenter/index'
url: '/pages/invoice/add'
})
break;
case 6:
uni.navigateTo({
url: '/pages/appcenter/index'
url: '/pages/reimbursementapplication/index'
})
break;
case 7:
@ -523,13 +523,11 @@
.top_class {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 28rpx 28rpx;
.top_class-one {
flex-basis: 25%;
margin-bottom: 42rpx;
text-align: center;

View File

@ -49,25 +49,25 @@
<view class="title">电话号码</view>
<input type="text" v-model="listobj.invoice_phone" placeholder="电话号码">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">纳税人识别号 </view>
<input type="text" v-model="listobj.invoice_tax" placeholder="纳税人识别号">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">开户行</view>
<input type="text" v-model="listobj.invoice_bank" placeholder="开户行">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">银行账号</view>
<input type="text" v-model="listobj.invoice_account" placeholder="银行账号">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">银行营业网点 </view>
<input type="text" v-model="listobj.invoice_banking" placeholder="银行营业网点">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">地址</view>
<input type="text" v-model="listobj.invoice_address" placeholder="地址">
</view>
@ -221,14 +221,19 @@
columns: [],
check_admin_name: '',
copy_names: '',
isshow:true,
columns1: [{
name: '企业',
name: '增值税专用发票',
id: 1,
},
{
name: '个人',
name: '普通发票',
id: 2,
},{
name: '专用发票',
id: 3
}
],
list: [{
cate_title: "",
@ -386,6 +391,11 @@
deliveryWayChangetwo(e) {
this.listobj.type = e.detail.value
if(e.detail.value==2){
this.isshow=false
}else{
this.isshow=true
}
},
//
leavaShow(item, i) {
@ -564,6 +574,10 @@
position: relative;
padding-bottom: 100px;
}
/deep/.u-action-sheet item-wrap{
overflow: auto;
max-height: 50vh;
}
.leave_box,
.flow_path {

View File

@ -45,29 +45,29 @@
<view class="title">开票抬头</view>
<input type="text" v-model="listobj.invoice_title" placeholder="开票抬头">
</view>
<view class="cont_cell">
<view class="cont_cell" >
<view class="title">电话号码</view>
<input type="text" v-model="listobj.invoice_phone" placeholder="电话号码">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">纳税人识别号 </view>
<input type="text" v-model="listobj.invoice_tax" placeholder="纳税人识别号">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">开户行</view>
<input type="text" v-model="listobj.invoice_bank" placeholder="开户行">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">银行账号</view>
<input type="text" v-model="listobj.invoice_account" placeholder="银行账号">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">银行营业网点 </view>
<input type="text" v-model="listobj.invoice_banking" placeholder="银行营业网点">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">地址</view>
<input type="text" v-model="listobj.invoice_address" placeholder="地址">
</view>
@ -222,14 +222,19 @@
columns: [],
check_admin_name: '',
copy_names: '',
isshow:true,
columns1: [{
name: '企业',
name: '增值税专用发票',
id: 1,
},
{
name: '个人',
name: '普通发票',
id: 2,
},{
name: '专用发票',
id: 3
}
],
list: [{
cate_title: "",
@ -287,6 +292,11 @@
this.listobj.type=res.data.detail.type
this.selected = res.data.detail.type.toString()
// console.log(res.data.detail.type)
if( res.data.detail.type==2){
this.isshow=false
}else{
this.isshow=true
}
},
reset() {
this.list = [{
@ -409,6 +419,13 @@
deliveryWayChangetwo(e) {
this.listobj.type = e.detail.value
if(e.detail.value==2){
this.isshow=false
}else{
this.isshow=true
}
},
//
leavaShow(item, i) {
@ -587,7 +604,10 @@
position: relative;
padding-bottom: 100px;
}
/deep/.u-action-sheet__item-wrap {
overflow: auto;
max-height: 50vh;
}
.leave_box,
.flow_path {
width: 100%;

View File

@ -48,25 +48,25 @@
<view class="title">电话号码</view>
<input type="text" v-model="listobj.invoice_phone" placeholder="电话号码">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">纳税人识别号 </view>
<input type="text" v-model="listobj.invoice_tax" placeholder="纳税人识别号">
</view>
<view class="cont_cell">
<view class="title">开户行</view>
<view class="cont_cell" v-if="isshow">
<view class="title" v-if="isshow">开户行</view>
<input type="text" v-model="listobj.invoice_bank" placeholder="开户行">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">银行账号</view>
<input type="text" v-model="listobj.invoice_account" placeholder="银行账号">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">银行营业网点 </view>
<input type="text" v-model="listobj.invoice_banking" placeholder="银行营业网点">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">地址</view>
<input type="text" v-model="listobj.invoice_address" placeholder="地址">
</view>
@ -112,8 +112,8 @@
</view>
</view>-->
<!-- </view> -->
<view class="flow_path">
<view class="cont_cell" style="padding-top: 20rpx;" v-if="listobj.check_status!= 4&& ktype!=0">
<view class="flow_path" v-if="listobj.check_status!= 5&&listobj.check_status!= 2&& ktype!=0">
<view class="cont_cell" style="padding-top: 20rpx;" v-if="listobj.check_status!= 4">
<view class="title" style="width: 200rpx;">撤回意见</view>
<textarea v-model="mscontent" placeholder="请输入撤回意见" />
</view>
@ -145,7 +145,7 @@
</view>
</view>
<view class="" v-if="ktype!=0">
<view class="" v-if="ktype!=0&&listobj.check_status!= 5&&listobj.check_status!= 2 ">
<view class="bot_btn" v-if="listobj.check_status!= 4">
<!-- <view class="reset" @click="reset">重置</view> -->
<view class="submit_btn" @click="submiteBtn">撤回</view>
@ -258,14 +258,19 @@
columns: [],
check_admin_name: '',
copy_names: '',
isshow:true,
columns1: [{
name: '企业',
name: '增值税专用发票',
id: 1,
},
{
name: '个人',
name: '普通发票',
id: 2,
},{
name: '专用发票',
id: 3
}
],
list: [{
cate_title: "",
@ -315,9 +320,19 @@
this.check_record = res.data.check_record
this.check_admin_name = res.data.detail.check_user
this.copy_names = res.data.detail.copy_user
this.selected= res.data.detail.type.toString()
if( res.data.detail.type==2){
this.isshow=false
}else{
this.isshow=true
}
if( res.data.detail.type==2){
this.isshow=false
}else{
this.isshow=true
}
},
reset() {
this.list = [{
@ -439,6 +454,11 @@
deliveryWayChangetwo(e) {
this.listobj.type = e.detail.value
if(e.detail.value==2){
this.isshow=false
}else{
this.isshow=true
}
},
//
leavaShow(item, i) {

View File

@ -48,25 +48,25 @@
<view class="title">电话号码</view>
<input type="text" v-model="listobj.invoice_phone" placeholder="电话号码">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">纳税人识别号 </view>
<input type="text" v-model="listobj.invoice_tax" placeholder="纳税人识别号">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">开户行</view>
<input type="text" v-model="listobj.invoice_bank" placeholder="开户行">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">银行账号</view>
<input type="text" v-model="listobj.invoice_account" placeholder="银行账号">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">银行营业网点 </view>
<input type="text" v-model="listobj.invoice_banking" placeholder="银行营业网点">
</view>
<view class="cont_cell">
<view class="cont_cell" v-if="isshow">
<view class="title">地址</view>
<input type="text" v-model="listobj.invoice_address" placeholder="地址">
</view>
@ -131,9 +131,9 @@
<view class="cont_cell">
<view class="title">审核状态</view>
{{listobj.check_status=='0'?'待审核':listobj.check_status=='1'?'审核中':listobj.check_status=='2'?'审核通过':listobj.check_status=='3'?'审核不通过':'撤销审核'}}
{{listobj.check_status=='0'?'待审核':listobj.check_status=='1'?'审核中':listobj.check_status=='2'?'审核通过':listobj.check_status=='3'?'审核不通过':listobj.check_status=='4'?'已撤销':listobj.check_status=='5'?'已开具':'已作废'}}
</view>
<view class="cont_cell" >
<view class="cont_cell">
<view class="title">当前审核人</view>
<input type="text" v-model="admin_name" placeholder="请选择" disabled>
</view>
@ -142,9 +142,9 @@
<input type="text" v-model="copy_names" placeholder="请选择" disabled>
</view>
<view class="" >
<view class="" v-if="listobj.check_status!= 5&&listobj.check_status!= 2">
<view class="title" style="margin-bottom: 30rpx;">审核节点</view>
<radio-group class="select_group" @change="deliveryWayChangetwo1" >
<radio-group class="select_group" @change="deliveryWayChangetwo1">
<label class="radio_select" style="margin-right:15rpx;display: flex; margin-bottom: 30rpx;">
<view style="margin-right:5rpx;">
<radio value="1" />
@ -163,13 +163,14 @@
</radio-group>
</view>
</block>
<view class="cont_cell" style="padding-top: 20rpx;" v-if="listobj.check_status!= 4">
<view class="cont_cell" style="padding-top: 20rpx;"
v-if="listobj.check_status!= 4&&listobj.check_status!= 5&&listobj.check_status!= 2">
<view class="title" style="width: 200rpx;">审核意见</view>
<textarea v-model="mscontent" placeholder="请输入审核意见" />
</view>
</view>
<view class="bot_btn">
<view class="bot_btn" v-if="listobj.check_status!= 5&&listobj.check_status!= 2">
<view class="reset" @click="appflowcheck()">拒绝</view>
<view class="submit_btn" @click=" appflowcheck1()">通过</view>
</view>
@ -275,14 +276,19 @@
columns: [],
check_admin_name: '',
copy_names: '',
isshow: true,
columns1: [{
name: '企业',
name: '增值税专用发票',
id: 1,
},
{
name: '个人',
name: '普通发票',
id: 2,
}, {
name: '专用发票',
id: 3
}
],
list: [{
cate_title: "",
@ -294,8 +300,8 @@
fileArray: [],
check_record: [],
dnum: 0,
check:'',
admin_name :""
check: '',
admin_name: ""
}
},
onLoad(option) {
@ -333,8 +339,11 @@
this.admin_name = res.data.detail.check_user
this.copy_names = res.data.detail.copy_user
this.selected = res.data.detail.type.toString()
if (res.data.detail.type == 2) {
this.isshow = false
} else {
this.isshow = true
}
},
reset() {
this.list = [{
@ -432,9 +441,9 @@
let res = await appflow_check(data)
console.log(res)
if(res.code==0){
if (res.code == 0) {
uni.navigateBack({
delta:1
delta: 1
})
}
Toast(res.msg)
@ -488,7 +497,7 @@
}
},
//
//
deliveryWayChangetwo1(e) {
console.log(e)
this.check = e.detail.value

View File

@ -11,7 +11,7 @@
<view class="content-middle-one">
<view class="middle-one" v-for="(item,k) in list2" @click="tian(item)">
<liu-swipe-action :index="k" @clickItem="clickItem" :btnList='btnList'>
<liu-swipe-action :index="k" @clickItem="clickItem" :btnList='btnList3' v-if="item.check_status==5">
<view class="middle-one-title">
<view class="one-titlea">
<view class="name">
@ -23,24 +23,109 @@
</view>
<view class="one-titleb"
:style="{'color':item.status==1?'#34A853':item.status==2?'#F9AA32':item.status==3?'#F02828':''}">
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
<text v-if='item.check_status==5'>已开具</text>
<text v-if='item.check_status==10'>已作废</text>
<text v-if='item.status==1'>已通过</text>
<text v-if='item.status==2'>待审批</text>
<text v-if='item.status==3'>审批拒绝</text>
</view>
</view>
<view class="middle-one-time">类型{{item.flow_type}}</view>
<view class="middle-one-time">开票金额:{{item.amount}}</view>
<view class="middle-one-time">审核人{{item.check_user}}</view>
</liu-swipe-action>
<!-- <view class="middle-edit" @click="editpart(item)">
编辑
</view> -->
<!--<view class="middle-delete" @click="clickItem(item)">
删除
</view> -->
<liu-swipe-action :index="k" @clickItem="clickItem" :btnList='btnList2' v-else-if="item.check_status==10">
<view class="middle-one-title">
<view class="one-titlea">
<view class="name">
{{item.name}}
</view>
<view class="parment">
{{item.name1}}
</view>
</view>
<view class="one-titleb"
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
<text v-if='item.check_status==5'>已开具</text>
<text v-if='item.check_status==10'>已作废</text>
</view>
</view>
<view class="middle-one-time">类型{{item.flow_type}}</view>
<view class="middle-one-time">开票金额:{{item.amount}}</view>
<view class="middle-one-time">审核人{{item.check_user}}</view>
</liu-swipe-action>
<liu-swipe-action :index="k" @clickItem="clickItem" :btnList='btnList1' v-else-if="item.check_status==2">
<view class="middle-one-title">
<view class="one-titlea">
<view class="name">
{{item.name}}
</view>
<view class="parment">
{{item.name1}}
</view>
</view>
<view class="one-titleb"
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
<text v-if='item.check_status==5'>已开具</text>
<text v-if='item.check_status==10'>已作废</text>
</view>
</view>
<view class="middle-one-time">类型{{item.flow_type}}</view>
<view class="middle-one-time">开票金额:{{item.amount}}</view>
<view class="middle-one-time">审核人{{item.check_user}}</view>
</liu-swipe-action>
<liu-swipe-action :index="k" @clickItem="clickItem" :btnList='btnList' v-else>
<view class="middle-one-title">
<view class="one-titlea">
<view class="name">
{{item.name}}
</view>
<view class="parment">
{{item.name1}}
</view>
</view>
<view class="one-titleb"
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
<text v-if='item.check_status==5'>已开具</text>
<text v-if='item.check_status==10'>已作废</text>
</view>
</view>
<view class="middle-one-time">类型{{item.flow_type}}</view>
<view class="middle-one-time">开票金额:{{item.amount}}</view>
<view class="middle-one-time">审核人{{item.check_user}}</view>
</liu-swipe-action>
</view>
</view>
</view>
<u-empty v-if="loadConfig.status=='nomore'&& list2.length==0" text="没有信息"
@ -57,8 +142,11 @@
import {
invoiceindex,invoicelist,invoicecopy
invoiceindex,
invoicelist,
invoicecopy,
tovoid,
novoid
} from '@/api/oa.js'
export default {
@ -81,23 +169,50 @@
],
list2: [],
num: 0,
btnList: [{
btnList: [],
btnList3: [{
id: '1',
name: '编辑',
width: '100rpx',
name: '编辑开票',
width: '130rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}, {
id: '2',
name: '删除',
name: '作废',
height: '100rpx',
width: '100rpx',
bgColor: '#ed656d',
color: '#FFFFFF',
fontSize: '28rpx'
}],
btnList2: [{
id: '1',
name: '编辑开票',
width: '130rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}, {
id: '4',
name: '反作废',
height: '100rpx',
width: '100rpx',
bgColor: '#ed656d',
color: '#FFFFFF',
fontSize: '28rpx'
}],
btnList1: [{
id: '3',
name: '开票',
width: '100rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}],
loadConfig: {
page: 1,
limit: 5,
@ -220,43 +335,83 @@
//
clickItem(data) {
console.log(data)
if (data.id == 2) {
uni.showModal({
content: '确定要删除吗',
content: '确定要作废发票吗',
success: (e) => {
if (e.confirm) {
this.deleteDepartment(data.id)
this.deleteDepartment(this.list2[data.index].id)
}
}
})
} else {
this.editpart(this.list2[data.index])
} else if (data.id == 4) {
uni.showModal({
content: '确定要反作废发票吗',
success: (e) => {
if (e.confirm) {
this.deleteDepartment1(this.list2[data.index].id)
}
}
})
} else if (data.id == 3) {
this.editpart(this.list2[data.index], '3')
} else {
this.editpart(this.list2[data.index], '1')
}
},
//
//
async deleteDepartment(id) {
const res = await deleteproject_task({
const res = await tovoid({
id: id
})
if (res.code == 0) {
this.loadConfig.status = "loading"
if (this.num == 0) {
this.initList()
} else if (this.num == 1) {
this.initList1()
} else {
this.initList2()
}
}
Toast(res.msg);
},
//
editpart(item) {
//
async deleteDepartment1(id) {
const res = await novoid({
id: id
})
if (res.code == 0) {
if (this.num == 0) {
this.initList()
} else if (this.num == 1) {
this.initList1()
} else {
this.initList2()
}
}
Toast(res.msg);
},
//
editpart(item, type) {
uni.navigateTo({
url: '/pages/invoice/bianji??type=' + item.type
url: '/pages/addfinanceincome/index??type=' + type + "&data=" + encodeURIComponent(JSON
.stringify(
item))
})
},
tian(item) {
@ -265,11 +420,11 @@
uni.navigateTo({
url: '/pages/invoice/handle?type=' + item.id
})
}else if (this.num == 2){
} else if (this.num == 2) {
uni.navigateTo({
url: '/pages/invoice/detail?type=' + item.id +'&num=0'
url: '/pages/invoice/detail?type=' + item.id + '&num=0'
})
}else{
} else {
uni.navigateTo({
url: '/pages/invoice/detail?type=' + item.id
})
@ -303,6 +458,7 @@
.content-middle {
.content-middle-one {
.middle-one {
width: 694rpx;
@ -311,6 +467,7 @@
background: #FFFFFF;
padding: 0 25rpx;
padding-bottom: 25rpx;
.middle-edit {

View File

@ -10,7 +10,7 @@
<view class="middle-oneb">
<view class="middle-oneb-a">
<view class="threeb-a-a">
<image :src="items.img" mode=""></image>
<image src="@/static/images/gw.png" mode=""></image>
</view>
<view class="threeb-a-b">
<view class="name"> <text>{{items.title}}</text></view>
@ -80,7 +80,7 @@
};
},
onShow() {
this.getDocumentList()
this.initList()
},
onReachBottom() {
this.getDocumentList()
@ -89,10 +89,16 @@
uni.stopPullDownRefresh()
},
methods: {
async initList() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list = [];
await this.getDocumentList();
},
//
async getDocumentList() {
try {
if (this.loadConfig.status == "nomore") return;
this.loadConfig.status = "loading"
let res = await userposition({
@ -108,9 +114,7 @@
this.loadConfig.page++;
}
this.list = [...this.list, ...res?.data]
} catch (e) {
this.loadConfig.status = "nomore"
}
},
//
adddepart() {
@ -140,7 +144,7 @@
})
if (res.code == 0) {
this.getDocumentList()
this.initList()
}
Toast(res.msg);
@ -196,7 +200,7 @@
display: flex;
justify-content: space-between;
margin-bottom: 25rpx;
.middle-oneb-a {
display: flex;
@ -242,7 +246,7 @@
.middle-oneb-b {
width: 80rpx;
height: 80rpx;
margin-top: 45rpx;
image {

View File

@ -12,13 +12,14 @@
<view class="leava_type flex_a_c"
v-if="fieldValue.type=='select'&& fieldKey!='copy_uids'&&fieldKey!='check_admin_ids'&&fieldKey!='flow_id'&&fieldKey!='name'">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" @click="leixin(fieldKey)"
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" disabled @click="leixin(fieldKey)"
v-if="fieldKey!='name'">
</view>
<view class="leava_type flex_a_c" v-if="fieldKey=='name'">
<view class="leava_type flex_a_c" v-if="fieldKey=='name'&fieldValue.type=='select'">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" @click="leixiner()">
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" disabled @click="leixiner()">
</view>
<view class=""
v-if="fieldValue.type=='input'&&fieldKey!='end_time'&&fieldKey!='start_time'&&fieldKey!='duration'">
@ -31,21 +32,31 @@
<view class="" v-if=" fieldKey=='start_time'">
<view class="cont_cell">
<view class="title">{{ timeData[0].title }}</view>
<input type="text" v-model="timeData[0].time" placeholder="请选择时间" disabled
@click="selectTime(timeData[0])">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
@click="selectTime1">
</view>
</view>
<view class="" v-if="fieldKey=='end_time'">
<view class="cont_cell">
<view class="title">{{ timeData[1].title }}</view>
<input type="text" v-model="timeData[1].time" placeholder="请选择时间" disabled
@click="selectTime(timeData[1])">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
@click="selectTime2">
</view>
</view>
<view class="" v-if="fieldKey=='detail_time'">
<view class="cont_cell">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
@click="selectTime3(fieldKey)">
</view>
</view>
<view class="" v-if="fieldKey=='duration'">
<view class="cont_cell">
<view class="title">{{fieldValue.title}}</view>
@ -70,7 +81,7 @@
</view>
<view class="leave_box" style="padding-top: 30rpx;" >
<view class="leave_box" style="padding-top: 30rpx;">
<block v-for="(item, i) in fileArray" :key="i">
<view class="file flex_a_c_j_sb">
<view class="l_file">
@ -127,7 +138,7 @@
@confirm="leavaType">
</u-picker> -->
<u-action-sheet :show="leavaTypeShow" keyName="name" :actions="columns" title="请选择工作类别"
<u-action-sheet :show="leavaTypeShow" keyName="name" :actions="columns" title="请选择"
@close="leavaTypeShow = false" @select="leavaType">
</u-action-sheet>
@ -138,13 +149,13 @@
<!-- 审核人 -->
<u-picker :show="branchShow" :defaultIndex='defaultIndex' ref="branchRef" :columns="branchColumns"
@confirm="branchConfirm" @change="columnCode" :closeOnClickOverlay="true" @close="branchShowclose"
keyName="name">
@confirm="branchConfirm" @change="columnCode" :closeOnClickOverlay="true" keyName="name"
@close="branchShow = false" @cancel="branchShow = false">
</u-picker>
<u-picker :show="branchShow1" :defaultIndex='defaultIndex1' ref="branchRef" :columns="branchColumns1"
@confirm="branchConfirm1" @change="columnCode1" :closeOnClickOverlay="true" @close="branchShowclose1"
keyName="name">
@confirm="branchConfirm1" @change="columnCode1" :closeOnClickOverlay="true" @close="branchShow1 = false"
@cancel="branchShow1 = false" keyName="name">
</u-picker>
<u-picker :show="branchShow2" :defaultIndex='defaultIndex2' ref="branchRef" :columns="branchColumns2"
@confirm="branchConfirm2" @change="columnCode2" :closeOnClickOverlay="true" @close="branchShowclose2"
@ -156,11 +167,14 @@
@close="flag1 = false" @confirm="confirm" keyName="title" :defaultIndex=[0,0]>
</u-picker>
<!-- 选择时间 -->
<block v-for="(item,i) in timeData" :key="i">
<u-datetime-picker :show="item.timeShow" v-model="item.timeVal" mode="datetime" :maxDate="1786778555000"
:minDate="timestamp" closeOnClickOverlay @confirm="timeConfirm($event,i)"
@cancel="item.timeShow = false" @close="item.timeShow = false"></u-datetime-picker>
</block>
<u-datetime-picker :show="timeShow1" mode="datetime" :maxDate="1786778555000" closeOnClickOverlay
@confirm="timeConfirm1($event)" @cancel="timeShow1 = false" @close="timeShow1 = false"></u-datetime-picker>
<u-datetime-picker :show="timeShow2" mode="datetime" :maxDate="1786778555000" closeOnClickOverlay
@confirm="timeConfirm2($event)" @cancel="timeShow2 = false" @close="timeShow2 = false"></u-datetime-picker>
<u-datetime-picker :show="timeShow" mode="datetime" :maxDate="1786778555000" :minDate="timestamp"
closeOnClickOverlay @confirm="timeConfirm3($event)" @cancel="timeShow = false"
@close="timeShow = false"></u-datetime-picker>
</view>
</template>
@ -207,7 +221,9 @@
content: '', //
flowPathShow: false,
flowPathSheet: [],
timeShow: false,
timeShow1: false,
timeShow2: false,
timeData: [{
title: '开始时间:',
timeShow: false,
@ -259,7 +275,6 @@
token: this.$store.state.app.token
}
},
this.typeId = option.type
if (option.type == 8) {
this.timeData[0].title = '借用日期:'
@ -329,71 +344,98 @@
})
this.dataobj = res.data.field
console.log(this.columns)
this.meatelist.type= this.dataobj1.detail.type
if ('name' in res.data.field) {
this.meatelist.name = 'name' in this.dataobj1.detail ? this.dataobj1.detail.name : ''
if (this.typeId != 53) {
this.timeData[0].time = 'start_time' in this.dataobj1.detail ? this.dataobj1.detail.start_time : ''
this.timeData[1].time = 'end_time' in this.dataobj1.detail ? this.dataobj1.detail.end_time : ''
}
if ('start_time' in res.data.field) {
this.meatelist.start_time = 'start_time' in this.dataobj1.detail ? this.dataobj1.detail
.start_time : ''
}
if ('end_time' in res.data.field) {
this.meatelist.end_time = 'end_time' in this.dataobj1.detail ? this.dataobj1.detail.end_time : ''
}
if ('mobile' in res.data.field) {
this.meatelist.mobile = 'mobile' in this.dataobj1.detail ? this.dataobj1.detail.mobile : ''
// this.meatelist.department_type = 'department_type' in this.dataobj1.detail ? this.dataobj1.detail
// .department_type_title : ''
// this.meatelist.detail_type = 'detail_type' in this.dataobj1.detail ? this.dataobj1.detail
// .detail_type_title : ''
// this.meatelist.other_type = 'other_type' in this.dataobj1.detail ? this.dataobj1.detail
// .other_type_title : ''
}
if ('remark' in res.data.field) {
this.meatelist.remark = 'remark' in this.dataobj1.detail ? this.dataobj1.detail.remark : ''
}
if ('remark1' in res.data.field) {
this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
}
if ( 'address' in res.data.field) {
this.meatelist.address = 'address' in this.dataobj1.detail ? this.dataobj1.detail.address : ''
this.meatelist.detail_time = 'detail_time' in this.dataobj1.detail ? this.dataobj1.detail.detail_time :
''
}
if ('detail_time' in res.data.field) {
this.meatelist.detail_time = 'detail_time' in this.dataobj1.detail ? this.dataobj1.detail
.detail_time :
''
}
if ('content' in res.data.field) {
this.meatelist.content = 'content' in this.dataobj1.detail ? this.dataobj1.detail.content : ''
}
// this.check_admin_name = 'check_user' in this.dataobj1.detail ? this.dataobj1.detail.check_user : ''
// this.check_admin_ids = 'check_admin_ids' in this.dataobj1.detail ? this.dataobj1.detail
// .check_admin_ids : ''
// this.copy_uids = 'copy_uids' in this.dataobj1.detail ? this.dataobj1.detail.copy_uids : ''
// this.copy_names = 'copy_user' in this.dataobj1.detail ? this.dataobj1.detail.copy_user : ''
this.flowPath = 'file_ids_title' in this.dataobj1.detail ? this.dataobj1.detail.file_ids_title : ''
this.flow_id = 'flow_id' in this.dataobj1.detail ? this.dataobj1.detail.flow_id : ''
// this.flowPath = 'file_ids_title' in this.dataobj1.detail ? this.dataobj1.detail.file_ids_title : ''
// this.flow_id = 'flow_id' in this.dataobj1.detail ? this.dataobj1.detail.flow_id : ''
// console.log('num' in res.data.field)
if ('num' in res.data.field) {
this.meatelist.num = 'num' in this.dataobj1.detail ? this.dataobj1.detail.num : ''
}
if ('num1' in res.data.field) {
this.meatelist.num1 = 'num1' in this.dataobj1.detail ? this.dataobj1.detail.num1 : ''
}
if ('amount' in res.data.field) {
this.meatelist.amount = 'amount' in this.dataobj1.detail ? this.dataobj1.detail.amount : ''
}
if ('bank' in res.data.field) {
this.meatelist.bank = 'bank' in this.dataobj1.detail ? this.dataobj1.detail.bank : ''
}
if ('flow_id' in res.data.field) {
const detailTypeOptions1 = Object.entries(res.data.field.flow_id.item).map(([id, name]) => ({
id,
name
}));
// console.log(detailTypeOptions1)
this.flowPathSheet = detailTypeOptions1
}
if ('fileArray' in this.dataobj1.detail) {
this.fileArray = res.data.detail.fileArray.map(function(obj) {
if (this.dataobj1 && this.dataobj1.detail && this.dataobj1.detail.fileArray) {
this.fileArray = this.dataobj1.detail.fileArray.map(function(obj) {
return {
name: obj.name,
filesize: obj.filesize
};
});
}
// this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
// this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
// console.log(this.timeData)
if ('detail_type' in this.dataobj) {
if ('detail_type' in res.data.field) {
const detailTypeOptions = Object.entries(this.dataobj.detail_type.item).map(([id, name]) => ({
id,
name
@ -401,7 +443,7 @@
this.columns = detailTypeOptions
for (let i in this.columns) {
console.log(this.columns[i].id, this.dataobj1.detail.detail_type, '1111111111111')
if (this.columns[i].id == this.dataobj1.detail.detail_type) {
this.meatelist.detail_type = this.columns[i].name
this.leavaTypeId1 = this.columns[i].id
@ -413,7 +455,7 @@
}
if ('department_type' in this.dataobj) {
if ('department_type' in res.data.field) {
const detailTypeOptions = Object.entries(this.dataobj.department_type.item).map(([id, name]) => ({
id,
name
@ -432,7 +474,7 @@
}
if ('other_type' in this.dataobj) {
if ('other_type' in res.data.field) {
const detailTypeOptions = Object.entries(this.dataobj.other_type.item).map(([id, name]) => ({
id,
name
@ -451,7 +493,7 @@
}
if ('position_type' in this.dataobj) {
if ('position_type' in res.data.field) {
const detailTypeOptions = Object.entries(this.dataobj.position_type.item).map(([id, name]) => ({
id,
name
@ -643,8 +685,15 @@
branchShowclose2() {
this.branchShow2 = false
},
selectTime(item) {
item.timeShow = true
selectTime1(item) {
this.timeShow1 = true
},
selectTime2(item) {
this.timeShow2 = true
},
selectTime3(item) {
this.timeShow = true
},
async branchHandler(e) {
const {
@ -725,17 +774,25 @@
},
timeConfirm(e, i) {
this.timeData[i].time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
// this.timeData[i].timeDay = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
// this.timeData[i].timeHour = uni.$u.timeFormat(e.value, 'hh:MM')
this.timeData[i].timeShow = false
timeConfirm1(e, i) {
this.meatelist.start_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeData[0].time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeShow1 = false
},
timeConfirm2(e, i) {
this.meatelist.end_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeData[1].time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeShow2 = false
},
timeConfirm3(e, i) {
this.meatelist.detail_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
this.timeShow = false
},
async submiteBtn() {
console.log(this.leavaTypeId2)
let subData;
let subData1;
if (this.typeId == 6 || this.typeId == 7 || this.typeId == 8 || this.typeId == 21) {
@ -744,7 +801,6 @@
fileIds.push(item.id)
});
subData1 = {
detail_type: this.leavaTypeId1,
content: this.meatelist.content,
check_admin_name: this.check_admin_name,
check_admin_ids: this.check_admin_ids,
@ -752,55 +808,38 @@
file_ids: fileIds.join(','),
copy_names: this.copy_names,
copy_uids: this.copy_uids,
type: this.typeId,
}
if (this.typeId == 8) {
subData1.start_time = this.timeData[0].time,
subData1.end_time = this.timeData[1].time,
if (this.meatelist.hasOwnProperty('department_type')) {
subData1.department_type = this.leavaTypeId
subData1.detail_type = this.leavaTypeId1
}
if (this.meatelist.hasOwnProperty('other_type')) {
subData1.other_type = this.leavaTypeId2
}
if (this.typeId == 21) {
subData1.department_type = this.leavaTypeId
if (this.meatelist.hasOwnProperty('position_type')) {
subData1.position_type = this.leavaTypeId3
// console.log(subData1.position_type , this.leavaTypeId3)
}
console.log(this.meatelist.hasOwnProperty('detail_type'))
if (this.meatelist.hasOwnProperty('detail_type')) {
subData1.detail_type = this.leavaTypeId1
}
if (this.meatelist.hasOwnProperty('num') && this.typeId == 20) {
subData1.num = this.leavaTypeId4
}
if (this.meatelist.hasOwnProperty('num1') && this.typeId == 20) {
subData1.num1 = this.leavaTypeId5
}
subData = {
...this.meatelist,
...subData1
}
} else {
let fileIds = [];
this.fileArray.map((item, i) => {
fileIds.push(item.id)
});
subData = {
detail_type: this.leavaTypeId1,
start_time: this.timeData[0].time,
end_time: this.timeData[1].time,
duration: this.manHour,
content: this.meatelist.content,
check_admin_name: this.check_admin_name,
check_admin_ids: this.check_admin_ids,
flow_id: this.flow_id,
file_ids: fileIds.join(','),
copy_names: this.copy_names,
copy_uids: this.copy_uids,
type: this.typeId,
file: ''
}
}
@ -947,7 +986,10 @@
position: relative;
padding-bottom: 100px;
}
/deep/.u-action-sheet__item-wrap {
overflow: auto;
max-height: 50vh;
}
.leave_box,
.flow_path {
width: 100%;

View File

@ -14,7 +14,7 @@
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" @click="leixin(fieldKey)">
</view>
<view class="leava_type flex_a_c" v-if="fieldKey=='name'&&typeId!=24">
<view class="leava_type flex_a_c" v-if="fieldKey=='name'&fieldValue.type=='select'">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" @click="leixiner()">
</view>
@ -30,18 +30,27 @@
<view class="" v-if=" fieldKey=='start_time'">
<view class="cont_cell">
<view class="title">{{ timeData[0].title }}</view>
<input type="text" v-model="timeData[0].time" placeholder="请选择时间" disabled
@click="selectTime(timeData[0])">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
>
</view>
</view>
<view class="" v-if="fieldKey=='end_time'">
<view class="cont_cell">
<view class="title">{{ timeData[1].title }}</view>
<input type="text" v-model="timeData[1].time" placeholder="请选择时间" disabled
@click="selectTime(timeData[1])">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
>
</view>
</view>
<view class="" v-if="fieldKey=='detail_time'">
<view class="cont_cell">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
@click="selectTime3(fieldKey)">
</view>
</view>
@ -89,11 +98,17 @@
</view>
<view class="" style="background-color: #fff;margin-bottom: 30rpx;">
<view class="cont_cell" style="padding-top: 20rpx;" v-if="ktype!=0 &&dataobj1.detail.check_status!=2">
<view class="title" style="width: 200rpx;">撤回意见</view>
<textarea v-model="mscontent" placeholder="请输入撤回意见" />
</view>
</view>
<view class="cont_details">
<view class="examine">审批流程</view>
<view class="cont_cell"
v-if="dataobj1 && dataobj1.detail && dataobj1.detail.hasOwnProperty('check_status')">
<view class="cont_cell">
<view class="title">审核状态</view>
{{dataobj1.detail.check_status=='0'?'待审核':dataobj1.detail.check_status=='1'?'审核中':dataobj1.detail.check_status=='2'?'审核通过':dataobj1.detail.check_status=='3'?'审核不通过':'撤销审核'}}
@ -115,13 +130,11 @@
了此申请操作意见{{item.content}}</text>
</view>
</block>
<view class="cont_cell" style="padding-top: 20rpx;" v-if="ktype!=0">
<view class="title" style="width: 200rpx;">撤回意见</view>
<textarea v-model="mscontent" placeholder="请输入撤回意见" />
</view>
</view>
<view class="" v-if="ktype!=0">
<view class="" v-if="ktype!=0&&dataobj1.detail.check_status!=2">
<view class="bot_btn" v-if='dataobj1.detail.check_status!=4'>
<!-- <view class="reset" @click="appflowcheck()">拒绝</view>
<view class="submit_btn" @click=" appflowcheck1()">通过</view> -->
@ -295,24 +308,25 @@
})
this.dataobj = res.data.field
this.meatelist.name = 'name' in this.dataobj1.detail ? this.dataobj1.detail.name : ''
this.timeData[0].time = 'start_time' in this.dataobj1.detail ? this.dataobj1.detail.start_time : ''
this.timeData[1].time = 'end_time' in this.dataobj1.detail ? this.dataobj1.detail.end_time : ''
this.meatelist.mobile = 'mobile' in this.dataobj1.detail ? this.dataobj1.detail.mobile : ''
this.meatelist.department_type = 'department_type' in this.dataobj1.detail ? this.dataobj1.detail
.department_type_title : ''
this.meatelist.detail_type = 'detail_type' in this.dataobj1.detail ? this.dataobj1.detail
.detail_type_title : ''
this.meatelist.other_type = 'other_type' in this.dataobj1.detail ? this.dataobj1.detail
.other_type_title : ''
this.meatelist.remark = 'remark' in this.dataobj1.detail ? this.dataobj1.detail.remark : ''
this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
this.meatelist.address = 'address' in this.dataobj1.detail ? this.dataobj1.detail.address : ''
this.meatelist.detail_time = 'detail_time' in this.dataobj1.detail ? this.dataobj1.detail.detail_time :
''
this.meatelist.content = 'content' in this.dataobj1.detail ? this.dataobj1.detail.content : ''
this.meatelist.num = 'num' in this.dataobj1.detail ? this.dataobj1.detail.num : ''
this.meatelist=this.dataobj1.detail
// this.meatelist.name = 'name' in this.dataobj1.detail ? this.dataobj1.detail.name : ''
// this.timeData[0].time = 'start_time' in this.dataobj1.detail ? this.dataobj1.detail.start_time : ''
// this.timeData[1].time = 'end_time' in this.dataobj1.detail ? this.dataobj1.detail.end_time : ''
// this.meatelist.mobile = 'mobile' in this.dataobj1.detail ? this.dataobj1.detail.mobile : ''
// this.meatelist.department_type = 'department_type' in this.dataobj1.detail ? this.dataobj1.detail
// .department_type_title : ''
// this.meatelist.detail_type = 'detail_type' in this.dataobj1.detail ? this.dataobj1.detail
// .detail_type_title : ''
// this.meatelist.other_type = 'other_type' in this.dataobj1.detail ? this.dataobj1.detail
// .other_type_title : ''
// this.meatelist.remark = 'remark' in this.dataobj1.detail ? this.dataobj1.detail.remark : ''
// this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
// this.meatelist.address = 'address' in this.dataobj1.detail ? this.dataobj1.detail.address : ''
// this.meatelist.detail_time = 'detail_time' in this.dataobj1.detail ? this.dataobj1.detail.detail_time :
// ''
// this.meatelist.content = 'content' in this.dataobj1.detail ? this.dataobj1.detail.content : ''
// this.meatelist.num = 'num' in this.dataobj1.detail ? this.dataobj1.detail.num : ''
// this.meatelist.num1 = 'num1' in this.dataobj1.detail ? this.dataobj1.detail.num1 : ''
// this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
// this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
@ -325,7 +339,7 @@
// console.log(this.meatelist.detail_type, '2222222222')
console.log(this.dataobj1.detail.detail_type)
// console.log(this.dataobj1.detail.detail_type)
if ('detail_type' in this.dataobj) {
const detailTypeOptions = Object.entries(this.dataobj.detail_type.item).map(([id, name]) => ({
@ -340,7 +354,7 @@
this.meatelist.detail_type = this.columns[i].name
this.leavaTypeId1 = this.columns[i].id
}
console.log(this.meatelist.detail_type, this.leavaTypeId1)
}
@ -402,13 +416,15 @@
}
if (this.dataobj1 && this.dataobj1.detail && this.dataobj1.detail.fileArray) {
this.fileArray = this.dataobj1.detail.fileArray.map(function(obj) {
return {
name: obj.name,
filesize: obj.filesize
};
});
}

View File

@ -1,5 +1,6 @@
<template>
<view class="leave_request">
<view class="leave_box">
@ -11,12 +12,12 @@
v-if="fieldValue.type=='select'&&fieldKey!='copy_uids'&&fieldKey!='check_admin_ids'&&fieldKey!='flow_id'&&fieldKey!='name'">
<view class="title">{{fieldValue.title}}</view>
<!-- {{fieldKey}} -->
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" @click="leixin(fieldKey)">
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" disabled @click="leixin(fieldKey)">
</view>
<view class="leava_type flex_a_c" v-if="fieldKey=='name'&&typeId!=24">
<view class="leava_type flex_a_c" v-if="fieldKey=='name'&fieldValue.type=='select'">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" @click="leixiner()">
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" disabled @click="leixiner()">
</view>
<view class=""
@ -44,6 +45,15 @@
@click="selectTime(timeData[1])">
</view>
</view>
<view class="" v-if="fieldKey=='detail_time'">
<view class="cont_cell">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
@click="selectTime3(fieldKey)">
</view>
</view>
<view class="" v-if="fieldKey=='duration'">
<view class="cont_cell">
@ -71,42 +81,36 @@
<u-icon @click="delImg(i)" name="close-circle" color="#333333" size="28"></u-icon>
</view>
</block>
<!-- <view class="upload_box flex_a_c_j_sb" @click="seleckImage">
<view>
<view class="title">选择文件并上传</view>
<view class="text">
上传前请规范命名最大只能上传100M的文件<br />
超过请压缩成多个文件上传
</view>
</view>
<u-icon name="plus-circle" color="#333333" size="28"></u-icon>
</view> -->
</view>
<!-- <view class="" v-for="(itemValue, itemKey) in fieldValue.item" :key="itemKey">
{{ itemKey }}: {{ itemValue }}
</view> -->
</view>
</view>
<view class="cont_details">
<view class="examine">审批流程</view>
<view class="flow_path">
<view class="">
<view class="cont_cell">
<view class="title" >审核状态</view>
<view class="title">审批流程</view>
</view>
<view class="cont_cell">
<view class="title">审核状态</view>
{{dataobj1.detail.check_status=='0'?'待审核':dataobj1.detail.check_status=='1'?'审核中':dataobj1.detail.check_status=='2'?'审核通过':dataobj1.detail.check_status=='3'?'审核不通过':'撤销审核'}}
</view>
<view class="cont_cell">
<view class="title">当前审核人</view>
<input type="text" v-model="dataobj1.detail.check_user" disabled placeholder="请假工时">
<view class="title">审核人</view>
<input type="text" v-model="dataobj1.detail.check_user" placeholder="请选择" disabled>
</view>
<view class="cont_cell">
<view class="title">抄送人</view>
<input type="text" v-model="dataobj1.detail.copy_user" disabled placeholder="请假工时">
<input type="text" v-model="dataobj1.detail.copy_user" placeholder="请选择" disabled>
</view>
<view class="examine">审批记录</view>
<block v-for="(item,i) in check_record" :key="i">
<view class="record" style="display: flex;">
<view class="circle"></view>
@ -114,9 +118,11 @@
了此申请操作意见{{item.content}}</text>
</view>
</block>
</view>
<view class="flow_path">
</view>
<view class="" v-if="dataobj1.detail.check_status!=2">
<radio-group class="select_group" @change="deliveryWayChangetwo">
<label class="radio_select" style="margin-right:15rpx;display: flex; margin-bottom: 30rpx;">
<view style="margin-right:5rpx;">
@ -128,37 +134,39 @@
<view style="margin-right:5rpx;">
<radio value="2" />
</view>
<view @click="branchShow = true" style="display: flex;">
<view class="cont_cell" style="margin-top: -25rpx;">
<view class="title"> 下一位审批人</view>
<input type="text" v-model="check_admin_name" placeholder="请选择" disabled>
<input type="text" @click="leixiner1" v-model="check_admin_name" placeholder="请选择" disabled>
</view>
</label>
</radio-group>
<!-- <view class="cont_cell" @click="branchShow1 = true">
<view class="title">抄送人</view>
<input type="text" v-model="copy_names" placeholder="请选择" disabled>
</view> -->
<view class="cont_cell" style="padding-top: 20rpx;">
</view>
<view class="cont_cell" style="padding-top: 20rpx;" v-if="dataobj1.detail.check_status!=2">
<view class="title" style="width: 200rpx;">审核意见</view>
<textarea v-model="mscontent" placeholder="请输入审核意见" />
</view>
<view class="bot_btn" v-if="dataobj1.detail.check_status!=2">
<view class="reset" @click="appflowcheck()">拒绝</view>
<view class="submit_btn" @click="appflowcheck1()">通过</view>
</view>
<view class="bot_btn">
<view class="reset" @click="appflowcheck()">拒绝</view>
<view class="submit_btn" @click=" appflowcheck1()">通过</view>
</view>
<u-picker :show="branchShow" :defaultIndex='defaultIndex' ref="branchRef" :columns="branchColumns"
@confirm="branchConfirm" @change="columnCode" :closeOnClickOverlay="true" @close="branchShowclose"
keyName="name">
@cancel='branchShowclose' @confirm="branchConfirm" @change="columnCode" closeOnClickOverlay
@close="branchShowclose" keyName="name">
</u-picker>
</view>
</template>
<script>
import {
oaUploads
} from '../../api/upload'
@ -248,6 +256,7 @@
this.applist(option.type)
this.getDocumentList()
this.getDocumentList2()
},
onShow() {
// this.getFlow()
@ -327,7 +336,10 @@
this.meatelist.detail_time = 'detail_time' in this.dataobj1.detail ? this.dataobj1.detail.detail_time :
''
this.meatelist.content = 'content' in this.dataobj1.detail ? this.dataobj1.detail.content : ''
this.meatelist.num = 'num' in this.dataobj1.detail ? this.dataobj1.detail.num : ''
this.meatelist.num1 = 'num1' in this.dataobj1.detail ? this.dataobj1.detail.num1 : ''
this.meatelist.amount = 'amount' in this.dataobj1.detail ? this.dataobj1.detail.amount : ''
this.meatelist.bank = 'bank' in this.dataobj1.detail ? this.dataobj1.detail.bank : ''
// this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
// this.meatelist.remark1 = 'remark1' in this.dataobj1.detail ? this.dataobj1.detail.remark1 : ''
@ -353,9 +365,9 @@
if (this.columns[i].id == this.dataobj1.detail.detail_type) {
this.meatelist.detail_type = this.columns[i].name
this.leavaTypeId1= this.columns[i].id
this.leavaTypeId1 = this.columns[i].id
}
console.log(this.meatelist.detail_type,this.leavaTypeId1)
// console.log(this.meatelist.detail_type,this.leavaTypeId1)
}
@ -371,7 +383,7 @@
for (let i in this.columns) {
if (this.columns[i].id == this.dataobj1.detail.department_type) {
this.meatelist.department_type= this.columns[i].name
this.meatelist.department_type = this.columns[i].name
this.leavaTypeId = this.columns[i].id
}
// console.log(this.meatelist.detail_type)
@ -391,7 +403,7 @@
if (this.columns[i].id == this.dataobj1.detail.other_type) {
this.meatelist.other_type = this.columns[i].name
this.leavaTypeId2= this.columns[i].id
this.leavaTypeId2 = this.columns[i].id
}
// console.log(this.meatelist.detail_type)
}
@ -410,7 +422,7 @@
if (this.columns[i].id == this.dataobj1.detail.position_type) {
this.meatelist.position_type = this.columns[i].name
this.leavaTypeId3= this.columns[i].id
this.leavaTypeId3 = this.columns[i].id
}
// console.log(this.meatelist.detail_type)
}
@ -418,6 +430,43 @@
}
if ('num' in this.dataobj && this.dataobj1.detail.type == 20) {
const detailTypeOptions = Object.entries(this.dataobj.num.item).map(([id, name]) => ({
id,
name
}));
this.columns = detailTypeOptions
for (let i in this.columns) {
if (this.columns[i].id == this.dataobj1.detail.num) {
this.meatelist.num = this.columns[i].name
this.leavaTypeId4 = this.columns[i].id
}
// console.log(this.meatelist.detail_type)
}
}
if ('num1' in this.dataobj && this.dataobj1.detail.type == 20) {
const detailTypeOptions = Object.entries(this.dataobj.num1.item).map(([id, name]) => ({
id,
name
}));
this.columns = detailTypeOptions
for (let i in this.columns) {
if (this.columns[i].id == this.dataobj1.detail.num1) {
this.meatelist.num1 = this.columns[i].name
this.leavaTypeId5 = this.columns[i].id
}
// console.log(this.meatelist.detail_type)
}
}
},
//
@ -462,6 +511,8 @@
this.branchColumns = codelist
this.branchColumns1 = codelist
this.branchColumns2 = codelist
console.log(this.branchColumns)
})
@ -541,7 +592,7 @@
//
deliveryWayChangetwo(e) {
console.log(e)
// console.log(e)
this.check = e.detail.value
},
//
@ -555,7 +606,12 @@
check_admin_ids: this.check_admin_ids.toString()
}
let res = await appflow_check(data)
if (res.code == 0) {
uni.navigateBack({
delta: 1
})
}
Toast(res.msg)
},
@ -570,6 +626,12 @@
}
let res = await appflow_check(data)
console.log(res)
if (res.code == 0) {
uni.navigateBack({
delta: 1
})
}
Toast(res.msg)
},
leixin(e) {
@ -604,7 +666,12 @@
leixiner() {
this.branchShow2 = true
},
leixiner1() {
this.branchShow = true
console.log('111111111', this.branchShow)
},
branchShowclose() {
this.branchShow = false
},
branchShowclose1() {
@ -613,29 +680,7 @@
selectTime(item) {
item.timeShow = true
},
async branchHandler(e) {
const {
columnIndex,
value,
values, // values
index,
// pickerref
picker = this.$refs.branchRef
} = e
// ()
if (columnIndex === 0) {
// pickerthis
let res = await getEmployeeAPI({
did: value[0].id
})
if (res.length < 1) {
res[0] = {
name: '无'
}
}
picker.setColumnValues(1, res)
}
},
// columnIndexvaluevalues
branchConfirm(e) {
console.log('confirm', e, e.value[2].name)
@ -772,6 +817,11 @@
try {
const res = await appapprove(subData)
if (res.code == 0) {
uni.navigateBack({
delta: 1
})
}
Toast('提交成功')
} catch (e) {
Toast('提交失败')
@ -837,7 +887,10 @@
position: relative;
padding-bottom: 100px;
}
/deep/.u-action-sheet__item-wrap {
overflow: auto;
max-height: 50vh;
}
.leave_box,
.flow_path {
width: 100%;
@ -961,6 +1014,7 @@
margin: 31.58rpx;
}
}
:last-child {
.circle {
&::before {

File diff suppressed because it is too large Load Diff

View File

@ -15,13 +15,14 @@
<view class="leava_type flex_a_c"
v-if="fieldValue.type=='select'&& fieldKey!='copy_uids'&&fieldKey!='check_admin_ids'&&fieldKey!='flow_id'&&fieldKey!='name'">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" @click="leixin(fieldKey)"
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" disabled @click="leixin(fieldKey)"
v-if="fieldKey!='name'">
</view>
<view class="leava_type flex_a_c" v-if="fieldKey=='name'&typeId!=6">
<view class="leava_type flex_a_c" v-if="fieldKey=='name'&fieldValue.type=='select'">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" @click="leixiner()">
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择" disabled @click="leixiner()">
</view>
<view class=""
v-if="fieldValue.type=='input'&&fieldKey!='end_time'&&fieldKey!='start_time'&&fieldKey!='duration'">
@ -34,21 +35,32 @@
<view class="" v-if=" fieldKey=='start_time'">
<view class="cont_cell">
<view class="title">{{ timeData[0].title }}</view>
<input type="text" v-model="timeData[0].time" placeholder="请选择时间" disabled
@click="selectTime(timeData[0])">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
@click="selectTime1">
</view>
</view>
<view class="" v-if="fieldKey=='end_time'">
<view class="cont_cell">
<view class="title">{{ timeData[1].title }}</view>
<input type="text" v-model="timeData[1].time" placeholder="请选择时间" disabled
@click="selectTime(timeData[1])">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
@click="selectTime2">
</view>
</view>
<view class="" v-if="fieldKey=='detail_time'">
<view class="cont_cell">
<view class="title">{{fieldValue.title}}</view>
<input type="text" v-model="meatelist[fieldKey]" placeholder="请选择时间" disabled
@click="selectTime3(fieldKey)">
</view>
</view>
<view class="" v-if="fieldKey=='duration'">
<view class="cont_cell">
<view class="title">{{fieldValue.title}}</view>
@ -130,7 +142,7 @@
@confirm="leavaType">
</u-picker> -->
<u-action-sheet :show="leavaTypeShow" keyName="name" :actions="columns" title="请选择工作类别"
<u-action-sheet :show="leavaTypeShow" keyName="name" :actions="columns" title="请选择类别"
@close="leavaTypeShow = false" @select="leavaType">
</u-action-sheet>
@ -142,33 +154,39 @@
<!-- 审核人 -->
<u-picker :show="branchShow" :defaultIndex='defaultIndex' ref="branchRef" :columns="branchColumns"
@confirm="branchConfirm" @change="columnCode" :closeOnClickOverlay="true" @close="branchShowclose"
keyName="name">
keyName="name" @cancel="branchShowclose">
</u-picker>
<u-picker :show="branchShow1" :defaultIndex='defaultIndex1' ref="branchRef" :columns="branchColumns1"
@confirm="branchConfirm1" @change="columnCode1" :closeOnClickOverlay="true" @close="branchShowclose1"
keyName="name">
@cancel="branchShowclose1" keyName="name">
</u-picker>
<u-picker :show="branchShow2" :defaultIndex='defaultIndex2' ref="branchRef" :columns="branchColumns2"
@confirm="branchConfirm2" @change="columnCode2" :closeOnClickOverlay="true" @close="branchShowclose2"
keyName="name">
@cancel="branchShowclose2" keyName="name">
</u-picker>
<u-picker :show="flag1" :columns="columns3" ref="uPicker4" @cancel="flag1 = false" closeOnClickOverlay
@close="flag1 = false" @confirm="confirm" keyName="title" :defaultIndex=[0,0]>
</u-picker>
<!-- 选择时间 -->
<block v-for="(item,i) in timeData" :key="i">
<u-datetime-picker :show="item.timeShow" v-model="item.timeVal" mode="datetime" :maxDate="1786778555000"
:minDate="timestamp" closeOnClickOverlay @confirm="timeConfirm($event,i)"
@cancel="item.timeShow = false" @close="item.timeShow = false"></u-datetime-picker>
</block>
<u-datetime-picker :show="timeShow1" mode="datetime" :maxDate="1786778555000" closeOnClickOverlay
@confirm="timeConfirm1($event)" @cancel="timeShow1 = false" @close="timeShow1 = false"></u-datetime-picker>
<u-datetime-picker :show="timeShow2" mode="datetime" :maxDate="1786778555000" closeOnClickOverlay
@confirm="timeConfirm2($event)" @cancel="timeShow2 = false" @close="timeShow2 = false"></u-datetime-picker>
<u-datetime-picker :show="timeShow" mode="datetime" :maxDate="1786778555000" :minDate="timestamp"
closeOnClickOverlay @confirm="timeConfirm3($event)" @cancel="timeShow = false"
@close="timeShow = false"></u-datetime-picker>
</view>
</template>
<script>
import {
oaUploads
} from '../../api/upload'
@ -211,7 +229,9 @@
content: '', //
flowPathShow: false,
flowPathSheet: [],
timeShow: false,
timeShow1: false,
timeShow2: false,
timeData: [{
title: '开始时间:',
timeShow: false,
@ -235,6 +255,8 @@
leavaTypeId1: '',
leavaTypeId2: '',
leavaTypeId3: '',
leavaTypeId4: '',
leavaTypeId5: '',
columns: [],
//
branchShow: false,
@ -265,10 +287,10 @@
},
this.typeId = option.type
if (option.type == 8) {
this.timeData[0].title = '借用日期:'
this.timeData[1].title = '拟归还日期:'
}
// if (option.type == 8) {
// this.timeData[0].title = ''
// this.timeData[1].title = ''
// }
this.applist(option.type)
this.getDocumentList()
this.getDocumentList2()
@ -288,6 +310,7 @@
leaveHours
} = this.calculateLeaveDaysAndHours(this.timeData[0].time, this.timeData[1]
.time)
console.log(leaveHours, leaveDays)
this.manHour = leaveHours + '小时'
this.daysNum = leaveDays + '天'
}
@ -477,6 +500,20 @@
this.columns = detailTypeOptions
}
if (e == 'num') {
const detailTypeOptions = Object.entries(this.dataobj.num.item).map(([id, name]) => ({
id,
name
}));
this.columns = detailTypeOptions
}
if (e == 'num1') {
const detailTypeOptions = Object.entries(this.dataobj.num1.item).map(([id, name]) => ({
id,
name
}));
this.columns = detailTypeOptions
}
if (e == 'position_type') {
const detailTypeOptions = Object.entries(this.dataobj.position_type.item).map(([id, name]) => ({
id,
@ -500,8 +537,15 @@
branchShowclose2() {
this.branchShow2 = false
},
selectTime(item) {
item.timeShow = true
selectTime1(item) {
this.timeShow1 = true
},
selectTime2(item) {
this.timeShow2 = true
},
selectTime3(item) {
this.timeShow = true
},
async branchHandler(e) {
const {
@ -563,6 +607,12 @@
} else if (this.datatype == 'position_type') {
this.meatelist.position_type = e.name
this.leavaTypeId3 = e.id
} else if (this.datatype == 'num') {
this.meatelist.num = e.name
this.leavaTypeId4 = e.id
} else if (this.datatype == 'num1') {
this.meatelist.num1 = e.name
this.leavaTypeId5 = e.id
}
console.log(this.leavaTypeId2)
// console.log(this.meatelist,this.datatype, e.name)
@ -588,21 +638,29 @@
})
this.flowPathSheet = flow
},
timeConfirm(e, i) {
this.timeData[i].time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeData[i].timeDay = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
this.timeData[i].timeHour = uni.$u.timeFormat(e.value, 'hh:MM')
this.timeData[i].timeShow = false
timeConfirm1(e, i) {
this.meatelist.start_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeData[0].time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeShow1 = false
},
timeConfirm2(e, i) {
this.meatelist.end_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeData[1].time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss')
this.timeShow2 = false
},
timeConfirm3(e, i) {
this.meatelist.detail_time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
this.timeShow = false
},
async submiteBtn() {
console.log(this.leavaTypeId2)
console.log(this.leavaTypeId2, this.meatelist)
if (this.meatelist.mobile) {
const pattern = /^1[3456789]\d{9}$/;
if (!pattern.test(this.meatelist.mobile)) return Toast('手机号输入错误')
}
let subData;
let subData1;
if (this.typeId == 6 || this.typeId == 7 || this.typeId == 8 || this.typeId == 21) {
@ -611,7 +669,6 @@
fileIds.push(item.id)
});
subData1 = {
detail_type: this.leavaTypeId1,
content: this.meatelist.content,
check_admin_name: this.check_admin_name,
check_admin_ids: this.check_admin_ids,
@ -622,54 +679,30 @@
type: this.typeId,
}
if (this.typeId == 8) {
subData1.start_time = this.timeData[0].timeDay + ' ' + this.timeData[0].timeHour,
subData1.end_time = this.timeData[1].timeDay + ' ' + this.timeData[1].timeHour,
subData1.department_type = this.leavaTypeId
subData1.detail_type = this.leavaTypeId1
subData1.other_type = this.leavaTypeId2
}
if (this.typeId == 21) {
if (this.meatelist.hasOwnProperty('department_type')) {
subData1.department_type = this.leavaTypeId
}
if (this.meatelist.hasOwnProperty('other_type')) {
subData1.other_type = this.leavaTypeId2
}
if (this.meatelist.hasOwnProperty('position_type')) {
subData1.position_type = this.leavaTypeId3
// console.log(subData1.position_type , this.leavaTypeId3)
}
if (this.meatelist.hasOwnProperty('detail_type')) {
subData1.detail_type = this.leavaTypeId1
}
if (this.meatelist.hasOwnProperty('num') && this.typeId == 20) {
subData1.num = this.leavaTypeId4
}
if (this.meatelist.hasOwnProperty('num1') && this.typeId == 20) {
subData1.num1 = this.leavaTypeId5
}
subData = {
...this.meatelist,
...subData1
}
} else {
let fileIds = [];
this.fileArray.map((item, i) => {
fileIds.push(item.id)
});
subData = {
detail_type: this.leavaTypeId1,
start_time: this.timeData[0].timeDay + ' ' + this.timeData[0].timeHour,
end_time: this.timeData[1].timeDay + ' ' + this.timeData[1].timeHour,
duration: this.manHour,
content: this.meatelist.content,
check_admin_name: this.check_admin_name,
check_admin_ids: this.check_admin_ids,
flow_id: this.flow_id,
file_ids: fileIds.join(','),
copy_names: this.copy_names,
copy_uids: this.copy_uids,
type: this.typeId,
file: ''
}
}
console.log(subData)
try {
@ -775,7 +808,7 @@
/**
* 计算两个时间戳之间相差的小时数
* */
calculateLeaveDaysAndHours(leaveStartTime, leaveEndTime, hoursPerDay = 8) {
calculateLeaveDaysAndHours(leaveStartTime, leaveEndTime) {
const startDate = new Date(leaveStartTime);
const endDate = new Date(leaveEndTime);
@ -783,8 +816,12 @@
const leaveDays = Math.floor((endDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1000)) + 1;
//
const leaveHours = leaveDays * hoursPerDay;
// const leaveHours = leaveDays * hoursPerDay;
const timeDifference = endDate.getTime() - startDate.getTime();
//
const leaveHours = timeDifference / (1000 * 60 * 60); //
// console.log(leaveHours)
//
return {
leaveDays,
@ -822,7 +859,10 @@
margin-bottom: 35.09rpx;
padding-bottom: 28.07rpx;
}
/deep/.u-action-sheet__item-wrap {
overflow: auto;
max-height: 50vh;
}
.bot_btn {
position: fixed;
bottom: 0;

View File

@ -1,24 +1,17 @@
<template>
<view class="leave_request">
<view class="leave_box" v-for='(item,i) in dataobj' :key="i">
{{ item.title }} - {{ item.type }}
<view class="leava_type flex_a_c" >
<view class="title">{{dataobj[i].title}}</view>
<view class="leave_box" v-for='(item,i) in dataobj' >
{{item}}
<view class="leava_type flex_a_c">
<view class="title">请假类型</view>
<input type="text" v-model="leavaTypeVal" placeholder="请选择" disabled @click="leavaTypeShow = true">
</view>
{{i}}
<view class="cont_cell">
<view class="title">{{ dataobj[i]}}</view>
<input type="text" v-model="item.time" placeholder="请选择时间" disabled @click="selectTime(item)">
</view>
<!-- <block v-for="(item,i) in timeData" :key="i">
<block v-for="(item,i) in timeData" :key="i">
<view class="cont_cell">
<view class="title">{{ item.title }}</view>
<input type="text" v-model="item.time" placeholder="请选择时间" disabled @click="selectTime(item)">
</view>
</block> -->
</block>
<view class="cont_cell">
<view class="title">请假工时</view>
<input type="text" v-model="manHour" disabled placeholder="请假工时">
@ -110,7 +103,9 @@
<script>
import {
oaLeaveData
} from '@/static/server/server.js'
import {
oaUploads
} from '../../api/upload'
@ -462,7 +457,7 @@
content: '确定删除图片?',
success: res => {
if (res.confirm) {
that.fileArray.splice(i, 1)
that.fileArray.splice((i, 1))
} else if (res.cancel) {
console.log('用户点击取消');
}

View File

@ -93,7 +93,7 @@
id: id
})
console.log(res.data)
this.list = res.data
}

View File

@ -9,7 +9,7 @@
<view class="middle-threeb" v-for="(items,j) in list" :key='j' @click="msg(items.id)"
style="margin-bottom: 20rpx;">
<liu-swipe-action :index="j">
<liu-swipe-action :index="j" @clickItem="clickItem(items)" :btnList='btnList'>
<view class="middle-threeb-title">
<view class="title">
{{items.msg_type}}
@ -49,38 +49,7 @@
</view>
</view>
</view>
<view class="content-middle" :style="{'position':'absolute','top':0+'px','zIndex':999}" v-if="!screenShow">
<view class="content-middle-one">
<view class="middle-one">
<view class="content-middle-search">
<text class='iconfont icon-sousuo2'></text>
<input type='text' :value='keyword' placeholder='搜索任务状态、优先级、部门等' placeholder-class='placeholder'
@input="setValue"></input>
</view>
<view class="content-middle-sai" @click="screen" style="background-color: #3274F9;">
<view class="content-middle-saia">
<image src="../../static/images/saixuan.png" mode="aspectFit"></image>
</view>
<view class="content-middle-saib" style="color: #fff;">
筛选
</view>
</view>
</view>
<view class="content-middle-search-detail">
<view class="search-detail" v-for="(item,k) in klist" :key='k'>
<view class="search-detail-one">
<view class="name">
{{item.name}}
</view>
</view>
<view class="search-detail-two">
{{item.name1}}
</view>
</view>
</view>
</view>
</view>
<u-empty v-if="loadConfig.status=='nomore'&& list.length==0" text="没有信息"
icon="/static/empty/data.png"></u-empty>
@ -93,7 +62,8 @@
<script>
import {
appmessageinbox,
appmessageread
appmessageread,
deletemessage
} from '@/api/oa.js'
import {
HTTP_REQUEST_URL
@ -107,14 +77,6 @@
return {
show: false,
btnList: [{
id: '1',
name: '编辑',
width: '100rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}, {
id: '2',
name: '删除',
height: '100rpx',
@ -185,14 +147,29 @@
},
clickItem(data) {
uni.showModal({
content: '确定要删除吗',
success: (e) => {
if (e.confirm) {
this.deleteDepartment(data.id)
}
}
})
//
},
async deleteDepartment(id) {
const res = await deletemessage({
ids: id
})
Toast(res.msg)
this.initList()
},
//
async msg(id) {
const res = await appmessageread({
id: id
})
uni.navigateTo({
url: '/pages/message/detail??type=' + id
})
@ -221,7 +198,6 @@
};
</script>
<style lang="scss">
page {
padding-bottom: 88rpx;

View File

@ -28,7 +28,7 @@
<view class="middle-three">
<view class="middle-threeb" v-for="(items,j) in list" :key='j' @click="deleteDepartment(items.id)">
<view class="middle-threeb" v-for="(items,j) in list" :key='j'>
<liu-swipe-action :index="j" @clickItem="clickItem" :btnList='btnList'>
<view class="middle-threeb-title">
<view class="title">
@ -101,7 +101,7 @@
</view>
</view>
</view>
<view class="content-middle-two">
<view class="content-middle-two">
<view class="middle-two-img">
<image src="@/static/images/ja2.png" mode="aspectFit"></image>
</view>
@ -510,6 +510,7 @@
}
}
.content-middle-two {
width: 386rpx;
height: 70rpx;
@ -540,6 +541,4 @@
}
}
</style>

View File

@ -147,7 +147,7 @@
async initList() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list2 = [];
this.list = [];
await this.getDocumentList();
},
//
@ -186,7 +186,7 @@
success: (e) => {
if (e.confirm) {
this.deleteDepartment(this.list[data.index].id)
this.initList()
}
}
})

View File

@ -36,12 +36,12 @@
<view class="achor">工作类型<text>{{item.cate_name}}</text></view>
<view class="achor">关联项目<text>{{item.project_name}}</text></view>
<view class="achor">预估工时<text>{{item.plan_hours}}</text></view>
<view class="middle-edit" @click="editpart(item)">
<!-- <view class="middle-edit" @click="editpart(item)">
编辑
</view>
<view class="middle-delete" @click="clickItem(item)">
删除
</view>
</view> -->
</view>
</view>

View File

@ -24,11 +24,13 @@
</view>
<view class="one-titleb"
:style="{'color':item.status==1?'#34A853':item.status==2?'#F9AA32':item.status==3?'#F02828':''}">
<text v-if='item.status==1'>已通过</text>
<text v-if='item.status==2'>待审批</text>
<text v-if='item.status==3'>审批拒绝</text>
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
<text v-if='item.check_status==5'>已开具</text>
</view>
</view>
<view class="middle-one-time">报销部门{{item.department}}</view>
@ -44,6 +46,10 @@
</view>
</view>
</view>
<u-empty v-if="loadConfig.status=='nomore'&& list2.length==0" text="没有信息"
icon="/static/empty/data.png"></u-empty>
<u-loadmore v-else :status="loadConfig.status" :loading-text="loadConfig.loadingText"
:loadmore-text="loadConfig.loadmoreText" :nomore-text="loadConfig.nomoreText" />
</view>
</template>
@ -80,23 +86,7 @@
],
list2: [],
num: 0,
btnList: [{
id: '1',
name: '编辑',
width: '100rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}, {
id: '2',
name: '删除',
height: '100rpx',
width: '100rpx',
bgColor: '#ed656d',
color: '#FFFFFF',
fontSize: '28rpx'
}],
btnList: [],
loadConfig: {
page: 1,
limit: 5,
@ -158,7 +148,7 @@
async initList2() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list3 = [];
this.list2 = [];
await this.getlist2()
},
async getlist() {
@ -251,8 +241,14 @@
})
if (res.code == 0) {
this.loadConfig.status = "loading"
if (this.num == 0) {
this.initList()
} else if (this.num == 1) {
this.initList1()
} else {
this.initList2()
}
}
Toast(res.msg);
@ -269,11 +265,11 @@
uni.navigateTo({
url: '/pages/reimbursementapplication/handle?type=' + item.id
})
}else if (this.num == 2){
} else if (this.num == 2) {
uni.navigateTo({
url: '/pages/reimbursementapplication/detail?type=' + item.id +'&num=0'
url: '/pages/reimbursementapplication/detail?type=' + item.id + '&num=0'
})
}else{
} else {
uni.navigateTo({
url: '/pages/reimbursementapplication/detail?type=' + item.id
})

View File

@ -426,6 +426,11 @@
const res = await appexpense(this.listobj)
if(res.code==0){
uni.navigateBack({
delta:1
})
}
Toast('提交成功')

View File

@ -56,9 +56,10 @@
</view>
<view class="leave_box" style="padding-top: 100rpx;" v-if="fileArray.length>0">
<!-- <view class="leave_box" style="padding-top: 100rpx;" v-if="fileArray.length>0"> -->
<!-- 附件 -->
<view class="" style="background-color: #fff;">
<block v-for="(item, i) in fileArray" :key="i">
<view class="file flex_a_c_j_sb">
<view class="l_file">
@ -69,10 +70,11 @@
<!-- <u-icon @click="delImg(i)" name="close-circle" color="#333333" size="28"></u-icon> -->
</view>
</block>
</view>
<view class="leave_box" style="padding-top: 100rpx;" v-if="listobj.check_status!= 4&& ktype!=0">
<view class="cont_cell" style="padding-top: 20rpx;" >
<view class="leave_box" style="padding-top: 100rpx;" v-if="listobj.check_status!= 4&& listobj.check_status!= 2&&ktype!=0">
<view class="cont_cell" style="padding-top: 20rpx;">
<view class="title" style="width: 200rpx;">撤回意见</view>
<textarea v-model="mscontent" placeholder="请输入撤回意见" />
</view>
@ -92,7 +94,7 @@
<view class="cont_cell">
<view class="title">审核状态</view>
{{listobj.check_status=='0'?'待审核':listobj.check_status=='1'?'审核中':listobj.check_status=='2'?'审核通过':listobj.check_status=='3'?'审核不通过':'撤销审核'}}
{{listobj.check_status=='0'?'待审核':listobj.check_status=='1'?'审核中':listobj.check_status=='2'?'审核通过':listobj.check_status=='3'?'审核不通过':listobj.check_status=='4'?'撤销审核':'已开具'}}
</view>
<view class="cont_cell">
<view class="title">审核人</view>
@ -105,7 +107,7 @@
</view>
<view class="" v-if="ktype!=0">
<view class="" v-if=" listobj.check_status!= 2&&ktype!=0">
<view class="bot_btn" v-if="listobj.check_status!= 4">
<!-- <view class="reset" @click="reset">重置</view> -->
<view class="submit_btn" @click="submiteBtn">撤回</view>
@ -118,8 +120,8 @@
</view>
<u-action-sheet :show="leavaTypeShow" keyName="name" :actions="columns" title="请选择"
@close="leavaTypeShow = false" @select="leavaType">
<u-action-sheet :show="leavaTypeShow" keyName="name" :actions="columns" title="请选择" @close="leavaTypeShow = false"
@select="leavaType">
</u-action-sheet>
<u-action-sheet :show="leavaTypeShow1" keyName="name" :actions="actions4" title="请选择"
@close="leavaTypeShow1 = false" @select="leavaType1">
@ -210,12 +212,12 @@
fileArray: [],
dnum: 0,
check_record: [],
ktype:1
ktype: 1
}
},
onLoad(option) {
this.detail(option.type)
this.ktype=option.num
this.ktype = option.num
},
onShow() {
this.fileOption = {
@ -254,12 +256,16 @@
this.check_record = res.data.check_record
this.listobj.check_status = res.data.detail.check_status
if ( res.data && res.data.detail && res.data.detail.fileArray) {
this.fileArray = res.data.detail.fileArray.map(function(obj) {
return {
name: obj.name,
filesize: obj.filesize
};
});
}
// console.log(this.fileArray,'11111111111', res.data.detail)
},

View File

@ -56,7 +56,7 @@
</view>
<view class="leave_box" style="padding-top: 100rpx;">
<view class="leave_box" style="padding-top: 100rpx; background-color: #fff;">
<!-- 附件 -->
<block v-for="(item, i) in fileArray" :key="i">
@ -81,7 +81,7 @@
<view class="cont_cell">
<view class="title">审核状态</view>
{{listobj.check_status=='0'?'待审核':listobj.check_status=='1'?'审核中':listobj.check_status=='2'?'审核通过':listobj.check_status=='3'?'审核不通过':'撤销审核'}}
{{listobj.check_status=='0'?'待审核':listobj.check_status=='1'?'审核中':listobj.check_status=='2'?'审核通过':listobj.check_status=='3'?'审核不通过':listobj.check_status=='4'?'审核不通过':'已打款'}}
</view>
<view class="cont_cell">
<view class="title">审核人</view>
@ -99,7 +99,7 @@
了此申请操作意见{{item.content}}</text>
</view>
</block>
<view class="" >
<view class="" v-if='listobj.check_status!=2'>
<view class="title" style="margin-bottom: 30rpx;">审核节点</view>
<radio-group class="select_group" @change="deliveryWayChangetwo1" >
<label class="radio_select" style="margin-right:15rpx;display: flex; margin-bottom: 30rpx;">
@ -119,14 +119,14 @@
</label>
</radio-group>
</view>
<view class="cont_cell" style="padding-top: 20rpx;" v-if="listobj.check_status!= 4">
<view class="cont_cell" style="padding-top: 20rpx;" v-if="listobj.check_status!= 4&&listobj.check_status!=2">
<view class="title" style="width: 200rpx;">审核意见</view>
<textarea v-model="mscontent" placeholder="请输入审核意见" />
</view>
</view>
<view class="bot_btn">
<view class="bot_btn" v-if='listobj.check_status!=2'>
<view class="reset" @click="appflowcheck()">拒绝</view>
<view class="submit_btn" @click=" appflowcheck1()">通过</view>
</view>

View File

@ -125,12 +125,12 @@
</u-action-sheet>
<!-- 部门选择 -->
<u-picker :show="branchShow" :defaultIndex='defaultIndex' ref="branchRef" :columns="branchColumns"
<u-picker :show="branchShow" :defaultIndex='defaultIndex' ref="branchRef" :columns="branchColumns" @cancel='branchShowclose'
@confirm="branchConfirm" @change="columnCode" :closeOnClickOverlay="true" @close="branchShowclose"
keyName="name">
</u-picker>
<u-picker :show="branchShow1" :defaultIndex='defaultIndex1' ref="branchRef" :columns="branchColumns1"
<u-picker :show="branchShow1" :defaultIndex='defaultIndex1' ref="branchRef" :columns="branchColumns1" @cancel='branchShowclose1'
@confirm="branchConfirm1" @change="columnCode1" :closeOnClickOverlay="true" @close="branchShowclose1"
keyName="name">
</u-picker>
@ -205,7 +205,7 @@
}
},
onLoad(option) {
this.detail(option.type)
// this.detail(option.type)
},
onShow() {
this.fileOption = {
@ -226,24 +226,7 @@
methods: {
async detail(id) {
let res = await appexpenseview({
id: id
})
// console.log(res,'111')
this.listobj.code = res.data.detail.code
this.listobj.id=id
this.list=res.data.detail.list
this.listobj.expense_time= res.data.detail.expense_time
this.listobj.income_month = res.data.detail.income_month
this.check_admin_name= res.data.detail.check_user
this.copy_names= res.data.detail.copy_user
this.project= res.data.detail.ptname
this.listobj.flow_id=res.data.flows.id
this.listobj.ptid= res.data.detail.ptid
},
reset() {
this.list = [{
name: "",
@ -419,6 +402,11 @@
const res = await appexpense(this.listobj)
if(res.code==0){
uni.navigateBack({
delta:1
})
}
Toast('提交成功')

View File

@ -13,7 +13,7 @@
</view>
<view class="content-middle-one">
<view class="middle-one" v-for="(item,j) in list" :key='j'>
<liu-swipe-action :index="j" @clickItem="clickItem" :btnList='btnList'>
<view class="middle-onea">
<view class="middle-onea-title">
{{item.name}}
@ -31,16 +31,16 @@
<view class="achor">职位{{item.position}}</view>
<view class="achor">部门负责人{{item.lead_admin}}</view>
<view class="achor">交接人{{item.connect_names}}</view>
<view class="textare-three">
<view class="textare-three" v-if="item.remark.length>0">
<textarea :value="item.remark" placeholder="" />
</view>
<view class="middle-edit" @click="editpart(item)">
<!-- <view class="middle-edit" @click="editpart(item)">
编辑
</view>
<view class="middle-delete" @click="clickItem(item)">
删除
</view>
</view> -->
</view>
</view>
@ -50,7 +50,7 @@
</view>
</liu-swipe-action>
</view>
</view>
@ -64,7 +64,7 @@
</view>
<view class="content-bootom-two">添加离职档案</view>
</view>
<u-empty v-if="loadConfig.status=='nomore'&& list.length==0" text="没有信息"
<u-empty v-if="loadConfig.status=='nomore'&& list.length==0" text="没有信息"
icon="/static/empty/data.png"></u-empty>
<u-loadmore v-else :status="loadConfig.status" :loading-text="loadConfig.loadingText"
:loadmore-text="loadConfig.loadmoreText" :nomore-text="loadConfig.nomoreText" />
@ -85,6 +85,14 @@
return {
show: false,
btnList: [{
id: '1',
name: '编辑',
width: '100rpx',
height: '100rpx',
bgColor: '#5f92f7',
color: '#FFFFFF',
fontSize: '28rpx'
}, {
id: '2',
name: '删除',
width: '100rpx',
@ -92,7 +100,7 @@
color: '#FFFFFF',
fontSize: '28rpx'
}],
loadConfig: {
loadConfig: {
page: 1,
limit: 5,
loadingText: '努力加载中',
@ -108,7 +116,7 @@ loadConfig: {
};
},
onShow() {
this.getDocumentList()
this.initList()
},
onReachBottom() {
this.getDocumentList()
@ -117,16 +125,25 @@ loadConfig: {
uni.stopPullDownRefresh()
},
methods: {
async initList() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list = [];
await this.getDocumentList()
},
//
async getDocumentList() {
try {
if (this.loadConfig.status == "nomore") return;
this.loadConfig.status = "loading"
let res = await leaveuserpersonal({
page: this.loadConfig.page,
limit: this.loadConfig.limit,
keyword: this.keyword
})
@ -137,9 +154,7 @@ loadConfig: {
this.loadConfig.page++;
}
this.list = [...this.list, ...res.data?.data]
} catch (e) {
this.loadConfig.status = "nomore"
}
},
//
adddepart() {
@ -150,15 +165,20 @@ loadConfig: {
//
clickItem(data) {
// console.log(e)
if (data.id == 2) {
uni.showModal({
content: '确定要删除吗',
success: (e) => {
if (e.confirm) {
this.deleteDepartment(data.id)
this.deleteDepartment(this.list[data.index].id)
}
}
})
} else {
this.editpart(this.list[data.index])
}
},
//
@ -169,7 +189,7 @@ loadConfig: {
})
if (res.code == 0) {
this.getDocumentList()
this.initList()
}
Toast(res.msg);
@ -185,13 +205,13 @@ loadConfig: {
},
//
search() {
this.getDocumentList()
this.initList()
},
//
setValue(e) {
this.keyword = e.detail.value
if (this.keyword.length == 0) {
this.getDocumentList()
this.initList()
}
}
}

View File

@ -21,7 +21,7 @@
</view>
<view class="content-middle-two">
<view class="middle-two" v-for="(item,i) in list" :key='i'>
<view class="middle-two" v-for="(item,i) in list" :key='i' @click="arryitem(item)">
<view class="middle-twoa">
<image :src="item.img" mode=""></image>
</view>
@ -30,7 +30,7 @@
</view>
</view>
</view>
<view class="content-middle-three">
<view class="content-middle-three" v-if="list2.length>0">
<view class="middle-three" v-for="(item,k) in list2" :key='k'>
<view class="middle-threea">企业员工</view>
@ -38,7 +38,8 @@
<view class="middle-threeb">
<view class="middle-threeb-a">
<view class="threeb-a-a">
<image :src="item.img" mode=""></image>
<image src="@/static/images/gw.png" mode=""></image>
</view>
<view class="threeb-a-b">
<view class="name">{{item.department}} <text>({{item.name}})</text></view>
@ -60,8 +61,8 @@
<view class="middle-one">
<view class="content-middle-search">
<text class='iconfont icon-sousuo2'></text>
<input type='text' :value='searchValue' placeholder='点击搜索商品、店铺名称'
placeholder-class='placeholder' @input="setValue"></input>
<input type='text' :value='keyword' placeholder='点击搜索商品、店铺名称' placeholder-class='placeholder'
@input="setValue"></input>
</view>
<view class="content-middle-sai" @click="screen" style="background-color: #3274F9;">
<view class="content-middle-saia">
@ -73,26 +74,17 @@
</view>
</view>
<view class="content-middle-search-detail">
<view class="search-detail">
<view class="search-detail" v-for="(item,i) in list3" :key='i' @click="saixun(item)">
<view class="search-detail-one">
<view class="name">
员工状态
</view>
</view>
<view class="search-detail-two">
正常登陆
</view>
</view>
<view class="search-detail">
<view class="search-detail-one">
<view class="name">
员工状态
</view>
</view>
<view class="search-detail-two">
正常登陆
{{item.title}}
</view>
</view>
</view>
</view>
</view>
@ -103,7 +95,7 @@
</view>
<view class="content-bootom-two">添加企业员工</view>
</view>
<u-empty v-if="loadConfig.status=='nomore'&& list.length==0" text="没有信息"
<u-empty v-if="loadConfig.status=='nomore'&& list2.length==0" text="没有信息"
icon="/static/empty/data.png"></u-empty>
<u-loadmore v-else :status="loadConfig.status" :loading-text="loadConfig.loadingText"
:loadmore-text="loadConfig.loadmoreText" :nomore-text="loadConfig.nomoreText" />
@ -130,23 +122,44 @@
name: '人力资源',
keyword: '',
list: [{
name: '请假申请',
img: require('@/static/images/s2.png')
name: '部门架构',
id: 1,
img: require('@/static/images/r1.png')
}, {
name: '出差申请',
img: require('@/static/images/s4.png')
name: '岗位职称',
id: 2,
img: require('@/static/images/r2.png')
}, {
name: '请假申请',
img: require('@/static/images/s3.png')
name: '人事调动',
id: 3,
img: require('@/static/images/r3.png')
}, {
name: '采购申请',
img: require('@/static/images/s5.png')
name: '离职档案',
id: 4,
img: require('@/static/images/r4.png')
}],
list2: [],
list3: [{
title: '禁止登录',
id: 0
},
{
title: '正常',
id: 1
}, {
title: '离职',
id: 2
}
],
btnList: [{
id: '2',
name: '删除',
@ -163,6 +176,7 @@
nomoreText: '我也是有底线的~~',
status: 'loadmore'
},
status: '',
merchantData: {
},
@ -183,6 +197,33 @@
uni.stopPullDownRefresh()
},
methods: {
arryitem(item) {
console.log(item.id)
switch (item.id) {
case 1:
uni.navigateTo({
url: '/pages/structure/index'
})
break;
case 2:
uni.navigateTo({
url: '/pages/jobtitle/index'
})
break;
case 3:
uni.navigateTo({
url: '/pages/personneltransfer/index'
})
break;
case 4:
uni.navigateTo({
url: '/pages/resignationprofile/index'
})
break;
}
},
screen() {
this.screenShow = !this.screenShow
},
@ -201,7 +242,8 @@
let res = await comanyerlist({
page: this.loadConfig.page,
limit: this.loadConfig.limit,
keyword: this.keyword
status: this.status,
keyword: this.keyword,
})
console.log(res.data.data)
this.loadConfig.status = "loadmore"
@ -229,6 +271,7 @@
success: (e) => {
if (e.confirm) {
this.deleteDepartment(data.id)
this.initList()
}
}
})
@ -243,7 +286,7 @@
})
if (res.code == 0) {
this.getDocumentList()
this.initList()
}
Toast(res.msg);
@ -257,15 +300,20 @@
},
//
search() {
this.getDocumentList()
this.initList()
},
//
setValue(e) {
this.keyword = e.detail.value
if (this.keyword.length == 0) {
this.getDocumentList()
}
}
this.initList()
},
//
saixun(item) {
this.status = item.id
this.initList()
this.screenShow = true
},
}
};
</script>
@ -496,7 +544,7 @@
margin-top: 5rpx;
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
margin-right: 40rpx;
image {
width: 100%;

View File

@ -12,10 +12,11 @@
<view class="middle-oneb">
<view class="middle-oneb-a">
<view class="threeb-a-a">
<image :src="items.img" mode=""></image>
<image src="@/static/images/gw.png" mode=""></image>
</view>
<view class="threeb-a-b">
<view class="name"> <text>{{items.title}}</text></view>
<view class="name" style="margin-bottom: 20rpx;"> <text>{{items.title}}</text>
</view>
<view class="achor">{{items.remark}}{{item.phone}}</view>
</view>
</view>
@ -106,7 +107,7 @@
async initList() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list1 = [];
this.list = [];
await this.getDocumentList();
},
//
@ -204,6 +205,7 @@
padding: 28rpx 28rpx;
.middle-one {
.middle-onea {
font-size: 35rpx;
font-family: PingFang SC-Bold, PingFang SC;
@ -215,7 +217,7 @@
.middle-oneb {
display: flex;
justify-content: space-between;
margin-bottom: 25rpx;
.middle-oneb-a {
display: flex;
@ -261,7 +263,7 @@
.middle-oneb-b {
width: 80rpx;
height: 80rpx;
margin-top: 45rpx;
image {

View File

@ -40,8 +40,8 @@
</view>
<view class="work">
<view class="wr_item">
<view class="wr_t_left">(技术部):</view>
<view class="wr_t_right">技术专业</view>
<view class="wr_t_left">{{ oaUserInfo.department_name}}</view>
<!-- <view class="wr_t_right">技术专业</view> -->
</view>
</view>
</view>
@ -95,7 +95,7 @@
userInfo
} from '@/api/oaUser.js'
import {
appmessageinbox
msgcount
} from '@/api/oa.js'
import {
@ -111,11 +111,14 @@
type: "warning",
value: 100,
uniMP: false,
myOaData: [{
name: '工资详情',
icon: require('@/static/images/w1.png'),
paths: '/pages/users/salarydetails/index'
}, {
myOaData: [
// {
// name: '',
// icon: require('@/static/images/w1.png'),
// paths: '/pages/users/salarydetails/index'
// },
{
name: '公示文档',
icon: require('@/static/images/w2.png'),
@ -189,12 +192,8 @@
},
methods: {
async listmsg() {
let res = await appmessageinbox({
page: 1,
limit: 10000,
})
this.value=res.data.data.length
let res = await msgcount()
this.value=res.data.msg_num
},

View File

@ -14,12 +14,12 @@
<script>
import parser from "@/components/jyf-parser/jyf-parser"
export default {
components:{
components: {
parser
},
data() {
return {
detail:{},
detail: {},
tagStyle: {
img: 'width:100%;display:block;',
@ -35,15 +35,15 @@
onShow() {
},
methods:{
methods: {
}
}
</script>
<style lang="scss">
.con-detail{
.title{
.con-detail {
.title {
text-align: center;
font-size: 30rpx;
font-weight: 700;
@ -51,8 +51,12 @@
}
.content{
margin:20rpx 20rpx;
.content {
margin: 20rpx 20rpx;
word-wrap: break-word;
overflow: hidden;
}
}
</style>

474
pages/waitlist/index.vue Normal file
View File

@ -0,0 +1,474 @@
<template>
<view class="content">
<view class="content-banner">
<view class="" v-for="(item,i ) in list" :key="i" @click="apparent(i)">
<view class="banner-name" :style="{'color':i==num?'#3274F9':''}">{{item.name}}</view>
<view class="banner-xian" v-if='i==num'></view>
</view>
</view>
<view class="content-middle">
<view class="content-middle-one" v-if="num==0">
<view class="middle-one" v-for="(item,k) in list2" @click="tian(item,num)">
<liu-swipe-action :index="k" @clickItem="clickItem" :btnList='btnList'>
<view class="middle-one-title">
<view class="one-titlea">
<view class="name">
{{item.name}}
</view>
<view class="parment">
{{item.name1}}
</view>
</view>
<view class="one-titleb"
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
</view>
</view>
<view class="middle-one-time">类型{{item.flow_type}}</view>
<view class="middle-one-time">开始时间{{item.start_time}}</view>
<view class="middle-one-time">结束时间{{item.end_time}}</view>
</liu-swipe-action>
<!-- <view class="middle-edit" @click="editpart(item)">
编辑
</view>
<view class="middle-delete" @click="clickItem(item)">
删除
</view> -->
</view>
</view>
<view class="content-middle-one" v-if="num==1">
<view class="middle-one" v-for="(item,k) in list2" @click="tian(item,num)">
<liu-swipe-action :index="k" @clickItem="clickItem" :btnList='btnList'>
<view class="middle-one-title">
<view class="one-titlea">
<view class="name">
{{item.check_user}}
</view>
<view class="parment">
{{item.code}}
</view>
</view>
<view class="one-titleb"
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
<text v-if='item.check_status==5'>已开具</text>
</view>
</view>
<view class="middle-one-time">报销部门{{item.department}}</view>
<view class="middle-one-time">入账月份{{item.income_month}}</view>
<view class="middle-one-time">报销人审核人{{item.admin_name}}</view>
</liu-swipe-action>
<!-- <view class="middle-edit" @click="editpart(item)">
编辑
</view>
<view class="middle-delete" @click="clickItem(item)">
删除
</view> -->
</view>
</view>
<view class="content-middle-one" v-if="num==2">
<view class="middle-one" v-for="(item,k) in list2" @click="tian(item,num)">
<liu-swipe-action :index="k" @clickItem="clickItem" :btnList='btnList'>
<view class="middle-one-title">
<view class="one-titlea">
<view class="name">
{{item.name}}
</view>
<view class="parment">
{{item.name1}}
</view>
</view>
<view class="one-titleb"
:style="{'color':item.check_status==0?'#000':item.check_status==1?'#dddddd':item.check_status==2?'#34A853':item.check_status==3?'#F9AA32':'#F02828'}">
<text v-if='item.check_status==0'>待审核</text>
<text v-if='item.check_status==1'>审核中</text>
<text v-if='item.check_status==2'>审核通过</text>
<text v-if='item.check_status==3'>审核不通过</text>
<text v-if='item.check_status==4'>撤销审核</text>
<text v-if='item.check_status==5'>已开具</text>
<text v-if='item.check_status==10'>已作废</text>
</view>
</view>
<view class="middle-one-time">类型{{item.flow_type}}</view>
<view class="middle-one-time">开票金额:{{item.amount}}</view>
<view class="middle-one-time">审核人{{item.check_user}}</view>
</liu-swipe-action>
<!-- <view class="middle-edit" @click="editpart(item)">
编辑
</view>
<view class="middle-delete" @click="clickItem(item)">
删除
</view> -->
</view>
</view>
</view>
<u-empty v-if="loadConfig.status=='nomore'&& list2.length==0" text="没有信息"
icon="/static/empty/data.png"></u-empty>
<u-loadmore v-else :status="loadConfig.status" :loading-text="loadConfig.loadingText"
:loadmore-text="loadConfig.loadmoreText" :nomore-text="loadConfig.nomoreText" />
</view>
</template>
<script>
import {
Toast
} from '@/libs/uniApi.js'
import {
approvelist,
apexpenselist,
invoicelist
} from '@/api/oa.js'
export default {
data() {
return {
title: 'Hello',
statusBarHeight: 0,
list: [{
name: '待审审批',
value: 8
}, {
name: '待审报销',
value: 8
},
{
name: '待审发票',
value: 8
}
],
list2: [],
num: 0,
btnList: [],
loadConfig: {
page: 1,
limit: 5,
loadingText: '努力加载中',
loadmoreText: '轻轻上拉',
nomoreText: '我也是有底线的~~',
status: 'loadmore'
},
}
},
onLoad() {
},
onShow() {
if (this.num == 0) {
this.initList()
} else if (this.num == 1) {
this.initList1()
} else {
this.initList2()
}
},
onReachBottom() {
if (this.num == 0) {
this.getlist()
} else if (this.num == 1) {
this.getlist1()
} else {
this.getlist2()
}
},
onPullDownRefresh() {
uni.stopPullDownRefresh()
},
methods: {
apparent(index) {
this.num = index
if (index == 0) {
this.initList()
} else if (index == 1) {
this.initList1()
} else {
this.initList2()
}
},
async initList() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list2 = [];
await this.getlist()
},
async initList1() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list2 = [];
await this.getlist1()
},
async initList2() {
this.loadConfig.page = 1;
this.loadConfig.status = "loadmore";
this.list2 = [];
await this.getlist2()
},
async getlist() {
if (this.loadConfig.status == "nomore") return;
this.loadConfig.status = "loading"
let res = await approvelist({
page: this.loadConfig.page,
limit: this.loadConfig.limit,
// keyword: this.keyword
})
this.loadConfig.status = "loadmore"
if (res.data.data.length < this.loadConfig.limit) {
this.loadConfig.status = "nomore"
} else {
this.loadConfig.page++;
}
this.list2 = [...this.list2, ...res.data?.data]
console.log(this.list2)
},
async getlist1() {
if (this.loadConfig.status == "nomore") return;
this.loadConfig.status = "loading"
let res = await apexpenselist({
page: this.loadConfig.page,
limit: this.loadConfig.limit,
// keyword: this.keyword
})
this.loadConfig.status = "loadmore"
if (res.data.data.length < this.loadConfig.limit) {
this.loadConfig.status = "nomore"
} else {
this.loadConfig.page++;
}
this.list2 = [...this.list2, ...res.data?.data]
},
async getlist2() {
if (this.loadConfig.status == "nomore") return;
this.loadConfig.status = "loading"
let res = await invoicelist({
page: this.loadConfig.page,
limit: this.loadConfig.limit,
// keyword: this.keyword
})
this.loadConfig.status = "loadmore"
if (res.data.data.length < this.loadConfig.limit) {
this.loadConfig.status = "nomore"
} else {
this.loadConfig.page++;
}
this.list2 = [...this.list2, ...res.data?.data]
},
//
adddepart() {
uni.navigateTo({
url: '/pages/addquest/index?type=' + 0
})
},
//
clickItem(data) {
// console.log(e)
if (data.id == 2) {
uni.showModal({
content: '确定要删除吗',
success: (e) => {
if (e.confirm) {
this.deleteDepartment(data.id)
}
}
})
} else {
this.editpart(this.list2[data.index])
}
},
//
async deleteDepartment(id) {
const res = await deleteexpenseflow({
id: id
})
if (res.code == 0) {
this.loadConfig.status = "loading"
this.initList()
}
Toast(res.msg);
},
//
editpart(item) {
uni.navigateTo({
url: '/pages/reimbursementapplication/index??type=' + item.type
})
},
tian(item,num) {
if (num == 0) {
uni.navigateTo({
url: '/pages/leave_request/index?type=' + item.id
})
} else if (num == 1) {
uni.navigateTo({
url: '/pages/reimbursementapplication/handle?type=' + item.id
})
} else {
uni.navigateTo({
url: '/pages/invoice/handle?type=' + item.id
})
}
}
}
}
</script>
<style lang='scss'>
.content-banner {
display: flex;
justify-content: space-between;
padding: 20rpx 40rpx;
.banner-name {
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
}
.banner-xian {
width: 70rpx;
height: 5rpx;
margin: 10rpx auto;
background-color: #3274F9;
}
}
.content-middle {
.content-middle-one {
.middle-one {
width: 694rpx;
margin: 0 auto;
margin-bottom: 21rpx;
background: #FFFFFF;
padding: 0 25rpx;
padding-bottom: 25rpx;
.middle-edit {
width: 645rpx;
height: 63rpx;
line-height: 63rpx;
background: #e6e5d9;
text-align: center;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #000;
margin-bottom: 20rpx;
}
.middle-delete {
width: 645rpx;
height: 63rpx;
line-height: 63rpx;
background: #F02828;
text-align: center;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
}
.middle-one-title {
border-bottom: 1px solid #CCCCCC;
margin-bottom: 10rpx;
display: flex;
line-height: 82rpx;
justify-content: space-between;
.one-titlea {
display: flex;
.name {
font-size: 32rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
}
.parment {
font-size: 25rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #3274F9;
margin-top: 5rpx;
margin-left: 10rpx;
}
}
.one-titleb {
font-size: 32rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
}
}
.middle-one-detail {
height: 90rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
}
.middle-one-time {
font-size: 25rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
margin-top: 20rpx;
margin-bottom: 20rpx;
}
}
}
}
</style>

BIN
static/images/fa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
static/images/gw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
static/images/r1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
static/images/r2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
static/images/r3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
static/images/r4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,27 @@
## 1.0.112023-10-29
1. imgMode默认值改成aspectFit
## 1.0.102023-08-13
1. 优化nvue方便自定义图标
## 1.0.92023-07-28
1. 修改几个对应错误图标的BUG
## 1.0.82023-07-24
1. 优化 支持base64图片
## 1.0.72023-07-17
1. 修复 uv-icon 恢复uv-empty相关的图标
## 1.0.62023-07-13
1. 修复icon设置name属性对应图标错误的BUG
## 1.0.52023-07-04
1. 更新图标,删除一些不常用的图标
2. 删除base64修改成ttf文件引入读取图标
3. 自定义图标文档说明https://www.uvui.cn/guide/customIcon.html
## 1.0.42023-07-03
1. 修复主题颜色在APP不生效的BUG
## 1.0.32023-05-24
1. 将线上ttf字体包替换成base64避免加载时或者网络差时候显示白色方块
## 1.0.22023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.12023-05-10
1. 修复小程序中异常显示
## 1.0.02023-05-04
新发版

View File

@ -0,0 +1,160 @@
export default {
'uvicon-level': 'e68f',
'uvicon-checkbox-mark': 'e659',
'uvicon-folder': 'e694',
'uvicon-movie': 'e67c',
'uvicon-star-fill': 'e61e',
'uvicon-star': 'e618',
'uvicon-phone-fill': 'e6ac',
'uvicon-phone': 'e6ba',
'uvicon-apple-fill': 'e635',
'uvicon-backspace': 'e64d',
'uvicon-attach': 'e640',
'uvicon-empty-data': 'e671',
'uvicon-empty-address': 'e68a',
'uvicon-empty-favor': 'e662',
'uvicon-empty-car': 'e657',
'uvicon-empty-order': 'e66b',
'uvicon-empty-list': 'e672',
'uvicon-empty-search': 'e677',
'uvicon-empty-permission': 'e67d',
'uvicon-empty-news': 'e67e',
'uvicon-empty-history': 'e685',
'uvicon-empty-coupon': 'e69b',
'uvicon-empty-page': 'e60e',
'uvicon-empty-wifi-off': 'e6cc',
'uvicon-reload': 'e627',
'uvicon-order': 'e695',
'uvicon-server-man': 'e601',
'uvicon-search': 'e632',
'uvicon-more-dot-fill': 'e66f',
'uvicon-scan': 'e631',
'uvicon-map': 'e665',
'uvicon-map-fill': 'e6a8',
'uvicon-tags': 'e621',
'uvicon-tags-fill': 'e613',
'uvicon-eye': 'e664',
'uvicon-eye-fill': 'e697',
'uvicon-eye-off': 'e69c',
'uvicon-eye-off-outline': 'e688',
'uvicon-mic': 'e66d',
'uvicon-mic-off': 'e691',
'uvicon-calendar': 'e65c',
'uvicon-trash': 'e623',
'uvicon-trash-fill': 'e6ce',
'uvicon-play-left': 'e6bf',
'uvicon-play-right': 'e6b3',
'uvicon-minus': 'e614',
'uvicon-plus': 'e625',
'uvicon-info-circle': 'e69f',
'uvicon-info-circle-fill': 'e6a7',
'uvicon-question-circle': 'e622',
'uvicon-question-circle-fill': 'e6bc',
'uvicon-close': 'e65a',
'uvicon-checkmark': 'e64a',
'uvicon-checkmark-circle': 'e643',
'uvicon-checkmark-circle-fill': 'e668',
'uvicon-setting': 'e602',
'uvicon-setting-fill': 'e6d0',
'uvicon-heart': 'e6a2',
'uvicon-heart-fill': 'e68b',
'uvicon-camera': 'e642',
'uvicon-camera-fill': 'e650',
'uvicon-more-circle': 'e69e',
'uvicon-more-circle-fill': 'e684',
'uvicon-chat': 'e656',
'uvicon-chat-fill': 'e63f',
'uvicon-bag': 'e647',
'uvicon-error-circle': 'e66e',
'uvicon-error-circle-fill': 'e655',
'uvicon-close-circle': 'e64e',
'uvicon-close-circle-fill': 'e666',
'uvicon-share': 'e629',
'uvicon-share-fill': 'e6bb',
'uvicon-share-square': 'e6c4',
'uvicon-shopping-cart': 'e6cb',
'uvicon-shopping-cart-fill': 'e630',
'uvicon-bell': 'e651',
'uvicon-bell-fill': 'e604',
'uvicon-list': 'e690',
'uvicon-list-dot': 'e6a9',
'uvicon-zhifubao-circle-fill': 'e617',
'uvicon-weixin-circle-fill': 'e6cd',
'uvicon-weixin-fill': 'e620',
'uvicon-qq-fill': 'e608',
'uvicon-qq-circle-fill': 'e6b9',
'uvicon-moments-circel-fill': 'e6c2',
'uvicon-moments': 'e6a0',
'uvicon-car': 'e64f',
'uvicon-car-fill': 'e648',
'uvicon-warning-fill': 'e6c7',
'uvicon-warning': 'e6c1',
'uvicon-clock-fill': 'e64b',
'uvicon-clock': 'e66c',
'uvicon-edit-pen': 'e65d',
'uvicon-edit-pen-fill': 'e679',
'uvicon-email': 'e673',
'uvicon-email-fill': 'e683',
'uvicon-minus-circle': 'e6a5',
'uvicon-plus-circle': 'e603',
'uvicon-plus-circle-fill': 'e611',
'uvicon-file-text': 'e687',
'uvicon-file-text-fill': 'e67f',
'uvicon-pushpin': 'e6d1',
'uvicon-pushpin-fill': 'e6b6',
'uvicon-grid': 'e68c',
'uvicon-grid-fill': 'e698',
'uvicon-play-circle': 'e6af',
'uvicon-play-circle-fill': 'e62a',
'uvicon-pause-circle-fill': 'e60c',
'uvicon-pause': 'e61c',
'uvicon-pause-circle': 'e696',
'uvicon-gift-fill': 'e6b0',
'uvicon-gift': 'e680',
'uvicon-kefu-ermai': 'e660',
'uvicon-server-fill': 'e610',
'uvicon-coupon-fill': 'e64c',
'uvicon-coupon': 'e65f',
'uvicon-integral': 'e693',
'uvicon-integral-fill': 'e6b1',
'uvicon-home-fill': 'e68e',
'uvicon-home': 'e67b',
'uvicon-account': 'e63a',
'uvicon-account-fill': 'e653',
'uvicon-thumb-down-fill': 'e628',
'uvicon-thumb-down': 'e60a',
'uvicon-thumb-up': 'e612',
'uvicon-thumb-up-fill': 'e62c',
'uvicon-lock-fill': 'e6a6',
'uvicon-lock-open': 'e68d',
'uvicon-lock-opened-fill': 'e6a1',
'uvicon-lock': 'e69d',
'uvicon-red-packet': 'e6c3',
'uvicon-photo-fill': 'e6b4',
'uvicon-photo': 'e60d',
'uvicon-volume-off-fill': 'e6c8',
'uvicon-volume-off': 'e6bd',
'uvicon-volume-fill': 'e624',
'uvicon-volume': 'e605',
'uvicon-download': 'e670',
'uvicon-arrow-up-fill': 'e636',
'uvicon-arrow-down-fill': 'e638',
'uvicon-play-left-fill': 'e6ae',
'uvicon-play-right-fill': 'e6ad',
'uvicon-arrow-downward': 'e634',
'uvicon-arrow-leftward': 'e63b',
'uvicon-arrow-rightward': 'e644',
'uvicon-arrow-upward': 'e641',
'uvicon-arrow-down': 'e63e',
'uvicon-arrow-right': 'e63c',
'uvicon-arrow-left': 'e646',
'uvicon-arrow-up': 'e633',
'uvicon-skip-back-left': 'e6c5',
'uvicon-skip-forward-right': 'e61f',
'uvicon-arrow-left-double': 'e637',
'uvicon-man': 'e675',
'uvicon-woman': 'e626',
'uvicon-en': 'e6b8',
'uvicon-twitte': 'e607',
'uvicon-twitter-circle-fill': 'e6cf'
}

View File

@ -0,0 +1,90 @@
export default {
props: {
// 图标类名
name: {
type: String,
default: ''
},
// 图标颜色,可接受主题色
color: {
type: String,
default: '#606266'
},
// 字体大小单位px
size: {
type: [String, Number],
default: '16px'
},
// 是否显示粗体
bold: {
type: Boolean,
default: false
},
// 点击图标的时候传递事件出去的index用于区分点击了哪一个
index: {
type: [String, Number],
default: null
},
// 触摸图标时的类名
hoverClass: {
type: String,
default: ''
},
// 自定义扩展前缀,方便用户扩展自己的图标库
customPrefix: {
type: String,
default: 'uvicon'
},
// 图标右边或者下面的文字
label: {
type: [String, Number],
default: ''
},
// label的位置只能右边或者下边
labelPos: {
type: String,
default: 'right'
},
// label的大小
labelSize: {
type: [String, Number],
default: '15px'
},
// label的颜色
labelColor: {
type: String,
default: '#606266'
},
// label与图标的距离
space: {
type: [String, Number],
default: '3px'
},
// 图片的mode
imgMode: {
type: String,
default: 'aspectFit'
},
// 用于显示图片小图标时,图片的宽度
width: {
type: [String, Number],
default: ''
},
// 用于显示图片小图标时,图片的高度
height: {
type: [String, Number],
default: ''
},
// 用于解决某些情况下,让图标垂直居中的用途
top: {
type: [String, Number],
default: 0
},
// 是否阻止事件传播
stop: {
type: Boolean,
default: false
},
...uni.$uv?.props?.icon
}
}

View File

@ -0,0 +1,226 @@
<template>
<view
class="uv-icon"
@tap="clickHandler"
:class="['uv-icon--' + labelPos]"
>
<image
class="uv-icon__img"
v-if="isImg"
:src="name"
:mode="imgMode"
:style="[imgStyle, $uv.addStyle(customStyle)]"
></image>
<text
v-else
class="uv-icon__icon"
:class="uClasses"
:style="[iconStyle, $uv.addStyle(customStyle)]"
:hover-class="hoverClass"
>{{icon}}</text>
<!-- 这里进行空字符串判断如果仅仅是v-if="label"可能会出现传递0的时候结果也无法显示 -->
<text
v-if="label !== ''"
class="uv-icon__label"
:style="{
color: labelColor,
fontSize: $uv.addUnit(labelSize),
marginLeft: labelPos == 'right' ? $uv.addUnit(space) : 0,
marginTop: labelPos == 'bottom' ? $uv.addUnit(space) : 0,
marginRight: labelPos == 'left' ? $uv.addUnit(space) : 0,
marginBottom: labelPos == 'top' ? $uv.addUnit(space) : 0
}"
>{{ label }}</text>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
// #ifdef APP-NVUE
// nvueweexdom
// https://weex.apache.org/zh/docs/modules/dom.html#addrule
import iconUrl from './uvicons.ttf';
const domModule = weex.requireModule('dom')
domModule.addRule('fontFace', {
'fontFamily': "uvicon-iconfont",
'src': "url('" + iconUrl + "')"
})
// #endif
// unicode
import icons from './icons';
import props from './props.js';
/**
* icon 图标
* @description 基于字体的图标集包含了大多数常见场景的图标
* @tutorial https://www.uvui.cn/components/icon.html
* @property {String} name 图标名称见示例图标集
* @property {String} color 图标颜色,可接受主题色 默认 color['uv-content-color']
* @property {String | Number} size 图标字体大小单位px 默认 '16px'
* @property {Boolean} bold 是否显示粗体 默认 false
* @property {String | Number} index 点击图标的时候传递事件出去的index用于区分点击了哪一个
* @property {String} hoverClass 图标按下去的样式类用法同uni的view组件的hoverClass参数详情见官网
* @property {String} customPrefix 自定义扩展前缀方便用户扩展自己的图标库 默认 'uicon'
* @property {String | Number} label 图标右侧的label文字
* @property {String} labelPos label相对于图标的位置只能right或bottom 默认 'right'
* @property {String | Number} labelSize label字体大小单位px 默认 '15px'
* @property {String} labelColor 图标右侧的label文字颜色 默认 color['uv-content-color']
* @property {String | Number} space label与图标的距离单位px 默认 '3px'
* @property {String} imgMode 图片的mode
* @property {String | Number} width 显示图片小图标时的宽度
* @property {String | Number} height 显示图片小图标时的高度
* @property {String | Number} top 图标在垂直方向上的定位 用于解决某些情况下让图标垂直居中的用途 默认 0
* @property {Boolean} stop 是否阻止事件传播 默认 false
* @property {Object} customStyle icon的样式对象形式
* @event {Function} click 点击图标时触发
* @event {Function} touchstart 事件触摸时触发
* @example <uv-icon name="photo" color="#2979ff" size="28"></uv-icon>
*/
export default {
name: 'uv-icon',
emits: ['click'],
mixins: [mpMixin, mixin, props],
data() {
return {
colorType: [
'primary',
'success',
'info',
'error',
'warning'
]
}
},
computed: {
uClasses() {
let classes = []
classes.push(this.customPrefix)
classes.push(this.customPrefix + '-' + this.name)
//
if (this.color && this.colorType.includes(this.color)) classes.push('uv-icon__icon--' + this.color)
// 使[a, b, c]
//
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
classes = classes.join(' ')
//#endif
return classes
},
iconStyle() {
let style = {}
style = {
fontSize: this.$uv.addUnit(this.size),
lineHeight: this.$uv.addUnit(this.size),
fontWeight: this.bold ? 'bold' : 'normal',
//
top: this.$uv.addUnit(this.top)
}
//
if (this.color && !this.colorType.includes(this.color)) style.color = this.color
return style
},
// name"/"
isImg() {
const isBase64 = this.name.indexOf('data:') > -1 && this.name.indexOf('base64') > -1;
return this.name.indexOf('/') !== -1 || isBase64;
},
imgStyle() {
let style = {}
// widthheight使使size
style.width = this.width ? this.$uv.addUnit(this.width) : this.$uv.addUnit(this.size)
style.height = this.height ? this.$uv.addUnit(this.height) : this.$uv.addUnit(this.size)
return style
},
//
icon() {
// nameunicode
const code = icons['uvicon-' + this.name];
// #ifdef APP-NVUE
if(!code) {
return code ? unescape(`%u${code}`) : ['uvicon'].indexOf(this.customPrefix) > -1 ? unescape(`%u${this.name}`) : '';
}
// #endif
return code ? unescape(`%u${code}`) : ['uvicon'].indexOf(this.customPrefix) > -1 ? this.name : '';
}
},
methods: {
clickHandler(e) {
this.$emit('click', this.index)
//
this.stop && this.preventEvent(e)
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
//
$uv-icon-primary: $uv-primary !default;
$uv-icon-success: $uv-success !default;
$uv-icon-info: $uv-info !default;
$uv-icon-warning: $uv-warning !default;
$uv-icon-error: $uv-error !default;
$uv-icon-label-line-height: 1 !default;
/* #ifndef APP-NVUE */
// nvue
@font-face {
font-family: 'uvicon-iconfont';
src: url('./uvicons.ttf') format('truetype');
}
/* #endif */
.uv-icon {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
&--left {
flex-direction: row-reverse;
align-items: center;
}
&--right {
flex-direction: row;
align-items: center;
}
&--top {
flex-direction: column-reverse;
justify-content: center;
}
&--bottom {
flex-direction: column;
justify-content: center;
}
&__icon {
font-family: uvicon-iconfont;
position: relative;
@include flex;
align-items: center;
&--primary {
color: $uv-icon-primary;
}
&--success {
color: $uv-icon-success;
}
&--error {
color: $uv-icon-error;
}
&--warning {
color: $uv-icon-warning;
}
&--info {
color: $uv-icon-info;
}
}
&__img {
/* #ifndef APP-NVUE */
height: auto;
will-change: transform;
/* #endif */
}
&__label {
/* #ifndef APP-NVUE */
line-height: $uv-icon-label-line-height;
/* #endif */
}
}
</style>

Binary file not shown.

View File

@ -0,0 +1,83 @@
{
"id": "uv-icon",
"displayName": "uv-icon 图标 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.0.11",
"description": "基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。",
"keywords": [
"uv-ui,uvui,uv-icon,icon,图标,字体图标"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"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",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,15 @@
## uv-icon 图标库
> **组件名uv-icon**
基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。
# <a href="https://www.uvui.cn/components/icon.html" target="_blank">查看文档</a>
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
### [更多插件请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
#### 如使用过程中有任何问题反馈或者您对uv-ui有一些好的建议欢迎加入uv-ui官方交流群<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

View File

@ -0,0 +1,11 @@
## 1.0.42023-10-13
1. 优化
## 1.0.32023-10-13
1. unmounted兼容vue3
## 1.0.22023-05-27
1. 不支持抖音小程序说明
## 1.0.12023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.02023-05-10
uv-swipe-action 滑动单元格

View File

@ -0,0 +1,256 @@
/**
* 此为wxs模块只支持APP-VUE微信和QQ小程序以及H5平台
* wxs内部不支持es6语法变量只能使用var定义无法使用解构箭头函数等特性
*/
// 开始触摸
function touchstart(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照此快照是属于整个组件的在touchstart和touchmove事件中都能获取到相同的结果
var state = instance.getState()
if (state.disable) return
var touches = event.touches
// 如果进行的是多指触控,不允许进行操作
if (touches && touches.length > 1) return
// 标识当前为滑动中状态
state.moving = true
// 记录触摸开始点的坐标值
state.startX = touches[0].pageX
state.startY = touches[0].pageY
}
// 触摸滑动
function touchmove(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照
var state = instance.getState()
if (state.disabled || !state.moving) return
var touches = event.touches
var pageX = touches[0].pageX
var pageY = touches[0].pageY
var moveX = pageX - state.startX
var moveY = pageY - state.startY
var buttonsWidth = state.buttonsWidth
// 移动的X轴距离大于Y轴距离也即终点与起点位置连线与X轴夹角小于45度时禁止页面滚动
if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
event.preventDefault()
event.stopPropagation()
}
// 如果移动的X轴距离小于Y轴距离也即终点位置与起点位置连线与Y轴夹角小于45度时认为是页面上下滑动而不是左右滑动单元格
if (Math.abs(moveX) < Math.abs(moveY)) return
// 限制右滑的距离不允许内容部分往右偏移右滑会导致X轴偏移值大于0以此做判断
// 此处不能直接return因为滑动过程中会缺失某些关键点坐标会导致错乱最好的办法就是
// 在超出后设置为0
if (state.status === 'open') {
// 在开启状态下,向左滑动,需忽略
if (moveX < 0) moveX = 0
// 想要收起菜单,最大能移动的距离为按钮的总宽度
if (moveX > buttonsWidth) moveX = buttonsWidth
// 如果是已经打开了的状态,向左滑动时,移动收起菜单
moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
} else {
// 关闭状态下,右滑动需忽略
if (moveX > 0) moveX = 0
// 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
// 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
moveSwipeAction(moveX, instance, ownerInstance)
}
}
// 触摸结束
function touchend(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照
var state = instance.getState()
if (!state.moving || state.disabled) return
var touches = event.changedTouches ? event.changedTouches[0] : {}
var pageX = touches.pageX
var pageY = touches.pageY
var moveX = pageX - state.startX
if (state.status === 'open') {
// 在展开的状态下,继续左滑,无需操作
if (moveX < 0) return
// 在开启状态下点击一下内容区域moveX为0也即没有进行移动这时执行收起菜单逻辑
if (moveX === 0) {
return closeSwipeAction(instance, ownerInstance)
}
// 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
if (Math.abs(moveX) < state.threshold) {
openSwipeAction(instance, ownerInstance)
} else {
// 如果滑动距离大于阈值,则执行收起逻辑
closeSwipeAction(instance, ownerInstance)
}
} else {
// 在关闭的状态下,右滑,无需操作
if (moveX > 0) return
// 理由同上
if (Math.abs(moveX) < state.threshold) {
closeSwipeAction(instance, ownerInstance)
} else {
openSwipeAction(instance, ownerInstance)
}
}
}
// 获取过渡时间
function getDuration(value) {
if (value.toString().indexOf('s') >= 0) return value
return value > 30 ? value + 'ms' : value + 's'
}
// 滑动结束时判断滑动的方向
function getMoveDirection(instance, ownerInstance) {
var state = instance.getState()
}
// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
function moveSwipeAction(moveX, instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
var len = buttons.length
var previewButtonsMoveX = 0
// 设置菜单内容部分的偏移
instance.requestAnimationFrame(function() {
instance.setStyle({
// 设置translateX的值
'transition': 'none',
transform: 'translateX(' + moveX + 'px)',
'-webkit-transform': 'translateX(' + moveX + 'px)'
})
// 折叠按钮动画
for (var i = len - 1; i >= 0; i--) {
// 通过比例,得出元素自身该移动的距离
var translateX = state.buttons[i].width / state.buttonsWidth * moveX
// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
var realTranslateX = translateX + previewButtonsMoveX
buttons[i].setStyle({
// 在移动期间,不能使用过渡效果,否则会造成卡顿,本质原因是每次移动一点,就要花一定时间去过渡这个过程
'transition': 'none',
'transform': 'translateX(' + realTranslateX + 'px)',
'-webkit-transform': 'translateX(' + realTranslateX + 'px)'
})
// 记录本按钮之前的所有按钮的移动距离之和
previewButtonsMoveX += translateX
}
})
}
// 一次性展开滑动菜单
function openSwipeAction(instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
var len = buttons.length
// 处理duration单位问题
const duration = getDuration(state.duration)
// 展开过程中是向左移动所以X的偏移应该为负值
var buttonsWidth = -state.buttonsWidth
var previewButtonsMoveX = 0
instance.requestAnimationFrame(function() {
// 设置菜单主体内容
instance.setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(' + buttonsWidth + 'px)',
'-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
})
// 设置各个隐藏的按钮为展开的状态
for (var i = len - 1; i >= 0; i--) {
// 通过比例,得出元素自身该移动的距离
var translateX = state.buttons[i].width / state.buttonsWidth * buttonsWidth
// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
var realTranslateX = translateX + previewButtonsMoveX
buttons[i].setStyle({
// 在移动期间,需要加上动画效果
'transition': 'transform ' + duration,
'transform': 'translateX(' + realTranslateX + 'px)',
'-webkit-transform': 'translateX(' + realTranslateX + 'px)'
})
// 记录本按钮之前的所有按钮的移动距离之和
previewButtonsMoveX += translateX
}
})
setStatus('open', instance)
}
// 标记菜单的当前状态open-已经打开close-已经关闭
function setStatus(status, instance) {
var state = instance.getState()
state.status = status
}
// 一次性收起滑动菜单
function closeSwipeAction(instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
var len = buttons.length
// 处理duration单位问题
const duration = getDuration(state.duration)
instance.requestAnimationFrame(function() {
// 设置菜单主体内容
instance.setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(0px)',
'-webkit-transform': 'translateX(0px)'
})
// 设置各个隐藏的按钮为收起的状态
for (var i = len - 1; i >= 0; i--) {
buttons[i].setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(0px)',
'-webkit-transform': 'translateX(0px)'
})
}
})
setStatus('close', instance)
}
// show的状态发生变化
function showChange(newValue, oldValue, ownerInstance, instance) {
var state = instance.getState()
if (state.disabled) return
// 打开或关闭单元格
if (newValue) {
openSwipeAction(instance, ownerInstance)
} else {
closeSwipeAction(instance, ownerInstance)
}
}
// 菜单尺寸发生变化
function sizeChange(newValue, oldValue, ownerInstance, instance) {
// wxs内的局部变量快照
var state = instance.getState()
state.disabled = newValue.disabled
state.duration = newValue.duration
state.show = newValue.show
state.threshold = newValue.threshold
state.buttons = newValue.buttons
var len = state.buttons.length
if (len) {
var buttonsWidth = 0
var buttons = newValue.buttons
for (var i = 0; i < len; i++) {
buttonsWidth += buttons[i].width
}
}
state.buttonsWidth = buttonsWidth
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
sizeChange: sizeChange
}

View File

@ -0,0 +1,225 @@
/**
* 此为wxs模块只支持APP-VUE微信和QQ小程序以及H5平台
* wxs内部不支持es6语法变量只能使用var定义无法使用解构箭头函数等特性
*/
// 开始触摸
function touchstart(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照此快照是属于整个组件的在touchstart和touchmove事件中都能获取到相同的结果
var state = instance.getState()
if (state.disabled) return
var touches = event.touches
// 如果进行的是多指触控,不允许进行操作
if (touches && touches.length > 1) return
// 标识当前为滑动中状态
state.moving = true
// 记录触摸开始点的坐标值
state.startX = touches[0].pageX
state.startY = touches[0].pageY
ownerInstance.callMethod('closeOther')
}
// 触摸滑动
function touchmove(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照
var state = instance.getState()
if (state.disabled || !state.moving) return
var touches = event.touches
var pageX = touches[0].pageX
var pageY = touches[0].pageY
var moveX = pageX - state.startX
var moveY = pageY - state.startY
var buttonsWidth = state.buttonsWidth
// 移动的X轴距离大于Y轴距离也即终点与起点位置连线与X轴夹角小于45度时禁止页面滚动
if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
event.preventDefault && event.preventDefault()
event.stopPropagation && event.stopPropagation()
}
// 如果移动的X轴距离小于Y轴距离也即终点位置与起点位置连线与Y轴夹角小于45度时认为是页面上下滑动而不是左右滑动单元格
if (Math.abs(moveX) < Math.abs(moveY)) return
// 限制右滑的距离不允许内容部分往右偏移右滑会导致X轴偏移值大于0以此做判断
// 此处不能直接return因为滑动过程中会缺失某些关键点坐标会导致错乱最好的办法就是
// 在超出后设置为0
if (state.status === 'open') {
// 在开启状态下,向左滑动,需忽略
if (moveX < 0) moveX = 0
// 想要收起菜单,最大能移动的距离为按钮的总宽度
if (moveX > buttonsWidth) moveX = buttonsWidth
// 如果是已经打开了的状态,向左滑动时,移动收起菜单
moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
} else {
// 关闭状态下,右滑动需忽略
if (moveX > 0) moveX = 0
// 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
// 只要是在滑过程中,就不断移动单元格内容部分,从而使隐藏的菜单显示出来
moveSwipeAction(moveX, instance, ownerInstance)
}
}
// 触摸结束
function touchend(event, ownerInstance) {
// 触发事件的组件的ComponentDescriptor实例
var instance = event.instance
// wxs内的局部变量快照
var state = instance.getState()
if (!state.moving || state.disabled) return
var touches = event.changedTouches ? event.changedTouches[0] : {}
var pageX = touches.pageX
var pageY = touches.pageY
var moveX = pageX - state.startX
if (state.status === 'open') {
// 在展开的状态下,继续左滑,无需操作
if (moveX < 0) return
// 在开启状态下点击一下内容区域moveX为0也即没有进行移动这时执行收起菜单逻辑
if (moveX === 0) {
return closeSwipeAction(instance, ownerInstance)
}
// 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
if (Math.abs(moveX) < state.threshold) {
openSwipeAction(instance, ownerInstance)
} else {
// 如果滑动距离大于阈值,则执行收起逻辑
closeSwipeAction(instance, ownerInstance)
}
} else {
// 在关闭的状态下,右滑,无需操作
if (moveX > 0) return
// 理由同上
if (Math.abs(moveX) < state.threshold) {
closeSwipeAction(instance, ownerInstance)
} else {
openSwipeAction(instance, ownerInstance)
}
}
}
// 获取过渡时间
function getDuration(value) {
if (value.toString().indexOf('s') >= 0) return value
return value > 30 ? value + 'ms' : value + 's'
}
// 滑动结束时判断滑动的方向
function getMoveDirection(instance, ownerInstance) {
var state = instance.getState()
}
// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
function moveSwipeAction(moveX, instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
// 设置菜单内容部分的偏移
instance.requestAnimationFrame(function() {
instance.setStyle({
// 设置translateX的值
'transition': 'none',
transform: 'translateX(' + moveX + 'px)',
'-webkit-transform': 'translateX(' + moveX + 'px)'
})
})
}
// 一次性展开滑动菜单
function openSwipeAction(instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
// 处理duration单位问题
var duration = getDuration(state.duration)
// 展开过程中是向左移动所以X的偏移应该为负值
var buttonsWidth = -state.buttonsWidth
instance.requestAnimationFrame(function() {
// 设置菜单主体内容
instance.setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(' + buttonsWidth + 'px)',
'-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
})
})
setStatus('open', instance, ownerInstance)
}
// 标记菜单的当前状态open-已经打开close-已经关闭
function setStatus(status, instance, ownerInstance) {
var state = instance.getState()
state.status = status
ownerInstance.callMethod('setState', status)
}
// 一次性收起滑动菜单
function closeSwipeAction(instance, ownerInstance) {
var state = instance.getState()
// 获取所有按钮的实例,需要通过它去设置按钮的位移
var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
var len = buttons.length
// 处理duration单位问题
var duration = getDuration(state.duration)
instance.requestAnimationFrame(function() {
// 设置菜单主体内容
instance.setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(0px)',
'-webkit-transform': 'translateX(0px)'
})
// 设置各个隐藏的按钮为收起的状态
for (var i = len - 1; i >= 0; i--) {
buttons[i].setStyle({
'transition': 'transform ' + duration,
'transform': 'translateX(0px)',
'-webkit-transform': 'translateX(0px)'
})
}
})
setStatus('close', instance, ownerInstance)
}
// status的状态发生变化
function statusChange(newValue, oldValue, ownerInstance, instance) {
var state = instance.getState()
if (state.disabled) return
// 打开或关闭单元格
if (newValue === 'close' && state.status === 'open') {
closeSwipeAction(instance, ownerInstance)
} else if(newValue === 'open' && state.status === 'close') {
openSwipeAction(instance, ownerInstance)
}
}
// 菜单尺寸发生变化
function sizeChange(newValue, oldValue, ownerInstance, instance) {
// wxs内的局部变量快照
var state = instance.getState()
state.disabled = newValue.disabled
state.duration = newValue.duration
state.show = newValue.show
state.threshold = newValue.threshold
state.buttons = newValue.buttons
if (state.buttons) {
var len = state.buttons.length
var buttonsWidth = 0
var buttons = newValue.buttons
for (var i = 0; i < len; i++) {
buttonsWidth += buttons[i].width
}
}
state.buttonsWidth = buttonsWidth
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
sizeChange: sizeChange,
statusChange: statusChange
}

View File

@ -0,0 +1,264 @@
// nvue操作dom的库用于获取dom的尺寸信息
const dom = uni.requireNativePlugin('dom')
// nvue中用于操作元素动画的库类似于uni.animation只不过uni.animation不能用于nvue
const animation = uni.requireNativePlugin('animation')
import { sleep } from '@/uni_modules/uv-ui-tools/libs/function/index.js'
export default {
data() {
return {
// 是否滑动中
moving: false,
// 状态open-打开状态close-关闭状态
status: 'close',
// 开始触摸点的X和Y轴坐标
startX: 0,
startY: 0,
// 所有隐藏按钮的尺寸信息数组
buttons: [],
// 所有按钮的总宽度
buttonsWidth: 0,
// 记录上一次移动的位置值
moveX: 0,
// 记录上一次滑动的位置,用于前后两次做对比,如果移动的距离小于某一阈值,则认为前后之间没有移动,为了解决可能存在的通信阻塞问题
lastX: 0
}
},
computed: {
// 获取过渡时间
getDuratin() {
let duration = String(this.duration)
// 如果ms为单位返回ms的数值部分
if (duration.indexOf('ms') >= 0) return parseInt(duration)
// 如果s为单位为了得到ms的数值需要乘以1000
if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
// 如果值传了数值且小于30认为是s单位
duration = Number(duration)
return duration < 30 ? duration * 1000 : duration
}
},
watch: {
show: {
immediate: true,
handler(n) {
}
}
},
mounted() {
sleep(20).then(() => {
this.queryRect()
})
},
methods: {
close() {
this.closeSwipeAction()
},
// 触摸单元格
touchstart(event) {
if (this.disabled) return
this.closeOther()
const { touches } = event
// 记录触摸开始点的坐标值
this.startX = touches[0].pageX
this.startY = touches[0].pageY
},
// // 触摸滑动
touchmove(event) {
if (this.disabled) return
const { touches } = event
const { pageX } = touches[0]
const { pageY } = touches[0]
let moveX = pageX - this.startX
const moveY = pageY - this.startY
const { buttonsWidth } = this
const len = this.buttons.length
// 判断前后两次的移动距离,如果小于一定值,则不进行移动处理
if (Math.abs(pageX - this.lastX) < 0.3) return
this.lastX = pageX
// 移动的X轴距离大于Y轴距离也即终点与起点位置连线与X轴夹角小于45度时禁止页面滚动
if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > this.threshold) {
event.stopPropagation()
}
// 如果移动的X轴距离小于Y轴距离也即终点位置与起点位置连线与Y轴夹角小于45度时认为是页面上下滑动而不是左右滑动单元格
if (Math.abs(moveX) < Math.abs(moveY)) return
// 限制右滑的距离不允许内容部分往右偏移右滑会导致X轴偏移值大于0以此做判断
// 此处不能直接return因为滑动过程中会缺失某些关键点坐标会导致错乱最好的办法就是
// 在超出后设置为0
if (this.status === 'open') {
// 在开启状态下,向左滑动,需忽略
if (moveX < 0) moveX = 0
// 想要收起菜单,最大能移动的距离为按钮的总宽度
if (moveX > buttonsWidth) moveX = buttonsWidth
// 如果是已经打开了的状态,向左滑动时,移动收起菜单
this.moveSwipeAction(-buttonsWidth + moveX)
} else {
// 关闭状态下,右滑动需忽略
if (moveX > 0) moveX = 0
// 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
// 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
this.moveSwipeAction(moveX)
}
},
// 单元格结束触摸
touchend(event) {
if (this.disabled) return
const touches = event.changedTouches ? event.changedTouches[0] : {}
const { pageX } = touches
const { pageY } = touches
const { buttonsWidth } = this
this.moveX = pageX - this.startX
if (this.status === 'open') {
// 在展开的状态下,继续左滑,无需操作
if (this.moveX < 0) this.moveX = 0
if (this.moveX > buttonsWidth) this.moveX = buttonsWidth
// 在开启状态下点击一下内容区域moveX为0也即没有进行移动这时执行收起菜单逻辑
if (this.moveX === 0) {
return this.closeSwipeAction()
}
// 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
if (Math.abs(this.moveX) < this.threshold) {
this.openSwipeAction()
} else {
// 如果滑动距离大于阈值,则执行收起逻辑
this.closeSwipeAction()
}
} else {
// 在关闭的状态下,右滑,无需操作
if (this.moveX >= 0) this.moveX = 0
if (this.moveX <= -buttonsWidth) this.moveX = -buttonsWidth
// 理由同上
if (Math.abs(this.moveX) < this.threshold) {
this.closeSwipeAction()
} else {
this.openSwipeAction()
}
}
},
// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
moveSwipeAction(moveX) {
if (this.moving) return
this.moving = true
let previewButtonsMoveX = 0
const len = this.buttons.length
animation.transition(this.$refs['uv-swipe-action-item__content'].ref, {
styles: {
transform: `translateX(${moveX}px)`
},
timingFunction: 'linear'
}, () => {
this.moving = false
})
// 按钮的组的长度
for (let i = len - 1; i >= 0; i--) {
const buttonRef = this.$refs[`uv-swipe-action-item__right__button-${i}`][0].ref
// 通过比例,得出元素自身该移动的距离
const translateX = this.buttons[i].width / this.buttonsWidth * moveX
// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
const realTranslateX = translateX + previewButtonsMoveX
animation.transition(buttonRef, {
styles: {
transform: `translateX(${realTranslateX}px)`
},
duration: 0,
delay: 0,
timingFunction: 'linear'
}, () => {})
// 记录本按钮之前的所有按钮的移动距离之和
previewButtonsMoveX += translateX
}
},
// 关闭菜单
closeSwipeAction() {
if (this.status === 'close') return
this.moving = true
const { buttonsWidth } = this
animation.transition(this.$refs['uv-swipe-action-item__content'].ref, {
styles: {
transform: 'translateX(0px)'
},
duration: this.getDuratin,
timingFunction: 'ease-in-out'
}, () => {
this.status = 'close'
this.moving = false
this.closeHandler()
})
// 按钮的组的长度
const len = this.buttons.length
for (let i = len - 1; i >= 0; i--) {
const buttonRef = this.$refs[`uv-swipe-action-item__right__button-${i}`][0].ref
// 如果不满足边界条件,返回
if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
animation.transition(buttonRef, {
styles: {
transform: 'translateX(0px)'
},
duration: this.getDuratin,
timingFunction: 'ease-in-out'
}, () => {})
}
},
// 打开菜单
openSwipeAction() {
if (this.status === 'open') return
this.moving = true
const buttonsWidth = -this.buttonsWidth
let previewButtonsMoveX = 0
animation.transition(this.$refs['uv-swipe-action-item__content'].ref, {
styles: {
transform: `translateX(${buttonsWidth}px)`
},
duration: this.getDuratin,
timingFunction: 'ease-in-out'
}, () => {
this.status = 'open'
this.moving = false
this.openHandler()
})
// 按钮的组的长度
const len = this.buttons.length
for (let i = len - 1; i >= 0; i--) {
const buttonRef = this.$refs[`uv-swipe-action-item__right__button-${i}`][0].ref
// 如果不满足边界条件,返回
if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
// 通过比例,得出元素自身该移动的距离
const translateX = this.buttons[i].width / this.buttonsWidth * buttonsWidth
// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
const realTranslateX = translateX + previewButtonsMoveX
animation.transition(buttonRef, {
styles: {
transform: `translateX(${realTranslateX}px)`
},
duration: this.getDuratin,
timingFunction: 'ease-in-out'
}, () => {})
previewButtonsMoveX += translateX
}
},
// 查询按钮节点信息
queryRect() {
// 历遍所有按钮数组通过getRectByDom返回一个promise
const promiseAll = this.rightOptions.map((item, index) => this.getRectByDom(this.$refs[`uv-swipe-action-item__right__button-${index}`][0]))
// 通过promise.all方法让所有按钮的查询结果返回一个数组的形式
Promise.all(promiseAll).then((sizes) => {
this.buttons = sizes
// 计算所有按钮总宽度
this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
})
},
// 通过nvue的dom模块查询节点信息
getRectByDom(ref) {
return new Promise((resolve) => {
dom.getComponentRect(ref, (res) => {
resolve(res.size)
})
})
}
}
}

View File

@ -0,0 +1,182 @@
// nvue操作dom的库用于获取dom的尺寸信息
const dom = uni.requireNativePlugin('dom');
const bindingX = uni.requireNativePlugin('bindingx');
const animation = uni.requireNativePlugin('animation');
import { getDuration, getPx } from '@/uni_modules/uv-ui-tools/libs/function/index.js'
export default {
data() {
return {
// 所有按钮的总宽度
buttonsWidth: 0,
// 是否正在移动中
moving: false
}
},
computed: {
// 获取过渡时间
getDuratin() {
let duration = String(this.duration)
// 如果ms为单位返回ms的数值部分
if (duration.indexOf('ms') >= 0) return parseInt(duration)
// 如果s为单位为了得到ms的数值需要乘以1000
if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
// 如果值传了数值且小于30认为是s单位
duration = Number(duration)
return duration < 30 ? duration * 1000 : duration
}
},
watch: {
show(n) {
if(n) {
this.moveCellByAnimation('open')
} else {
this.moveCellByAnimation('close')
}
}
},
mounted() {
setTimeout(()=>{
this.initialize()
},20)
},
methods: {
initialize() {
this.queryRect()
},
// 关闭单元格,用于打开一个,自动关闭其他单元格的场景
closeHandler() {
if(this.status === 'open') {
// 如果在打开状态下,进行点击的话,直接关闭单元格
return this.moveCellByAnimation('close') && this.unbindBindingX()
}
},
// 点击单元格
clickHandler() {
// 如果在移动中被点击,进行忽略
if(this.moving) return
// 尝试关闭其他打开的单元格
this.parent && this.parent.closeOther(this)
if(this.status === 'open') {
// 如果在打开状态下,进行点击的话,直接关闭单元格
return this.moveCellByAnimation('close') && this.unbindBindingX()
}
},
// 滑动单元格
onTouchstart(e) {
// 如果当前正在移动中或者disabled状态则返回
if(this.moving || this.disabled) {
return this.unbindBindingX()
}
if(this.status === 'open') {
// 如果在打开状态下,进行点击的话,直接关闭单元格
return this.moveCellByAnimation('close') && this.unbindBindingX()
}
// 特殊情况下e可能不为一个对象
e?.stopPropagation && e.stopPropagation()
e?.preventDefault && e.preventDefault()
this.moving = true
// 获取元素ref
const content = this.getContentRef()
let expression = `min(max(${-this.buttonsWidth}, x), 0)`
// 尝试关闭其他打开的单元格
this.parent && this.parent.closeOther(this)
// 阿里为了KPI而开源的BindingX
this.panEvent = bindingX.bind({
anchor: content,
eventType: 'pan',
props: [{
element: content,
// 绑定width属性设置其宽度值
property: 'transform.translateX',
expression
}]
}, (res) => {
this.moving = false
if (res.state === 'end' || res.state === 'exit') {
const deltaX = res.deltaX
if(deltaX <= -this.buttonsWidth || deltaX >= 0) {
// 如果触摸滑动的过程中大于单元格的总宽度或者大于0意味着已经动过滑动达到了打开或者关闭的状态
// 这里直接进行状态的标记
this.$nextTick(() => {
this.status = deltaX <= -this.buttonsWidth ? 'open' : 'close'
})
} else if(Math.abs(deltaX) > getPx(this.threshold)) {
// 在移动大于阈值、并且小于总按钮宽度时,进行自动打开或者关闭
// 移动距离大于0时意味着需要关闭状态
if(Math.abs(deltaX) < this.buttonsWidth) {
this.moveCellByAnimation(deltaX > 0 ? 'close' : 'open')
}
} else {
// 在小于阈值时,进行关闭操作(如果在打开状态下将不会执行bindingX)
this.moveCellByAnimation('close')
}
}
})
},
// 释放bindingX
unbindBindingX() {
// 释放上一次的资源
if (this?.panEvent?.token != 0) {
bindingX.unbind({
token: this.panEvent?.token,
// pan为手势事件
eventType: 'pan'
})
}
},
// 查询按钮节点信息
queryRect() {
// 历遍所有按钮数组通过getRectByDom返回一个promise
const promiseAll = this.options.map(async(item, index) => {
return await this.getRectByDom(this.$refs[`uv-swipe-action-item__right__button-${index}`][0])
})
// 通过promise.all方法让所有按钮的查询结果返回一个数组的形式
Promise.all(promiseAll).then(sizes => {
this.buttons = sizes
// 计算所有按钮总宽度
this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
})
},
// 通过nvue的dom模块查询节点信息
getRectByDom(ref) {
return new Promise(resolve => {
dom.getComponentRect(ref, res => {
resolve(res.size)
})
})
},
// 移动单元格到左边或者右边尽头
moveCellByAnimation(status = 'open') {
if(this.moving) return
// 标识当前状态
this.moveing = true
const content = this.getContentRef()
const x = status === 'open' ? -this.buttonsWidth : 0
animation.transition(content, {
styles: {
transform: `translateX(${x}px)`,
},
duration: getDuration(this.duration, false),
timingFunction: 'ease-in-out'
}, () => {
this.moving = false
this.status = status
this.unbindBindingX()
})
},
// 获取元素ref
getContentRef() {
return this.$refs['uv-swipe-action-item__content'].ref
}
},
// #ifdef VUE2
beforeDestroy() {
this.unbindBindingX()
},
// #endif
// #ifdef VUE3
unmounted() {
this.unbindBindingX()
}
// #endif
}

View File

@ -0,0 +1,40 @@
export default {
props: {
// 控制打开或者关闭
show: {
type: Boolean,
default: false
},
// 标识符如果是v-for可用index索引值
name: {
type: [String, Number],
default: ''
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// 是否自动关闭其他swipe按钮组
autoClose: {
type: Boolean,
default: true
},
// 滑动距离阈值,只有大于此值,才被认为是要打开菜单
threshold: {
type: Number,
default: 20
},
// 右侧按钮内容
options: {
type: Array,
default: () => []
},
// 动画过渡时间单位ms
duration: {
type: [String, Number],
default: 300
},
...uni.$uv?.props?.swipeActionItem
}
}

View File

@ -0,0 +1,200 @@
<template>
<view class="uv-swipe-action-item" ref="uv-swipe-action-item">
<view class="uv-swipe-action-item__right">
<slot name="button">
<view v-for="(item,index) in options" :key="index" class="uv-swipe-action-item__right__button"
:ref="`uv-swipe-action-item__right__button-${index}`" :style="[{
alignItems: item.style && item.style.borderRadius ? 'center' : 'stretch'
}]" @tap="buttonClickHandler(item, index)">
<view class="uv-swipe-action-item__right__button__wrapper" :style="[{
backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
borderRadius: item.style && item.style.borderRadius ? item.style.borderRadius : '0',
padding: item.style && item.style.borderRadius ? '0' : '0 15px',
}, item.style]">
<uv-icon v-if="item.icon" :name="item.icon"
:color="item.style && item.style.color ? item.style.color : '#ffffff'"
:size="item.iconSize ? $uv.addUnit(item.iconSize) : item.style && item.style.fontSize ? $uv.getPx(item.style.fontSize) * 1.2 : 17"
:customStyle="{
marginRight: item.text ? '2px' : 0
}"></uv-icon>
<text v-if="item.text" class="uv-swipe-action-item__right__button__wrapper__text uv-line-1"
:style="[{
color: item.style && item.style.color ? item.style.color : '#ffffff',
fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px',
lineHeight: item.style && item.style.fontSize ? item.style.fontSize : '16px',
}]">{{ item.text }}</text>
</view>
</view>
</slot>
</view>
<!-- #ifndef APP-NVUE -->
<view class="uv-swipe-action-item__content" @touchstart="wxs.touchstart" @touchmove="wxs.touchmove"
@touchend="wxs.touchend" :status="status" :change:status="wxs.statusChange" :size="size"
:change:size="wxs.sizeChange">
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view class="uv-swipe-action-item__content" ref="uv-swipe-action-item__content" @panstart="onTouchstart"
@tap="clickHandler">
<!-- #endif -->
<slot />
</view>
</view>
</template>
<!-- #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ -->
<script src="./index.wxs" module="wxs" lang="wxs"></script>
<!-- #endif -->
<script>
import touch from '@/uni_modules/uv-ui-tools/libs/mixin/touch.js'
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
// #ifdef APP-NVUE
import nvue from './nvue.js';
// #endif
// #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ
import wxs from './wxs.js';
// #endif
/**
* SwipeActionItem 滑动单元格子组件
* @description 该组件一般用于左滑唤出操作菜单的场景用的最多的是左滑删除操作
* @tutorial https://www.uvui.cn/components/swipeAction.html
* @property {Boolean} show 控制打开或者关闭默认 false
* @property {String | Number} index 标识符如果是v-for可用index索引
* @property {Boolean} disabled 是否禁用默认 false
* @property {Boolean} autoClose 是否自动关闭其他swipe按钮组默认 true
* @property {Number} threshold 滑动距离阈值只有大于此值才被认为是要打开菜单默认 30
* @property {Array} options 右侧按钮内容
* @property {String | Number} duration 动画过渡时间单位ms默认 350
* @event {Function(index)} open 组件打开时触发
* @event {Function(index)} close 组件关闭时触发
* @example <uv-swipe-action><uv-swipe-action-item :options="options1" ></uv-swipe-action-item></uv-swipe-action>
*/
export default {
name: 'uv-swipe-action-item',
emits: ['click'],
// #ifndef APP-NVUE
mixins: [mpMixin, mixin, props, touch],
// #endif
// #ifdef APP-NVUE
mixins: [mpMixin, mixin, props, nvue , touch],
// #endif
// #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ
mixins: [mpMixin, mixin, props, touch, wxs],
// #endif
data() {
return {
//
size: {},
// uv-swipe-action
parentData: {
autoClose: true,
},
// open-close-
status: 'close',
}
},
watch: {
// wxs
wxsInit(newValue, oldValue) {
this.queryRect()
}
},
computed: {
wxsInit() {
return [this.disabled, this.autoClose, this.threshold, this.options, this.duration]
}
},
mounted() {
// #ifdef MP-TOUTIAO
this.$uv.error('抖音小程序暂不支持wxs故该组件暂不支持抖音小程序');
// #endif
this.init()
},
methods: {
init() {
//
this.updateParentData()
// #ifndef APP-NVUE
this.$uv.sleep().then(() => {
this.queryRect()
})
// #endif
},
updateParentData() {
// mixin
this.getParentData('uv-swipe-action')
},
// #ifndef APP-NVUE
//
queryRect() {
this.$uvGetRect('.uv-swipe-action-item__right__button', true).then(buttons => {
this.size = {
buttons,
show: this.show,
disabled: this.disabled,
threshold: this.threshold,
duration: this.duration
}
})
},
// #endif
//
buttonClickHandler(item, index) {
this.$emit('click', {
index,
name: this.name
})
}
},
}
</script>
<style lang="scss" scoped>
$show-lines: 1;
@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
.uv-swipe-action-item {
position: relative;
overflow: hidden;
/* #ifndef APP-NVUE || MP-WEIXIN */
touch-action: pan-y;
/* #endif */
&__content {
background-color: #FFFFFF;
z-index: 10;
}
&__right {
position: absolute;
top: 0;
bottom: 0;
right: 0;
@include flex;
&__button {
@include flex;
justify-content: center;
overflow: hidden;
align-items: center;
&__wrapper {
@include flex;
align-items: center;
justify-content: center;
padding: 0 15px;
&__text {
@include flex;
align-items: center;
color: #FFFFFF;
font-size: 15px;
text-align: center;
justify-content: center;
}
}
}
}
}
</style>

View File

@ -0,0 +1,15 @@
export default {
methods: {
// 关闭时执行
closeHandler() {
this.status = 'close'
},
setState(status) {
this.status = status
},
closeOther() {
// 尝试关闭其他打开的单元格
this.parent && this.parent.closeOther(this)
}
}
}

View File

@ -0,0 +1,10 @@
export default {
props: {
// 是否自动关闭其他swipe按钮组
autoClose: {
type: Boolean,
default: true
},
...uni.$uv?.props?.swipeAction
}
}

View File

@ -0,0 +1,65 @@
<template>
<view class="uv-swipe-action">
<slot></slot>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
/**
* SwipeAction 滑动单元格
* @description 该组件一般用于左滑唤出操作菜单的场景用的最多的是左滑删除操作
* @tutorial https://www.uvui.cn/components/swipeAction.html
* @property {Boolean} autoClose 是否自动关闭其他swipe按钮组
* @event {Function(index)} click 点击组件时触发
* @example <uv-swipe-action><uv-swipe-action-item :rightOptions="options1" ></uv-swipe-action-item></uv-swipe-action>
*/
export default {
name: 'uv-swipe-action',
mixins: [mpMixin, mixin, props],
data() {
return {}
},
provide() {
return {
swipeAction: this
}
},
computed: {
// computeduv-swipe-action-item
// parentDatawatch(uv-swipe-action-item)
//
parentData() {
return [this.autoClose]
}
},
watch: {
//
parentData() {
if (this.children.length) {
this.children.map(child => {
// (uv-swipe-action-item)updateParentData()
typeof(child.updateParentData) === 'function' && child.updateParentData()
})
}
},
},
created() {
this.children = []
},
methods: {
closeOther(child) {
if (this.autoClose) {
//
this.children.map((item, index) => {
if (child !== item) {
item.closeHandler()
}
})
}
}
}
}
</script>

View File

@ -0,0 +1,88 @@
{
"id": "uv-swipe-action",
"displayName": "uv-swipe-action 滑动单元格 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.4",
"description": "滑动单元格组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作。",
"keywords": [
"uv-swipe-action",
"uvui",
"uv-ui",
"swipe-action",
"滑动单元格"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-icon"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"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",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,11 @@
## SwipeAction 滑动单元格
> **组件名uv-swipe-action**
该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作。
### <a href="https://www.uvui.cn/components/swipeAction.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题或者您对uv-ui有一些好的建议欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a><a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

View File

@ -0,0 +1,66 @@
## 1.1.202023-10-30
1. 1.1.16版本
## 1.1.192023-10-13
1. 兼容vue3
## 1.1.182023-10-12
1. 1.1.15版本
## 1.1.172023-09-27
1. 1.1.14版本发布
## 1.1.162023-09-15
1. 1.1.13版本发布
## 1.1.152023-09-15
1. 更新button.js相关按钮支持open-type="agreePrivacyAuthorization"
## 1.1.142023-09-14
1. 优化dayjs
## 1.1.132023-09-13
1. 优化,$uv中增加unit参数方便组件中使用
## 1.1.122023-09-10
1. 升级版本
## 1.1.112023-09-04
1. 1.1.11版本
## 1.1.102023-08-31
1. 修复customStyle和customClass存在冲突的问题
## 1.1.92023-08-27
1. 版本升级
2. 优化
## 1.1.82023-08-24
1. 版本升级
## 1.1.72023-08-22
1. 版本升级
## 1.1.62023-08-18
uvui版本1.1.6
## 1.0.152023-08-14
1. 更新uvui版本号
## 1.0.132023-08-06
1. 优化
## 1.0.122023-08-06
1. 修改版本号
## 1.0.112023-08-06
1. 路由增加events参数
2. 路由拦截修复
## 1.0.102023-08-01
1. 优化
## 1.0.92023-06-28
优化openType.js
## 1.0.82023-06-15
1. 修改支付宝报错的BUG
## 1.0.72023-06-07
1. 解决微信小程序使用uvui提示 Some selectors are not allowed in component wxss, including tag name selectors, ID selectors, and attribute selectors
2. 解决上述提示需要在uni.scss配置$uvui-nvue-style: false; 然后在APP.vue下面引入uvui内置的基础样式:@import '@/uni_modules/uv-ui-tools/index.scss';
## 1.0.62023-06-04
1. uv-ui-tools 优化工具组件,兼容更多功能
2. 小程序分享功能优化等
## 1.0.52023-06-02
1. 修改扩展使用mixin中方法的问题
## 1.0.42023-05-23
1. 兼容百度小程序修改bem函数
## 1.0.32023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.22023-05-10
1. 增加Http请求封装
2. 优化
## 1.0.12023-05-04
1. 修改名称及备注
## 1.0.02023-05-04
1. uv-ui工具集首次发布

View File

@ -0,0 +1,6 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@ -0,0 +1,79 @@
// 全局挂载引入http相关请求拦截插件
import Request from './libs/luch-request'
// 引入全局mixin
import mixin from './libs/mixin/mixin.js'
// 小程序特有的mixin
import mpMixin from './libs/mixin/mpMixin.js'
// #ifdef MP
import mpShare from '@/uni_modules/uv-ui-tools/libs/mixin/mpShare.js'
// #endif
// 路由封装
import route from './libs/util/route.js'
// 公共工具函数
import * as index from './libs/function/index.js'
// 防抖方法
import debounce from './libs/function/debounce.js'
// 节流方法
import throttle from './libs/function/throttle.js'
// 规则检验
import * as test from './libs/function/test.js'
// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制
import * as colorGradient from './libs/function/colorGradient.js'
// 配置信息
import config from './libs/config/config.js'
// 平台
import platform from './libs/function/platform'
const $uv = {
route,
config,
test,
date: index.timeFormat, // 另名date
...index,
colorGradient: colorGradient.colorGradient,
hexToRgb: colorGradient.hexToRgb,
rgbToHex: colorGradient.rgbToHex,
colorToRgba: colorGradient.colorToRgba,
http: new Request(),
debounce,
throttle,
platform,
mixin,
mpMixin
}
uni.$uv = $uv;
const install = (Vue,options={}) => {
// #ifndef APP-NVUE
const cloneMixin = index.deepClone(mixin);
delete cloneMixin?.props?.customClass;
delete cloneMixin?.props?.customStyle;
Vue.mixin(cloneMixin);
// #ifdef MP
if(options.mpShare){
Vue.mixin(mpShare);
}
// #endif
// #endif
// #ifdef VUE2
// 时间格式化同时两个名称date和timeFormat
Vue.filter('timeFormat', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
Vue.filter('date', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
// 将多久以前的方法,注入到全局过滤器
Vue.filter('timeFrom', (timestamp, format) => uni.$uv.timeFrom(timestamp, format));
// 同时挂载到uni和Vue.prototype中
// #ifndef APP-NVUE
// 只有vue挂载到Vue.prototype才有意义因为nvue中全局Vue.prototype和Vue.mixin是无效的
Vue.prototype.$uv = $uv;
// #endif
// #endif
// #ifdef VUE3
Vue.config.globalProperties.$uv = $uv;
// #endif
}
export default {
install
}

View File

@ -0,0 +1,7 @@
// 引入公共基础类
@import "./libs/css/common.scss";
// 非nvue的样式
/* #ifndef APP-NVUE */
@import "./libs/css/vue.scss";
/* #endif */

View File

@ -0,0 +1,34 @@
// 此版本发布于2023-10-30
const version = '1.1.16'
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.log(`\n %c uvui V${version} https://www.uvui.cn/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0; border-radius: 5px;');
}
export default {
v: version,
version,
// 主题名称
type: [
'primary',
'success',
'info',
'error',
'warning'
],
// 颜色部分本来可以通过scss的:export导出供js使用但是奈何nvue不支持
color: {
'uv-primary': '#2979ff',
'uv-warning': '#ff9900',
'uv-success': '#19be6b',
'uv-error': '#fa3534',
'uv-info': '#909399',
'uv-main-color': '#303133',
'uv-content-color': '#606266',
'uv-tips-color': '#909399',
'uv-light-color': '#c0c4cc'
},
// 默认单位可以通过配置为rpx那么在用于传入组件大小参数为数值时就默认为rpx
unit: 'px'
}

View File

@ -0,0 +1,32 @@
$uv-main-color: #303133 !default;
$uv-content-color: #606266 !default;
$uv-tips-color: #909193 !default;
$uv-light-color: #c0c4cc !default;
$uv-border-color: #dadbde !default;
$uv-bg-color: #f3f4f6 !default;
$uv-disabled-color: #c8c9cc !default;
$uv-primary: #3c9cff !default;
$uv-primary-dark: #398ade !default;
$uv-primary-disabled: #9acafc !default;
$uv-primary-light: #ecf5ff !default;
$uv-warning: #f9ae3d !default;
$uv-warning-dark: #f1a532 !default;
$uv-warning-disabled: #f9d39b !default;
$uv-warning-light: #fdf6ec !default;
$uv-success: #5ac725 !default;
$uv-success-dark: #53c21d !default;
$uv-success-disabled: #a9e08f !default;
$uv-success-light: #f5fff0;
$uv-error: #f56c6c !default;
$uv-error-dark: #e45656 !default;
$uv-error-disabled: #f7b2b2 !default;
$uv-error-light: #fef0f0 !default;
$uv-info: #909399 !default;
$uv-info-dark: #767a82 !default;
$uv-info-disabled: #c4c6c9 !default;
$uv-info-light: #f4f4f5 !default;

View File

@ -0,0 +1,100 @@
// 超出行数自动显示行尾省略号最多5行
// 来自uvui的温馨提示当您在控制台看到此报错说明需要在App.vue的style标签加上lang="scss"
@for $i from 1 through 5 {
.uv-line-#{$i} {
/* #ifdef APP-NVUE */
// nvue下可以直接使用lines属性这是weex特有样式
lines: $i;
text-overflow: ellipsis;
overflow: hidden;
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
// vue下单行和多行显示省略号需要单独处理
@if $i == '1' {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} @else {
display: -webkit-box!important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: $i;
-webkit-box-orient: vertical!important;
}
/* #endif */
}
}
$uv-bordercolor: #dadbde;
@if variable-exists(uv-border-color) {
$uv-bordercolor: $uv-border-color;
}
// 此处加上!important并非随意乱用而是因为目前*.nvue页面编译到H5时
// App.vue的样式会被uni-app的view元素的自带border属性覆盖导致无效
// 综上这是uni-app的缺陷导致我们为了多端兼容而必须要加上!important
// 移动端兼容性较好直接使用0.5px去实现细边框不使用伪元素形式实现
.uv-border {
border-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-style: solid;
}
.uv-border-top {
border-top-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-top-style: solid;
}
.uv-border-left {
border-left-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-left-style: solid;
}
.uv-border-right {
border-right-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-right-style: solid;
}
.uv-border-bottom {
border-bottom-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-bottom-style: solid;
}
.uv-border-top-bottom {
border-top-width: 0.5px!important;
border-bottom-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-top-style: solid;
border-bottom-style: solid;
}
// 去除button的所有默认样式让其表现跟普通的viewtext元素一样
.uv-reset-button {
padding: 0;
background-color: transparent;
/* #ifndef APP-PLUS */
font-size: inherit;
line-height: inherit;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.uv-reset-button::after {
border: none;
}
/* #endif */
.uv-hover-class {
opacity: 0.7;
}

View File

@ -0,0 +1,23 @@
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
/* #ifndef APP-NVUE */
// 由于uvui是基于nvue环境进行开发的此环境中普通元素默认为flex-direction: column;
// 所以在非nvue中需要对元素进行重置为flex-direction: column; 否则可能会表现异常
$uvui-nvue-style: true !default;
@if $uvui-nvue-style == true {
view, scroll-view, swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
}
/* #endif */

View File

@ -0,0 +1,111 @@
// 超出行数自动显示行尾省略号最多5行
// 来自uvui的温馨提示当您在控制台看到此报错说明需要在App.vue的style标签加上lang="scss"
@if variable-exists(show-lines) {
@for $i from 1 through 5 {
.uv-line-#{$i} {
/* #ifdef APP-NVUE */
// nvue下可以直接使用lines属性这是weex特有样式
lines: $i;
text-overflow: ellipsis;
overflow: hidden;
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
// vue下单行和多行显示省略号需要单独处理
@if $i == '1' {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} @else {
display: -webkit-box!important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: $i;
-webkit-box-orient: vertical!important;
}
/* #endif */
}
}
}
@if variable-exists(show-border) {
$uv-bordercolor: #dadbde;
@if variable-exists(uv-border-color) {
$uv-bordercolor: $uv-border-color;
}
// 此处加上!important并非随意乱用而是因为目前*.nvue页面编译到H5时
// App.vue的样式会被uni-app的view元素的自带border属性覆盖导致无效
// 综上这是uni-app的缺陷导致我们为了多端兼容而必须要加上!important
// 移动端兼容性较好直接使用0.5px去实现细边框不使用伪元素形式实现
@if variable-exists(show-border-surround) {
.uv-border {
border-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-style: solid;
}
}
@if variable-exists(show-border-top) {
.uv-border-top {
border-top-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-top-style: solid;
}
}
@if variable-exists(show-border-left) {
.uv-border-left {
border-left-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-left-style: solid;
}
}
@if variable-exists(show-border-right) {
.uv-border-right {
border-right-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-right-style: solid;
}
}
@if variable-exists(show-border-bottom) {
.uv-border-bottom {
border-bottom-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-bottom-style: solid;
}
}
@if variable-exists(show-border-top-bottom) {
.uv-border-top-bottom {
border-top-width: 0.5px!important;
border-bottom-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-top-style: solid;
border-bottom-style: solid;
}
}
}
@if variable-exists(show-reset-button) {
// 去除button的所有默认样式让其表现跟普通的viewtext元素一样
.uv-reset-button {
padding: 0;
background-color: transparent;
/* #ifndef APP-PLUS */
font-size: inherit;
line-height: inherit;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.uv-reset-button::after {
border: none;
}
/* #endif */
}
@if variable-exists(show-hover) {
.uv-hover-class {
opacity: 0.7;
}
}

View File

@ -0,0 +1,40 @@
// 历遍生成4个方向的底部安全区
@each $d in top, right, bottom, left {
.uv-safe-area-inset-#{$d} {
padding-#{$d}: 0;
padding-#{$d}: constant(safe-area-inset-#{$d});
padding-#{$d}: env(safe-area-inset-#{$d});
}
}
//提升H5端uni.toast()的层级避免被uvui的modal等遮盖
/* #ifdef H5 */
uni-toast {
z-index: 10090;
}
uni-toast .uni-toast {
z-index: 10090;
}
/* #endif */
// 隐藏scroll-view的滚动条
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
$uvui-nvue-style: true !default;
@if $uvui-nvue-style == false {
view, scroll-view, swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
}

View File

@ -0,0 +1,134 @@
/**
* 求两个颜色之间的渐变值
* @param {string} startColor 开始的颜色
* @param {string} endColor 结束的颜色
* @param {number} step 颜色等分的份额
* */
function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) {
const startRGB = hexToRgb(startColor, false) // 转换为rgb数组模式
const startR = startRGB[0]
const startG = startRGB[1]
const startB = startRGB[2]
const endRGB = hexToRgb(endColor, false)
const endR = endRGB[0]
const endG = endRGB[1]
const endB = endRGB[2]
const sR = (endR - startR) / step // 总差值
const sG = (endG - startG) / step
const sB = (endB - startB) / step
const colorArr = []
for (let i = 0; i < step; i++) {
// 计算每一步的hex值
let hex = rgbToHex(`rgb(${Math.round((sR * i + startR))},${Math.round((sG * i + startG))},${Math.round((sB
* i + startB))})`)
// 确保第一个颜色值为startColor的值
if (i === 0) hex = rgbToHex(startColor)
// 确保最后一个颜色值为endColor的值
if (i === step - 1) hex = rgbToHex(endColor)
colorArr.push(hex)
}
return colorArr
}
// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
function hexToRgb(sColor, str = true) {
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
sColor = String(sColor).toLowerCase()
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
let sColorNew = '#'
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
}
sColor = sColorNew
}
// 处理六位的颜色值
const sColorChange = []
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
}
if (!str) {
return sColorChange
}
return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})`
} if (/^(rgb|RGB)/.test(sColor)) {
const arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
return arr.map((val) => Number(val))
}
return sColor
}
// 将rgb表示方式转换为hex表示方式
function rgbToHex(rgb) {
const _this = rgb
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
if (/^(rgb|RGB)/.test(_this)) {
const aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
let strHex = '#'
for (let i = 0; i < aColor.length; i++) {
let hex = Number(aColor[i]).toString(16)
hex = String(hex).length == 1 ? `${0}${hex}` : hex // 保证每个rgb的值为2位
if (hex === '0') {
hex += hex
}
strHex += hex
}
if (strHex.length !== 7) {
strHex = _this
}
return strHex
} if (reg.test(_this)) {
const aNum = _this.replace(/#/, '').split('')
if (aNum.length === 6) {
return _this
} if (aNum.length === 3) {
let numHex = '#'
for (let i = 0; i < aNum.length; i += 1) {
numHex += (aNum[i] + aNum[i])
}
return numHex
}
} else {
return _this
}
}
/**
* JS颜色十六进制转换为rgb或rgba,返回的格式为 rgba2552552550.5字符串
* sHex为传入的十六进制的色值
* alpha为rgba的透明度
*/
function colorToRgba(color, alpha) {
color = rgbToHex(color)
// 十六进制颜色值的正则表达式
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
/* 16进制颜色转为RGB格式 */
let sColor = String(color).toLowerCase()
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
let sColorNew = '#'
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
}
sColor = sColorNew
}
// 处理六位的颜色值
const sColorChange = []
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
}
// return sColorChange.join(',')
return `rgba(${sColorChange.join(',')},${alpha})`
}
return sColor
}
export {
colorGradient,
hexToRgb,
rgbToHex,
colorToRgba
}

View File

@ -0,0 +1,29 @@
let timeout = null
/**
* 防抖原理一定时间内只有最后一次操作再过wait毫秒后才执行函数
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function debounce(func, wait = 500, immediate = false) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout)
// 立即执行,此类情况一般用不到
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) typeof func === 'function' && func()
} else {
// 设置定时器当最后一次操作后timeout不会再被清除所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(() => {
typeof func === 'function' && func()
}, wait)
}
}
export default debounce

View File

@ -0,0 +1,167 @@
let _boundaryCheckingState = true; // 是否进行越界检查的全局开关
/**
* 把错误的数据转正
* @private
* @example strip(0.09999999999999998)=0.1
*/
function strip(num, precision = 15) {
return +parseFloat(Number(num).toPrecision(precision));
}
/**
* Return digits length of a number
* @private
* @param {*number} num Input number
*/
function digitLength(num) {
// Get digit length of e
const eSplit = num.toString().split(/[eE]/);
const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
return len > 0 ? len : 0;
}
/**
* 把小数转成整数,如果是小数则放大成整数
* @private
* @param {*number} num 输入数
*/
function float2Fixed(num) {
if (num.toString().indexOf('e') === -1) {
return Number(num.toString().replace('.', ''));
}
const dLen = digitLength(num);
return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
}
/**
* 检测数字是否越界如果越界给出提示
* @private
* @param {*number} num 输入数
*/
function checkBoundary(num) {
if (_boundaryCheckingState) {
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
console.warn(`${num} 超出了精度限制,结果可能不正确`);
}
}
}
/**
* 把递归操作扁平迭代化
* @param {number[]} arr 要操作的数字数组
* @param {function} operation 迭代操作
* @private
*/
function iteratorOperation(arr, operation) {
const [num1, num2, ...others] = arr;
let res = operation(num1, num2);
others.forEach((num) => {
res = operation(res, num);
});
return res;
}
/**
* 高精度乘法
* @export
*/
export function times(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, times);
}
const [num1, num2] = nums;
const num1Changed = float2Fixed(num1);
const num2Changed = float2Fixed(num2);
const baseNum = digitLength(num1) + digitLength(num2);
const leftValue = num1Changed * num2Changed;
checkBoundary(leftValue);
return leftValue / Math.pow(10, baseNum);
}
/**
* 高精度加法
* @export
*/
export function plus(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, plus);
}
const [num1, num2] = nums;
// 取最大的小数位
const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
// 把小数都转为整数然后再计算
return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
}
/**
* 高精度减法
* @export
*/
export function minus(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, minus);
}
const [num1, num2] = nums;
const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
}
/**
* 高精度除法
* @export
*/
export function divide(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, divide);
}
const [num1, num2] = nums;
const num1Changed = float2Fixed(num1);
const num2Changed = float2Fixed(num2);
checkBoundary(num1Changed);
checkBoundary(num2Changed);
// 重要这里必须用strip进行修正
return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
}
/**
* 四舍五入
* @export
*/
export function round(num, ratio) {
const base = Math.pow(10, ratio);
let result = divide(Math.round(Math.abs(times(num, base))), base);
if (num < 0 && result !== 0) {
result = times(result, -1);
}
// 位数不足则补0
return result;
}
/**
* 是否进行边界检查默认开启
* @param flag 标记开关true 为开启false 为关闭默认为 true
* @export
*/
export function enableBoundaryChecking(flag = true) {
_boundaryCheckingState = flag;
}
export default {
times,
plus,
minus,
divide,
round,
enableBoundaryChecking,
};

View File

@ -0,0 +1,734 @@
import { number, empty } from './test.js'
import { round } from './digit.js'
/**
* @description 如果value小于min取min如果value大于max取max
* @param {number} min
* @param {number} max
* @param {number} value
*/
function range(min = 0, max = 0, value = 0) {
return Math.max(min, Math.min(max, Number(value)))
}
/**
* @description 用于获取用户传递值的px值 如果用户传递了"xxpx"或者"xxrpx"取出其数值部分如果是"xxxrpx"还需要用过uni.upx2px进行转换
* @param {number|string} value 用户传递值的px值
* @param {boolean} unit
* @returns {number|string}
*/
function getPx(value, unit = false) {
if (number(value)) {
return unit ? `${value}px` : Number(value)
}
// 如果带有rpx先取出其数值部分再转为px值
if (/(rpx|upx)$/.test(value)) {
return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value)))
}
return unit ? `${parseInt(value)}px` : parseInt(value)
}
/**
* @description 进行延时以达到可以简写代码的目的 比如: await uni.$uv.sleep(20)将会阻塞20ms
* @param {number} value 堵塞时间 单位ms 毫秒
* @returns {Promise} 返回promise
*/
function sleep(value = 30) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, value)
})
}
/**
* @description 运行期判断平台
* @returns {string} 返回所在平台(小写)
* @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台
*/
function os() {
return uni.getSystemInfoSync().platform.toLowerCase()
}
/**
* @description 获取系统信息同步接口
* @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync
*/
function sys() {
return uni.getSystemInfoSync()
}
/**
* @description 取一个区间数
* @param {Number} min 最小值
* @param {Number} max 最大值
*/
function random(min, max) {
if (min >= 0 && max > 0 && max >= min) {
const gab = max - min + 1
return Math.floor(Math.random() * gab + min)
}
return 0
}
/**
* @param {Number} len uuid的长度
* @param {Boolean} firstU 将返回的首字母置为"u"
* @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制
*/
function guid(len = 32, firstU = true, radix = null) {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
const uuid = []
radix = radix || chars.length
if (len) {
// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
} else {
let r
// rfc4122标准要求返回的uuid中,某些位为固定的字符
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
uuid[14] = '4'
for (let i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]
}
}
}
// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
if (firstU) {
uuid.shift()
return `u${uuid.join('')}`
}
return uuid.join('')
}
/**
* @description 获取父组件的参数因为支付宝小程序不支持provide/inject的写法
this.$parent在非H5中可以准确获取到父组件但是在H5中需要多次this.$parent.$parent.xxx
这里默认值等于undefined有它的含义因为最顶层元素(组件)的$parent就是undefined意味着不传name
(默认为undefined)就是查找最顶层的$parent
* @param {string|undefined} name 父组件的参数名
*/
function $parent(name = undefined) {
let parent = this.$parent
// 通过while历遍这里主要是为了H5需要多层解析的问题
while (parent) {
// 父组件
if (parent.$options && parent.$options.name !== name) {
// 如果组件的name不相等继续上一级寻找
parent = parent.$parent
} else {
return parent
}
}
return false
}
/**
* @description 样式转换
* 对象转字符串或者字符串转对象
* @param {object | string} customStyle 需要转换的目标
* @param {String} target 转换的目的object-转为对象string-转为字符串
* @returns {object|string}
*/
function addStyle(customStyle, target = 'object') {
// 字符串转字符串,对象转对象情形,直接返回
if (empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' &&
typeof(customStyle) === 'string') {
return customStyle
}
// 字符串转对象
if (target === 'object') {
// 去除字符串样式中的两端空格(中间的空格不能去掉比如padding: 20px 0如果去掉了就错了),空格是无用的
customStyle = trim(customStyle)
// 根据";"将字符串转为数组形式
const styleArray = customStyle.split(';')
const style = {}
// 历遍数组,拼接成对象
for (let i = 0; i < styleArray.length; i++) {
// 'font-size:20px;color:red;',如此最后字符串有";"的话会导致styleArray最后一个元素为空字符串这里需要过滤
if (styleArray[i]) {
const item = styleArray[i].split(':')
style[trim(item[0])] = trim(item[1])
}
}
return style
}
// 这里为对象转字符串形式
let string = ''
for (const i in customStyle) {
// 驼峰转为中划线的形式否则css内联样式无法识别驼峰样式属性名
const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
string += `${key}:${customStyle[i]};`
}
// 去除两端空格
return trim(string)
}
/**
* @description 添加单位如果有rpxupx%px等单位结尾或者值为auto直接返回否则加上px单位结尾
* @param {string|number} value 需要添加单位的值
* @param {string} unit 添加的单位名 比如px
*/
function addUnit(value = 'auto', unit = uni?.$uv?.config?.unit ? uni?.$uv?.config?.unit : 'px') {
value = String(value)
// 用uvui内置验证规则中的number判断是否为数值
return number(value) ? `${value}${unit}` : value
}
/**
* @description 深度克隆
* @param {object} obj 需要深度克隆的对象
* @param cache 缓存
* @returns {*} 克隆后的对象或者原值不是对象
*/
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
let clone;
if (obj instanceof Date) {
clone = new Date(obj.getTime());
} else if (obj instanceof RegExp) {
clone = new RegExp(obj);
} else if (obj instanceof Map) {
clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]));
} else if (obj instanceof Set) {
clone = new Set(Array.from(obj, value => deepClone(value, cache)));
} else if (Array.isArray(obj)) {
clone = obj.map(value => deepClone(value, cache));
} else if (Object.prototype.toString.call(obj) === '[object Object]') {
clone = Object.create(Object.getPrototypeOf(obj));
cache.set(obj, clone);
for (const [key, value] of Object.entries(obj)) {
clone[key] = deepClone(value, cache);
}
} else {
clone = Object.assign({}, obj);
}
cache.set(obj, clone);
return clone;
}
/**
* @description JS对象深度合并
* @param {object} target 需要拷贝的对象
* @param {object} source 拷贝的来源对象
* @returns {object|boolean} 深度合并后的对象或者false入参有不是对象
*/
function deepMerge(target = {}, source = {}) {
target = deepClone(target)
if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target;
const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target);
for (const prop in source) {
if (!source.hasOwnProperty(prop)) continue;
const sourceValue = source[prop];
const targetValue = merged[prop];
if (sourceValue instanceof Date) {
merged[prop] = new Date(sourceValue);
} else if (sourceValue instanceof RegExp) {
merged[prop] = new RegExp(sourceValue);
} else if (sourceValue instanceof Map) {
merged[prop] = new Map(sourceValue);
} else if (sourceValue instanceof Set) {
merged[prop] = new Set(sourceValue);
} else if (typeof sourceValue === 'object' && sourceValue !== null) {
merged[prop] = deepMerge(targetValue, sourceValue);
} else {
merged[prop] = sourceValue;
}
}
return merged;
}
/**
* @description error提示
* @param {*} err 错误内容
*/
function error(err) {
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.error(`uvui提示${err}`)
}
}
/**
* @description 打乱数组
* @param {array} array 需要打乱的数组
* @returns {array} 打乱后的数组
*/
function randomArray(array = []) {
// 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0
return array.sort(() => Math.random() - 0.5)
}
// padStart 的 polyfill因为某些机型或情况还无法支持es7的padStart比如电脑版的微信小程序
// 所以这里做一个兼容polyfill的兼容处理
if (!String.prototype.padStart) {
// 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
String.prototype.padStart = function(maxLength, fillString = ' ') {
if (Object.prototype.toString.call(fillString) !== '[object String]') {
throw new TypeError(
'fillString must be String'
)
}
const str = this
// 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
if (str.length >= maxLength) return String(str)
const fillLength = maxLength - str.length
let times = Math.ceil(fillLength / fillString.length)
while (times >>= 1) {
fillString += fillString
if (times === 1) {
fillString += fillString
}
}
return fillString.slice(0, fillLength) + str
}
}
/**
* @description 格式化时间
* @param {String|Number} dateTime 需要格式化的时间戳
* @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd
* @returns {string} 返回格式化后的字符串
*/
function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') {
let date
// 若传入时间为假值,则取当前时间
if (!dateTime) {
date = new Date()
}
// 若为unix秒时间戳则转为毫秒时间戳逻辑有点奇怪但不敢改以保证历史兼容
else if (/^\d{10}$/.test(dateTime?.toString().trim())) {
date = new Date(dateTime * 1000)
}
// 若用户传入字符串格式时间戳new Date无法解析需做兼容
else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) {
date = new Date(Number(dateTime))
}
// 处理平台性差异在Safari/Webkit中new Date仅支持/作为分割符的字符串时间
// 处理 '2022-07-10 01:02:03',跳过 '2022-07-10T01:02:03'
else if (typeof dateTime === 'string' && dateTime.includes('-') && !dateTime.includes('T')) {
date = new Date(dateTime.replace(/-/g, '/'))
}
// 其他都认为符合 RFC 2822 规范
else {
date = new Date(dateTime)
}
const timeSource = {
'y': date.getFullYear().toString(), // 年
'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月
'd': date.getDate().toString().padStart(2, '0'), // 日
'h': date.getHours().toString().padStart(2, '0'), // 时
'M': date.getMinutes().toString().padStart(2, '0'), // 分
's': date.getSeconds().toString().padStart(2, '0') // 秒
// 有其他格式化字符需求可以继续添加,必须转化成字符串
}
for (const key in timeSource) {
const [ret] = new RegExp(`${key}+`).exec(formatStr) || []
if (ret) {
// 年可能只需展示两位
const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0
formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex))
}
}
return formatStr
}
/**
* @description 时间戳转为多久之前
* @param {String|Number} timestamp 时间戳
* @param {String|Boolean} format
* 格式化规则如果为时间格式字符串超出一定时间范围返回固定的时间格式
* 如果为布尔值false无论什么时间都返回多久以前的格式
* @returns {string} 转化后的内容
*/
function timeFrom(timestamp = null, format = 'yyyy-mm-dd') {
if (timestamp == null) timestamp = Number(new Date())
timestamp = parseInt(timestamp)
// 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位)
if (timestamp.toString().length == 10) timestamp *= 1000
let timer = (new Date()).getTime() - timestamp
timer = parseInt(timer / 1000)
// 如果小于5分钟,则返回"刚刚",其他以此类推
let tips = ''
switch (true) {
case timer < 300:
tips = '刚刚'
break
case timer >= 300 && timer < 3600:
tips = `${parseInt(timer / 60)}分钟前`
break
case timer >= 3600 && timer < 86400:
tips = `${parseInt(timer / 3600)}小时前`
break
case timer >= 86400 && timer < 2592000:
tips = `${parseInt(timer / 86400)}天前`
break
default:
// 如果format为false则无论什么时间戳都显示xx之前
if (format === false) {
if (timer >= 2592000 && timer < 365 * 86400) {
tips = `${parseInt(timer / (86400 * 30))}个月前`
} else {
tips = `${parseInt(timer / (86400 * 365))}年前`
}
} else {
tips = timeFormat(timestamp, format)
}
}
return tips
}
/**
* @description 去除空格
* @param String str 需要去除空格的字符串
* @param String pos both(左右)|left|right|all 默认both
*/
function trim(str, pos = 'both') {
str = String(str)
if (pos == 'both') {
return str.replace(/^\s+|\s+$/g, '')
}
if (pos == 'left') {
return str.replace(/^\s*/, '')
}
if (pos == 'right') {
return str.replace(/(\s*$)/g, '')
}
if (pos == 'all') {
return str.replace(/\s+/g, '')
}
return str
}
/**
* @description 对象转url参数
* @param {object} data,对象
* @param {Boolean} isPrefix,是否自动加上"?"
* @param {string} arrayFormat 规则 indices|brackets|repeat|comma
*/
function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') {
const prefix = isPrefix ? '?' : ''
const _result = []
if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets'
for (const key in data) {
const value = data[key]
// 去掉为空的参数
if (['', undefined, null].indexOf(value) >= 0) {
continue
}
// 如果值为数组,另行处理
if (value.constructor === Array) {
// e.g. {ids: [1, 2, 3]}
switch (arrayFormat) {
case 'indices':
// 结果: ids[0]=1&ids[1]=2&ids[2]=3
for (let i = 0; i < value.length; i++) {
_result.push(`${key}[${i}]=${value[i]}`)
}
break
case 'brackets':
// 结果: ids[]=1&ids[]=2&ids[]=3
value.forEach((_value) => {
_result.push(`${key}[]=${_value}`)
})
break
case 'repeat':
// 结果: ids=1&ids=2&ids=3
value.forEach((_value) => {
_result.push(`${key}=${_value}`)
})
break
case 'comma':
// 结果: ids=1,2,3
let commaStr = ''
value.forEach((_value) => {
commaStr += (commaStr ? ',' : '') + _value
})
_result.push(`${key}=${commaStr}`)
break
default:
value.forEach((_value) => {
_result.push(`${key}[]=${_value}`)
})
}
} else {
_result.push(`${key}=${value}`)
}
}
return _result.length ? prefix + _result.join('&') : ''
}
/**
* 显示消息提示框
* @param {String} title 提示的内容长度与 icon 取值有关
* @param {Number} duration 提示的延迟时间单位毫秒默认2000
*/
function toast(title, duration = 2000) {
uni.showToast({
title: String(title),
icon: 'none',
duration
})
}
/**
* @description 根据主题type值,获取对应的图标
* @param {String} type 主题名称,primary|info|error|warning|success
* @param {boolean} fill 是否使用fill填充实体的图标
*/
function type2icon(type = 'success', fill = false) {
// 如果非预置值,默认为success
if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success'
let iconName = ''
// 目前(2019-12-12),info和primary使用同一个图标
switch (type) {
case 'primary':
iconName = 'info-circle'
break
case 'info':
iconName = 'info-circle'
break
case 'error':
iconName = 'close-circle'
break
case 'warning':
iconName = 'error-circle'
break
case 'success':
iconName = 'checkmark-circle'
break
default:
iconName = 'checkmark-circle'
}
// 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的
if (fill) iconName += '-fill'
return iconName
}
/**
* @description 数字格式化
* @param {number|string} number 要格式化的数字
* @param {number} decimals 保留几位小数
* @param {string} decimalPoint 小数点符号
* @param {string} thousandsSeparator 千分位符号
* @returns {string} 格式化后的数字
*/
function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') {
number = (`${number}`).replace(/[^0-9+-Ee.]/g, '')
const n = !isFinite(+number) ? 0 : +number
const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals)
const sep = (typeof thousandsSeparator === 'undefined') ? ',' : thousandsSeparator
const dec = (typeof decimalPoint === 'undefined') ? '.' : decimalPoint
let s = ''
s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.')
const re = /(-?\d+)(\d{3})/
while (re.test(s[0])) {
s[0] = s[0].replace(re, `$1${sep}$2`)
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || ''
s[1] += new Array(prec - s[1].length + 1).join('0')
}
return s.join(dec)
}
/**
* @description 获取duration值
* 如果带有ms或者s直接返回如果大于一定值认为是ms单位小于一定值认为是s单位
* 比如以30位阈值那么300大于30可以理解为用户想要的是300ms而不是想花300s去执行一个动画
* @param {String|number} value 比如: "1s"|"100ms"|1|100
* @param {boolean} unit 提示: 如果是false 默认返回number
* @return {string|number}
*/
function getDuration(value, unit = true) {
const valueNum = parseInt(value)
if (unit) {
if (/s$/.test(value)) return value
return value > 30 ? `${value}ms` : `${value}s`
}
if (/ms$/.test(value)) return valueNum
if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000
return valueNum
}
/**
* @description 日期的月或日补零操作
* @param {String} value 需要补零的值
*/
function padZero(value) {
return `00${value}`.slice(-2)
}
/**
* @description 在uv-form的子组件内容发生变化或者失去焦点时尝试通知uv-form执行校验方法
* @param {*} instance
* @param {*} event
*/
function formValidate(instance, event) {
const formItem = $parent.call(instance, 'uv-form-item')
const form = $parent.call(instance, 'uv-form')
// 如果发生变化的input或者textarea等其父组件中有uv-form-item或者uv-form等就执行form的validate方法
// 同时将form-item的pros传递给form让其进行精确对象验证
if (formItem && form) {
form.validateField(formItem.prop, () => {}, event)
}
}
/**
* @description 获取某个对象下的属性用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式
* @param {object} obj 对象
* @param {string} key 需要获取的属性字段
* @returns {*}
*/
function getProperty(obj, key) {
if (!obj) {
return
}
if (typeof key !== 'string' || key === '') {
return ''
}
if (key.indexOf('.') !== -1) {
const keys = key.split('.')
let firstObj = obj[keys[0]] || {}
for (let i = 1; i < keys.length; i++) {
if (firstObj) {
firstObj = firstObj[keys[i]]
}
}
return firstObj
}
return obj[key]
}
/**
* @description 设置对象的属性值如果'a.b.c'的形式进行设置
* @param {object} obj 对象
* @param {string} key 需要设置的属性
* @param {string} value 设置的值
*/
function setProperty(obj, key, value) {
if (!obj) {
return
}
// 递归赋值
const inFn = function(_obj, keys, v) {
// 最后一个属性key
if (keys.length === 1) {
_obj[keys[0]] = v
return
}
// 0~length-1个key
while (keys.length > 1) {
const k = keys[0]
if (!_obj[k] || (typeof _obj[k] !== 'object')) {
_obj[k] = {}
}
const key = keys.shift()
// 自调用判断是否存在属性,不存在则自动创建对象
inFn(_obj[k], keys, v)
}
}
if (typeof key !== 'string' || key === '') {
} else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作
const keys = key.split('.')
inFn(obj, keys, value)
} else {
obj[key] = value
}
}
/**
* @description 获取当前页面路径
*/
function page() {
const pages = getCurrentPages();
const route = pages[pages.length - 1]?.route;
// 某些特殊情况下(比如页面进行redirectTo时的一些时机)pages可能为空数组
return `/${route ? route : ''}`
}
/**
* @description 获取当前路由栈实例数组
*/
function pages() {
const pages = getCurrentPages()
return pages
}
/**
* 获取页面历史栈指定层实例
* @param back {number} [0] - 0或者负数表示获取历史栈的哪一层0表示获取当前页面实例-1 表示获取上一个页面实例默认0
*/
function getHistoryPage(back = 0) {
const pages = getCurrentPages()
const len = pages.length
return pages[len - 1 + back]
}
/**
* @description 修改uvui内置属性值
* @param {object} props 修改内置props属性
* @param {object} config 修改内置config属性
* @param {object} color 修改内置color属性
* @param {object} zIndex 修改内置zIndex属性
*/
function setConfig({
props = {},
config = {},
color = {},
zIndex = {}
}) {
const {
deepMerge,
} = uni.$uv
uni.$uv.config = deepMerge(uni.$uv.config, config)
uni.$uv.props = deepMerge(uni.$uv.props, props)
uni.$uv.color = deepMerge(uni.$uv.color, color)
uni.$uv.zIndex = deepMerge(uni.$uv.zIndex, zIndex)
}
export {
range,
getPx,
sleep,
os,
sys,
random,
guid,
$parent,
addStyle,
addUnit,
deepClone,
deepMerge,
error,
randomArray,
timeFormat,
timeFrom,
trim,
queryParams,
toast,
type2icon,
priceFormat,
getDuration,
padZero,
formValidate,
getProperty,
setProperty,
page,
pages,
getHistoryPage,
setConfig
}

View File

@ -0,0 +1,75 @@
/**
* 注意
* 此部分内容在vue-cli模式下需要在vue.config.js加入如下内容才有效
* module.exports = {
* transpileDependencies: ['uview-v2']
* }
*/
let platform = 'none'
// #ifdef VUE3
platform = 'vue3'
// #endif
// #ifdef VUE2
platform = 'vue2'
// #endif
// #ifdef APP-PLUS
platform = 'plus'
// #endif
// #ifdef APP-NVUE
platform = 'nvue'
// #endif
// #ifdef H5
platform = 'h5'
// #endif
// #ifdef MP-WEIXIN
platform = 'weixin'
// #endif
// #ifdef MP-ALIPAY
platform = 'alipay'
// #endif
// #ifdef MP-BAIDU
platform = 'baidu'
// #endif
// #ifdef MP-TOUTIAO
platform = 'toutiao'
// #endif
// #ifdef MP-QQ
platform = 'qq'
// #endif
// #ifdef MP-KUAISHOU
platform = 'kuaishou'
// #endif
// #ifdef MP-360
platform = '360'
// #endif
// #ifdef MP
platform = 'mp'
// #endif
// #ifdef QUICKAPP-WEBVIEW
platform = 'quickapp-webview'
// #endif
// #ifdef QUICKAPP-WEBVIEW-HUAWEI
platform = 'quickapp-webview-huawei'
// #endif
// #ifdef QUICKAPP-WEBVIEW-UNION
platform = 'quckapp-webview-union'
// #endif
export default platform

View File

@ -0,0 +1,287 @@
/**
* 验证电子邮箱格式
*/
function email(value) {
return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value)
}
/**
* 验证手机格式
*/
function mobile(value) {
return /^1([3589]\d|4[5-9]|6[1-2,4-7]|7[0-8])\d{8}$/.test(value)
}
/**
* 验证URL格式
*/
function url(value) {
return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/
.test(value)
}
/**
* 验证日期格式
*/
function date(value) {
if (!value) return false
// 判断是否数值或者字符串数值(意味着为时间戳)转为数值否则new Date无法识别字符串时间戳
if (number(value)) value = +value
return !/Invalid|NaN/.test(new Date(value).toString())
}
/**
* 验证ISO类型的日期格式
*/
function dateISO(value) {
return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
}
/**
* 验证十进制数字
*/
function number(value) {
return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value)
}
/**
* 验证字符串
*/
function string(value) {
return typeof value === 'string'
}
/**
* 验证整数
*/
function digits(value) {
return /^\d+$/.test(value)
}
/**
* 验证身份证号码
*/
function idCard(value) {
return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
}
/**
* 是否车牌号
*/
function carNo(value) {
// 新能源车牌
const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/
// 旧车牌
const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/
if (value.length === 7) {
return creg.test(value)
} if (value.length === 8) {
return xreg.test(value)
}
return false
}
/**
* 金额,只允许2位小数
*/
function amount(value) {
// 金额,只允许保留两位小数
return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value)
}
/**
* 中文
*/
function chinese(value) {
const reg = /^[\u4e00-\u9fa5]+$/gi
return reg.test(value)
}
/**
* 只能输入字母
*/
function letter(value) {
return /^[a-zA-Z]*$/.test(value)
}
/**
* 只能是字母或者数字
*/
function enOrNum(value) {
// 英文或者数字
const reg = /^[0-9a-zA-Z]*$/g
return reg.test(value)
}
/**
* 验证是否包含某个值
*/
function contains(value, param) {
return value.indexOf(param) >= 0
}
/**
* 验证一个值范围[min, max]
*/
function range(value, param) {
return value >= param[0] && value <= param[1]
}
/**
* 验证一个长度范围[min, max]
*/
function rangeLength(value, param) {
return value.length >= param[0] && value.length <= param[1]
}
/**
* 是否固定电话
*/
function landline(value) {
const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/
return reg.test(value)
}
/**
* 判断是否为空
*/
function empty(value) {
switch (typeof value) {
case 'undefined':
return true
case 'string':
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true
break
case 'boolean':
if (!value) return true
break
case 'number':
if (value === 0 || isNaN(value)) return true
break
case 'object':
if (value === null || value.length === 0) return true
for (const i in value) {
return false
}
return true
}
return false
}
/**
* 是否json字符串
*/
function jsonString(value) {
if (typeof value === 'string') {
try {
const obj = JSON.parse(value)
if (typeof obj === 'object' && obj) {
return true
}
return false
} catch (e) {
return false
}
}
return false
}
/**
* 是否数组
*/
function array(value) {
if (typeof Array.isArray === 'function') {
return Array.isArray(value)
}
return Object.prototype.toString.call(value) === '[object Array]'
}
/**
* 是否对象
*/
function object(value) {
return Object.prototype.toString.call(value) === '[object Object]'
}
/**
* 是否短信验证码
*/
function code(value, len = 6) {
return new RegExp(`^\\d{${len}}$`).test(value)
}
/**
* 是否函数方法
* @param {Object} value
*/
function func(value) {
return typeof value === 'function'
}
/**
* 是否promise对象
* @param {Object} value
*/
function promise(value) {
return object(value) && func(value.then) && func(value.catch)
}
/**
* @param {Object} value
*/
function image(value) {
const newValue = value.split('?')[0]
const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i
return IMAGE_REGEXP.test(newValue)
}
/**
* 是否视频格式
* @param {Object} value
*/
function video(value) {
const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i
return VIDEO_REGEXP.test(value)
}
/**
* 是否为正则对象
* @param {Object}
* @return {Boolean}
*/
function regExp(o) {
return o && Object.prototype.toString.call(o) === '[object RegExp]'
}
export {
email,
mobile,
url,
date,
dateISO,
number,
digits,
idCard,
carNo,
amount,
chinese,
letter,
enOrNum,
contains,
range,
rangeLength,
empty,
jsonString,
landline,
object,
array,
code,
func,
promise,
video,
image,
regExp,
string
}

View File

@ -0,0 +1,30 @@
let timer; let
flag
/**
* 节流原理在一定时间内只能触发一次
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function throttle(func, wait = 500, immediate = true) {
if (immediate) {
if (!flag) {
flag = true
// 如果是立即执行则在wait毫秒内开始时执行
typeof func === 'function' && func()
timer = setTimeout(() => {
flag = false
}, wait)
}
} else if (!flag) {
flag = true
// 如果是非立即执行则在wait毫秒内的结束处执行
timer = setTimeout(() => {
flag = false
typeof func === 'function' && func()
}, wait)
}
}
export default throttle

View File

@ -0,0 +1,97 @@
import buildURL from '../helpers/buildURL'
import buildFullPath from '../core/buildFullPath'
import settle from '../core/settle'
import { isUndefined } from '../utils'
/**
* 返回可选值存在的配置
* @param {Array} keys - 可选值数组
* @param {Object} config2 - 配置
* @return {{}} - 存在的配置项
*/
const mergeKeys = (keys, config2) => {
const config = {}
keys.forEach((prop) => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
return config
}
export default (config) => new Promise((resolve, reject) => {
const fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params)
const _config = {
url: fullPath,
header: config.header,
complete: (response) => {
config.fullPath = fullPath
response.config = config
try {
// 对可能字符串不是json 的情况容错
if (typeof response.data === 'string') {
response.data = JSON.parse(response.data)
}
// eslint-disable-next-line no-empty
} catch (e) {
}
settle(resolve, reject, response)
}
}
let requestTask
if (config.method === 'UPLOAD') {
delete _config.header['content-type']
delete _config.header['Content-Type']
const otherConfig = {
// #ifdef MP-ALIPAY
fileType: config.fileType,
// #endif
filePath: config.filePath,
name: config.name
}
const optionalKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef H5
'file',
// #endif
// #ifdef H5 || APP-PLUS
'timeout',
// #endif
'formData'
]
requestTask = uni.uploadFile({ ..._config, ...otherConfig, ...mergeKeys(optionalKeys, config) })
} else if (config.method === 'DOWNLOAD') {
// #ifdef H5 || APP-PLUS
if (!isUndefined(config.timeout)) {
_config.timeout = config.timeout
}
// #endif
requestTask = uni.downloadFile(_config)
} else {
const optionalKeys = [
'data',
'method',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4'
// #endif
]
requestTask = uni.request({ ..._config, ...mergeKeys(optionalKeys, config) })
}
if (config.getTask) {
config.getTask(requestTask, config)
}
})

View File

@ -0,0 +1,50 @@
'use strict'
function InterceptorManager() {
this.handlers = []
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled,
rejected
})
return this.handlers.length - 1
}
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null
}
}
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
this.handlers.forEach((h) => {
if (h !== null) {
fn(h)
}
})
}
export default InterceptorManager

View File

@ -0,0 +1,198 @@
/**
* @Class Request
* @description luch-request http请求插件
* @version 3.0.7
* @Author lu-ch
* @Date 2021-09-04
* @Email webwork.s@qq.com
* 文档: https://www.quanzhan.co/luch-request/
* github: https://github.com/lei-mu/luch-request
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
* HBuilderX: beat-3.0.4 alpha-3.0.4
*/
import dispatchRequest from './dispatchRequest'
import InterceptorManager from './InterceptorManager'
import mergeConfig from './mergeConfig'
import defaults from './defaults'
import { isPlainObject } from '../utils'
import clone from '../utils/clone'
export default class Request {
/**
* @param {Object} arg - 全局配置
* @param {String} arg.baseURL - 全局根路径
* @param {Object} arg.header - 全局header
* @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
* @param {String} arg.dataType = [json] - 全局默认的dataType
* @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType支付宝小程序不支持
* @param {Object} arg.custom - 全局默认的自定义参数
* @param {Number} arg.timeout - 全局默认的超时时间单位 ms默认60000H5(HBuilderX 2.9.9+)APP(HBuilderX 2.9.9+)微信小程序2.10.0支付宝小程序
* @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书默认true.仅App安卓端支持HBuilderX 2.3.3+
* @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证cookies默认false仅H5支持HBuilderX 2.6.15+
* @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4默认false App-Android 支持 (HBuilderX 2.8.0+)
* @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器默认statusCode >= 200 && statusCode < 300
*/
constructor(arg = {}) {
if (!isPlainObject(arg)) {
arg = {}
console.warn('设置全局参数必须接收一个Object')
}
this.config = clone({ ...defaults, ...arg })
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
/**
* @Function
* @param {Request~setConfigCallback} f - 设置全局默认配置
*/
setConfig(f) {
this.config = f(this.config)
}
middleware(config) {
config = mergeConfig(this.config, config)
const chain = [dispatchRequest, undefined]
let promise = Promise.resolve(config)
this.interceptors.request.forEach((interceptor) => {
chain.unshift(interceptor.fulfilled, interceptor.rejected)
})
this.interceptors.response.forEach((interceptor) => {
chain.push(interceptor.fulfilled, interceptor.rejected)
})
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise
}
/**
* @Function
* @param {Object} config - 请求配置项
* @prop {String} options.url - 请求路径
* @prop {Object} options.data - 请求参数
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json会尝试对返回的数据做一次 JSON.parse
* @prop {Object} [options.header = config.header] - 请求header
* @prop {Object} [options.method = config.method] - 请求方法
* @returns {Promise<unknown>}
*/
request(config = {}) {
return this.middleware(config)
}
get(url, options = {}) {
return this.middleware({
url,
method: 'GET',
...options
})
}
post(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'POST',
...options
})
}
// #ifndef MP-ALIPAY
put(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'PUT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
delete(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'DELETE',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
connect(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'CONNECT',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN || MP-BAIDU
head(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'HEAD',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
options(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'OPTIONS',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
trace(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'TRACE',
...options
})
}
// #endif
upload(url, config = {}) {
config.url = url
config.method = 'UPLOAD'
return this.middleware(config)
}
download(url, config = {}) {
config.url = url
config.method = 'DOWNLOAD'
return this.middleware(config)
}
}
/**
* setConfig回调
* @return {Object} - 返回操作后的config
* @callback Request~setConfigCallback
* @param {Object} config - 全局默认config
*/

View File

@ -0,0 +1,20 @@
'use strict'
import isAbsoluteURL from '../helpers/isAbsoluteURL'
import combineURLs from '../helpers/combineURLs'
/**
* Creates a new URL by combining the baseURL with the requestedURL,
* only when the requestedURL is not already an absolute URL.
* If the requestURL is absolute, this function returns the requestedURL untouched.
*
* @param {string} baseURL The base URL
* @param {string} requestedURL Absolute or relative URL to combine
* @returns {string} The combined full path
*/
export default function buildFullPath(baseURL, requestedURL) {
if (baseURL && !isAbsoluteURL(requestedURL)) {
return combineURLs(baseURL, requestedURL)
}
return requestedURL
}

View File

@ -0,0 +1,29 @@
/**
* 默认的全局配置
*/
export default {
baseURL: '',
header: {},
method: 'GET',
dataType: 'json',
// #ifndef MP-ALIPAY
responseType: 'text',
// #endif
custom: {},
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
timeout: 60000,
// #endif
// #ifdef APP-PLUS
sslVerify: true,
// #endif
// #ifdef H5
withCredentials: false,
// #endif
// #ifdef APP-PLUS
firstIpv4: false,
// #endif
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300
}
}

View File

@ -0,0 +1,3 @@
import adapter from '../adapters/index'
export default (config) => adapter(config)

View File

@ -0,0 +1,103 @@
import { deepMerge, isUndefined } from '../utils'
/**
* 合并局部配置优先的配置如果局部有该配置项则用局部如果全局有该配置项则用全局
* @param {Array} keys - 配置项
* @param {Object} globalsConfig - 当前的全局配置
* @param {Object} config2 - 局部配置
* @return {{}}
*/
const mergeKeys = (keys, globalsConfig, config2) => {
const config = {}
keys.forEach((prop) => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
} else if (!isUndefined(globalsConfig[prop])) {
config[prop] = globalsConfig[prop]
}
})
return config
}
/**
*
* @param globalsConfig - 当前实例的全局配置
* @param config2 - 当前的局部配置
* @return - 合并后的配置
*/
export default (globalsConfig, config2 = {}) => {
const method = config2.method || globalsConfig.method || 'GET'
let config = {
baseURL: globalsConfig.baseURL || '',
method,
url: config2.url || '',
params: config2.params || {},
custom: { ...(globalsConfig.custom || {}), ...(config2.custom || {}) },
header: deepMerge(globalsConfig.header || {}, config2.header || {})
}
const defaultToConfig2Keys = ['getTask', 'validateStatus']
config = { ...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2) }
// eslint-disable-next-line no-empty
if (method === 'DOWNLOAD') {
// #ifdef H5 || APP-PLUS
if (!isUndefined(config2.timeout)) {
config.timeout = config2.timeout
} else if (!isUndefined(globalsConfig.timeout)) {
config.timeout = globalsConfig.timeout
}
// #endif
} else if (method === 'UPLOAD') {
delete config.header['content-type']
delete config.header['Content-Type']
const uploadKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef MP-ALIPAY
'fileType',
// #endif
// #ifdef H5
'file',
// #endif
'filePath',
'name',
// #ifdef H5 || APP-PLUS
'timeout',
// #endif
'formData'
]
uploadKeys.forEach((prop) => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
// #ifdef H5 || APP-PLUS
if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
config.timeout = globalsConfig.timeout
}
// #endif
} else {
const defaultsKeys = [
'data',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4'
// #endif
]
config = { ...config, ...mergeKeys(defaultsKeys, globalsConfig, config2) }
}
return config
}

View File

@ -0,0 +1,16 @@
/**
* Resolve or reject a Promise based on response status.
*
* @param {Function} resolve A function that resolves the promise.
* @param {Function} reject A function that rejects the promise.
* @param {object} response The response.
*/
export default function settle(resolve, reject, response) {
const { validateStatus } = response.config
const status = response.statusCode
if (status && (!validateStatus || validateStatus(status))) {
resolve(response)
} else {
reject(response)
}
}

View File

@ -0,0 +1,69 @@
'use strict'
import * as utils from '../utils'
function encode(val) {
return encodeURIComponent(val)
.replace(/%40/gi, '@')
.replace(/%3A/gi, ':')
.replace(/%24/g, '$')
.replace(/%2C/gi, ',')
.replace(/%20/g, '+')
.replace(/%5B/gi, '[')
.replace(/%5D/gi, ']')
}
/**
* Build a URL by appending params to the end
*
* @param {string} url The base of the url (e.g., http://www.google.com)
* @param {object} [params] The params to be appended
* @returns {string} The formatted url
*/
export default function buildURL(url, params) {
/* eslint no-param-reassign:0 */
if (!params) {
return url
}
let serializedParams
if (utils.isURLSearchParams(params)) {
serializedParams = params.toString()
} else {
const parts = []
utils.forEach(params, (val, key) => {
if (val === null || typeof val === 'undefined') {
return
}
if (utils.isArray(val)) {
key = `${key}[]`
} else {
val = [val]
}
utils.forEach(val, (v) => {
if (utils.isDate(v)) {
v = v.toISOString()
} else if (utils.isObject(v)) {
v = JSON.stringify(v)
}
parts.push(`${encode(key)}=${encode(v)}`)
})
})
serializedParams = parts.join('&')
}
if (serializedParams) {
const hashmarkIndex = url.indexOf('#')
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex)
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}

View File

@ -0,0 +1,14 @@
'use strict'
/**
* Creates a new URL by combining the specified URLs
*
* @param {string} baseURL The base URL
* @param {string} relativeURL The relative URL
* @returns {string} The combined URL
*/
export default function combineURLs(baseURL, relativeURL) {
return relativeURL
? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}`
: baseURL
}

View File

@ -0,0 +1,14 @@
'use strict'
/**
* Determines whether the specified URL is absolute
*
* @param {string} url The URL to test
* @returns {boolean} True if the specified URL is absolute, otherwise false
*/
export default function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
}

View File

@ -0,0 +1,116 @@
type AnyObject = Record<string | number | symbol, any>
type HttpPromise<T> = Promise<HttpResponse<T>>;
type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask
export interface RequestTask {
abort: () => void;
offHeadersReceived: () => void;
onHeadersReceived: () => void;
}
export interface HttpRequestConfig<T = Tasks> {
/** 请求基地址 */
baseURL?: string;
/** 请求服务器接口地址 */
url?: string;
/** 请求查询参数,自动拼接为查询字符串 */
params?: AnyObject;
/** 请求体参数 */
data?: AnyObject;
/** 文件对应的 key */
name?: string;
/** HTTP 请求中其他额外的 form data */
formData?: AnyObject;
/** 要上传文件资源的路径。 */
filePath?: string;
/** 需要上传的文件列表。使用 files 时filePath 和 name 不生效App、H5 2.6.15+ */
files?: Array<{
name?: string;
file?: File;
uri: string;
}>;
/** 要上传的文件对象仅H52.6.15+)支持 */
file?: File;
/** 请求头信息 */
header?: AnyObject;
/** 请求方式 */
method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD";
/** 如果设为 json会尝试对返回的数据做一次 JSON.parse */
dataType?: string;
/** 设置响应的数据类型,支付宝小程序不支持 */
responseType?: "text" | "arraybuffer";
/** 自定义参数 */
custom?: AnyObject;
/** 超时时间仅微信小程序2.10.0)、支付宝小程序支持 */
timeout?: number;
/** DNS解析时优先使用ipv4仅 App-Android 支持 (HBuilderX 2.8.0+) */
firstIpv4?: boolean;
/** 验证 ssl 证书 仅5+App安卓端支持HBuilderX 2.3.3+ */
sslVerify?: boolean;
/** 跨域请求时是否携带凭证cookies仅H5支持HBuilderX 2.6.15+ */
withCredentials?: boolean;
/** 返回当前请求的task, options。请勿在此处修改options。 */
getTask?: (task: T, options: HttpRequestConfig<T>) => void;
/** 全局自定义验证器 */
validateStatus?: (statusCode: number) => boolean | void;
}
export interface HttpResponse<T = any> {
config: HttpRequestConfig;
statusCode: number;
cookies: Array<string>;
data: T;
errMsg: string;
header: AnyObject;
}
export interface HttpUploadResponse<T = any> {
config: HttpRequestConfig;
statusCode: number;
data: T;
errMsg: string;
}
export interface HttpDownloadResponse extends HttpResponse {
tempFilePath: string;
}
export interface HttpError {
config: HttpRequestConfig;
statusCode?: number;
cookies?: Array<string>;
data?: any;
errMsg: string;
header?: AnyObject;
}
export interface HttpInterceptorManager<V, E = V> {
use(
onFulfilled?: (config: V) => Promise<V> | V,
onRejected?: (config: E) => Promise<E> | E
): void;
eject(id: number): void;
}
export abstract class HttpRequestAbstract {
constructor(config?: HttpRequestConfig);
config: HttpRequestConfig;
interceptors: {
request: HttpInterceptorManager<HttpRequestConfig, HttpRequestConfig>;
response: HttpInterceptorManager<HttpResponse, HttpError>;
}
middleware<T = any>(config: HttpRequestConfig): HttpPromise<T>;
request<T = any>(config: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
get<T = any>(url: string, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
upload<T = any>(url: string, config?: HttpRequestConfig<UniApp.UploadTask>): HttpPromise<T>;
delete<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
head<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
post<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
put<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
connect<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
options<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
trace<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
download(url: string, config?: HttpRequestConfig<UniApp.DownloadTask>): Promise<HttpDownloadResponse>;
setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void;
}
declare class HttpRequest extends HttpRequestAbstract { }
export default HttpRequest;

View File

@ -0,0 +1,3 @@
import Request from './core/Request'
export default Request

View File

@ -0,0 +1,131 @@
'use strict'
// utils is a library of generic helper functions non-specific to axios
const { toString } = Object.prototype
/**
* Determine if a value is an Array
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Array, otherwise false
*/
export function isArray(val) {
return toString.call(val) === '[object Array]'
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
export function isObject(val) {
return val !== null && typeof val === 'object'
}
/**
* Determine if a value is a Date
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Date, otherwise false
*/
export function isDate(val) {
return toString.call(val) === '[object Date]'
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
export function isURLSearchParams(val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}
/**
* Iterate over an Array or an Object invoking a function for each item.
*
* If `obj` is an Array callback will be called passing
* the value, index, and complete array for each item.
*
* If 'obj' is an Object callback will be called passing
* the value, key, and complete object for each property.
*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
export function forEach(obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === 'undefined') {
return
}
// Force an array if not already something iterable
if (typeof obj !== 'object') {
/* eslint no-param-reassign:0 */
obj = [obj]
}
if (isArray(obj)) {
// Iterate over array values
for (let i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj)
}
} else {
// Iterate over object keys
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj)
}
}
}
}
/**
* 是否为boolean
* @param val
* @returns {boolean}
*/
export function isBoolean(val) {
return typeof val === 'boolean'
}
/**
* 是否为真正的对象{} new Object
* @param {any} obj - 检测的对象
* @returns {boolean}
*/
export function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
/**
* Function equal to merge with the difference being that no reference
* to original objects is kept.
*
* @see merge
* @param {Object} obj1 Object to merge
* @returns {Object} Result of all merge properties
*/
export function deepMerge(/* obj1, obj2, obj3, ... */) {
const result = {}
function assignValue(val, key) {
if (typeof result[key] === 'object' && typeof val === 'object') {
result[key] = deepMerge(result[key], val)
} else if (typeof val === 'object') {
result[key] = deepMerge({}, val)
} else {
result[key] = val
}
}
for (let i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue)
}
return result
}
export function isUndefined(val) {
return typeof val === 'undefined'
}

View File

@ -0,0 +1,264 @@
/* eslint-disable */
var clone = (function() {
'use strict';
function _instanceof(obj, type) {
return type != null && obj instanceof type;
}
var nativeMap;
try {
nativeMap = Map;
} catch(_) {
// maybe a reference error because no `Map`. Give it a dummy value that no
// value will ever be an instanceof.
nativeMap = function() {};
}
var nativeSet;
try {
nativeSet = Set;
} catch(_) {
nativeSet = function() {};
}
var nativePromise;
try {
nativePromise = Promise;
} catch(_) {
nativePromise = function() {};
}
/**
* Clones (copies) an Object using deep copying.
*
* This function supports circular references by default, but if you are certain
* there are no circular references in your object, you can save some CPU time
* by calling clone(obj, false).
*
* Caution: if `circular` is false and `parent` contains circular references,
* your program may enter an infinite loop and crash.
*
* @param `parent` - the object to be cloned
* @param `circular` - set to true if the object to be cloned may contain
* circular references. (optional - true by default)
* @param `depth` - set to a number if the object is only to be cloned to
* a particular depth. (optional - defaults to Infinity)
* @param `prototype` - sets the prototype to be used when cloning an object.
* (optional - defaults to parent prototype).
* @param `includeNonEnumerable` - set to true if the non-enumerable properties
* should be cloned as well. Non-enumerable properties on the prototype
* chain will be ignored. (optional - false by default)
*/
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
if (typeof circular === 'object') {
depth = circular.depth;
prototype = circular.prototype;
includeNonEnumerable = circular.includeNonEnumerable;
circular = circular.circular;
}
// maintain two arrays for circular references, where corresponding parents
// and children have the same index
var allParents = [];
var allChildren = [];
var useBuffer = typeof Buffer != 'undefined';
if (typeof circular == 'undefined')
circular = true;
if (typeof depth == 'undefined')
depth = Infinity;
// recurse this function so we don't reset allParents and allChildren
function _clone(parent, depth) {
// cloning null always returns null
if (parent === null)
return null;
if (depth === 0)
return parent;
var child;
var proto;
if (typeof parent != 'object') {
return parent;
}
if (_instanceof(parent, nativeMap)) {
child = new nativeMap();
} else if (_instanceof(parent, nativeSet)) {
child = new nativeSet();
} else if (_instanceof(parent, nativePromise)) {
child = new nativePromise(function (resolve, reject) {
parent.then(function(value) {
resolve(_clone(value, depth - 1));
}, function(err) {
reject(_clone(err, depth - 1));
});
});
} else if (clone.__isArray(parent)) {
child = [];
} else if (clone.__isRegExp(parent)) {
child = new RegExp(parent.source, __getRegExpFlags(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (clone.__isDate(parent)) {
child = new Date(parent.getTime());
} else if (useBuffer && Buffer.isBuffer(parent)) {
if (Buffer.from) {
// Node.js >= 5.10.0
child = Buffer.from(parent);
} else {
// Older Node.js versions
child = new Buffer(parent.length);
parent.copy(child);
}
return child;
} else if (_instanceof(parent, Error)) {
child = Object.create(parent);
} else {
if (typeof prototype == 'undefined') {
proto = Object.getPrototypeOf(parent);
child = Object.create(proto);
}
else {
child = Object.create(prototype);
proto = prototype;
}
}
if (circular) {
var index = allParents.indexOf(parent);
if (index != -1) {
return allChildren[index];
}
allParents.push(parent);
allChildren.push(child);
}
if (_instanceof(parent, nativeMap)) {
parent.forEach(function(value, key) {
var keyChild = _clone(key, depth - 1);
var valueChild = _clone(value, depth - 1);
child.set(keyChild, valueChild);
});
}
if (_instanceof(parent, nativeSet)) {
parent.forEach(function(value) {
var entryChild = _clone(value, depth - 1);
child.add(entryChild);
});
}
for (var i in parent) {
var attrs = Object.getOwnPropertyDescriptor(parent, i);
if (attrs) {
child[i] = _clone(parent[i], depth - 1);
}
try {
var objProperty = Object.getOwnPropertyDescriptor(parent, i);
if (objProperty.set === 'undefined') {
// no setter defined. Skip cloning this property
continue;
}
child[i] = _clone(parent[i], depth - 1);
} catch(e){
if (e instanceof TypeError) {
// when in strict mode, TypeError will be thrown if child[i] property only has a getter
// we can't do anything about this, other than inform the user that this property cannot be set.
continue
} else if (e instanceof ReferenceError) {
//this may happen in non strict mode
continue
}
}
}
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(parent);
for (var i = 0; i < symbols.length; i++) {
// Don't need to worry about cloning a symbol because it is a primitive,
// like a number or string.
var symbol = symbols[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
continue;
}
child[symbol] = _clone(parent[symbol], depth - 1);
Object.defineProperty(child, symbol, descriptor);
}
}
if (includeNonEnumerable) {
var allPropertyNames = Object.getOwnPropertyNames(parent);
for (var i = 0; i < allPropertyNames.length; i++) {
var propertyName = allPropertyNames[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
if (descriptor && descriptor.enumerable) {
continue;
}
child[propertyName] = _clone(parent[propertyName], depth - 1);
Object.defineProperty(child, propertyName, descriptor);
}
}
return child;
}
return _clone(parent, depth);
}
/**
* Simple flat clone using prototype, accepts only objects, usefull for property
* override on FLAT configuration object (no nested props).
*
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
* works.
*/
clone.clonePrototype = function clonePrototype(parent) {
if (parent === null)
return null;
var c = function () {};
c.prototype = parent;
return new c();
};
// private utility functions
function __objToStr(o) {
return Object.prototype.toString.call(o);
}
clone.__objToStr = __objToStr;
function __isDate(o) {
return typeof o === 'object' && __objToStr(o) === '[object Date]';
}
clone.__isDate = __isDate;
function __isArray(o) {
return typeof o === 'object' && __objToStr(o) === '[object Array]';
}
clone.__isArray = __isArray;
function __isRegExp(o) {
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
}
clone.__isRegExp = __isRegExp;
function __getRegExpFlags(re) {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
}
clone.__getRegExpFlags = __getRegExpFlags;
return clone;
})();
export default clone

View File

@ -0,0 +1,13 @@
export default {
props: {
lang: String,
sessionFrom: String,
sendMessageTitle: String,
sendMessagePath: String,
sendMessageImg: String,
showMessageCard: Boolean,
appParameter: String,
formType: String,
openType: String
}
}

View File

@ -0,0 +1,166 @@
import * as index from '../function/index.js';
import * as test from '../function/test.js';
export default {
// 定义每个组件都可能需要用到的外部样式以及类名
props: {
// 每个组件都有的父组件传递的样式,可以为字符串或者对象形式
customStyle: {
type: [Object, String],
default: () => ({})
},
customClass: {
type: String,
default: ''
},
// 跳转的页面路径
url: {
type: String,
default: ''
},
// 页面跳转的类型
linkType: {
type: String,
default: 'navigateTo'
}
},
data() {
return {}
},
onLoad() {
// getRect挂载到$uv上因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出
this.$uv.getRect = this.$uvGetRect
},
created() {
// 组件当中只有created声明周期为了能在组件使用故也在created中将方法挂载到$uv
this.$uv.getRect = this.$uvGetRect
},
computed: {
$uv() {
return {
...index,
test,
unit: uni?.$uv?.config?.unit
}
},
/**
* 生成bem规则类名
* 由于微信小程序H5nvue之间绑定class的差异无法通过:class="[bem()]"的形式进行同用
* 故采用如下折中做法最后返回的是数组一般平台或字符串支付宝和字节跳动平台类似['a', 'b', 'c']'a b c'的形式
* @param {String} name 组件名称
* @param {Array} fixed 一直会存在的类名
* @param {Array} change 会根据变量值为true或者false而出现或者隐藏的类名
* @returns {Array|string}
*/
bem() {
return function(name, fixed, change) {
// 类名前缀
const prefix = `uv-${name}--`
const classes = {}
if (fixed) {
fixed.map((item) => {
// 这里的类名,会一直存在
classes[prefix + this[item]] = true
})
}
if (change) {
change.map((item) => {
// 这里的类名会根据this[item]的值为true或者false而进行添加或者移除某一个类
this[item] ? (classes[prefix + item] = this[item]) : (delete classes[prefix + item])
})
}
return Object.keys(classes)
// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
// #ifdef MP-ALIPAY || MP-TOUTIAO || MP-LARK || MP-BAIDU
.join(' ')
// #endif
}
}
},
methods: {
// 跳转某一个页面
openPage(urlKey = 'url') {
const url = this[urlKey]
if (url) {
// 执行类似uni.navigateTo的方法
uni[this.linkType]({
url
})
}
},
// 查询节点信息
// 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸为支付宝的bug(2020-07-21)
// 解决办法为在组件根部再套一个没有任何作用的view元素
$uvGetRect(selector, all) {
return new Promise((resolve) => {
uni.createSelectorQuery()
.in(this)[all ? 'selectAll' : 'select'](selector)
.boundingClientRect((rect) => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect)
}
if (!all && rect) {
resolve(rect)
}
})
.exec()
})
},
getParentData(parentName = '') {
// 避免在created中去定义parent变量
if (!this.parent) this.parent = {}
// 这里的本质原理是,通过获取父组件实例(也即类似uv-radio的父组件uv-radio-group的this)
// 将父组件this中对应的参数赋值给本组件(uv-radio的this)的parentData对象中对应的属性
// 之所以需要这么做是因为所有端中头条小程序不支持通过this.parent.xxx去监听父组件参数的变化
// 此处并不会自动更新子组件的数据而是依赖父组件uv-radio-group去监听data的变化手动调用更新子组件的方法去重新获取
this.parent = this.$uv.$parent.call(this, parentName)
if (this.parent.children) {
// 如果父组件的children不存在本组件的实例才将本实例添加到父组件的children中
this.parent.children.indexOf(this) === -1 && this.parent.children.push(this)
}
if (this.parent && this.parentData) {
// 历遍parentData中的属性将parent中的同名属性赋值给parentData
Object.keys(this.parentData).map((key) => {
this.parentData[key] = this.parent[key]
})
}
},
// 阻止事件冒泡
preventEvent(e) {
e && typeof(e.stopPropagation) === 'function' && e.stopPropagation()
},
// 空操作
noop(e) {
this.preventEvent(e)
}
},
onReachBottom() {
uni.$emit('uvOnReachBottom')
},
beforeDestroy() {
// 判断当前页面是否存在parent和chldren一般在checkbox和checkbox-group父子联动的场景会有此情况
// 组件销毁时移除子组件在父组件children数组中的实例释放资源避免数据混乱
if (this.parent && test.array(this.parent.children)) {
// 组件销毁时移除父组件中的children数组中对应的实例
const childrenList = this.parent.children
childrenList.map((child, index) => {
// 如果相等,则移除
if (child === this) {
childrenList.splice(index, 1)
}
})
}
},
// 兼容vue3
unmounted() {
if (this.parent && test.array(this.parent.children)) {
// 组件销毁时移除父组件中的children数组中对应的实例
const childrenList = this.parent.children
childrenList.map((child, index) => {
// 如果相等,则移除
if (child === this) {
childrenList.splice(index, 1)
}
})
}
}
}

View File

@ -0,0 +1,8 @@
export default {
// #ifdef MP-WEIXIN
// 将自定义节点设置成虚拟的去掉自定义组件包裹层更加接近Vue组件的表现能更好的使用flex属性
options: {
virtualHost: true
}
// #endif
}

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